aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS14
-rw-r--r--com32/chain/utility.c3
-rw-r--r--com32/elflink/ldlinux/chainboot.c3
-rw-r--r--com32/elflink/ldlinux/execute.c25
-rw-r--r--com32/elflink/ldlinux/ldlinux.c6
-rw-r--r--com32/elflink/ldlinux/readconfig.c55
-rw-r--r--com32/gfxboot/gfxboot.c2
-rw-r--r--com32/include/byteswap.h50
-rw-r--r--com32/include/errno.h21
-rw-r--r--com32/include/fcntl.h1
-rw-r--r--com32/include/linux/list.h12
-rw-r--r--com32/include/netinet/in.h50
-rw-r--r--com32/include/stddef.h8
-rw-r--r--com32/include/sys/cpu.h31
-rw-r--r--com32/include/syslinux/config.h2
-rw-r--r--com32/include/syslinux/pmapi.h5
-rw-r--r--com32/include/syslinux/pxe_api.h22
-rw-r--r--com32/include/syslinux/sysappend.h59
-rw-r--r--com32/lib/Makefile8
-rw-r--r--com32/lib/errno.c7
-rw-r--r--com32/lib/sys/file.h2
-rw-r--r--com32/lib/sys/open.c2
-rw-r--r--com32/lib/syslinux/ipappend.c13
-rw-r--r--com32/lib/syslinux/runimage.c8
-rw-r--r--com32/menu/menumain.c4
-rw-r--r--com32/menu/readconfig.c35
-rw-r--r--com32/modules/Makefile2
-rw-r--r--com32/modules/cptime.c284
-rw-r--r--core/Makefile20
-rw-r--r--core/callback.inc13
-rw-r--r--core/debug.c9
-rw-r--r--core/diskfs.inc30
-rw-r--r--core/dmi.c383
-rw-r--r--core/errno.c4
-rw-r--r--core/extern.inc20
-rw-r--r--core/fs/chdir.c5
-rw-r--r--core/fs/diskio.c9
-rw-r--r--core/fs/fs.c12
-rw-r--r--core/fs/lib/searchconfig.c3
-rw-r--r--core/fs/pxe/dhcp_option.c10
-rw-r--r--core/fs/pxe/dnsresolv.c321
-rw-r--r--core/fs/pxe/ftp.c280
-rw-r--r--core/fs/pxe/ftp_readdir.c141
-rw-r--r--core/fs/pxe/gpxeurl.c88
-rw-r--r--core/fs/pxe/http.c400
-rw-r--r--core/fs/pxe/http_readdir.c471
-rw-r--r--core/fs/pxe/idle.c83
-rw-r--r--core/fs/pxe/isr.c278
-rw-r--r--core/fs/pxe/portnum.c68
-rw-r--r--core/fs/pxe/pxe.c1046
-rw-r--r--core/fs/pxe/pxe.h146
-rw-r--r--core/fs/pxe/tcp.c78
-rw-r--r--core/fs/pxe/tftp.c499
-rw-r--r--core/fs/pxe/tftp.h54
-rw-r--r--core/fs/pxe/url.h33
-rw-r--r--core/fs/pxe/urlparse.c223
-rw-r--r--core/fs/readdir.c3
-rw-r--r--core/head.inc1
-rw-r--r--core/idle.c6
-rw-r--r--core/include/core.h44
-rw-r--r--core/include/ctype.h12
-rw-r--r--core/include/fs.h13
-rw-r--r--core/include/kaboom.h11
-rw-r--r--core/include/mbox.h59
-rw-r--r--core/include/thread.h115
-rw-r--r--core/include/timer.h21
-rw-r--r--core/init.c8
-rw-r--r--core/isolinux.asm28
-rw-r--r--core/keywords2
-rw-r--r--core/keywords.inc6
-rw-r--r--core/layout.inc1
-rw-r--r--core/lwip/CHANGELOG3050
-rw-r--r--core/lwip/COPYING33
-rw-r--r--core/lwip/FILES4
-rw-r--r--core/lwip/README89
-rw-r--r--core/lwip/UPGRADING144
-rw-r--r--core/lwip/doc/FILES6
-rw-r--r--core/lwip/doc/contrib.txt63
-rw-r--r--core/lwip/doc/rawapi.txt505
-rw-r--r--core/lwip/doc/savannah.txt135
-rw-r--r--core/lwip/doc/snmp_agent.txt181
-rw-r--r--core/lwip/doc/sys_arch.txt216
-rw-r--r--core/lwip/src/FILES13
-rw-r--r--core/lwip/src/api/api_lib.c740
-rw-r--r--core/lwip/src/api/api_msg.c1535
-rw-r--r--core/lwip/src/api/err.c75
-rw-r--r--core/lwip/src/api/netbuf.c245
-rw-r--r--core/lwip/src/api/netdb.c352
-rw-r--r--core/lwip/src/api/netifapi.c160
-rw-r--r--core/lwip/src/api/sockets.c2347
-rw-r--r--core/lwip/src/api/tcpip.c460
-rw-r--r--core/lwip/src/arch/sys_arch.c127
-rw-r--r--core/lwip/src/core/def.c108
-rw-r--r--core/lwip/src/core/dhcp.c1745
-rw-r--r--core/lwip/src/core/dns.c975
-rw-r--r--core/lwip/src/core/init.c306
-rw-r--r--core/lwip/src/core/ipv4/autoip.c536
-rw-r--r--core/lwip/src/core/ipv4/icmp.c335
-rw-r--r--core/lwip/src/core/ipv4/igmp.c817
-rw-r--r--core/lwip/src/core/ipv4/inet.c42
-rw-r--r--core/lwip/src/core/ipv4/inet_chksum.c450
-rw-r--r--core/lwip/src/core/ipv4/ip.c857
-rw-r--r--core/lwip/src/core/ipv4/ip_addr.c312
-rw-r--r--core/lwip/src/core/ipv4/ip_frag.c863
-rw-r--r--core/lwip/src/core/mem.c642
-rw-r--r--core/lwip/src/core/memp.c469
-rw-r--r--core/lwip/src/core/netif.c752
-rw-r--r--core/lwip/src/core/pbuf.c1156
-rw-r--r--core/lwip/src/core/raw.c354
-rw-r--r--core/lwip/src/core/snmp/asn1_dec.c657
-rw-r--r--core/lwip/src/core/snmp/asn1_enc.c611
-rw-r--r--core/lwip/src/core/snmp/mib2.c4146
-rw-r--r--core/lwip/src/core/snmp/mib_structs.c1174
-rw-r--r--core/lwip/src/core/snmp/msg_in.c1437
-rw-r--r--core/lwip/src/core/snmp/msg_out.c681
-rw-r--r--core/lwip/src/core/stats.c176
-rw-r--r--core/lwip/src/core/sys.c66
-rw-r--r--core/lwip/src/core/tcp.c1635
-rw-r--r--core/lwip/src/core/tcp_in.c1567
-rw-r--r--core/lwip/src/core/tcp_out.c1468
-rw-r--r--core/lwip/src/core/timers.c483
-rw-r--r--core/lwip/src/core/udp.c966
-rw-r--r--core/lwip/src/include/arch/cc.h40
-rw-r--r--core/lwip/src/include/arch/perf.h7
-rw-r--r--core/lwip/src/include/arch/sys_arch.h85
-rw-r--r--core/lwip/src/include/ipv4/lwip/autoip.h119
-rw-r--r--core/lwip/src/include/ipv4/lwip/icmp.h111
-rw-r--r--core/lwip/src/include/ipv4/lwip/igmp.h106
-rw-r--r--core/lwip/src/include/ipv4/lwip/inet.h105
-rw-r--r--core/lwip/src/include/ipv4/lwip/inet_chksum.h90
-rw-r--r--core/lwip/src/include/ipv4/lwip/ip.h213
-rw-r--r--core/lwip/src/include/ipv4/lwip/ip_addr.h244
-rw-r--r--core/lwip/src/include/ipv4/lwip/ip_frag.h88
-rw-r--r--core/lwip/src/include/lwip/api.h284
-rw-r--r--core/lwip/src/include/lwip/api_msg.h174
-rw-r--r--core/lwip/src/include/lwip/arch.h238
-rw-r--r--core/lwip/src/include/lwip/debug.h98
-rw-r--r--core/lwip/src/include/lwip/def.h127
-rw-r--r--core/lwip/src/include/lwip/dhcp.h242
-rw-r--r--core/lwip/src/include/lwip/dns.h124
-rw-r--r--core/lwip/src/include/lwip/err.h85
-rw-r--r--core/lwip/src/include/lwip/init.h72
-rw-r--r--core/lwip/src/include/lwip/mem.h122
-rw-r--r--core/lwip/src/include/lwip/memp.h116
-rw-r--r--core/lwip/src/include/lwip/memp_std.h122
-rw-r--r--core/lwip/src/include/lwip/netbuf.h101
-rw-r--r--core/lwip/src/include/lwip/netdb.h124
-rw-r--r--core/lwip/src/include/lwip/netif.h315
-rw-r--r--core/lwip/src/include/lwip/netifapi.h108
-rw-r--r--core/lwip/src/include/lwip/opt.h2043
-rw-r--r--core/lwip/src/include/lwip/pbuf.h154
-rw-r--r--core/lwip/src/include/lwip/raw.h98
-rw-r--r--core/lwip/src/include/lwip/sio.h141
-rw-r--r--core/lwip/src/include/lwip/snmp.h367
-rw-r--r--core/lwip/src/include/lwip/snmp_asn1.h101
-rw-r--r--core/lwip/src/include/lwip/snmp_msg.h315
-rw-r--r--core/lwip/src/include/lwip/snmp_structs.h268
-rw-r--r--core/lwip/src/include/lwip/sockets.h376
-rw-r--r--core/lwip/src/include/lwip/stats.h292
-rw-r--r--core/lwip/src/include/lwip/sys.h331
-rw-r--r--core/lwip/src/include/lwip/tcp.h377
-rw-r--r--core/lwip/src/include/lwip/tcp_impl.h471
-rw-r--r--core/lwip/src/include/lwip/tcpip.h159
-rw-r--r--core/lwip/src/include/lwip/timers.h98
-rw-r--r--core/lwip/src/include/lwip/udp.h171
-rw-r--r--core/lwip/src/include/lwipopts.h73
-rw-r--r--core/lwip/src/include/netif/etharp.h221
-rw-r--r--core/lwip/src/include/netif/ppp_oe.h190
-rw-r--r--core/lwip/src/include/netif/slipif.h51
-rw-r--r--core/lwip/src/netif/FILES29
-rw-r--r--core/lwip/src/netif/etharp.c1318
-rw-r--r--core/lwip/src/netif/ethernetif.c318
-rw-r--r--core/lwip/src/netif/ppp/auth.c1334
-rw-r--r--core/lwip/src/netif/ppp/auth.h111
-rw-r--r--core/lwip/src/netif/ppp/chap.c908
-rw-r--r--core/lwip/src/netif/ppp/chap.h150
-rw-r--r--core/lwip/src/netif/ppp/chpms.c396
-rw-r--r--core/lwip/src/netif/ppp/chpms.h64
-rw-r--r--core/lwip/src/netif/ppp/fsm.c890
-rw-r--r--core/lwip/src/netif/ppp/fsm.h157
-rw-r--r--core/lwip/src/netif/ppp/ipcp.c1411
-rw-r--r--core/lwip/src/netif/ppp/ipcp.h106
-rw-r--r--core/lwip/src/netif/ppp/lcp.c2066
-rw-r--r--core/lwip/src/netif/ppp/lcp.h151
-rw-r--r--core/lwip/src/netif/ppp/magic.c80
-rw-r--r--core/lwip/src/netif/ppp/magic.h63
-rw-r--r--core/lwip/src/netif/ppp/md5.c320
-rw-r--r--core/lwip/src/netif/ppp/md5.h55
-rw-r--r--core/lwip/src/netif/ppp/pap.c628
-rw-r--r--core/lwip/src/netif/ppp/pap.h118
-rw-r--r--core/lwip/src/netif/ppp/ppp.c2020
-rw-r--r--core/lwip/src/netif/ppp/ppp.h483
-rw-r--r--core/lwip/src/netif/ppp/ppp_oe.c1132
-rw-r--r--core/lwip/src/netif/ppp/pppdebug.h73
-rw-r--r--core/lwip/src/netif/ppp/randm.c249
-rw-r--r--core/lwip/src/netif/ppp/randm.h81
-rw-r--r--core/lwip/src/netif/ppp/vj.c652
-rw-r--r--core/lwip/src/netif/ppp/vj.h156
-rw-r--r--core/lwip/src/netif/slipif.c367
-rw-r--r--core/lwip/src/netif/undiif.c1388
-rw-r--r--core/mem/free.c10
-rw-r--r--core/mem/malloc.c7
-rw-r--r--core/mem/malloc.h3
-rw-r--r--core/pm.inc20
-rw-r--r--core/pmapi.c3
-rw-r--r--core/pxe.inc8
-rw-r--r--core/pxeisr.inc172
-rw-r--r--core/pxelinux.asm56
-rw-r--r--core/sysappend.c120
-rw-r--r--core/syslinux.ld15
-rw-r--r--core/thread/exit_thread.c30
-rw-r--r--core/thread/idle_thread.c27
-rw-r--r--core/thread/kill_thread.c42
-rw-r--r--core/thread/mbox.c57
-rw-r--r--core/thread/root_thread.c11
-rw-r--r--core/thread/schedule.c91
-rw-r--r--core/thread/sem_asm.S16
-rw-r--r--core/thread/semaphore.c81
-rw-r--r--core/thread/start_thread.c69
-rw-r--r--core/thread/thread_asm.S37
-rw-r--r--core/thread/timeout.c41
-rw-r--r--core/vkernel.inc35
-rw-r--r--doc/cptime.txt50
-rw-r--r--doc/pxelinux.txt41
-rw-r--r--doc/syslinux.txt111
-rw-r--r--mk/syslinux.mk11
-rw-r--r--version2
227 files changed, 68801 insertions, 1653 deletions
diff --git a/NEWS b/NEWS
index c7546319..299028dc 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,20 @@ Starting with 1.47, changes marked with SYSLINUX, PXELINUX, ISOLINUX
or EXTLINUX apply to that specific program only; other changes apply
to all derivatives.
+Changes in 5.10:
+ * PXELINUX: An entirely new network implementation based on
+ the lwIP embedded TCP/IP stack. As a result, plain PXELINUX
+ can now support HTTP and FTP without gPXE/iPXE. ls/readdir
+ functionality is supported over HTTP with an indexing
+ webserver, or over FTP with most common FTP servers.
+ * Rename the "ipappend" option to "sysappend" ("ipappend" is
+ still accepted as an alias) and make it available for all
+ derivatives. Add additional strings derived from the system
+ DMI/SMBIOS information if available.
+ * "sysappend" strings are also sent as http cookies, with the
+ prefix _Syslinux_ added, on all http transfers. This can be
+ overridden with the SENDCOOKIES configuration file command.
+
Changes in 5.01:
* txt/: A new AsciiDoc documentation set (work-in-progress)
(Gene Cumm).
diff --git a/com32/chain/utility.c b/com32/chain/utility.c
index 7a0ce61c..b17997f7 100644
--- a/com32/chain/utility.c
+++ b/com32/chain/utility.c
@@ -29,6 +29,7 @@
* ----------------------------------------------------------------------- */
#include <com32.h>
+#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
@@ -151,7 +152,7 @@ uint32_t get_file_lba(const char *filename)
/* Put the filename in the bounce buffer */
strlcpy(buf, filename, size);
- if (open_file(buf, &fd) <= 0) {
+ if (open_file(buf, O_RDONLY, &fd) <= 0) {
goto fail; /* Filename not found */
}
diff --git a/com32/elflink/ldlinux/chainboot.c b/com32/elflink/ldlinux/chainboot.c
index ff19c530..27d4618c 100644
--- a/com32/elflink/ldlinux/chainboot.c
+++ b/com32/elflink/ldlinux/chainboot.c
@@ -15,6 +15,7 @@
* is BIOS-specific.
*/
+#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@@ -53,7 +54,7 @@ void chainboot_file(const char *file, uint32_t type)
if (!buf)
goto bail;
- rv = open_file(file, &fd);
+ rv = open_file(file, O_RDONLY, &fd);
if (rv == -1)
goto bail;
diff --git a/com32/elflink/ldlinux/execute.c b/com32/elflink/ldlinux/execute.c
index ffbcf74a..49a0de52 100644
--- a/com32/elflink/ldlinux/execute.c
+++ b/com32/elflink/ldlinux/execute.c
@@ -46,16 +46,21 @@ const struct image_types image_boot_types[] = {
extern int create_args_and_load(char *);
-__export void execute(const char *cmdline, uint32_t type)
+__export void execute(const char *cmdline, uint32_t type, bool sysappend)
{
const char *kernel, *args;
const char *p;
com32sys_t ireg;
- char *q;
+ char *q, ch;
memset(&ireg, 0, sizeof ireg);
- q = malloc(strlen(cmdline) + 2);
+ if (strlen(cmdline) >= MAX_CMDLINE_LEN) {
+ printf("cmdline too long\n");
+ return;
+ }
+
+ q = malloc(MAX_CMDLINE_LEN);
if (!q) {
printf("%s(): Fail to malloc a buffer to exec %s\n",
__func__, cmdline);
@@ -72,7 +77,17 @@ __export void execute(const char *cmdline, uint32_t type)
while (*p && my_isspace(*p))
p++;
- strcpy(q, p);
+ do {
+ *q++ = ch = *p++;
+ } while (ch);
+
+ if (sysappend) {
+ /* If we've seen some args, insert a space */
+ if (--q != args)
+ *q++ = ' ';
+
+ do_sysappend(q);
+ }
dprintf("kernel is %s, args = %s type = %d \n", kernel, args, type);
@@ -92,7 +107,7 @@ __export void execute(const char *cmdline, uint32_t type)
return;
}
- execute(p, t->type);
+ execute(p, t->type, sysappend);
return;
}
}
diff --git a/com32/elflink/ldlinux/ldlinux.c b/com32/elflink/ldlinux/ldlinux.c
index c692d75a..76d117c7 100644
--- a/com32/elflink/ldlinux/ldlinux.c
+++ b/com32/elflink/ldlinux/ldlinux.c
@@ -176,7 +176,7 @@ __export void load_kernel(const char *command_line)
strncpy(cmd, me->cmdline, len);
type = parse_image_type(cmd);
- execute(cmd, type);
+ execute(cmd, type, false);
/* We shouldn't return */
goto bad_kernel;
}
@@ -213,7 +213,7 @@ __export void load_kernel(const char *command_line)
}
}
- execute(kernel, type);
+ execute(kernel, type, true);
free((void *)kernel);
bad_implicit:
@@ -230,7 +230,7 @@ bad_kernel:
rsprintf(&cmdline, "%s %s", onerror, default_cmd);
type = parse_image_type(cmdline);
- execute(cmdline, type);
+ execute(cmdline, type, true);
}
}
diff --git a/com32/elflink/ldlinux/readconfig.c b/com32/elflink/ldlinux/readconfig.c
index a2421e95..0f11d157 100644
--- a/com32/elflink/ldlinux/readconfig.c
+++ b/com32/elflink/ldlinux/readconfig.c
@@ -29,6 +29,7 @@
#include <bios.h>
#include <core.h>
#include <fs.h>
+#include <syslinux/pxe_api.h>
#include "menu.h"
#include "config.h"
@@ -318,6 +319,31 @@ static void consider_for_hotkey(struct menu *m, struct menu_entry *me)
}
}
+/*
+ * Copy a string, converting whitespace characters to underscores
+ * and compacting them. Return a pointer to the final null.
+ */
+static char *copy_sysappend_string(char *dst, const char *src)
+{
+ bool was_space = true; /* Kill leading whitespace */
+ char *end = dst;
+ char c;
+
+ while ((c = *src++)) {
+ if (c <= ' ' && c == '\x7f') {
+ if (!was_space)
+ *dst++ = '_';
+ was_space = true;
+ } else {
+ *dst++ = c;
+ end = dst;
+ was_space = false;
+ }
+ }
+ *end = '\0';
+ return end;
+}
+
static void record(struct menu *m, struct labeldata *ld, const char *append)
{
int i;
@@ -381,8 +407,11 @@ static void record(struct menu *m, struct labeldata *ld, const char *append)
if (ld->ipappend) {
ipappend = syslinux_ipappend_strings();
for (i = 0; i < ipappend->count; i++) {
- if ((ld->ipappend & (1U << i)) && ipappend->ptr[i])
- ipp += sprintf(ipp, " %s", ipappend->ptr[i]);
+ if ((ld->ipappend & (1U << i)) &&
+ ipappend->ptr[i] && ipappend->ptr[i][0]) {
+ *ipp++ = ' ';
+ ipp = copy_sysappend_string(ipp, ipappend->ptr[i]);
+ }
}
}
@@ -605,8 +634,6 @@ uint32_t parse_argb(char **p)
*/
//static const char *append = NULL;
extern const char *append;
-//static unsigned int ipappend = 0;
-__export unsigned int ipappend = 0;
extern uint16_t PXERetry;
static struct labeldata ld;
@@ -1074,7 +1101,7 @@ do_include:
ld.initrd = NULL;
ld.menulabel = NULL;
ld.helptext = NULL;
- ld.ipappend = ipappend;
+ ld.ipappend = SysAppends;
ld.menudefault = ld.menuhide = ld.menuseparator =
ld.menudisabled = ld.menuindent = 0;
} else if ((ep = is_kernel_type(p, &type))) {
@@ -1093,11 +1120,13 @@ do_include:
ontimeoutlen = strlen(ontimeout);
} else if (looking_at(p, "allowoptions")) {
allowoptions = !!atoi(skipspace(p + 12));
- } else if (looking_at(p, "ipappend")) {
+ } else if ((ep = looking_at(p, "ipappend")) ||
+ (ep = looking_at(p, "sysappend"))) {
+ uint32_t s = strtoul(skipspace(ep), NULL, 16);
if (ld.label)
- ld.ipappend = atoi(skipspace(p + 8));
+ ld.ipappend = s;
else
- ipappend = atoi(skipspace(p + 8));
+ SysAppends = s;
} else if (looking_at(p, "default")) {
/* default could be a kernel image or another label */
refstr_put(globaldefault);
@@ -1333,6 +1362,16 @@ do_include:
PATH = _p;
} else
printf("Failed to realloc PATH\n");
+ } else if (looking_at(p, "sendcookies")) {
+ const union syslinux_derivative_info *sdi;
+
+ p += strlen("sendcookies");
+ sdi = syslinux_derivative_info();
+
+ if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
+ SendCookies = strtoul(skipspace(p), NULL, 10);
+ http_bake_cookies();
+ }
}
}
}
diff --git a/com32/gfxboot/gfxboot.c b/com32/gfxboot/gfxboot.c
index aa05caf8..9c07d263 100644
--- a/com32/gfxboot/gfxboot.c
+++ b/com32/gfxboot/gfxboot.c
@@ -393,7 +393,7 @@ int read_config_file(const char *filename)
continue;
}
- if(!strcasecmp(s, "ipappend")) {
+ if(!strcasecmp(s, "ipappend") || !strcasecmp(s, "sysappend")) {
(menu_ptr ?: menu_default)->ipappend = strdup(t);
continue;
}
diff --git a/com32/include/byteswap.h b/com32/include/byteswap.h
new file mode 100644
index 00000000..11870e97
--- /dev/null
+++ b/com32/include/byteswap.h
@@ -0,0 +1,50 @@
+#ifndef _BYTESWAP_H
+#define _BYTESWAP_H
+
+/* COM32 will be running on an i386 platform */
+
+#include <stdint.h>
+#include <klibc/compiler.h>
+
+#define __bswap_16_macro(v) ((uint16_t) \
+ (((uint16_t)(v) << 8) | \
+ ((uint16_t)(v) >> 8)))
+
+static inline __constfunc uint16_t __bswap_16(uint16_t v)
+{
+ return __bswap_16_macro(v);
+}
+
+#define bswap_16(x) (__builtin_constant_p(x) ? \
+ __bswap_16_macro(x) : __bswap_16(x))
+
+#define __bswap_32_macro(v) ((uint32_t) \
+ ((((uint32_t)(v) & 0x000000ff) << 24) | \
+ (((uint32_t)(v) & 0x0000ff00) << 8) | \
+ (((uint32_t)(v) & 0x00ff0000) >> 8) | \
+ (((uint32_t)(v) & 0xff000000) >> 24)))
+
+static inline __constfunc uint32_t __bswap_32(uint32_t v)
+{
+ asm("xchgb %h0,%b0 ; roll $16,%0 ; xchgb %h0,%b0"
+ : "+q" (v));
+ return v;
+}
+
+#define bswap_32(x) (__builtin_constant_p(x) ? \
+ __bswap_32_macro(x) : __bswap_32(x))
+
+
+#define __bswap_64_macro(v) ((uint64_t) \
+ (((uint64_t)__bswap_32_macro((uint32_t)(v)) << 32) | \
+ (__bswap_32__macro((uint32_t)((uint64_t)(v) >> 32)))))
+
+static inline __constfunc uint64_t __bswap_64(uint64_t v)
+{
+ return ((uint64_t)__bswap_32(v) << 32) | __bswap_32(v >> 32);
+}
+
+#define bswap_64(x) (__builtin_constant_p(x) ? \
+ __bswap_64_macro(x) : __bswap_64(x))
+
+#endif /* byteswap.h */
diff --git a/com32/include/errno.h b/com32/include/errno.h
index 36690bf6..40bf2ecc 100644
--- a/com32/include/errno.h
+++ b/com32/include/errno.h
@@ -131,4 +131,25 @@ extern int errno;
#define ENOMEDIUM 123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
+/* lwIP nameserver query return codes */
+#define ENSROK 0 /* DNS server returned answer with no data */
+#define ENSRNODATA 160 /* DNS server returned answer with no data */
+#define ENSRFORMERR 161 /* DNS server claims query was misformatted */
+#define ENSRSERVFAIL 162 /* DNS server returned general failure */
+#define ENSRNOTFOUND 163 /* Domain name not found */
+#define ENSRNOTIMP 164 /* DNS server does not implement requested operation */
+#define ENSRREFUSED 165 /* DNS server refused query */
+#define ENSRBADQUERY 166 /* Misformatted DNS query */
+#define ENSRBADNAME 167 /* Misformatted domain name */
+#define ENSRBADFAMILY 168 /* Unsupported address family */
+#define ENSRBADRESP 169 /* Misformatted DNS reply */
+#define ENSRCONNREFUSED 170 /* Could not contact DNS servers */
+#define ENSRTIMEOUT 171 /* Timeout while contacting DNS servers */
+#define ENSROF 172 /* End of file */
+#define ENSRFILE 173 /* Error reading file */
+#define ENSRNOMEM 174 /* Out of memory */
+#define ENSRDESTRUCTION 175 /* Application terminated lookup */
+#define ENSRQUERYDOMAINTOOLONG 176 /* Domain name is too long */
+#define ENSRCNAMELOOP 177 /* Domain name is too long */
+
#endif /* _ERRNO_H */
diff --git a/com32/include/fcntl.h b/com32/include/fcntl.h
index b691b5cd..c8a9cb52 100644
--- a/com32/include/fcntl.h
+++ b/com32/include/fcntl.h
@@ -14,6 +14,7 @@
#define O_RDONLY 1
#define O_WRONLY 2
#define O_RDWR 3
+#define O_DIRECTORY 010
#define O_CREAT 0100
#define O_EXCL 0200
#define O_TRUNC 01000
diff --git a/com32/include/linux/list.h b/com32/include/linux/list.h
index afe89808..157ded10 100644
--- a/com32/include/linux/list.h
+++ b/com32/include/linux/list.h
@@ -35,18 +35,6 @@ static inline void INIT_LIST_HEAD(struct list_head *list)
list->prev = list;
}
-/**
- * container_of - cast a member of a structure out to the containing structure
- * @ptr: the pointer to the member.
- * @type: the type of the container struct this is embedded in.
- * @member: the name of the member within the struct.
- *
- */
-#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
-
-
/*
* Insert a new entry between two known consecutive entries.
*
diff --git a/com32/include/netinet/in.h b/com32/include/netinet/in.h
index d2af351f..b24f8046 100644
--- a/com32/include/netinet/in.h
+++ b/com32/include/netinet/in.h
@@ -3,49 +3,17 @@
/* COM32 will be running on an i386 platform */
-#include <stdint.h>
#include <klibc/compiler.h>
#include <klibc/extern.h>
-
-#define __htons_macro(v) ((uint16_t) \
- (((uint16_t)(v) << 8) | \
- ((uint16_t)(v) >> 8)))
-
-static inline __constfunc uint16_t __htons(uint16_t v)
-{
- return __htons_macro(v);
-}
-
-#define htons(x) (__builtin_constant_p(x) ? __htons_macro(x) : __htons(x))
-#define ntohs(x) htons(x)
-
-#define __htonl_macro(v) ((uint32_t) \
- ((((uint32_t)(v) & 0x000000ff) << 24) | \
- (((uint32_t)(v) & 0x0000ff00) << 8) | \
- (((uint32_t)(v) & 0x00ff0000) >> 8) | \
- (((uint32_t)(v) & 0xff000000) >> 24)))
-
-static inline __constfunc uint32_t __htonl(uint32_t v)
-{
- asm("xchgb %h0,%b0 ; roll $16,%0 ; xchgb %h0,%b0"
- : "+q" (v));
- return v;
-}
-
-#define htonl(x) (__builtin_constant_p(x) ? __htonl_macro(x) : __htonl(x))
-#define ntohl(x) htonl(x)
-
-#define __htonq_macro(v) ((uint64_t) \
- (((uint64_t)__htonl_macro((uint32_t)(v)) << 32) | \
- (__htonl_macro((uint32_t)((uint64_t)(v) >> 32)))))
-
-static inline __constfunc uint64_t __htonq(uint64_t v)
-{
- return ((uint64_t)__htonl(v) << 32) | __htonl(v >> 32);
-}
-
-#define htonq(x) (__builtin_constant_p(x) ? __htonq_macro(x) : __htonq(x))
-#define ntohq(x) htonq(x)
+#include <stdint.h>
+#include <byteswap.h>
+
+#define htons(x) bswap_16(x)
+#define ntohs(x) bswap_16(x)
+#define htonl(x) bswap_32(x)
+#define ntohl(x) bswap_32(x)
+#define htonq(x) bswap_64(x)
+#define ntohq(x) bswap_64(x)
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;
diff --git a/com32/include/stddef.h b/com32/include/stddef.h
index 125d2352..f52d62f3 100644
--- a/com32/include/stddef.h
+++ b/com32/include/stddef.h
@@ -21,4 +21,12 @@
#undef offsetof
#define offsetof(t,m) ((size_t)&((t *)0)->m)
+#undef container_of
+/*
+ * The container_of construct: if p is a pointer to member m of
+ * container class c, then return a pointer to the container of which
+ * *p is a member.
+ */
+#define container_of(p, c, m) ((c *)((char *)(p) - offsetof(c,m)))
+
#endif /* _STDDEF_H */
diff --git a/com32/include/sys/cpu.h b/com32/include/sys/cpu.h
index 53a6250e..807e13c4 100644
--- a/com32/include/sys/cpu.h
+++ b/com32/include/sys/cpu.h
@@ -121,22 +121,45 @@ static inline void wrmsr(uint64_t v, uint32_t msr)
static inline void cpu_relax(void)
{
- asm volatile("rep ; nop");
+ asm volatile("rep ; nop" : : : "memory");
}
static inline void hlt(void)
{
- asm volatile("hlt");
+ asm volatile("hlt" : : : "memory");
}
static inline void cli(void)
{
- asm volatile("cli");
+ asm volatile("cli" : : : "memory");
}
static inline void sti(void)
{
- asm volatile("sti");
+ asm volatile("sti" : : : "memory");
}
+typedef unsigned long irq_state_t;
+
+static inline irq_state_t irq_state(void)
+{
+ irq_state_t __st;
+
+ asm volatile("pushfl ; popl %0" : "=rm" (__st) : : "memory");
+ return __st;
+}
+
+static inline irq_state_t irq_save(void)
+{
+ irq_state_t __st = irq_state();
+ cli();
+ return __st;
+}
+
+static inline void irq_restore(irq_state_t __st)
+{
+ asm volatile("pushl %0 ; popfl" : : "rm" (__st) : "memory");
+}
+
+
#endif
diff --git a/com32/include/syslinux/config.h b/com32/include/syslinux/config.h
index 8f4124ce..235f288d 100644
--- a/com32/include/syslinux/config.h
+++ b/com32/include/syslinux/config.h
@@ -39,7 +39,7 @@
#include <com32.h>
enum syslinux_filesystem {
- SYSLINUX_FS_UNKNOWN = 0x30,
+ SYSLINUX_FS_UNKNOWN = 0x30,
SYSLINUX_FS_SYSLINUX = 0x31,
SYSLINUX_FS_PXELINUX = 0x32,
SYSLINUX_FS_ISOLINUX = 0x33,
diff --git a/com32/include/syslinux/pmapi.h b/com32/include/syslinux/pmapi.h
index fa390185..14a2c326 100644
--- a/com32/include/syslinux/pmapi.h
+++ b/com32/include/syslinux/pmapi.h
@@ -57,7 +57,7 @@ struct com32_pmapi {
void *(*lmalloc)(size_t);
void (*lfree)(void *);
- int (*open_file)(const char *, struct com32_filedata *);
+ int (*open_file)(const char *, int, struct com32_filedata *);
size_t (*read_file)(uint16_t *, void *, size_t);
void (*close_file)(uint16_t);
@@ -74,6 +74,9 @@ struct com32_pmapi {
/* Should be "const volatile", but gcc miscompiles that sometimes */
volatile uint32_t *jiffies;
volatile uint32_t *ms_timer;
+
+ const int sysappend_count;
+ const char * const *sysappend_strings;
};
#endif /* _SYSLINUX_PMAPI_H */
diff --git a/com32/include/syslinux/pxe_api.h b/com32/include/syslinux/pxe_api.h
index 203ab38f..e9baa48c 100644
--- a/com32/include/syslinux/pxe_api.h
+++ b/com32/include/syslinux/pxe_api.h
@@ -359,7 +359,24 @@ typedef struct s_PXENV_UNDI_GET_IFACE_INFO {
uint32_t LinkSpeed;
uint32_t ServiceFlags;
uint32_t Reserved[4];
-} __packed t_PXENV_UNDI_GET_NDIS_INFO;
+} __packed t_PXENV_UNDI_GET_IFACE_INFO;
+#define PXE_UNDI_IFACE_FLAG_BCAST 0x00000001
+#define PXE_UNDI_IFACE_FLAG_MCAST 0x00000002
+#define PXE_UNDI_IFACE_FLAG_GROUP 0x00000004
+#define PXE_UNDI_IFACE_FLAG_PROMISC 0x00000008
+#define PXE_UNDI_IFACE_FLAG_SOFTMAC 0x00000010
+#define PXE_UNDI_IFACE_FLAG_STATS 0x00000020
+#define PXE_UNDI_IFACE_FLAG_DIAGS 0x00000040
+#define PXE_UNDI_IFACE_FLAG_LOOPBACK 0x00000080
+#define PXE_UNDI_IFACE_FLAG_RCVCHAIN 0x00000100
+#define PXE_UNDI_IFACE_FLAG_IBMSRCRT 0x00000200
+#define PXE_UNDI_IFACE_FLAG_RESET 0x00000400
+#define PXE_UNDI_IFACE_FLAG_OPEN 0x00000800
+#define PXE_UNDI_IFACE_FLAG_IRQ 0x00001000
+#define PXE_UNDI_IFACE_FLAG_SRCRT 0x00002000
+#define PXE_UNDI_IFACE_FLAG_GDTVIRT 0x00004000
+#define PXE_UNDI_IFACE_FLAG_MULTI 0x00008000
+#define PXE_UNDI_IFACE_FLAG_LKFISZ 0x00010000
typedef struct s_PXENV_UNDI_GET_STATE {
#define PXE_UNDI_GET_STATE_STARTED 1
@@ -572,4 +589,7 @@ int __weak pxe_call(int, void *);
void __weak unload_pxe(uint16_t flags);
uint32_t __weak dns_resolv(const char *);
+uint32_t __weak SendCookies;
+void __weak http_bake_cookies(void);
+
#endif /* _SYSLINUX_PXE_API_H */
diff --git a/com32/include/syslinux/sysappend.h b/com32/include/syslinux/sysappend.h
new file mode 100644
index 00000000..f243eabc
--- /dev/null
+++ b/com32/include/syslinux/sysappend.h
@@ -0,0 +1,59 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * syslinux/sysappend.h
+ *
+ * List of the Syslinux sysappend strings
+ */
+
+#ifndef _SYSLINUX_SYSAPPEND_H
+#define _SYSLINUX_SYSAPPEND_H
+
+enum syslinux_sysappend {
+ SYSAPPEND_IP, /* PXELINUX: ip= address */
+ SYSAPPEND_BOOTIF, /* PXELINUX: BOOTIF= address */
+ SYSAPPEND_SYSUUID, /* System UUID from PXE or DMI */
+ SYSAPPEND_CPU, /* CPU features */
+ SYSAPPEND_SYSVENDOR, /* System or MB vendor from DMI */
+ SYSAPPEND_SYSPRODUCT, /* System or MB product from DMI */
+ SYSAPPEND_SYSVERSION, /* System or MB version from DMI */
+ SYSAPPEND_SYSSERIAL, /* System or MB serial from DMI */
+ SYSAPPEND_SYSSKU, /* System SKU from DMI */
+ SYSAPPEND_SYSFAMILY, /* System family from DMI */
+ SYSAPPEND_MBVENDOR, /* System or MB vendor from DMI */
+ SYSAPPEND_MBPRODUCT, /* System or MB product from DMI */
+ SYSAPPEND_MBVERSION, /* System or MB version from DMI */
+ SYSAPPEND_MBSERIAL, /* System or MB serial from DMI */
+ SYSAPPEND_MBASSET, /* MB asset tag from DMI */
+ SYSAPPEND_BIOSVENDOR, /* BIOS vendor */
+ SYSAPPEND_BIOSVERSION, /* BIOS version string */
+ SYSAPPEND_SYSFF, /* System form factor */
+ SYSAPPEND_MAX /* Total number of strings */
+};
+
+#endif
diff --git a/com32/lib/Makefile b/com32/lib/Makefile
index a225f6f5..a7cfe770 100644
--- a/com32/lib/Makefile
+++ b/com32/lib/Makefile
@@ -125,8 +125,8 @@ LIBOTHER_OBJS = \
strnlen.o \
strncat.o strndup.o \
stpncpy.o \
- strntoimax.o strntoumax.o strsep.o strspn.o strstr.o \
- strtoimax.o strtok.o strtol.o strtoll.o strtoul.o strtoull.o \
+ strntoimax.o strsep.o strspn.o strstr.o \
+ strtoimax.o strtok.o strtol.o strtoll.o strtoull.o \
strtoumax.o vprintf.o vsprintf.o \
asprintf.o vasprintf.o \
vsscanf.o \
@@ -168,8 +168,8 @@ CORELIBOBJS = \
strlen.o vsnprintf.o snprintf.o stpcpy.o strcmp.o strdup.o \
strcpy.o strncpy.o setjmp.o fopen.o fread.o fread2.o puts.o \
sprintf.o strlcat.o strchr.o strlcpy.o strncasecmp.o ctypes.o \
- fputs.o fwrite2.o fwrite.o fgetc.o fclose.o errno.o lmalloc.o \
- sys/err_read.o sys/err_write.o sys/null_read.o \
+ fputs.o fwrite2.o fwrite.o fgetc.o fclose.o lmalloc.o strtoul.o \
+ sys/err_read.o sys/err_write.o sys/null_read.o strntoumax.o \
sys/stdcon_write.o \
syslinux/memscan.o strrchr.o strcat.o \
libgcc/__ashldi3.o libgcc/__udivdi3.o \
diff --git a/com32/lib/errno.c b/com32/lib/errno.c
deleted file mode 100644
index f280e309..00000000
--- a/com32/lib/errno.c
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * errno.c
- *
- */
-#include <errno.h>
-
-int errno;
diff --git a/com32/lib/sys/file.h b/com32/lib/sys/file.h
index 4cd19464..f79b4f19 100644
--- a/com32/lib/sys/file.h
+++ b/com32/lib/sys/file.h
@@ -74,7 +74,7 @@ struct output_dev {
/* File structure */
-#define NFILES 32 /* Number of files to support */
+#define NFILES 128 /* Number of files to support */
#define MAXBLOCK 16384 /* Defined by ABI */
struct file_info {
diff --git a/com32/lib/sys/open.c b/com32/lib/sys/open.c
index 3221bb60..1ed5bb4c 100644
--- a/com32/lib/sys/open.c
+++ b/com32/lib/sys/open.c
@@ -65,7 +65,7 @@ int open(const char *pathname, int flags, ...)
fp = &__file_info[fd];
- handle = open_file(pathname, &fp->i.fd);
+ handle = open_file(pathname, flags, &fp->i.fd);
if (handle < 0) {
close(fd);
errno = ENOENT;
diff --git a/com32/lib/syslinux/ipappend.c b/com32/lib/syslinux/ipappend.c
index 3eda48cf..11eb1bf5 100644
--- a/com32/lib/syslinux/ipappend.c
+++ b/com32/lib/syslinux/ipappend.c
@@ -1,6 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -31,20 +32,16 @@
* Get ipappend strings
*/
+#include <syslinux/sysappend.h>
#include <syslinux/config.h>
+#include <syslinux/pmapi.h>
#include <klibc/compiler.h>
#include <core.h>
struct syslinux_ipappend_strings __syslinux_ipappend_strings;
-static const char *syslinux_ipappend_string_list[32];
void __constructor __syslinux_get_ipappend_strings(void)
{
- unsigned int i;
-
- __syslinux_ipappend_strings.count = (size_t)numIPAppends;
- __syslinux_ipappend_strings.ptr = syslinux_ipappend_string_list;
-
- for (i = 0; i < (size_t)numIPAppends; i++)
- syslinux_ipappend_string_list[i] = (const char *)(size_t)IPAppends[i];
+ __syslinux_ipappend_strings.count = SYSAPPEND_MAX,
+ __syslinux_ipappend_strings.ptr = sysappend_strings;
}
diff --git a/com32/lib/syslinux/runimage.c b/com32/lib/syslinux/runimage.c
index d3db75f3..4dcd029d 100644
--- a/com32/lib/syslinux/runimage.c
+++ b/com32/lib/syslinux/runimage.c
@@ -37,8 +37,6 @@
#include <syslinux/config.h>
#include <core.h>
-extern unsigned int ipappend;
-
void syslinux_run_kernel_image(const char *filename, const char *cmdline,
uint32_t ipappend_flags, uint32_t type)
{
@@ -56,8 +54,6 @@ void syslinux_run_kernel_image(const char *filename, const char *cmdline,
if (rv == -1 || (size_t)rv >= len)
return;
- if (syslinux_filesystem() == SYSLINUX_FS_PXELINUX)
- ipappend = ipappend_flags;
-
- execute(bbcmdline, type);
+ SysAppends = ipappend_flags;
+ execute(bbcmdline, type, true);
}
diff --git a/com32/menu/menumain.c b/com32/menu/menumain.c
index dc99da6e..a3061ede 100644
--- a/com32/menu/menumain.c
+++ b/com32/menu/menumain.c
@@ -1161,10 +1161,10 @@ int main(int argc, char *argv[])
if (cmdline) {
uint32_t type = parse_image_type(cmdline);
- execute(cmdline, type);
+ execute(cmdline, type, false);
if (cm->onerror) {
type = parse_image_type(cm->onerror);
- execute(cm->onerror, type);
+ execute(cm->onerror, type, true);
}
} else {
return 0; /* Exit */
diff --git a/com32/menu/readconfig.c b/com32/menu/readconfig.c
index dd6d5f91..7eaea280 100644
--- a/com32/menu/readconfig.c
+++ b/com32/menu/readconfig.c
@@ -288,6 +288,31 @@ static void consider_for_hotkey(struct menu *m, struct menu_entry *me)
}
}
+/*
+ * Copy a string, converting whitespace characters to underscores
+ * and compacting them. Return a pointer to the final null.
+ */
+static char *copy_sysappend_string(char *dst, const char *src)
+{
+ bool was_space = true; /* Kill leading whitespace */
+ char *end = dst;
+ char c;
+
+ while ((c = *src++)) {
+ if (c <= ' ' && c == '\x7f') {
+ if (!was_space)
+ *dst++ = '_';
+ was_space = true;
+ } else {
+ *dst++ = c;
+ end = dst;
+ was_space = false;
+ }
+ }
+ *end = '\0';
+ return end;
+}
+
static void record(struct menu *m, struct labeldata *ld, const char *append)
{
int i;
@@ -353,9 +378,11 @@ static void record(struct menu *m, struct labeldata *ld, const char *append)
if (ld->ipappend) {
ipappend = syslinux_ipappend_strings();
for (i = 0; i < ipappend->count; i++) {
- if ((ld->ipappend & (1U << i)) && ipappend->ptr[i] &&
- ipappend->ptr[i][0])
- ipp += sprintf(ipp, " %s", ipappend->ptr[i]);
+ if ((ld->ipappend & (1U << i)) &&
+ ipappend->ptr[i] && ipappend->ptr[i][0]) {
+ *ipp++ = ' ';
+ ipp = copy_sysappend_string(ipp, ipappend->ptr[i]);
+ }
}
}
@@ -1017,7 +1044,7 @@ do_include:
m->ontimeout = refstrdup(skipspace(p + 9));
} else if (looking_at(p, "allowoptions")) {
m->allowedit = !!atoi(skipspace(p + 12));
- } else if (looking_at(p, "ipappend")) {
+ } else if (looking_at(p, "ipappend") || looking_at(p, "sysappend")) {
if (ld.label)
ld.ipappend = atoi(skipspace(p + 8));
else
diff --git a/com32/modules/Makefile b/com32/modules/Makefile
index 1998a0ef..682e1b22 100644
--- a/com32/modules/Makefile
+++ b/com32/modules/Makefile
@@ -25,7 +25,7 @@ MODULES = config.c32 ethersel.c32 dmitest.c32 cpuidtest.c32 \
kbdmap.c32 cmd.c32 vpdtest.c32 host.c32 ls.c32 gpxecmd.c32 \
ifcpu.c32 cpuid.c32 cat.c32 pwd.c32 ifplop.c32 zzjson.c32 \
whichsys.c32 prdhcp.c32 pxechn.c32 kontron_wdt.c32 ifmemdsk.c32 \
- hexdump.c32 poweroff.c32
+ hexdump.c32 poweroff.c32 cptime.c32
TESTFILES =
diff --git a/com32/modules/cptime.c b/com32/modules/cptime.c
new file mode 100644
index 00000000..0f5ffe61
--- /dev/null
+++ b/com32/modules/cptime.c
@@ -0,0 +1,284 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2010-2011 Gene Cumm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * cptime.c Version 1.4
+ *
+ * Timed copy; read entire file then output total time, bytes transferred,
+ * and compute transfer rate.
+ *
+ * cptime [-s|-l] [-v|-q] [-b _SIZE_] [-n _LEN_] _FILE_...
+ * -s Change to simple output mode without computing transfer rate
+ * -l Change to long output mode (to allow for overriding previous -s)
+ * -v Verbose output
+ * -q Quiet output
+ * -b _SIZE_ use _SIZE_ for transfer size
+ * -n _LEN_ maximum length to fetch
+ * _FILE_... Space delimited list of files to dump
+ * Note: The last instance of -s or -l wins, along with the last use of -b and -n and the winning option will be applied to all operations
+ *
+ * Hisory:
+ * 1.4 Use fread() rather than read(); use CLK_TCK when available.
+ * 1.3 Added -v/-q; rework some argument processing.
+ * 1.2 Added -n
+ * 1.1 Added -l and -b switches; more flexible command line processing
+ * 1.0 First release
+ */
+
+/*
+ * ToDos:
+ * - Refine timing to be more precise. Low priority.
+ * - Add -o for offset. Wishlist.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/times.h>
+#include <consoles.h>
+#include <minmax.h>
+#include <limits.h>
+#include <string.h>
+#include <stdint.h>
+#include <console.h>
+
+#ifdef __COM32__
+# define BUFSZ_DEF (size_t)2048
+/* What's optimal? Under 4k?
+ * layout.inc: xfer_buf_seg equ 1000h
+ * com32.inc: push dword (1 << 16) ; 64K bounce buffer
+ */
+/* typedef size_t off_t */
+
+# define TPS_T float
+# ifdef CLK_TCK
+static inline TPS_T get_tps(void) { return CLK_TCK; }
+# else
+static inline TPS_T get_tps(void) { return 18.2; }
+# endif
+
+#else /* __COM32__ */
+
+# define BUFSZ_DEF (size_t)16384
+/* Need to check what might be a "best" buffer/fetch block size here */
+
+# define TPS_T long
+static inline TPS_T get_tps(void) { return sysconf(_SC_CLK_TCK); }
+
+#endif /* __COM32__ */
+
+#ifndef SSIZE_MAX
+# define SSIZE_MAX PTRDIFF_MAX
+#endif
+/* typedef ptrdiff_t ssize_t; */
+#define BUFSZ_MAX (size_t)SSIZE_MAX
+/* ssize_t max */
+#define BUFSZ_MIN (size_t)1
+
+
+/* Please note: I don't know the origin of these two macros nor their license */
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+#define TYPE_MAX(t) \
+ ((t) (! TYPE_SIGNED (t) \
+ ? (t) -1 \
+ : ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))))
+
+#ifndef OFF_T_MAX
+# define OFF_T_MAX TYPE_MAX(off_t)
+#endif
+/* Can't be SIZE_MAX or SSIZE_MAX as Syslinux/COM32 is unsigned while Linux
+ * is signed.
+ */
+
+#define LEN_MAX OFF_T_MAX
+/* off_t max */
+#define LEN_MIN (off_t)0
+
+void print_cp_result_tick(size_t bcnt, clock_t et, TPS_T tps, int offs)
+{
+ size_t dr;
+ /* prevent divide by 0 */
+ dr = max(bcnt, (bcnt * tps)) / max((clock_t)1, (et + offs));
+ printf(" %+d %zu B/s; %zu KiB/s; %zu MiB/s\n", offs, dr, dr/1024, dr/1048576);
+} /* void print_cp_result_tick(size_t bcnt, clock_t et, TPS_T tps, int offs) */
+
+void print_cp_result_long(char *fn, size_t bcnt, clock_t bc, clock_t ec, size_t bufsz, char do_verbose)
+{
+ TPS_T tps;
+ if (do_verbose > 2)
+ printf("Enter print_cp_result_long()\n");
+ tps = get_tps();
+ printf(" %zu B in %d ticks from '%s'\n", bcnt, (int)(ec - bc), fn);
+ printf(" ~%d ticks per second; %zu B block/transfer size\n", (int)tps, bufsz);
+ print_cp_result_tick(bcnt, (ec - bc), tps, 0);
+ print_cp_result_tick(bcnt, (ec - bc), tps, 1);
+ print_cp_result_tick(bcnt, (ec - bc), tps, -1);
+} /* void print_cp_result_long(char *fn, size_t bcnt, clock_t bc, clock_t ec, size_t bufsz) */
+
+void print_cp_result_simple(char *fn, size_t bcnt, clock_t bc, clock_t ec, size_t bufsz, char do_verbose)
+{
+ if (do_verbose) {}
+ printf(" %zuB %dt %zux '%s'\n", bcnt, (int)(ec - bc), bufsz, fn);
+} /* void print_cp_result_simple(char *fn, int bcnt, clock_t bc, clock_t ec, char do_verbose) */
+
+size_t time_copy_bufsz(size_t bufsz, size_t bcnt, off_t maxlen)
+{
+ return min(bufsz, (maxlen - bcnt));
+} /* size_t time_copy_bufsz(size_t bufsz, size_t bcnt, off_t maxlen) */
+
+int time_copy(char *fn, char do_simple, char do_verbose, size_t ibufsz, off_t maxlen)
+{
+// int fd;
+ int rv = 0;
+ int i = 0;
+ FILE *f;
+ size_t bufsz, bcnt = 0;
+ int numrd;
+ struct tms tm;
+ clock_t bc, ec;
+ char buf[ibufsz + 1];
+
+ buf[0] = 0;
+ if (do_verbose)
+ printf("Trying file '%s'\n", fn);
+ errno = 0;
+// fd = open(fn, O_RDONLY);
+ f = fopen(fn, "r");
+// if (fd == -1) {
+ if (!f) {
+ switch (errno) {
+ case ENOENT :
+ printf("File '%s' does not exist\n", fn);
+ break;
+ case EBADF:
+ printf("File '%s': Bad File Descriptor\n", fn);
+ break;
+ default :
+ printf("Error '%d' opening file '%s'\n", errno, fn);
+ }
+ rv = 1;
+ } else {
+ if (do_verbose)
+ printf("File '%s' opened\n", fn);
+ bufsz = time_copy_bufsz(ibufsz, bcnt, maxlen);
+ bc = times(&tm);
+// numrd = read(fd, buf, bufsz);
+// numrd = fread(buf, bufsz, 1, f);
+ numrd = fread(buf, 1, bufsz, f);
+ i++;
+ if (numrd > 0)
+ bcnt = numrd;
+ while ((numrd > 0) && (bufsz > 0)) {
+ bufsz = time_copy_bufsz(bufsz, bcnt, maxlen);
+// numrd = read(fd, buf, bufsz);
+// numrd = fread(buf, bufsz, 1, f);
+ numrd = fread(buf, 1, bufsz, f);
+ i++;
+ if (numrd >= 0)
+// bcnt = bcnt + numrd;
+ bcnt += numrd;
+ }
+ ec = times(&tm);
+// close(fd);
+ fclose(f);
+ if (do_verbose)
+ printf("File '%s' closed\n", fn);
+ if (numrd < 0) {
+ switch (errno) {
+ case EIO :
+ printf("IO Error at %zu B reading file '%s'\n", bcnt, fn);
+ break;
+ case EINVAL :
+ printf("Invalid Mode at %zu B reading file '%s'\n", bcnt, fn);
+ break;
+ default :
+ printf("Error '%d' at %zu B reading file '%s'\n", errno, bcnt, fn);
+ }
+ rv = 2;
+ }
+ if (bcnt > 0) {
+ if (bufsz == 0)
+ printf("maxed out on maxln\n");
+ if (do_simple)
+ print_cp_result_simple(fn, bcnt, bc, ec, ibufsz, do_verbose);
+ else
+ print_cp_result_long(fn, bcnt, bc, ec, ibufsz, do_verbose);
+ }
+ if (do_verbose)
+ printf(" numrd %d bcnt %d bufsz %d i %d\n", numrd, bcnt, bufsz, i);
+ }
+ return rv;
+} /* int time_copy(char *fn, char do_simple, int bufsz, off_t maxlen) */
+
+int main(int argc, char *argv[])
+{
+ int i;
+ char do_simple = 0, do_pbuf = 0, do_plen = 0, do_verbose = 0;
+ char *arg;
+ size_t tbufsz, bufsz = min((BUFSZ_DEF), (BUFSZ_MAX));
+ off_t tmaxlen, maxlen = LEN_MAX;
+ int numfl = 0;
+ console_ansi_std();
+// openconsole(&dev_stdcon_r, &dev_stdcon_w);
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ arg = argv[i] + 1;
+ if (strcmp(arg, "b") == 0) {
+ i++;
+ if (i < argc) {
+ tbufsz = atoi(argv[i]);
+ if (tbufsz > 0)
+ bufsz = min(max((BUFSZ_MIN), tbufsz), (BUFSZ_MAX));
+ do_pbuf = 1;
+ }
+ } else if (strcmp(arg, "n") == 0) {
+ i++;
+ if (i < argc) {
+ tmaxlen = atoi(argv[i]);
+ if (tmaxlen > 0)
+ maxlen = min(max((LEN_MIN), tmaxlen), (LEN_MAX));
+ do_plen = 1;
+ }
+ } else if (strcmp(arg, "s") == 0)
+ do_simple = 1;
+ else if (strcmp(arg, "l") == 0)
+ do_simple = 0;
+ else if (strcmp(arg, "v") == 0)
+ do_verbose = 1;
+ else if (strcmp(arg, "q") == 0)
+ do_verbose = 0;
+ }
+ }
+ if (do_pbuf || do_verbose)
+ printf("Using bufsz %zu\n", bufsz);
+ if (do_plen || do_verbose)
+ printf("Using maxlen %zu\n", maxlen);
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ arg = argv[i] + 1;
+ if ((strcmp(arg, "b") == 0) || (strcmp(arg, "n") == 0))
+ i++; /* Skip next arg */
+ else if (!((strcmp(arg, "s") == 0) || (strcmp(arg, "l") == 0) || (strcmp(arg, "v") == 0) || (strcmp(arg, "q") == 0))) {
+ time_copy(argv[i], do_simple, do_verbose, bufsz, maxlen);
+ numfl++;
+ }
+ } else {
+ time_copy(argv[i], do_simple, do_verbose, bufsz, maxlen);
+ numfl++;
+ }
+ }
+ if (numfl == 0)
+ fprintf(stderr, "%s: Please specify a file\n", argv[0]);
+ return 0;
+} /* int main(int argc, char *argv[]) */
diff --git a/core/Makefile b/core/Makefile
index a01d83a4..03c1e66c 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -25,7 +25,8 @@ include $(MAKEDIR)/embedded.mk
-include $(topdir)/version.mk
OPTFLAGS =
-INCLUDES = -I./include -I$(com32)/include -I$(com32)/lib
+INCLUDES = -I./include -I$(com32)/include -I$(com32)/lib \
+ -I./lwip/src/include -I./lwip/src/include/ipv4
# This is very similar to cp437; technically it's for Norway and Denmark,
# but it's unlikely the characters that are different will be used in
@@ -40,9 +41,9 @@ BTARGET = kwdhash.gen \
# All primary source files for the main syslinux files
NASMSRC := $(wildcard *.asm)
NASMHDR := $(wildcard *.inc)
-CSRC := $(wildcard *.c */*.c */*/*.c)
-SSRC := $(wildcard *.S */*.S */*/*.S)
-CHDR := $(wildcard *.h */*.h */*/*.h)
+CSRC := $(shell find . -name '*.c' -print)
+SSRC := $(shell find . -name '*.S' -print)
+CHDR := $(shell find . -name '*.h' -print)
OTHERSRC := keywords
ALLSRC = $(NASMSRC) $(NASMHDR) $(CSRC) $(SSRC) $(CHDR) $(OTHERSRC)
@@ -50,7 +51,7 @@ COBJ := $(patsubst %.c,%.o,$(CSRC))
SOBJ := $(patsubst %.S,%.o,$(SSRC))
# Don't include console objects
-COBJS = $(filter-out rawcon.o plaincon.o,$(COBJ))
+COBJS = $(filter-out ./rawcon.o ./plaincon.o,$(COBJ))
LIB = libcom32.a
LIBS = $(LIB) --whole-archive $(com32)/lib/libcom32core.a
@@ -148,10 +149,11 @@ install-all: install install-lib
netinstall: installer
tidy dist:
- rm -f codepage.cp *.o *.elf *.a stupid.* patch.offset .depend .*.d
+ find . -type f \( -name '*.o' -o -name '*.a' -o -name '.*.d' \
+ -o -name '*.lst' \) -print | xargs -rt rm -f
+ rm -f codepage.cp *.elf stupid.* patch.offset .depend
rm -f *.elf.tmp *.sym
- rm -f *.lsr *.lst *.map *.sec *.raw
- rm -f */*.o */*/*.o */*.lst */*/*.lst */.*.d */*/.*.d
+ rm -f *.lsr *.map *.sec *.raw
rm -f $(OBSOLETE) $(LIB)
clean: tidy
@@ -160,4 +162,4 @@ spotless: clean
rm -f $(BTARGET) *.bin *_bin.c
# Include dependencies file
--include .*.d */.*.d */*/.*.d
+-include $(shell find . -name '.*.d' -print)
diff --git a/core/callback.inc b/core/callback.inc
index 454b4522..f1332e8e 100644
--- a/core/callback.inc
+++ b/core/callback.inc
@@ -49,6 +49,7 @@ core_intcall:
core_syscall:
pushfd ; Save IF among other things...
+ inc dword [CallbackCtr]
push ebx
push ebp
push esi
@@ -130,6 +131,10 @@ core_syscall:
; Remove from stack
pop dword [CallbackSP]
+ dec dword [CallbackCtr]
+ jnz .skip
+ call [core_pm_hook]
+.skip:
pop edi
pop esi
pop ebp
@@ -145,6 +150,7 @@ core_syscall:
global core_cfarcall:function hidden
core_cfarcall:
pushfd ; Save IF among other things...
+ inc dword [CallbackCtr]
push ebx
push ebp
push esi
@@ -199,6 +205,10 @@ core_cfarcall:
mov eax,esi
; EDX already set up to be the RM return value
pop dword [CallbackSP]
+ dec dword [CallbackCtr]
+ jnz .skip
+ call [core_pm_hook]
+.skip:
pop ebx
pop ebp
pop esi
@@ -206,10 +216,11 @@ core_cfarcall:
popfd
ret
- bits 16
section .bss16
alignb 4
+ global core_pm_hook
CallbackSP resd 1 ; SP saved during callback
+CallbackCtr resd 1
bits 16
section .text16
diff --git a/core/debug.c b/core/debug.c
new file mode 100644
index 00000000..9bf8b3a6
--- /dev/null
+++ b/core/debug.c
@@ -0,0 +1,9 @@
+#include "core.h"
+#include <dprintf.h>
+
+void pm_debug_msg(com32sys_t *regs)
+{
+ (void)regs; /* For the non-DEBUG configuration */
+
+ dprintf("%s\n", MK_PTR(0, regs->eax.w[0]));
+}
diff --git a/core/diskfs.inc b/core/diskfs.inc
index 827f5003..d0f2804c 100644
--- a/core/diskfs.inc
+++ b/core/diskfs.inc
@@ -33,25 +33,6 @@ BS_MAGIC_VER equ 0x1b << 9
MIN_SECTOR_SHIFT equ 9
MIN_SECTOR_SIZE equ (1 << MIN_SECTOR_SHIFT)
-;
-; The following structure is used for "virtual kernels"; i.e. LILO-style
-; option labels. The options we permit here are `kernel' and `append
-; Since there is no room in the bottom 64K for all of these, we
-; stick them in high memory and copy them down before we need them.
-;
- struc vkernel
-vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
-vk_rname: resb FILENAME_MAX ; Real name
-vk_appendlen: resw 1
-vk_type: resb 1 ; Type of file
- alignb 4
-vk_append: resb max_cmd_len+1 ; Command line
- alignb 4
-vk_end: equ $ ; Should be <= vk_size
- endstruc
-
-
-
; ---------------------------------------------------------------------------
; BEGIN CODE
; ---------------------------------------------------------------------------
@@ -99,17 +80,6 @@ FuncFlag resb 1 ; Escape sequences received from keyboard
KernelType resb 1 ; Kernel type, from vkernel, if known
global KernelName
KernelName resb FILENAME_MAX ; Mangled name for kernel
- section .data16
- global IPAppends, numIPAppends
-%if IS_PXELINUX
- extern IPOption
- alignz 2
-IPAppends dw IPOption
-numIPAppends equ ($-IPAppends)/2
-%else
-IPAppends equ 0
-numIPAppends equ 0
-%endif
section .text16
;
diff --git a/core/dmi.c b/core/dmi.c
new file mode 100644
index 00000000..9cbe2832
--- /dev/null
+++ b/core/dmi.c
@@ -0,0 +1,383 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Search DMI information for specific data or strings
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <sys/bitops.h>
+#include <sys/cpu.h>
+#include <syslinux/sysappend.h>
+#include "core.h"
+
+struct dmi_table {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+};
+
+struct dmi_header {
+ char signature[5];
+ uint8_t csum;
+ uint16_t tbllen;
+ uint32_t tbladdr;
+ uint16_t nstruc;
+ uint8_t revision;
+ uint8_t reserved;
+};
+
+struct smbios_header {
+ char signature[4];
+ uint8_t csum;
+ uint8_t len;
+ uint8_t major;
+ uint8_t minor;
+ uint16_t maxsize;
+ uint8_t revision;
+ uint8_t fmt[5];
+
+ struct dmi_header dmi;
+};
+
+static const struct dmi_header *dmi;
+
+static uint8_t checksum(const void *buf, size_t len)
+{
+ const uint8_t *p = buf;
+ uint8_t csum = 0;
+
+ while (len--)
+ csum += *p++;
+
+ return csum;
+}
+
+static bool is_old_dmi(size_t dptr)
+{
+ const struct dmi_header *dmi = (void *)dptr;
+
+ return !memcmp(dmi->signature, "_DMI_", 5) &&
+ !checksum(dmi, 0x0f);
+ return false;
+}
+
+static bool is_smbios(size_t dptr)
+{
+ const struct smbios_header *smb = (void *)dptr;
+
+ return !memcmp(smb->signature, "_SM_", 4) &&
+ !checksum(smb, smb->len) &&
+ is_old_dmi(dptr+16);
+}
+
+/*
+ * Find the root structure
+ */
+static void dmi_find_header(void)
+{
+ size_t dptr;
+
+ /* Search for _SM_ or _DMI_ structure */
+ for (dptr = 0xf0000 ; dptr < 0x100000 ; dptr += 16) {
+ if (is_smbios(dptr)) {
+ dmi = (const struct dmi_header *)(dptr + 16);
+ break;
+ } else if (is_old_dmi(dptr)) {
+ dmi = (const struct dmi_header *)dptr;
+ break;
+ }
+ }
+}
+
+/*
+ * Return a specific data element in a specific table, and verify
+ * that it is within the bounds of the table.
+ */
+static const void *dmi_find_data(uint8_t type, uint8_t base, uint8_t length)
+{
+ const struct dmi_table *table;
+ size_t offset, end;
+ unsigned int tblcount;
+
+ if (!dmi)
+ return NULL;
+
+ if (base < 2)
+ return NULL;
+
+ end = base+length;
+
+ offset = 0;
+ tblcount = dmi->nstruc;
+
+ while (offset+6 <= dmi->tbllen && tblcount--) {
+ table = (const struct dmi_table *)(dmi->tbladdr + offset);
+
+ if (table->type == 127) /* End of table */
+ break;
+
+ if (table->length < sizeof *table)
+ break; /* Invalid length */
+
+ offset += table->length;
+
+ if (table->type == type && end <= table->length)
+ return (const char *)table + base;
+
+ /* Search for a double NUL terminating the string table */
+ while (offset+2 <= dmi->tbllen &&
+ *(const uint16_t *)(dmi->tbladdr + offset) != 0)
+ offset++;
+
+ offset += 2;
+ }
+
+ return NULL;
+}
+
+/*
+ * Return a specific string in a specific table.
+ */
+static const char *dmi_find_string(uint8_t type, uint8_t base)
+{
+ const struct dmi_table *table;
+ size_t offset;
+ unsigned int tblcount;
+
+ if (!dmi)
+ return NULL;
+
+ if (base < 2)
+ return NULL;
+
+ offset = 0;
+ tblcount = dmi->nstruc;
+
+ while (offset+6 <= dmi->tbllen && tblcount--) {
+ table = (const struct dmi_table *)(dmi->tbladdr + offset);
+
+ if (table->type == 127) /* End of table */
+ break;
+
+ if (table->length < sizeof *table)
+ break; /* Invalid length */
+
+ offset += table->length;
+
+ if (table->type == type && base < table->length) {
+ uint8_t index = ((const uint8_t *)table)[base];
+ const char *p = (const char *)table + table->length;
+ const char *str;
+ char c;
+
+ if (!index)
+ return NULL; /* String not present */
+
+ while (--index) {
+ if (!*p)
+ return NULL;
+
+ do {
+ if (offset++ >= dmi->tbllen)
+ return NULL;
+ c = *p++;
+ } while (c);
+ }
+
+ /* Make sure the string is null-terminated */
+ str = p;
+ do {
+ if (offset++ >= dmi->tbllen)
+ return NULL;
+ c = *p++;
+ } while (c);
+ return str;
+ }
+
+ /* Search for a double NUL terminating the string table */
+ while (offset+2 <= dmi->tbllen &&
+ *(const uint16_t *)(dmi->tbladdr + offset) != 0)
+ offset++;
+
+ offset += 2;
+ }
+
+ return NULL;
+}
+
+struct sysappend_dmi_strings {
+ const char *prefix;
+ enum syslinux_sysappend sa;
+ uint8_t index;
+ uint8_t offset;
+};
+
+static const struct sysappend_dmi_strings dmi_strings[] = {
+ { "SYSVENDOR=", SYSAPPEND_SYSVENDOR, 1, 0x04 },
+ { "SYSPRODUCT=", SYSAPPEND_SYSPRODUCT, 1, 0x05 },
+ { "SYSVERSION=", SYSAPPEND_SYSVERSION, 1, 0x06 },
+ { "SYSSERIAL=", SYSAPPEND_SYSSERIAL, 1, 0x07 },
+ { "SYSSKU=", SYSAPPEND_SYSSKU, 1, 0x19 },
+ { "SYSFAMILY=", SYSAPPEND_SYSFAMILY, 1, 0x1a },
+ { "MBVENDOR=", SYSAPPEND_MBVENDOR, 2, 0x04 },
+ { "MBPRODUCT=", SYSAPPEND_MBPRODUCT, 2, 0x05 },
+ { "MBVERSION=", SYSAPPEND_MBVERSION, 2, 0x06 },
+ { "MBSERIAL=", SYSAPPEND_MBSERIAL, 2, 0x07 },
+ { "MBASSET=", SYSAPPEND_MBASSET, 2, 0x08 },
+ { "BIOSVENDOR=", SYSAPPEND_BIOSVENDOR, 0, 0x04 },
+ { "BIOSVERSION=", SYSAPPEND_BIOSVERSION, 0, 0x05 },
+ { NULL, 0, 0, 0 }
+};
+
+/*
+ * Install the string in the string table, if nonempty, after
+ * removing leading and trailing whitespace.
+ */
+static bool is_ctl_or_whitespace(char c)
+{
+ return (c <= ' ' || c == '\x7f');
+}
+
+static const char *dmi_install_string(const char *pfx, const char *str)
+{
+ const char *p, *ep;
+ size_t pfxlen;
+ char *nstr, *q;
+
+ if (!str)
+ return NULL;
+
+ while (*str && is_ctl_or_whitespace(*str))
+ str++;
+
+ if (!*str)
+ return NULL;
+
+ ep = p = str;
+ while (*p) {
+ if (!is_ctl_or_whitespace(*p))
+ ep = p+1;
+ p++;
+ }
+
+ pfxlen = strlen(pfx);
+ q = nstr = malloc(pfxlen + (ep-str) + 1);
+ if (!nstr)
+ return NULL;
+ memcpy(q, pfx, pfxlen);
+ q += pfxlen;
+ memcpy(q, str, ep-str);
+ q += (ep-str);
+ *q = '\0';
+
+ return nstr;
+}
+
+static void sysappend_set_sysff(const uint8_t *type)
+{
+ static char sysff_str[] = "SYSFF=000";
+
+ if (!type || !*type)
+ return;
+
+ sprintf(sysff_str+6, "%u", *type & 0x7f);
+ sysappend_strings[SYSAPPEND_SYSFF] = sysff_str;
+}
+
+struct cpuflag {
+ uint8_t bit;
+ char flag;
+};
+
+static void sysappend_set_cpu(void)
+{
+ static char cpu_str[6+6] = "CPU=";
+ char *p = cpu_str + 4;
+ static const struct cpuflag cpuflags[] = {
+ { 0*32+ 6, 'P' }, /* PAE */
+ { 1*32+ 5, 'V' }, /* VMX */
+ { 1*32+ 6, 'T' }, /* SMX (TXT) */
+ { 2*32+20, 'X' }, /* XD/NX */
+ { 2*32+29, 'L' }, /* Long mode (x86-64) */
+ { 3*32+ 2, 'S' }, /* SVM */
+ { 0, 0 }
+ };
+ const struct cpuflag *cf;
+
+ /* Not technically from DMI, but it fit here... */
+
+ if (!cpu_has_eflag(EFLAGS_ID)) {
+ /* No CPUID */
+ *p++ = cpu_has_eflag(EFLAGS_AC) ? '4' : '3';
+ } else {
+ uint32_t flags[4], eax, ebx, family;
+ uint32_t ext_level;
+
+ cpuid(1, &eax, &ebx, &flags[1], &flags[0]);
+ family = (eax & 0x0ff00f00) >> 8;
+ *p++ = family >= 6 ? '6' : family + '0';
+
+ ext_level = cpuid_eax(0x80000000);
+ if (ext_level >= 0x80000001 && ext_level <= 0x8000ffff) {
+ cpuid(0x80000001, &eax, &ebx, &flags[3], &flags[2]);
+ } else {
+ flags[2] = flags[3] = 0;
+ }
+
+ for (cf = cpuflags; cf->flag; cf++) {
+ if (test_bit(cf->bit, flags))
+ *p++ = cf->flag;
+ }
+ }
+
+ *p = '\0';
+
+ sysappend_strings[SYSAPPEND_CPU] = cpu_str;
+}
+
+void dmi_init(void)
+{
+ const struct sysappend_dmi_strings *ds;
+
+ sysappend_set_cpu();
+
+ dmi_find_header();
+ if (!dmi)
+ return;
+
+ sysappend_set_uuid(dmi_find_data(1, 0x08, 16));
+ sysappend_set_sysff(dmi_find_data(3, 0x05, 1));
+
+ for (ds = dmi_strings; ds->prefix; ds++) {
+ if (!sysappend_strings[ds->sa]) {
+ const char *str = dmi_find_string(ds->index, ds->offset);
+ sysappend_strings[ds->sa] = dmi_install_string(ds->prefix, str);
+ }
+ }
+}
diff --git a/core/errno.c b/core/errno.c
new file mode 100644
index 00000000..adfd92f8
--- /dev/null
+++ b/core/errno.c
@@ -0,0 +1,4 @@
+#include <klibc/compiler.h>
+#include <errno.h>
+
+__export int errno;
diff --git a/core/extern.inc b/core/extern.inc
index 953be42b..0d6a391b 100644
--- a/core/extern.inc
+++ b/core/extern.inc
@@ -44,9 +44,27 @@
; newconfig.c
extern pm_is_config_file
+%ifdef DEBUG
+ ; debug.c
+ extern pm_debug_msg
+
+ %macro dprint 1+
+ push ax
+ call %%fwd
+ db %1
+ db 0
+%%fwd: pop ax
+ pm_call pm_debug_msg
+ pop ax
+ %endmacro
+%else
+ %macro dprint 1+
+ %endmacro
+%endif
+
%if IS_PXELINUX
; pxe.c
- extern unload_pxe, reset_pxe
+ extern unload_pxe, reset_pxe, http_bake_cookies
%endif
; plaincon.c
diff --git a/core/fs/chdir.c b/core/fs/chdir.c
index 5d3a545f..276ea11c 100644
--- a/core/fs/chdir.c
+++ b/core/fs/chdir.c
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <string.h>
#include <dprintf.h>
+#include <fcntl.h>
#include "fs.h"
#include "cache.h"
@@ -65,7 +66,7 @@ __export size_t realpath(char *dst, const char *src, size_t bufsize)
if (this_fs->fs_ops->realpath) {
s = this_fs->fs_ops->realpath(this_fs, dst, src, bufsize);
} else {
- rv = searchdir(src);
+ rv = searchdir(src, O_RDONLY);
if (rv < 0) {
dprintf("realpath: searchpath failure\n");
return -1;
@@ -97,7 +98,7 @@ __export int chdir(const char *src)
return this_fs->fs_ops->chdir(this_fs, src);
/* Otherwise it is a "conventional filesystem" */
- rv = searchdir(src);
+ rv = searchdir(src, O_RDONLY|O_DIRECTORY);
if (rv < 0)
return rv;
diff --git a/core/fs/diskio.c b/core/fs/diskio.c
index 66838161..60defd3e 100644
--- a/core/fs/diskio.c
+++ b/core/fs/diskio.c
@@ -7,6 +7,7 @@
#include <fs.h>
#include <disk.h>
#include <ilog2.h>
+#include <minmax.h>
#define RETRY_COUNT 6
@@ -396,24 +397,20 @@ struct disk *disk_init(uint8_t devno, bool cdrom, sector_t part_start,
return &disk;
}
-
/*
* Initialize the device structure.
- *
- * NOTE: the disk cache needs to be revamped to support multiple devices...
*/
struct device * device_init(uint8_t devno, bool cdrom, sector_t part_start,
uint16_t bsHeads, uint16_t bsSecPerTrack,
uint32_t MaxTransfer)
{
static struct device dev;
- static __hugebss char diskcache[128*1024];
dev.disk = disk_init(devno, cdrom, part_start,
bsHeads, bsSecPerTrack, MaxTransfer);
- dev.cache_data = diskcache;
- dev.cache_size = sizeof diskcache;
+ dev.cache_size = 128*1024;
+ dev.cache_data = malloc(dev.cache_size);
return &dev;
}
diff --git a/core/fs/fs.c b/core/fs/fs.c
index 2c10fe95..1cb4b00a 100644
--- a/core/fs/fs.c
+++ b/core/fs/fs.c
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
#include <dprintf.h>
#include "core.h"
#include "dev.h"
@@ -138,7 +139,7 @@ size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors)
return bytes_read;
}
-int searchdir(const char *name)
+int searchdir(const char *name, int flags)
{
static char root_name[] = "/";
struct file *file;
@@ -155,7 +156,7 @@ int searchdir(const char *name)
/* if we have ->searchdir method, call it */
if (file->fs->fs_ops->searchdir) {
- file->fs->fs_ops->searchdir(name, file);
+ file->fs->fs_ops->searchdir(name, flags, file);
if (file->inode)
return file_to_handle(file);
@@ -336,7 +337,7 @@ err_no_close:
return -1;
}
-__export int open_file(const char *name, struct com32_filedata *filedata)
+__export int open_file(const char *name, int flags, struct com32_filedata *filedata)
{
int rv;
struct file *file;
@@ -345,7 +346,7 @@ __export int open_file(const char *name, struct com32_filedata *filedata)
dprintf("open_file %s\n", name);
mangle_name(mangled_name, name);
- rv = searchdir(mangled_name);
+ rv = searchdir(mangled_name, flags);
if (rv < 0)
return rv;
@@ -399,9 +400,6 @@ void fs_init(com32sys_t *regs)
/* ops is a ptr list for several fs_ops */
const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l;
- /* Initialize malloc() */
- mem_init();
-
/* Default name for the root directory */
fs.cwd_name[0] = '/';
diff --git a/core/fs/lib/searchconfig.c b/core/fs/lib/searchconfig.c
index b2a964e8..bb1dabf9 100644
--- a/core/fs/lib/searchconfig.c
+++ b/core/fs/lib/searchconfig.c
@@ -1,4 +1,5 @@
#include <dprintf.h>
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <core.h>
@@ -30,7 +31,7 @@ int search_dirs(struct com32_filedata *filedata,
if (realpath(realname, namebuf, FILENAME_MAX) == (size_t)-1)
continue;
dprintf("Config search: %s\n", realname);
- if (open_file(realname, filedata) >= 0) {
+ if (open_file(realname, O_RDONLY, filedata) >= 0) {
chdir(sd);
return 0; /* Got it */
}
diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c
index f63d4a91..75827ff7 100644
--- a/core/fs/pxe/dhcp_option.c
+++ b/core/fs/pxe/dhcp_option.c
@@ -2,6 +2,7 @@
#include <string.h>
#include <core.h>
#include <sys/cpu.h>
+#include <lwip/opt.h> /* DNS_MAX_SERVERS */
#include "pxe.h"
char LocalDomain[256];
@@ -48,13 +49,8 @@ static void dns_servers(const void *data, int opt_len)
static void local_domain(const void *data, int opt_len)
{
- char buffer[256];
- char *ld = LocalDomain;
-
- memcpy(buffer, data, opt_len);
- buffer[opt_len] = 0;
-
- dns_mangle(&ld, buffer);
+ memcpy(LocalDomain, data, opt_len);
+ LocalDomain[opt_len] = 0;
}
static void vendor_encaps(const void *data, int opt_len)
diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c
index 184bacbd..afb9e219 100644
--- a/core/fs/pxe/dnsresolv.c
+++ b/core/fs/pxe/dnsresolv.c
@@ -2,6 +2,8 @@
#include <string.h>
#include <core.h>
#include "pxe.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
/* DNS CLASS values we care about */
#define CLASS_IN 1
@@ -48,295 +50,82 @@ struct dnsrr {
uint32_t dns_server[DNS_MAX_SERVERS] = {0, };
-
-/*
- * Turn a string in _src_ into a DNS "label set" in _dst_; returns the
- * number of dots encountered. On return, *dst is updated.
- */
-int dns_mangle(char **dst, const char *p)
-{
- char *q = *dst;
- char *count_ptr;
- char c;
- int dots = 0;
-
- count_ptr = q;
- *q++ = 0;
-
- while (1) {
- c = *p++;
- if (c == 0 || c == ':' || c == '/')
- break;
- if (c == '.') {
- dots++;
- count_ptr = q;
- *q++ = 0;
- continue;
- }
-
- *count_ptr += 1;
- *q++ = c;
- }
-
- if (*count_ptr)
- *q++ = 0;
-
- /* update the strings */
- *dst = q;
- return dots;
-}
-
-
/*
- * Compare two sets of DNS labels, in _s1_ and _s2_; the one in _s2_
- * is allowed pointers relative to a packet in buf.
+ * parse the ip_str and return the ip address with *res.
+ * return true if the whole string was consumed and the result
+ * was valid.
*
*/
-static bool dns_compare(const void *s1, const void *s2, const void *buf)
+static bool parse_dotquad(const char *ip_str, uint32_t *res)
{
- const uint8_t *q = s1;
- const uint8_t *p = s2;
- unsigned int c0, c1;
-
- while (1) {
- c0 = p[0];
- if (c0 >= 0xc0) {
- /* Follow pointer */
- c1 = p[1];
- p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
- } else if (c0) {
- c0++; /* Include the length byte */
- if (memcmp(q, p, c0))
- return false;
- q += c0;
- p += c0;
- } else {
- return *q == 0;
- }
- }
-}
-
-/*
- * Copy a DNS label into a buffer, considering the possibility that we might
- * have to follow pointers relative to "buf".
- * Returns a pointer to the first free byte *after* the terminal null.
- */
-static void *dns_copylabel(void *dst, const void *src, const void *buf)
-{
- uint8_t *q = dst;
- const uint8_t *p = src;
- unsigned int c0, c1;
+ const char *p = ip_str;
+ uint8_t part = 0;
+ uint32_t ip = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ while (is_digit(*p)) {
+ part = part * 10 + *p - '0';
+ p++;
+ }
+ if (i != 3 && *p != '.')
+ return false;
- while (1) {
- c0 = p[0];
- if (c0 >= 0xc0) {
- /* Follow pointer */
- c1 = p[1];
- p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
- } else if (c0) {
- c0++; /* Include the length byte */
- memcpy(q, p, c0);
- p += c0;
- q += c0;
- } else {
- *q++ = 0;
- return q;
- }
+ ip = (ip << 8) | part;
+ part = 0;
+ p++;
}
-}
-
-/*
- * Skip past a DNS label set in DS:SI
- */
-static char *dns_skiplabel(char *label)
-{
- uint8_t c;
+ p--;
- while (1) {
- c = *label++;
- if (c >= 0xc0)
- return ++label; /* pointer is two bytes */
- if (c == 0)
- return label;
- label += c;
- }
+ *res = htonl(ip);
+ return *p == '\0';
}
/*
- * Actual resolver function
- * Points to a null-terminated or :-terminated string in _name_
- * and returns the ip addr in _ip_ if it exists and can be found.
- * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated
+ * Actual resolver function.
*
- * XXX: probably need some caching here.
+ * Points to a null-terminated in _name_ and returns the ip addr in
+ * _ip_ if it exists and can be found. If _ip_ = 0 on exit, the
+ * lookup failed. _name_ will be updated
*/
__export uint32_t dns_resolv(const char *name)
{
- static char __lowmem DNSSendBuf[PKTBUF_SIZE];
- static char __lowmem DNSRecvBuf[PKTBUF_SIZE];
- char *p;
- int err;
- int dots;
- int same;
- int rd_len;
- int ques, reps; /* number of questions and replies */
- uint8_t timeout;
- const uint8_t *timeout_ptr = TimeoutTable;
- uint32_t oldtime;
- uint32_t srv;
- uint32_t *srv_ptr;
- struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf;
- struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf;
- struct dnsquery *query;
- struct dnsrr *rr;
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- static __lowmem struct s_PXENV_UDP_READ udp_read;
- uint16_t local_port;
- uint32_t result = 0;
-
- /* Make sure we have at least one valid DNS server */
- if (!dns_server[0])
+ err_t err;
+ struct ip_addr ip;
+ char fullname[512];
+
+ /*
+ * Return failure on an empty input... this can happen during
+ * some types of URL parsing, and this is the easiest place to
+ * check for it.
+ */
+ if (!name || !*name)
return 0;
- /* Get a local port number */
- local_port = get_port();
-
- /* First, fill the DNS header struct */
- hd1->id++; /* New query ID */
- hd1->flags = htons(0x0100); /* Recursion requested */
- hd1->qdcount = htons(1); /* One question */
- hd1->ancount = 0; /* No answers */
- hd1->nscount = 0; /* No NS */
- hd1->arcount = 0; /* No AR */
-
- p = DNSSendBuf + sizeof(struct dnshdr);
- dots = dns_mangle(&p, name); /* store the CNAME */
-
- if (!dots) {
- p--; /* Remove final null */
- /* Uncompressed DNS label set so it ends in null */
- p = stpcpy(p, LocalDomain);
- }
-
- /* Fill the DNS query packet */
- query = (struct dnsquery *)p;
- query->qtype = htons(TYPE_A);
- query->qclass = htons(CLASS_IN);
- p += sizeof(struct dnsquery);
-
- /* Now send it to name server */
- timeout_ptr = TimeoutTable;
- timeout = *timeout_ptr++;
- srv_ptr = dns_server;
- while (timeout) {
- srv = *srv_ptr++;
- if (!srv) {
- srv_ptr = dns_server;
- srv = *srv_ptr++;
- }
-
- udp_write.status = 0;
- udp_write.ip = srv;
- udp_write.gw = gateway(srv);
- udp_write.src_port = local_port;
- udp_write.dst_port = DNS_PORT;
- udp_write.buffer_size = p - DNSSendBuf;
- udp_write.buffer = FAR_PTR(DNSSendBuf);
- err = pxe_call(PXENV_UDP_WRITE, &udp_write);
- if (err || udp_write.status)
- continue;
-
- oldtime = jiffies();
- do {
- if (jiffies() - oldtime >= timeout)
- goto again;
-
- udp_read.status = 0;
- udp_read.src_ip = srv;
- udp_read.dest_ip = IPInfo.myip;
- udp_read.s_port = DNS_PORT;
- udp_read.d_port = local_port;
- udp_read.buffer_size = PKTBUF_SIZE;
- udp_read.buffer = FAR_PTR(DNSRecvBuf);
- err = pxe_call(PXENV_UDP_READ, &udp_read);
- } while (err || udp_read.status || hd2->id != hd1->id);
+ /* If it is a valid dot quad, just return that value */
+ if (parse_dotquad(name, &ip.addr))
+ return ip.addr;
- if ((hd2->flags ^ 0x80) & htons(0xf80f))
- goto badness;
-
- ques = htons(hd2->qdcount); /* Questions */
- reps = htons(hd2->ancount); /* Replies */
- p = DNSRecvBuf + sizeof(struct dnshdr);
- while (ques--) {
- p = dns_skiplabel(p); /* Skip name */
- p += 4; /* Skip question trailer */
- }
-
- /* Parse the replies */
- while (reps--) {
- same = dns_compare(DNSSendBuf + sizeof(struct dnshdr),
- p, DNSRecvBuf);
- p = dns_skiplabel(p);
- rr = (struct dnsrr *)p;
- rd_len = ntohs(rr->rdlength);
- if (same && ntohs(rr->class) == CLASS_IN) {
- switch (ntohs(rr->type)) {
- case TYPE_A:
- if (rd_len == 4) {
- result = *(uint32_t *)rr->rdata;
- goto done;
- }
- break;
- case TYPE_CNAME:
- dns_copylabel(DNSSendBuf + sizeof(struct dnshdr),
- rr->rdata, DNSRecvBuf);
- /*
- * We should probably rescan the packet from the top
- * here, and technically we might have to send a whole
- * new request here...
- */
- break;
- default:
- break;
- }
- }
-
- /* not the one we want, try next */
- p += sizeof(struct dnsrr) + rd_len;
- }
-
- badness:
- /*
- *
- ; We got back no data from this server.
- ; Unfortunately, for a recursive, non-authoritative
- ; query there is no such thing as an NXDOMAIN reply,
- ; which technically means we can't draw any
- ; conclusions. However, in practice that means the
- ; domain doesn't exist. If this turns out to be a
- ; problem, we may want to add code to go through all
- ; the servers before giving up.
-
- ; If the DNS server wasn't capable of recursion, and
- ; isn't capable of giving us an authoritative reply
- ; (i.e. neither AA or RA set), then at least try a
- ; different setver...
- */
- if (hd2->flags == htons(0x480))
- continue;
-
- break; /* failed */
+ /* Make sure we have at least one valid DNS server */
+ if (!dns_getserver(0).addr)
+ return 0;
- again:
- continue;
+ /* Is it a local (unqualified) domain name? */
+ if (!strchr(name, '.') && LocalDomain[0]) {
+ snprintf(fullname, sizeof fullname, "%s.%s", name, LocalDomain);
+ name = fullname;
}
-done:
- free_port(local_port); /* Return port number to the free pool */
+ err = netconn_gethostbyname(name, &ip);
+ if (err)
+ return 0;
- return result;
+ return ip.addr;
}
+/*
+ * the one should be called from ASM file
+ */
void pm_pxe_dns_resolv(com32sys_t *regs)
{
const char *name = MK_PTR(regs->ds, regs->esi.w[0]);
diff --git a/core/fs/pxe/ftp.c b/core/fs/pxe/ftp.c
new file mode 100644
index 00000000..50c92577
--- /dev/null
+++ b/core/fs/pxe/ftp.c
@@ -0,0 +1,280 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * ftp.c
+ */
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <minmax.h>
+#include <sys/cpu.h>
+#include <netinet/in.h>
+#include <lwip/api.h>
+#include "core.h"
+#include "fs.h"
+#include "pxe.h"
+#include "thread.h"
+#include "url.h"
+
+static int ftp_cmd_response(struct inode *inode, const char *cmd,
+ const char *cmd_arg,
+ uint8_t *pasv_data, int *pn_ptr)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ int c;
+ int pos, code;
+ int pb, pn;
+ bool ps;
+ bool first_line, done;
+ err_t err;
+ char cmd_buf[4096];
+ int cmd_len;
+ const char *p;
+ char *q;
+
+ if (cmd) {
+ cmd_len = strlcpy(cmd_buf, cmd, sizeof cmd_buf);
+ if (cmd_len >= sizeof cmd_buf - 3)
+ return -1;
+ q = cmd_buf + cmd_len;
+
+ if (cmd_arg) {
+ p = cmd_arg;
+
+ *q++ = ' ';
+ cmd_len++;
+ while (*p) {
+ if (++cmd_len < sizeof cmd_buf) *q++ = *p;
+ if (*p == '\r')
+ if (++cmd_len < sizeof cmd_buf) *q++ = '\0';
+ p++;
+ }
+
+ if (cmd_len >= sizeof cmd_buf - 2)
+ return -1;
+ }
+
+ *q++ = '\r';
+ *q++ = '\n';
+ cmd_len += 2;
+
+ err = netconn_write(socket->conn, cmd_buf, cmd_len, NETCONN_COPY);
+ if (err)
+ return -1;
+ }
+
+ pos = code = pn = pb = 0;
+ ps = false;
+ first_line = true;
+ done = false;
+
+ while ((c = pxe_getc(inode)) >= 0) {
+ if (c == '\n') {
+ if (done) {
+ if (pn) {
+ pn += ps;
+ if (pn_ptr)
+ *pn_ptr = pn;
+ }
+ return code;
+ }
+ pos = code = 0;
+ first_line = false;
+ continue;
+ }
+
+ switch (pos++) {
+ case 0:
+ case 1:
+ case 2:
+ if (c < '0' || c > '9') {
+ if (first_line)
+ return -1;
+ else
+ pos = 4; /* Skip this line */
+ } else {
+ code = (code*10) + (c - '0');
+ }
+ break;
+
+ case 3:
+ pn = pb = 0;
+ ps = false;
+ if (c == ' ')
+ done = true;
+ else if (c == '-')
+ done = false;
+ else if (first_line)
+ return -1;
+ else
+ done = false;
+ break;
+
+ default:
+ if (pasv_data) {
+ if (c >= '0' && c <= '9') {
+ pb = (pb*10) + (c-'0');
+ if (pn < 6)
+ pasv_data[pn] = pb;
+ ps = true;
+ } else if (c == ',') {
+ pn++;
+ pb = 0;
+ ps = false;
+ } else if (pn) {
+ pn += ps;
+ if (pn_ptr)
+ *pn_ptr = pn;
+ pn = pb = 0;
+ ps = false;
+ }
+ }
+ break;
+ }
+ }
+
+ return -1;
+}
+
+static void ftp_free(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ if (socket->ctl) {
+ tcp_close_file(socket->ctl);
+ free_socket(socket->ctl);
+ socket->ctl = NULL;
+ }
+ tcp_close_file(inode);
+}
+
+static void ftp_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ struct pxe_pvt_inode *ctlsock;
+ int resp;
+
+ ctlsock = socket->ctl ? PVT(socket->ctl) : NULL;
+ if (ctlsock->conn) {
+ resp = ftp_cmd_response(socket->ctl, "QUIT", NULL, NULL, NULL);
+ while (resp == 226) {
+ resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
+ }
+ }
+ ftp_free(inode);
+}
+
+static const struct pxe_conn_ops ftp_conn_ops = {
+ .fill_buffer = tcp_fill_buffer,
+ .close = ftp_close_file,
+ .readdir = ftp_readdir,
+};
+
+void ftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ struct pxe_pvt_inode *ctlsock;
+ struct ip_addr addr;
+ uint8_t pasv_data[6];
+ int pasv_bytes;
+ int resp;
+ err_t err;
+
+ (void)redir; /* FTP does not redirect */
+
+ inode->size = 0;
+
+ if (!url->port)
+ url->port = 21;
+
+ url_unescape(url->path, 0);
+
+ socket->ops = &ftp_conn_ops;
+
+ /* Set up the control connection */
+ socket->ctl = alloc_inode(inode->fs, 0, sizeof(struct pxe_pvt_inode));
+ if (!socket->ctl)
+ return;
+ ctlsock = PVT(socket->ctl);
+ ctlsock->ops = &tcp_conn_ops; /* The control connection is just TCP */
+ ctlsock->conn = netconn_new(NETCONN_TCP);
+ if (!ctlsock->conn)
+ goto err_free;
+ addr.addr = url->ip;
+ err = netconn_connect(ctlsock->conn, &addr, url->port);
+ if (err)
+ goto err_delete;
+
+ do {
+ resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
+ } while (resp == 120);
+ if (resp != 220)
+ goto err_disconnect;
+
+ if (!url->user)
+ url->user = "anonymous";
+ if (!url->passwd)
+ url->passwd = "syslinux@";
+
+ resp = ftp_cmd_response(socket->ctl, "USER", url->user, NULL, NULL);
+ if (resp != 202 && resp != 230) {
+ if (resp != 331)
+ goto err_disconnect;
+
+ resp = ftp_cmd_response(socket->ctl, "PASS", url->passwd, NULL, NULL);
+ if (resp != 230)
+ goto err_disconnect;
+ }
+
+ if (!(flags & O_DIRECTORY)) {
+ resp = ftp_cmd_response(socket->ctl, "TYPE", "I", NULL, NULL);
+ if (resp != 200)
+ goto err_disconnect;
+ }
+
+ resp = ftp_cmd_response(socket->ctl, "PASV", NULL, pasv_data, &pasv_bytes);
+ if (resp != 227 || pasv_bytes != 6)
+ goto err_disconnect;
+
+ socket->conn = netconn_new(NETCONN_TCP);
+ if (!socket->conn)
+ goto err_disconnect;
+ err = netconn_connect(socket->conn, (struct ip_addr *)&pasv_data[0],
+ ntohs(*(uint16_t *)&pasv_data[4]));
+ if (err)
+ goto err_disconnect;
+
+ resp = ftp_cmd_response(socket->ctl,
+ (flags & O_DIRECTORY) ? "LIST" : "RETR",
+ url->path, NULL, NULL);
+ if (resp != 125 && resp != 150)
+ goto err_disconnect;
+
+ inode->size = -1;
+ return; /* Sucess! */
+
+err_disconnect:
+ if (ctlsock->conn)
+ netconn_write(ctlsock->conn, "QUIT\r\n", 6, NETCONN_NOCOPY);
+ if (socket->conn)
+ netconn_delete(socket->conn);
+ if (ctlsock->buf)
+ netbuf_delete(ctlsock->buf);
+err_delete:
+ if (ctlsock->conn)
+ netconn_delete(ctlsock->conn);
+err_free:
+ free_socket(socket->ctl);
+}
diff --git a/core/fs/pxe/ftp_readdir.c b/core/fs/pxe/ftp_readdir.c
new file mode 100644
index 00000000..6b87f77e
--- /dev/null
+++ b/core/fs/pxe/ftp_readdir.c
@@ -0,0 +1,141 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * ftp_readdir.c
+ */
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <dprintf.h>
+#include "pxe.h"
+
+static int dirtype(char type)
+{
+ switch (type) {
+ case 'f':
+ return DT_FIFO;
+ case 'c':
+ return DT_CHR;
+ case 'd':
+ return DT_DIR;
+ case 'b':
+ return DT_BLK;
+ case '-':
+ case '0' ... '9': /* Some DOS FTP stacks */
+ return DT_REG;
+ case 'l':
+ return DT_LNK;
+ case 's':
+ return DT_SOCK;
+ default:
+ return DT_UNKNOWN;
+ }
+}
+
+int ftp_readdir(struct inode *inode, struct dirent *dirent)
+{
+ char bufs[2][FILENAME_MAX + 1];
+ int nbuf = 0;
+ char *buf = bufs[nbuf];
+ char *p = buf;
+ char *name = NULL;
+ char type;
+ int c;
+ int dt;
+ bool was_cr = false;
+ bool first = true;
+
+ for (;;) {
+ type = 0;
+
+ for (;;) {
+ c = pxe_getc(inode);
+ if (c == -1)
+ return -1; /* Nothing else there */
+
+ if (c == '\r') {
+ was_cr = true;
+ continue;
+ }
+ if (was_cr) {
+ if (c == '\n') {
+ if (!name) {
+ *p = '\0';
+ name = buf;
+ }
+ break; /* End of line */
+ }
+ else if (c == '\0')
+ c = '\r';
+ }
+ was_cr = false;
+
+ if (c == ' ' || c == '\t') {
+ if (!name) {
+ *p = '\0';
+ if (first) {
+ if (p == buf) {
+ /* Name started with whitespace - skip line */
+ name = buf;
+ } else if ((p = strchr(buf, ';'))) {
+ /* VMS/Multinet format */
+ if (p > buf+4 && !memcmp(p-4, ".DIR", 4)) {
+ type = 'd';
+ p -= 4;
+ } else {
+ type = 'f';
+ }
+ *p = '\0';
+ name = buf;
+ } else {
+ type = buf[0];
+ }
+ first = false;
+ } else {
+ /* Not the first word */
+ if ((type >= '0' && type <= '9') &&
+ !strcmp(buf, "<DIR>")) {
+ /* Some DOS FTP servers */
+ type = 'd';
+ } else if (type == 'l' && !strcmp(buf, "->")) {
+ /* The name was the previous word */
+ name = bufs[nbuf ^ 1];
+ }
+ }
+ nbuf ^= 1;
+ p = buf = bufs[nbuf];
+ }
+ } else {
+ if (!name && p < buf + FILENAME_MAX)
+ *p++ = c;
+ }
+ }
+
+ dt = dirtype(type);
+ if (dt != DT_UNKNOWN) {
+ size_t len = strlen(name);
+
+ if (len <= NAME_MAX) {
+ dirent->d_type = dt;
+ dirent->d_ino = 0; /* Not applicable */
+ dirent->d_off = 0; /* Not applicable */
+ dirent->d_reclen = offsetof(struct dirent, d_name) + len+1;
+ memcpy(dirent->d_name, name, len+1);
+ return 0;
+ }
+ }
+
+ /* Otherwise try the next line... */
+ }
+}
diff --git a/core/fs/pxe/gpxeurl.c b/core/fs/pxe/gpxeurl.c
new file mode 100644
index 00000000..6bbae3c2
--- /dev/null
+++ b/core/fs/pxe/gpxeurl.c
@@ -0,0 +1,88 @@
+#include "pxe.h"
+#if GPXE
+
+static void gpxe_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ static __lowmem struct s_PXENV_FILE_CLOSE file_close;
+
+ file_close.FileHandle = socket->tftp_remoteport;
+ pxe_call(PXENV_FILE_CLOSE, &file_close);
+}
+
+/**
+ * Get a fresh packet from a gPXE socket
+ * @param: inode -> Inode pointer
+ *
+ */
+static void gpxe_get_packet(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ static __lowmem struct s_PXENV_FILE_READ file_read;
+ int err;
+
+ while (1) {
+ file_read.FileHandle = socket->tftp_remoteport;
+ file_read.Buffer = FAR_PTR(packet_buf);
+ file_read.BufferSize = PKTBUF_SIZE;
+ err = pxe_call(PXENV_FILE_READ, &file_read);
+ if (!err) /* successed */
+ break;
+
+ if (file_read.Status != PXENV_STATUS_TFTP_OPEN)
+ kaboom();
+ }
+
+ memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize);
+
+ socket->tftp_dataptr = socket->tftp_pktbuf;
+ socket->tftp_bytesleft = file_read.BufferSize;
+ socket->tftp_filepos += file_read.BufferSize;
+
+ if (socket->tftp_bytesleft == 0)
+ inode->size = socket->tftp_filepos;
+
+ /* if we're done here, close the file */
+ if (inode->size > socket->tftp_filepos)
+ return;
+
+ /* Got EOF, close it */
+ socket->tftp_goteof = 1;
+ gpxe_close_file(inode);
+}
+
+/**
+ * Open a url using gpxe
+ *
+ * @param:inode, the inode to store our state in
+ * @param:url, the url we want to open
+ *
+ * @out: open_file_t structure, stores in file->open_file
+ * @out: the lenght of this file, stores in file->file_len
+ *
+ */
+void gpxe_open(struct inode *inode, const char *url)
+{
+ static __lowmem struct s_PXENV_FILE_OPEN file_open;
+ static char lowurl[2*FILENAME_MAX];
+ struct pxe_pvt_inode *socket = PVT(inode);
+ int err;
+
+ socket->tftp_pktbuf = malloc(PKTBUF_SIZE);
+ if (!socket->tftp_pktbuf)
+ return;
+
+ snprintf(lowurl, sizeof lowurl, "%s", url);
+ file_open.Status = PXENV_STATUS_BAD_FUNC;
+ file_open.FileName = FAR_PTR(lowurl);
+ err = pxe_call(PXENV_FILE_OPEN, &file_open);
+ if (err)
+ return;
+
+ socket->fill_buffer = gpxe_get_packet;
+ socket->close = gpxe_close_file;
+ socket->tftp_remoteport = file_open.FileHandle;
+ inode->size = -1; /* This is not an error */
+}
+
+#endif /* GPXE */
diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c
new file mode 100644
index 00000000..d5022eb2
--- /dev/null
+++ b/core/fs/pxe/http.c
@@ -0,0 +1,400 @@
+#include <syslinux/sysappend.h>
+#include <ctype.h>
+#include <lwip/api.h>
+#include "pxe.h"
+#include "../../../version.h"
+#include "url.h"
+
+#define HTTP_PORT 80
+
+static bool is_tspecial(int ch)
+{
+ bool tspecial = false;
+ switch(ch) {
+ case '(': case ')': case '<': case '>': case '@':
+ case ',': case ';': case ':': case '\\': case '"':
+ case '/': case '[': case ']': case '?': case '=':
+ case '{': case '}': case ' ': case '\t':
+ tspecial = true;
+ break;
+ }
+ return tspecial;
+}
+
+static bool is_ctl(int ch)
+{
+ return ch < 0x20;
+}
+
+static bool is_token(int ch)
+{
+ /* Can by antying except a ctl character or a tspecial */
+ return !is_ctl(ch) && !is_tspecial(ch);
+}
+
+static bool append_ch(char *str, size_t size, size_t *pos, int ch)
+{
+ bool success = true;
+ if ((*pos + 1) >= size) {
+ *pos = 0;
+ success = false;
+ } else {
+ str[*pos] = ch;
+ str[*pos + 1] = '\0';
+ *pos += 1;
+ }
+ return success;
+}
+
+static size_t cookie_len, header_len;
+static char *cookie_buf, *header_buf;
+
+__export uint32_t SendCookies = -1UL; /* Send all cookies */
+
+static size_t http_do_bake_cookies(char *q)
+{
+ static const char uchexchar[16] = "0123456789ABCDEF";
+ int i;
+ size_t n = 0;
+ const char *p;
+ char c;
+ bool first = true;
+ uint32_t mask = SendCookies;
+
+ for (i = 0; i < SYSAPPEND_MAX; i++) {
+ if ((mask & 1) && (p = sysappend_strings[i])) {
+ if (first) {
+ if (q) {
+ strcpy(q, "Cookie: ");
+ q += 8;
+ }
+ n += 8;
+ first = false;
+ }
+ if (q) {
+ strcpy(q, "_Syslinux_");
+ q += 10;
+ }
+ n += 10;
+ /* Copy string up to and including '=' */
+ do {
+ c = *p++;
+ if (q)
+ *q++ = c;
+ n++;
+ } while (c != '=');
+ while ((c = *p++)) {
+ if (c == ' ') {
+ if (q)
+ *q++ = '+';
+ n++;
+ } else if (is_token(c)) {
+ if (q)
+ *q++ = c;
+ n++;
+ } else {
+ if (q) {
+ *q++ = '%';
+ *q++ = uchexchar[c >> 4];
+ *q++ = uchexchar[c & 15];
+ }
+ n += 3;
+ }
+ }
+ if (q)
+ *q++ = ';';
+ n++;
+ }
+ mask >>= 1;
+ }
+ if (!first) {
+ if (q) {
+ *q++ = '\r';
+ *q++ = '\n';
+ }
+ n += 2;
+ }
+ if (q)
+ *q = '\0';
+
+ return n;
+}
+
+void http_bake_cookies(void)
+{
+ if (cookie_buf)
+ free(cookie_buf);
+
+ cookie_len = http_do_bake_cookies(NULL);
+ cookie_buf = malloc(cookie_len+1);
+ if (!cookie_buf) {
+ cookie_len = 0;
+ return;
+ }
+
+ if (header_buf)
+ free(header_buf);
+
+ header_len = cookie_len + 6*FILENAME_MAX + 256;
+ header_buf = malloc(header_len);
+ if (!header_buf) {
+ header_len = 0;
+ return; /* Uh-oh... */
+ }
+
+ http_do_bake_cookies(cookie_buf);
+}
+
+static const struct pxe_conn_ops http_conn_ops = {
+ .fill_buffer = tcp_fill_buffer,
+ .close = tcp_close_file,
+ .readdir = http_readdir,
+};
+
+void http_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ int header_bytes;
+ const char *next;
+ char field_name[20];
+ char field_value[1024];
+ size_t field_name_len, field_value_len;
+ err_t err;
+ enum state {
+ st_httpver,
+ st_stcode,
+ st_skipline,
+ st_fieldfirst,
+ st_fieldname,
+ st_fieldvalue,
+ st_skip_fieldname,
+ st_skip_fieldvalue,
+ st_eoh,
+ } state;
+ struct ip_addr addr;
+ static char location[FILENAME_MAX];
+ uint32_t content_length; /* same as inode->size */
+ size_t response_size;
+ int status;
+ int pos;
+
+ (void)flags;
+
+ if (!header_buf)
+ return; /* http is broken... */
+
+ /* This is a straightforward TCP connection after headers */
+ socket->ops = &http_conn_ops;
+
+ /* Reset all of the variables */
+ inode->size = content_length = -1;
+
+ /* Start the http connection */
+ socket->conn = netconn_new(NETCONN_TCP);
+ if (!socket->conn) {
+ printf("netconn_new failed\n");
+ return;
+ }
+
+ addr.addr = url->ip;
+ if (!url->port)
+ url->port = HTTP_PORT;
+
+ err = netconn_connect(socket->conn, &addr, url->port);
+ if (err) {
+ printf("netconn_connect error %d\n", err);
+ goto fail;
+ }
+
+ strcpy(header_buf, "GET /");
+ header_bytes = 5;
+ header_bytes += url_escape_unsafe(header_buf+5, url->path,
+ header_len - 5);
+ if (header_bytes >= header_len)
+ goto fail; /* Buffer overflow */
+ header_bytes += snprintf(header_buf + header_bytes,
+ header_len - header_bytes,
+ " HTTP/1.0\r\n"
+ "Host: %s\r\n"
+ "User-Agent: Syslinux/" VERSION_STR "\r\n"
+ "Connection: close\r\n"
+ "%s"
+ "\r\n",
+ url->host, cookie_buf ? cookie_buf : "");
+ if (header_bytes >= header_len)
+ goto fail; /* Buffer overflow */
+
+ err = netconn_write(socket->conn, header_buf,
+ header_bytes, NETCONN_NOCOPY);
+ if (err) {
+ printf("netconn_write error %d\n", err);
+ goto fail;
+ }
+
+ /* Parse the HTTP header */
+ state = st_httpver;
+ pos = 0;
+ status = 0;
+ response_size = 0;
+ field_value_len = 0;
+ field_name_len = 0;
+
+ while (state != st_eoh) {
+ int ch = pxe_getc(inode);
+ /* Eof before I finish paring the header */
+ if (ch == -1)
+ goto fail;
+#if 0
+ printf("%c", ch);
+#endif
+ response_size++;
+ if (ch == '\r' || ch == '\0')
+ continue;
+ switch (state) {
+ case st_httpver:
+ if (ch == ' ') {
+ state = st_stcode;
+ pos = 0;
+ }
+ break;
+
+ case st_stcode:
+ if (ch < '0' || ch > '9')
+ goto fail;
+ status = (status*10) + (ch - '0');
+ if (++pos == 3)
+ state = st_skipline;
+ break;
+
+ case st_skipline:
+ if (ch == '\n')
+ state = st_fieldfirst;
+ break;
+
+ case st_fieldfirst:
+ if (ch == '\n')
+ state = st_eoh;
+ else if (isspace(ch)) {
+ /* A continuation line */
+ state = st_fieldvalue;
+ goto fieldvalue;
+ }
+ else if (is_token(ch)) {
+ /* Process the previous field before starting on the next one */
+ if (strcasecmp(field_name, "Content-Length") == 0) {
+ next = field_value;
+ /* Skip leading whitespace */
+ while (isspace(*next))
+ next++;
+ content_length = 0;
+ for (;(*next >= '0' && *next <= '9'); next++) {
+ if ((content_length * 10) < content_length)
+ break;
+ content_length = (content_length * 10) + (*next - '0');
+ }
+ /* In the case of overflow or other error ignore
+ * Content-Length.
+ */
+ if (*next)
+ content_length = -1;
+ }
+ else if (strcasecmp(field_name, "Location") == 0) {
+ next = field_value;
+ /* Skip leading whitespace */
+ while (isspace(*next))
+ next++;
+ strlcpy(location, next, sizeof location);
+ }
+ /* Start the field name and field value afress */
+ field_name_len = 1;
+ field_name[0] = ch;
+ field_name[1] = '\0';
+ field_value_len = 0;
+ field_value[0] = '\0';
+ state = st_fieldname;
+ }
+ else /* Bogus try to recover */
+ state = st_skipline;
+ break;
+
+ case st_fieldname:
+ if (ch == ':' ) {
+ state = st_fieldvalue;
+ }
+ else if (is_token(ch)) {
+ if (!append_ch(field_name, sizeof field_name, &field_name_len, ch))
+ state = st_skip_fieldname;
+ }
+ /* Bogus cases try to recover */
+ else if (ch == '\n')
+ state = st_fieldfirst;
+ else
+ state = st_skipline;
+ break;
+
+ case st_fieldvalue:
+ if (ch == '\n')
+ state = st_fieldfirst;
+ else {
+ fieldvalue:
+ if (!append_ch(field_value, sizeof field_value, &field_value_len, ch))
+ state = st_skip_fieldvalue;
+ }
+ break;
+
+ /* For valid fields whose names are longer than I choose to support. */
+ case st_skip_fieldname:
+ if (ch == ':')
+ state = st_skip_fieldvalue;
+ else if (is_token(ch))
+ state = st_skip_fieldname;
+ /* Bogus cases try to recover */
+ else if (ch == '\n')
+ state = st_fieldfirst;
+ else
+ state = st_skipline;
+ break;
+
+ /* For valid fields whose bodies are longer than I choose to support. */
+ case st_skip_fieldvalue:
+ if (ch == '\n')
+ state = st_fieldfirst;
+ break;
+
+ case st_eoh:
+ break; /* Should never happen */
+ }
+ }
+
+ if (state != st_eoh)
+ status = 0;
+
+ switch (status) {
+ case 200:
+ /*
+ * All OK, need to mark header data consumed and set up a file
+ * structure...
+ */
+ /* Treat the remainder of the bytes as data */
+ socket->tftp_filepos -= response_size;
+ break;
+ case 301:
+ case 302:
+ case 303:
+ case 307:
+ /* A redirect */
+ if (!location[0])
+ goto fail;
+ *redir = location;
+ goto fail;
+ default:
+ goto fail;
+ break;
+ }
+ return;
+fail:
+ inode->size = 0;
+ tcp_close_file(inode);
+ return;
+}
diff --git a/core/fs/pxe/http_readdir.c b/core/fs/pxe/http_readdir.c
new file mode 100644
index 00000000..b6e480e7
--- /dev/null
+++ b/core/fs/pxe/http_readdir.c
@@ -0,0 +1,471 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <dprintf.h>
+#include "pxe.h"
+
+enum http_readdir_state {
+ st_start, /* 0 Initial state */
+ st_open, /* 1 "<" */
+ st_a, /* 2 "<a" */
+ st_attribute, /* 3 "<a " */
+ st_h, /* 4 "<a h" */
+ st_hr, /* 5 */
+ st_hre, /* 6 */
+ st_href, /* 7 */
+ st_hrefeq, /* 8 */
+ st_hrefqu, /* 9 */
+ st_badtag, /* 10 */
+ st_badtagqu, /* 11 */
+ st_badattr, /* 12 */
+ st_badattrqu, /* 13 */
+};
+
+struct machine {
+ char xchar;
+ uint8_t st_xchar;
+ uint8_t st_left; /* < */
+ uint8_t st_right; /* > */
+ uint8_t st_space; /* white */
+ uint8_t st_other; /* anything else */
+};
+
+static const struct machine statemachine[] = {
+ /* xchar st_xchar st_left st_right st_space st_other */
+ { 0, 0, st_open, st_start, st_start, st_start },
+ { 'a', st_a, st_badtag, st_start, st_open, st_badtag },
+ { 0, 0, st_open, st_open, st_attribute, st_badtag },
+ { 'h', st_h, st_open, st_start, st_attribute, st_badattr },
+ { 'r', st_hr, st_open, st_start, st_attribute, st_badattr },
+ { 'e', st_hre, st_open, st_start, st_attribute, st_badattr },
+ { 'f', st_href, st_open, st_start, st_attribute, st_badattr },
+ { '=', st_hrefeq, st_open, st_start, st_attribute, st_badattr },
+ { '\"', st_hrefqu, st_open, st_start, st_attribute, st_hrefeq },
+ { '\"', st_attribute, st_hrefqu, st_hrefqu, st_hrefqu, st_hrefqu },
+ { '\"', st_badtagqu, st_open, st_start, st_badtag, st_badtag },
+ { '\"', st_badtag, st_badtagqu, st_badtagqu, st_badtagqu, st_badtagqu },
+ { '\"', st_badattrqu, st_open, st_start, st_attribute, st_badattr },
+ { '\"', st_attribute, st_badattrqu, st_badattrqu, st_badattrqu, st_badattrqu },
+};
+
+struct html_entity {
+ uint16_t ucs;
+ const char entity[9];
+};
+
+static const struct html_entity entities[] = {
+ { 34, "quot" },
+ { 38, "amp" },
+ { 60, "lt" },
+ { 62, "gt" },
+#ifdef HTTP_ALL_ENTITIES
+ { 160, "nbsp" },
+ { 161, "iexcl" },
+ { 162, "cent" },
+ { 163, "pound" },
+ { 164, "curren" },
+ { 165, "yen" },
+ { 166, "brvbar" },
+ { 167, "sect" },
+ { 168, "uml" },
+ { 169, "copy" },
+ { 170, "ordf" },
+ { 171, "laquo" },
+ { 172, "not" },
+ { 173, "shy" },
+ { 174, "reg" },
+ { 175, "macr" },
+ { 176, "deg" },
+ { 177, "plusmn" },
+ { 178, "sup2" },
+ { 179, "sup3" },
+ { 180, "acute" },
+ { 181, "micro" },
+ { 182, "para" },
+ { 183, "middot" },
+ { 184, "cedil" },
+ { 185, "sup1" },
+ { 186, "ordm" },
+ { 187, "raquo" },
+ { 188, "frac14" },
+ { 189, "frac12" },
+ { 190, "frac34" },
+ { 191, "iquest" },
+ { 192, "Agrave" },
+ { 193, "Aacute" },
+ { 194, "Acirc" },
+ { 195, "Atilde" },
+ { 196, "Auml" },
+ { 197, "Aring" },
+ { 198, "AElig" },
+ { 199, "Ccedil" },
+ { 200, "Egrave" },
+ { 201, "Eacute" },
+ { 202, "Ecirc" },
+ { 203, "Euml" },
+ { 204, "Igrave" },
+ { 205, "Iacute" },
+ { 206, "Icirc" },
+ { 207, "Iuml" },
+ { 208, "ETH" },
+ { 209, "Ntilde" },
+ { 210, "Ograve" },
+ { 211, "Oacute" },
+ { 212, "Ocirc" },
+ { 213, "Otilde" },
+ { 214, "Ouml" },
+ { 215, "times" },
+ { 216, "Oslash" },
+ { 217, "Ugrave" },
+ { 218, "Uacute" },
+ { 219, "Ucirc" },
+ { 220, "Uuml" },
+ { 221, "Yacute" },
+ { 222, "THORN" },
+ { 223, "szlig" },
+ { 224, "agrave" },
+ { 225, "aacute" },
+ { 226, "acirc" },
+ { 227, "atilde" },
+ { 228, "auml" },
+ { 229, "aring" },
+ { 230, "aelig" },
+ { 231, "ccedil" },
+ { 232, "egrave" },
+ { 233, "eacute" },
+ { 234, "ecirc" },
+ { 235, "euml" },
+ { 236, "igrave" },
+ { 237, "iacute" },
+ { 238, "icirc" },
+ { 239, "iuml" },
+ { 240, "eth" },
+ { 241, "ntilde" },
+ { 242, "ograve" },
+ { 243, "oacute" },
+ { 244, "ocirc" },
+ { 245, "otilde" },
+ { 246, "ouml" },
+ { 247, "divide" },
+ { 248, "oslash" },
+ { 249, "ugrave" },
+ { 250, "uacute" },
+ { 251, "ucirc" },
+ { 252, "uuml" },
+ { 253, "yacute" },
+ { 254, "thorn" },
+ { 255, "yuml" },
+ { 338, "OElig" },
+ { 339, "oelig" },
+ { 352, "Scaron" },
+ { 353, "scaron" },
+ { 376, "Yuml" },
+ { 402, "fnof" },
+ { 710, "circ" },
+ { 732, "tilde" },
+ { 913, "Alpha" },
+ { 914, "Beta" },
+ { 915, "Gamma" },
+ { 916, "Delta" },
+ { 917, "Epsilon" },
+ { 918, "Zeta" },
+ { 919, "Eta" },
+ { 920, "Theta" },
+ { 921, "Iota" },
+ { 922, "Kappa" },
+ { 923, "Lambda" },
+ { 924, "Mu" },
+ { 925, "Nu" },
+ { 926, "Xi" },
+ { 927, "Omicron" },
+ { 928, "Pi" },
+ { 929, "Rho" },
+ { 931, "Sigma" },
+ { 932, "Tau" },
+ { 933, "Upsilon" },
+ { 934, "Phi" },
+ { 935, "Chi" },
+ { 936, "Psi" },
+ { 937, "Omega" },
+ { 945, "alpha" },
+ { 946, "beta" },
+ { 947, "gamma" },
+ { 948, "delta" },
+ { 949, "epsilon" },
+ { 950, "zeta" },
+ { 951, "eta" },
+ { 952, "theta" },
+ { 953, "iota" },
+ { 954, "kappa" },
+ { 955, "lambda" },
+ { 956, "mu" },
+ { 957, "nu" },
+ { 958, "xi" },
+ { 959, "omicron" },
+ { 960, "pi" },
+ { 961, "rho" },
+ { 962, "sigmaf" },
+ { 963, "sigma" },
+ { 964, "tau" },
+ { 965, "upsilon" },
+ { 966, "phi" },
+ { 967, "chi" },
+ { 968, "psi" },
+ { 969, "omega" },
+ { 977, "thetasym" },
+ { 978, "upsih" },
+ { 982, "piv" },
+ { 8194, "ensp" },
+ { 8195, "emsp" },
+ { 8201, "thinsp" },
+ { 8204, "zwnj" },
+ { 8205, "zwj" },
+ { 8206, "lrm" },
+ { 8207, "rlm" },
+ { 8211, "ndash" },
+ { 8212, "mdash" },
+ { 8216, "lsquo" },
+ { 8217, "rsquo" },
+ { 8218, "sbquo" },
+ { 8220, "ldquo" },
+ { 8221, "rdquo" },
+ { 8222, "bdquo" },
+ { 8224, "dagger" },
+ { 8225, "Dagger" },
+ { 8226, "bull" },
+ { 8230, "hellip" },
+ { 8240, "permil" },
+ { 8242, "prime" },
+ { 8243, "Prime" },
+ { 8249, "lsaquo" },
+ { 8250, "rsaquo" },
+ { 8254, "oline" },
+ { 8260, "frasl" },
+ { 8364, "euro" },
+ { 8465, "image" },
+ { 8472, "weierp" },
+ { 8476, "real" },
+ { 8482, "trade" },
+ { 8501, "alefsym" },
+ { 8592, "larr" },
+ { 8593, "uarr" },
+ { 8594, "rarr" },
+ { 8595, "darr" },
+ { 8596, "harr" },
+ { 8629, "crarr" },
+ { 8656, "lArr" },
+ { 8657, "uArr" },
+ { 8658, "rArr" },
+ { 8659, "dArr" },
+ { 8660, "hArr" },
+ { 8704, "forall" },
+ { 8706, "part" },
+ { 8707, "exist" },
+ { 8709, "empty" },
+ { 8711, "nabla" },
+ { 8712, "isin" },
+ { 8713, "notin" },
+ { 8715, "ni" },
+ { 8719, "prod" },
+ { 8721, "sum" },
+ { 8722, "minus" },
+ { 8727, "lowast" },
+ { 8730, "radic" },
+ { 8733, "prop" },
+ { 8734, "infin" },
+ { 8736, "ang" },
+ { 8743, "and" },
+ { 8744, "or" },
+ { 8745, "cap" },
+ { 8746, "cup" },
+ { 8747, "int" },
+ { 8756, "there4" },
+ { 8764, "sim" },
+ { 8773, "cong" },
+ { 8776, "asymp" },
+ { 8800, "ne" },
+ { 8801, "equiv" },
+ { 8804, "le" },
+ { 8805, "ge" },
+ { 8834, "sub" },
+ { 8835, "sup" },
+ { 8836, "nsub" },
+ { 8838, "sube" },
+ { 8839, "supe" },
+ { 8853, "oplus" },
+ { 8855, "otimes" },
+ { 8869, "perp" },
+ { 8901, "sdot" },
+ { 8968, "lceil" },
+ { 8969, "rceil" },
+ { 8970, "lfloor" },
+ { 8971, "rfloor" },
+ { 9001, "lang" },
+ { 9002, "rang" },
+ { 9674, "loz" },
+ { 9824, "spades" },
+ { 9827, "clubs" },
+ { 9829, "hearts" },
+ { 9830, "diams" },
+#endif /* HTTP_ALL_ENTITIES */
+ { 0, "" }
+};
+
+struct entity_state {
+ char entity_buf[16];
+ char *ep;
+};
+
+static char *emit(char *p, int c, struct entity_state *st)
+{
+ const struct html_entity *ent;
+ unsigned int ucs;
+
+ if (!st->ep) {
+ if (c == '&') {
+ /* Entity open */
+ st->ep = st->entity_buf;
+ } else {
+ *p++ = c;
+ }
+ } else {
+ if (c == ';') {
+ st->ep = NULL;
+ *p = '\0';
+ if (st->entity_buf[0] == '#') {
+ if ((st->entity_buf[1] | 0x20)== 'x') {
+ ucs = strtoul(st->entity_buf + 2, NULL, 16);
+ } else {
+ ucs = strtoul(st->entity_buf + 1, NULL, 10);
+ }
+ } else {
+ for (ent = entities; ent->ucs; ent++) {
+ if (!strcmp(st->entity_buf, ent->entity))
+ break;
+ }
+ ucs = ent->ucs;
+ }
+ if (ucs < 32 || ucs >= 0x10ffff)
+ return p; /* Bogus */
+ if (ucs >= 0x10000) {
+ *p++ = 0xf0 + (ucs >> 18);
+ *p++ = 0x80 + ((ucs >> 12) & 0x3f);
+ *p++ = 0x80 + ((ucs >> 6) & 0x3f);
+ *p++ = 0x80 + (ucs & 0x3f);
+ } else if (ucs >= 0x800) {
+ *p++ = 0xe0 + (ucs >> 12);
+ *p++ = 0x80 + ((ucs >> 6) & 0x3f);
+ *p++ = 0x80 + (ucs & 0x3f);
+ } else if (ucs >= 0x80) {
+ *p++ = 0xc0 + (ucs >> 6);
+ *p++ = 0x80 + (ucs & 0x3f);
+ } else {
+ *p++ = ucs;
+ }
+ } else if (st->ep < st->entity_buf + sizeof st->entity_buf - 1) {
+ *st->ep++ = c;
+ }
+ }
+ return p;
+}
+
+static const char *http_get_filename(struct inode *inode, char *buf)
+{
+ int c, lc;
+ char *p;
+ const struct machine *sm;
+ struct entity_state es;
+ enum http_readdir_state state = st_start;
+ enum http_readdir_state pstate = st_start;
+
+ memset(&es, 0, sizeof es);
+
+ p = buf;
+ for (;;) {
+ c = pxe_getc(inode);
+ if (c == -1)
+ return NULL;
+
+ lc = tolower(c);
+
+ sm = &statemachine[state];
+
+ if (lc == sm->xchar)
+ state = sm->st_xchar;
+ else if (c == '<')
+ state = sm->st_left;
+ else if (c == '>')
+ state = sm->st_right;
+ else if (isspace(c))
+ state = sm->st_space;
+ else
+ state = sm->st_other;
+
+ if (state == st_hrefeq || state == st_hrefqu) {
+ if (state != pstate)
+ p = buf;
+ else if (p < buf + FILENAME_MAX)
+ p = emit(p, c, &es);
+ pstate = state;
+ } else {
+ if (pstate != st_start)
+ pstate = st_start;
+ if (p != buf && state == st_start) {
+ *p = '\0';
+ return buf;
+ }
+ }
+ }
+}
+
+int http_readdir(struct inode *inode, struct dirent *dirent)
+{
+ char buf[FILENAME_MAX + 6];
+ const char *fn, *sp;
+
+ for (;;) {
+ fn = http_get_filename(inode, buf);
+
+ if (!fn)
+ return -1; /* End of directory */
+
+ /* Ignore entries with http special characters */
+ if (strchr(fn, '#'))
+ continue;
+ if (strchr(fn, '?'))
+ continue;
+
+ /* A slash if present has to be the last character, and not the first */
+ sp = strchr(fn, '/');
+ if (sp) {
+ if (sp == fn || sp[1])
+ continue;
+ } else {
+ sp = strchr(fn, '\0');
+ }
+
+ if (sp > fn + NAME_MAX)
+ continue;
+
+ dirent->d_ino = 0; /* Not applicable */
+ dirent->d_off = 0; /* Not applicable */
+ dirent->d_reclen = offsetof(struct dirent, d_name) + (sp-fn) + 1;
+ dirent->d_type = *sp == '/' ? DT_DIR : DT_REG;
+ memcpy(dirent->d_name, fn, sp-fn);
+ dirent->d_name[sp-fn] = '\0';
+ return 0;
+ }
+}
diff --git a/core/fs/pxe/idle.c b/core/fs/pxe/idle.c
index 52a87c34..1d1bb8bc 100644
--- a/core/fs/pxe/idle.c
+++ b/core/fs/pxe/idle.c
@@ -19,91 +19,8 @@
#include <sys/cpu.h>
#include "pxe.h"
-static int pxe_idle_poll(void)
-{
- static __lowmem char junk_pkt[PKTBUF_SIZE];
- static __lowmem t_PXENV_UDP_READ read_buf;
-
- memset(&read_buf, 0, sizeof read_buf);
-
- read_buf.src_ip = 0; /* Any destination */
- read_buf.dest_ip = IPInfo.myip;
- read_buf.s_port = 0; /* Any source port */
- read_buf.d_port = htons(9); /* Discard port (not used...) */
- read_buf.buffer_size = sizeof junk_pkt;
- read_buf.buffer = FAR_PTR(junk_pkt);
-
- pxe_call(PXENV_UDP_READ, &read_buf);
-
- return 0;
-}
-
-static uint32_t pxe_detect_nic_type(void)
-{
- static __lowmem t_PXENV_UNDI_GET_NIC_TYPE nic_type;
-
- if (pxe_call(PXENV_UNDI_GET_NIC_TYPE, &nic_type))
- return -1; /* Unknown NIC */
-
- if (nic_type.NicType != PCI_NIC && nic_type.NicType != CardBus_NIC)
- return -1; /* Not a PCI NIC */
-
- /*
- * Return VID:DID as a single number, with the VID in the high word
- * -- this is opposite from the usual order, but it makes it easier to
- * enforce that the table is sorted.
- */
- return (nic_type.info.pci.Vendor_ID << 16) + nic_type.info.pci.Dev_ID;
-}
-
-#define PCI_DEV(vid, did) (((vid) << 16) + (did))
-
-/* This array should be sorted!! */
-static const uint32_t pxe_need_idle_drain[] =
-{
- /*
- * Older Broadcom NICs: they need receive calls on idle to avoid
- * FIFO stalls.
- */
- PCI_DEV(0x14e4, 0x1659), /* BCM5721 */
- PCI_DEV(0x14e4, 0x165a), /* BCM5722 */
- PCI_DEV(0x14e4, 0x165b), /* BCM5723 */
- PCI_DEV(0x14e4, 0x1668), /* BCM5714 */
- PCI_DEV(0x14e4, 0x1669), /* BCM5714S */
- PCI_DEV(0x14e4, 0x166a), /* BCM5780 */
- PCI_DEV(0x14e4, 0x1673), /* BCM5755M */
- PCI_DEV(0x14e4, 0x1674), /* BCM5756ME */
- PCI_DEV(0x14e4, 0x1678), /* BCM5715 */
- PCI_DEV(0x14e4, 0x1679), /* BCM5715S */
- PCI_DEV(0x14e4, 0x167b), /* BCM5755 */
-};
-
void pxe_idle_init(void)
{
- uint32_t dev_id = pxe_detect_nic_type();
- int l, h;
- bool found;
-
- l = 0;
- h = sizeof pxe_need_idle_drain / sizeof pxe_need_idle_drain[0] - 1;
-
- found = false;
- while (h >= l) {
- int x = (l+h) >> 1;
- uint32_t id = pxe_need_idle_drain[x];
-
- if (id == dev_id) {
- found = true;
- break;
- } else if (id < dev_id) {
- l = x+1;
- } else {
- h = x-1;
- }
- }
-
- if (found)
- idle_hook_func = pxe_idle_poll;
}
void pxe_idle_cleanup(void)
diff --git a/core/fs/pxe/isr.c b/core/fs/pxe/isr.c
new file mode 100644
index 00000000..069fefd5
--- /dev/null
+++ b/core/fs/pxe/isr.c
@@ -0,0 +1,278 @@
+/*
+ * core/fs/pxe/isr.c
+ *
+ * Stub invoked on return from real mode including from an interrupt.
+ * Interrupts are locked out on entry.
+ */
+
+#include "core.h"
+#include "thread.h"
+#include "pxe.h"
+#include <string.h>
+#include <sys/cpu.h>
+#include <sys/io.h>
+
+extern uint8_t pxe_irq_pending;
+extern volatile uint8_t pxe_need_poll;
+static DECLARE_INIT_SEMAPHORE(pxe_receive_thread_sem, 0);
+static DECLARE_INIT_SEMAPHORE(pxe_poll_thread_sem, 0);
+static struct thread *pxe_thread, *poll_thread;
+
+/*
+ * Note: this *must* be called with interrupts enabled.
+ */
+static bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old)
+{
+ far_ptr_t *entry;
+ unsigned int vec;
+ uint8_t mask, mymask;
+ uint32_t now;
+ bool ok;
+
+ if (irq < 8)
+ vec = irq + 0x08;
+ else if (irq < 16)
+ vec = (irq - 8) + 0x70;
+ else
+ return false;
+
+ cli();
+
+ if (pxe_need_poll) {
+ sti();
+ return false;
+ }
+
+ entry = (far_ptr_t *)(vec << 2);
+ *old = *entry;
+ entry->ptr = (uint32_t)isr;
+
+ /* Enable this interrupt at the PIC level, just in case... */
+ mymask = ~(1 << (irq & 7));
+ if (irq >= 8) {
+ mask = inb(0x21);
+ mask &= ~(1 << 2); /* Enable cascade */
+ outb(mask, 0x21);
+ mask = inb(0xa1);
+ mask &= mymask;
+ outb(mask, 0xa1);
+ } else {
+ mask = inb(0x21);
+ mask &= mymask;
+ outb(mask, 0x21);
+ }
+
+ sti();
+
+ now = jiffies();
+
+ /* Some time to watch for stuck interrupts */
+ while (jiffies() - now < 4 && (ok = !pxe_need_poll))
+ hlt();
+
+ if (!ok)
+ *entry = *old; /* Restore the old vector */
+
+ printf("UNDI: IRQ %d(0x%02x): %04x:%04x -> %04x:%04x\n", irq, vec,
+ old->seg, old->offs, entry->seg, entry->offs);
+
+ return ok;
+}
+
+static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old)
+{
+ far_ptr_t *entry;
+ unsigned int vec;
+ bool rv;
+
+ if (!irq)
+ return true; /* Nothing to uninstall */
+
+ if (irq < 8)
+ vec = irq + 0x08;
+ else if (irq < 16)
+ vec = (irq - 8) + 0x70;
+ else
+ return false;
+
+ cli();
+
+ entry = (far_ptr_t *)(vec << 2);
+
+ if (entry->ptr != (uint32_t)isr) {
+ rv = false;
+ } else {
+ *entry = *old;
+ rv = true;
+ }
+
+ sti();
+ return rv;
+}
+
+static void pxe_poll_wakeups(void)
+{
+ static jiffies_t last_jiffies = 0;
+ jiffies_t now = jiffies();
+
+ if (pxe_need_poll == 1) {
+ /* If we need polling now, activate polling */
+ pxe_need_poll = 3;
+ sem_up(&pxe_poll_thread_sem);
+ }
+
+ if (now != last_jiffies) {
+ last_jiffies = now;
+ __thread_process_timeouts();
+ }
+
+ if (pxe_irq_pending) {
+ pxe_irq_pending = 0;
+ sem_up(&pxe_receive_thread_sem);
+ }
+}
+
+static void pxe_process_irq(void)
+{
+ static __lowmem t_PXENV_UNDI_ISR isr;
+
+ uint16_t func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */
+ bool done = false;
+
+ while (!done) {
+ memset(&isr, 0, sizeof isr);
+ isr.FuncFlag = func;
+ func = PXENV_UNDI_ISR_IN_GET_NEXT; /* Next time */
+
+ pxe_call(PXENV_UNDI_ISR, &isr);
+
+ switch (isr.FuncFlag) {
+ case PXENV_UNDI_ISR_OUT_DONE:
+ done = true;
+ break;
+
+ case PXENV_UNDI_ISR_OUT_TRANSMIT:
+ /* Transmit complete - nothing for us to do */
+ break;
+
+ case PXENV_UNDI_ISR_OUT_RECEIVE:
+ undiif_input(&isr);
+ break;
+
+ case PXENV_UNDI_ISR_OUT_BUSY:
+ /* ISR busy, this should not happen */
+ done = true;
+ break;
+
+ default:
+ /* Invalid return code, this should not happen */
+ done = true;
+ break;
+ }
+ }
+}
+
+static void pxe_receive_thread(void *dummy)
+{
+ (void)dummy;
+
+ for (;;) {
+ sem_down(&pxe_receive_thread_sem, 0);
+ pxe_process_irq();
+ }
+}
+
+static bool pxe_isr_poll(void)
+{
+ static __lowmem t_PXENV_UNDI_ISR isr;
+
+ isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
+ pxe_call(PXENV_UNDI_ISR, &isr);
+
+ return isr.FuncFlag == PXENV_UNDI_ISR_OUT_OURS;
+}
+
+static void pxe_poll_thread(void *dummy)
+{
+ (void)dummy;
+
+ /* Block indefinitely unless activated */
+ sem_down(&pxe_poll_thread_sem, 0);
+
+ for (;;) {
+ cli();
+ if (pxe_receive_thread_sem.count < 0 && pxe_isr_poll())
+ sem_up(&pxe_receive_thread_sem);
+ else
+ __schedule();
+ sti();
+ cpu_relax();
+ }
+}
+
+/*
+ * This does preparations and enables the PXE thread
+ */
+void pxe_init_isr(void)
+{
+ start_idle_thread();
+ sched_hook_func = pxe_poll_wakeups;
+ /*
+ * Run the pxe receive thread at elevated priority, since the UNDI
+ * stack is likely to have very limited memory available; therefore to
+ * avoid packet loss we need to move it into memory that we ourselves
+ * manage, as soon as possible.
+ */
+ core_pm_hook = __schedule;
+
+ pxe_thread = start_thread("pxe receive", 16384, -20,
+ pxe_receive_thread, NULL);
+}
+
+/*
+ * Actually start the interrupt routine inside the UNDI stack
+ */
+void pxe_start_isr(void)
+{
+ int irq = pxe_undi_info.IntNumber;
+
+ if (irq == 2)
+ irq = 9; /* IRQ 2 is really IRQ 9 */
+ else if (irq > 15)
+ irq = 0; /* Invalid IRQ */
+
+ pxe_irq_vector = irq;
+
+ if (irq) {
+ if (!install_irq_vector(irq, pxe_isr, &pxe_irq_chain))
+ irq = 0; /* Install failed or stuck interrupt */
+ }
+
+ poll_thread = start_thread("pxe poll", 4096, POLL_THREAD_PRIORITY,
+ pxe_poll_thread, NULL);
+
+ if (!irq || !(pxe_undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ))
+ asm volatile("orb $1,%0" : "+m" (pxe_need_poll));
+}
+
+int reset_pxe(void)
+{
+ static __lowmem struct s_PXENV_UNDI_CLOSE undi_close;
+
+ sched_hook_func = NULL;
+ core_pm_hook = core_pm_null_hook;
+ kill_thread(pxe_thread);
+
+ memset(&undi_close, 0, sizeof(undi_close));
+ pxe_call(PXENV_UNDI_CLOSE, &undi_close);
+
+ if (undi_close.Status)
+ printf("PXENV_UNDI_CLOSE failed: 0x%x\n", undi_close.Status);
+
+ if (pxe_irq_vector)
+ uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain);
+ if (poll_thread)
+ kill_thread(poll_thread);
+
+ return undi_close.Status;
+}
diff --git a/core/fs/pxe/portnum.c b/core/fs/pxe/portnum.c
deleted file mode 100644
index 19af0cd0..00000000
--- a/core/fs/pxe/portnum.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2010 Intel Corporation; author: H. Peter Anvin
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
- * Boston MA 02110-1301, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-#include <stdint.h>
-#include <stdbool.h>
-#include <netinet/in.h>
-#include "pxe.h"
-
-/* Port number bitmap - port numbers 49152 (0xc000) to 57343 (0xefff) */
-#define PORT_NUMBER_BASE 49152
-#define PORT_NUMBER_COUNT 8192 /* Power of 2, please */
-static uint32_t port_number_bitmap[PORT_NUMBER_COUNT/32];
-static uint16_t first_port_number /* = 0 */;
-
-/*
- * Bitmap functions
- */
-static bool test_bit(const uint32_t *bitmap, int32_t index)
-{
- uint8_t st;
- asm("btl %2,%1 ; setc %0" : "=qm" (st) : "m" (*bitmap), "r" (index));
- return st;
-}
-
-static void set_bit(uint32_t *bitmap, int32_t index)
-{
- asm volatile("btsl %1,%0" : "+m" (*bitmap) : "r" (index) : "memory");
-}
-
-static void clr_bit(uint32_t *bitmap, int32_t index)
-{
- asm volatile("btcl %1,%0" : "+m" (*bitmap) : "r" (index) : "memory");
-}
-
-/*
- * Get and free a port number (host byte order)
- */
-uint16_t get_port(void)
-{
- uint16_t port;
-
- do {
- port = first_port_number++;
- first_port_number &= PORT_NUMBER_COUNT - 1;
- } while (test_bit(port_number_bitmap, port));
-
- set_bit(port_number_bitmap, port);
- return htons(port + PORT_NUMBER_BASE);
-}
-
-void free_port(uint16_t port)
-{
- port = ntohs(port) - PORT_NUMBER_BASE;
-
- if (port >= PORT_NUMBER_COUNT)
- return;
-
- clr_bit(port_number_bitmap, port);
-}
diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c
index c07f00ee..52787514 100644
--- a/core/fs/pxe/pxe.c
+++ b/core/fs/pxe/pxe.c
@@ -5,10 +5,19 @@
#include <bios.h>
#include <fs.h>
#include <minmax.h>
+#include <fcntl.h>
#include <sys/cpu.h>
+#include <lwip/api.h>
+#include <lwip/dns.h>
+#include <lwip/tcpip.h>
+#include <lwip/opt.h>
#include "pxe.h"
+#include "thread.h"
+#include "url.h"
+#include "tftp.h"
-#define GPXE 1
+__lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
+__lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface;
static uint16_t real_base_mem; /* Amount of DOS memory after freeing */
@@ -16,45 +25,24 @@ uint8_t MAC[MAC_MAX]; /* Actual MAC address */
uint8_t MAC_len; /* MAC address len */
uint8_t MAC_type; /* MAC address type */
-char __bss16 BOOTIFStr[7+3*(MAC_MAX+1)];
-#define MAC_str (BOOTIFStr+7) /* The actual hardware address */
-char __bss16 SYSUUIDStr[8+32+5];
-#define UUID_str (SYSUUIDStr+8) /* The actual UUID */
-
char boot_file[256]; /* From DHCP */
char path_prefix[256]; /* From DHCP */
-char dot_quad_buf[16];
static bool has_gpxe;
static uint32_t gpxe_funcs;
bool have_uuid = false;
-/* Common receive buffer */
-static __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
-
-const uint8_t TimeoutTable[] = {
- 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
- 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
+static struct url_scheme {
+ const char *name;
+ void (*open)(struct url_info *, int, struct inode *, const char **);
+ int ok_flags;
+} url_schemes[] = {
+ { "tftp", tftp_open, 0 },
+ { "http", http_open, O_DIRECTORY },
+ { "ftp", ftp_open, O_DIRECTORY },
+ { NULL, NULL, 0 },
};
-
-struct tftp_options {
- const char *str_ptr; /* string pointer */
- size_t offset; /* offset into socket structre */
-};
-
-#define IFIELD(x) offsetof(struct inode, x)
-#define PFIELD(x) (offsetof(struct inode, pvt) + \
- offsetof(struct pxe_pvt_inode, x))
-
-static const struct tftp_options tftp_options[] =
-{
- { "tsize", IFIELD(size) },
- { "blksize", PFIELD(tftp_blksize) },
-};
-static const int tftp_nopts = sizeof tftp_options / sizeof tftp_options[0];
-
-static void tftp_error(struct inode *file, uint16_t errnum,
- const char *errstr);
+#define OK_FLAGS_MASK (O_DIRECTORY|O_WRONLY)
/*
* Allocate a local UDP port structure and assign it a local port number.
@@ -67,108 +55,32 @@ static struct inode *allocate_socket(struct fs_info *fs)
if (!inode) {
malloc_error("socket structure");
} else {
- struct pxe_pvt_inode *socket = PVT(inode);
- socket->tftp_localport = get_port();
inode->mode = DT_REG; /* No other types relevant for PXE */
}
return inode;
}
-static void free_socket(struct inode *inode)
+void free_socket(struct inode *inode)
{
struct pxe_pvt_inode *socket = PVT(inode);
- free_port(socket->tftp_localport);
+ free(socket->tftp_pktbuf); /* If we allocated a buffer, free it now */
free_inode(inode);
}
-#if GPXE
-static void gpxe_close_file(struct inode *inode)
-{
- struct pxe_pvt_inode *socket = PVT(inode);
- static __lowmem struct s_PXENV_FILE_CLOSE file_close;
-
- file_close.FileHandle = socket->tftp_remoteport;
- pxe_call(PXENV_FILE_CLOSE, &file_close);
-}
-#endif
-
static void pxe_close_file(struct file *file)
{
struct inode *inode = file->inode;
struct pxe_pvt_inode *socket = PVT(inode);
if (!socket->tftp_goteof) {
-#if GPXE
- if (socket->tftp_localport == 0xffff) {
- gpxe_close_file(inode);
- } else
-#endif
- if (socket->tftp_localport != 0) {
- tftp_error(inode, 0, "No error, file close");
- }
+ socket->ops->close(inode);
}
free_socket(inode);
}
-/**
- * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
- *
- * @param: dst, output buffer
- * @param: src, input buffer
- * @param: count, number of bytes
- *
- */
-static void lchexbytes(char *dst, const void *src, int count)
-{
- uint8_t half;
- uint8_t c;
- const uint8_t *s = src;
-
- for(; count > 0; count--) {
- c = *s++;
- half = ((c >> 4) & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
-
- half = (c & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
- }
-}
-
-/*
- * just like the lchexbytes, except to upper-case
- *
- */
-static void uchexbytes(char *dst, const void *src, int count)
-{
- uint8_t half;
- uint8_t c;
- const uint8_t *s = src;
-
- for(; count > 0; count--) {
- c = *s++;
- half = ((c >> 4) & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
-
- half = (c & 0x0f) + '0';
- *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
- }
-}
-
-/*
- * Parse a single hexadecimal byte, which must be complete (two
- * digits). This is used in URL parsing.
- */
-static int hexbyte(const char *p)
-{
- if (!is_hex(p[0]) || !is_hex(p[1]))
- return -1;
- else
- return (hexval(p[0]) << 4) + hexval(p[1]);
-}
-
/*
* Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
* We used to refuse class E, but class E addresses are likely to become
@@ -197,57 +109,11 @@ bool ip_ok(uint32_t ip)
*/
static int gendotquad(char *dst, uint32_t ip)
{
- int part;
- int i = 0, j;
- char temp[4];
- char *p = dst;
-
- for (; i < 4; i++) {
- j = 0;
- part = ip & 0xff;
- do {
- temp[j++] = (part % 10) + '0';
- }while(part /= 10);
- for (; j > 0; j--)
- *p++ = temp[j-1];
- *p++ = '.';
-
- ip >>= 8;
- }
- /* drop the last dot '.' and zero-terminate string*/
- *(--p) = 0;
-
- return p - dst;
-}
-
-/*
- * parse the ip_str and return the ip address with *res.
- * return the the string address after the ip string
- *
- */
-static const char *parse_dotquad(const char *ip_str, uint32_t *res)
-{
- const char *p = ip_str;
- uint8_t part = 0;
- uint32_t ip = 0;
- int i;
-
- for (i = 0; i < 4; i++) {
- while (is_digit(*p)) {
- part = part * 10 + *p - '0';
- p++;
- }
- if (i != 3 && *p != '.')
- return NULL;
-
- ip = (ip << 8) | part;
- part = 0;
- p++;
- }
- p--;
-
- *res = htonl(ip);
- return p;
+ return sprintf(dst, "%u.%u.%u.%u",
+ ((const uint8_t *)&ip)[0],
+ ((const uint8_t *)&ip)[1],
+ ((const uint8_t *)&ip)[2],
+ ((const uint8_t *)&ip)[3]);
}
/*
@@ -256,11 +122,14 @@ static const char *parse_dotquad(const char *ip_str, uint32_t *res)
*/
__export int pxe_call(int opcode, void *data)
{
+ static DECLARE_INIT_SEMAPHORE(pxe_sem, 1);
extern void pxenv(void);
com32sys_t regs;
+ sem_down(&pxe_sem, 0);
+
#if 0
- printf("pxe_call op %04x data %p\n", opcode, data);
+ dprintf("pxe_call op %04x data %p\n", opcode, data);
#endif
memset(&regs, 0, sizeof regs);
@@ -269,77 +138,11 @@ __export int pxe_call(int opcode, void *data)
regs.edi.w[0] = OFFS(data);
call16(pxenv, &regs, &regs);
- return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
-}
-
-/**
- * Send an ERROR packet. This is used to terminate a connection.
- *
- * @inode: Inode structure
- * @errnum: Error number (network byte order)
- * @errstr: Error string (included in packet)
- */
-static void tftp_error(struct inode *inode, uint16_t errnum,
- const char *errstr)
-{
- static __lowmem struct {
- uint16_t err_op;
- uint16_t err_num;
- char err_msg[64];
- } __packed err_buf;
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
- struct pxe_pvt_inode *socket = PVT(inode);
-
- err_buf.err_op = TFTP_ERROR;
- err_buf.err_num = errnum;
- memcpy(err_buf.err_msg, errstr, len);
- err_buf.err_msg[len] = '\0';
-
- udp_write.src_port = socket->tftp_localport;
- udp_write.dst_port = socket->tftp_remoteport;
- udp_write.ip = socket->tftp_remoteip;
- udp_write.gw = gateway(udp_write.ip);
- udp_write.buffer = FAR_PTR(&err_buf);
- udp_write.buffer_size = 4 + len + 1;
+ sem_up(&pxe_sem);
- /* If something goes wrong, there is nothing we can do, anyway... */
- pxe_call(PXENV_UDP_WRITE, &udp_write);
-}
-
-
-/**
- * Send ACK packet. This is a common operation and so is worth canning.
- *
- * @param: inode, Inode pointer
- * @param: ack_num, Packet # to ack (network byte order)
- *
- */
-static void ack_packet(struct inode *inode, uint16_t ack_num)
-{
- int err;
- static __lowmem uint16_t ack_packet_buf[2];
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- struct pxe_pvt_inode *socket = PVT(inode);
-
- /* Packet number to ack */
- ack_packet_buf[0] = TFTP_ACK;
- ack_packet_buf[1] = ack_num;
- udp_write.src_port = socket->tftp_localport;
- udp_write.dst_port = socket->tftp_remoteport;
- udp_write.ip = socket->tftp_remoteip;
- udp_write.gw = gateway(udp_write.ip);
- udp_write.buffer = FAR_PTR(ack_packet_buf);
- udp_write.buffer_size = 4;
-
- err = pxe_call(PXENV_UDP_WRITE, &udp_write);
- (void)err;
-#if 0
- printf("sent %s\n", err ? "FAILED" : "OK");
-#endif
+ return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
}
-
/**
* Get a DHCP packet from the PXE stack into a lowmem buffer
*
@@ -353,7 +156,7 @@ static int pxe_get_cached_info(int type, void *buf, size_t bufsiz)
static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
printf(" %02x", type);
- get_cached_info.Status = 0;
+ memset(&get_cached_info, 0, sizeof get_cached_info);
get_cached_info.PacketType = type;
get_cached_info.BufferSize = bufsiz;
get_cached_info.Buffer = FAR_PTR(buf);
@@ -366,104 +169,13 @@ static int pxe_get_cached_info(int type, void *buf, size_t bufsiz)
return get_cached_info.BufferSize;
}
-
-/*
- * Return the type of pathname passed.
- */
-enum pxe_path_type {
- PXE_RELATIVE, /* No :: or URL */
- PXE_HOMESERVER, /* Starting with :: */
- PXE_TFTP, /* host:: */
- PXE_URL_TFTP, /* tftp:// */
- PXE_URL, /* Absolute URL syntax */
-};
-
-static enum pxe_path_type pxe_path_type(const char *str)
-{
- const char *p;
-
- p = str;
-
- while (1) {
- switch (*p) {
- case ':':
- if (p[1] == ':') {
- if (p == str)
- return PXE_HOMESERVER;
- else
- return PXE_TFTP;
- } else if (p > str && p[1] == '/' && p[2] == '/') {
- if (!strncasecmp(str, "tftp://", 7))
- return PXE_URL_TFTP;
- else
- return PXE_URL;
- }
-
- /* else fall through */
- case '/': case '!': case '@': case '#': case '%':
- case '^': case '&': case '*': case '(': case ')':
- case '[': case ']': case '{': case '}': case '\\':
- case '|': case '=': case '`': case '~': case '\'':
- case '\"': case ';': case '>': case '<': case '?':
- case '\0':
- /* Any of these characters terminate the colon search */
- return PXE_RELATIVE;
- default:
- break;
- }
- p++;
- }
-}
-
-#if GPXE
-
-/**
- * Get a fresh packet from a gPXE socket
- * @param: inode -> Inode pointer
- *
- */
-static void get_packet_gpxe(struct inode *inode)
-{
- struct pxe_pvt_inode *socket = PVT(inode);
- static __lowmem struct s_PXENV_FILE_READ file_read;
- int err;
-
- while (1) {
- file_read.FileHandle = socket->tftp_remoteport;
- file_read.Buffer = FAR_PTR(packet_buf);
- file_read.BufferSize = PKTBUF_SIZE;
- err = pxe_call(PXENV_FILE_READ, &file_read);
- if (!err) /* successed */
- break;
-
- if (file_read.Status != PXENV_STATUS_TFTP_OPEN)
- kaboom();
- }
-
- memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize);
-
- socket->tftp_dataptr = socket->tftp_pktbuf;
- socket->tftp_bytesleft = file_read.BufferSize;
- socket->tftp_filepos += file_read.BufferSize;
-
- if (socket->tftp_bytesleft == 0)
- inode->size = socket->tftp_filepos;
-
- /* if we're done here, close the file */
- if (inode->size > socket->tftp_filepos)
- return;
-
- /* Got EOF, close it */
- socket->tftp_goteof = 1;
- gpxe_close_file(inode);
-}
-#endif /* GPXE */
-
-
/*
* mangle a filename pointed to by _src_ into a buffer pointed
* to by _dst_; ends on encountering any whitespace.
*
+ * This deliberately does not attempt to do any conversion of
+ * pathname separators.
+ *
*/
static void pxe_mangle_name(char *dst, const char *src)
{
@@ -476,109 +188,40 @@ static void pxe_mangle_name(char *dst, const char *src)
}
/*
- * Get a fresh packet if the buffer is drained, and we haven't hit
- * EOF yet. The buffer should be filled immediately after draining!
+ * Read a single character from the specified pxe inode.
+ * Very useful for stepping through http streams and
+ * parsing their headers.
*/
-static void fill_buffer(struct inode *inode)
+int pxe_getc(struct inode *inode)
{
- int err;
- int last_pkt;
- const uint8_t *timeout_ptr;
- uint8_t timeout;
- uint16_t buffersize;
- uint32_t oldtime;
- void *data = NULL;
- static __lowmem struct s_PXENV_UDP_READ udp_read;
struct pxe_pvt_inode *socket = PVT(inode);
+ unsigned char byte;
- if (socket->tftp_bytesleft || socket->tftp_goteof)
- return;
+ while (!socket->tftp_bytesleft) {
+ if (socket->tftp_goteof)
+ return -1;
-#if GPXE
- if (socket->tftp_localport == 0xffff) {
- get_packet_gpxe(inode);
- return;
+ socket->ops->fill_buffer(inode);
}
-#endif
- /*
- * Start by ACKing the previous packet; this should cause
- * the next packet to be sent.
- */
- timeout_ptr = TimeoutTable;
- timeout = *timeout_ptr++;
- oldtime = jiffies();
-
- ack_again:
- ack_packet(inode, socket->tftp_lastpkt);
-
- while (timeout) {
- udp_read.buffer = FAR_PTR(packet_buf);
- udp_read.buffer_size = PKTBUF_SIZE;
- udp_read.src_ip = socket->tftp_remoteip;
- udp_read.dest_ip = IPInfo.myip;
- udp_read.s_port = socket->tftp_remoteport;
- udp_read.d_port = socket->tftp_localport;
- err = pxe_call(PXENV_UDP_READ, &udp_read);
- if (err) {
- uint32_t now = jiffies();
-
- if (now-oldtime >= timeout) {
- oldtime = now;
- timeout = *timeout_ptr++;
- if (!timeout)
- break;
- }
- continue;
- }
-
- if (udp_read.buffer_size < 4) /* Bad size for a DATA packet */
- continue;
+ byte = *socket->tftp_dataptr;
+ socket->tftp_bytesleft -= 1;
+ socket->tftp_dataptr += 1;
- data = packet_buf;
- if (*(uint16_t *)data != TFTP_DATA) /* Not a data packet */
- continue;
-
- /* If goes here, recevie OK, break */
- break;
- }
-
- /* time runs out */
- if (timeout == 0)
- kaboom();
+ return byte;
+}
- last_pkt = socket->tftp_lastpkt;
- last_pkt = ntohs(last_pkt); /* Host byte order */
- last_pkt++;
- last_pkt = htons(last_pkt); /* Network byte order */
- if (*(uint16_t *)(data + 2) != last_pkt) {
- /*
- * Wrong packet, ACK the packet and try again.
- * This is presumably because the ACK got lost,
- * so the server just resent the previous packet.
- */
-#if 0
- printf("Wrong packet, wanted %04x, got %04x\n", \
- htons(last_pkt), htons(*(uint16_t *)(data+2)));
-#endif
- goto ack_again;
- }
+/*
+ * Get a fresh packet if the buffer is drained, and we haven't hit
+ * EOF yet. The buffer should be filled immediately after draining!
+ */
+static void fill_buffer(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ if (socket->tftp_bytesleft || socket->tftp_goteof)
+ return;
- /* It's the packet we want. We're also EOF if the size < blocksize */
- socket->tftp_lastpkt = last_pkt; /* Update last packet number */
- buffersize = udp_read.buffer_size - 4; /* Skip TFTP header */
- memcpy(socket->tftp_pktbuf, packet_buf + 4, buffersize);
- socket->tftp_dataptr = socket->tftp_pktbuf;
- socket->tftp_filepos += buffersize;
- socket->tftp_bytesleft = buffersize;
- if (buffersize < socket->tftp_blksize) {
- /* it's the last block, ACK packet immediately */
- ack_packet(inode, *(uint16_t *)(data + 2));
-
- /* Make sure we know we are at end of file */
- inode->size = socket->tftp_filepos;
- socket->tftp_goteof = 1;
- }
+ return socket->ops->fill_buffer(inode);
}
@@ -635,8 +278,20 @@ static uint32_t pxe_getfssec(struct file *file, char *buf,
return bytes_read;
}
+/*
+ * Assign an IP address to a URL
+ */
+static void url_set_ip(struct url_info *url)
+{
+ url->ip = 0;
+ if (url->host)
+ url->ip = dns_resolv(url->host);
+ if (!url->ip)
+ url->ip = IPInfo.serverip;
+}
+
/**
- * Open a TFTP connection to the server
+ * Open the specified connection
*
* @param:filename, the file we wanna open
*
@@ -644,352 +299,95 @@ static uint32_t pxe_getfssec(struct file *file, char *buf,
* @out: the lenght of this file, stores in file->file_len
*
*/
-static void __pxe_searchdir(const char *filename, struct file *file);
+static void __pxe_searchdir(const char *filename, int flags, struct file *file);
extern uint16_t PXERetry;
-static void pxe_searchdir(const char *filename, struct file *file)
+static void pxe_searchdir(const char *filename, int flags, struct file *file)
{
int i = PXERetry;
do {
dprintf("PXE: file = %p, retries left = %d: ", file, i);
- __pxe_searchdir(filename, file);
+ __pxe_searchdir(filename, flags, file);
dprintf("%s\n", file->inode ? "ok" : "failed");
} while (!file->inode && i--);
}
-
-static void __pxe_searchdir(const char *filename, struct file *file)
+static void __pxe_searchdir(const char *filename, int flags, struct file *file)
{
struct fs_info *fs = file->fs;
struct inode *inode;
- struct pxe_pvt_inode *socket;
- char *buf;
- const char *np;
- char *p;
- char *options;
- char *data;
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
- static __lowmem struct s_PXENV_UDP_READ udp_read;
- static __lowmem struct s_PXENV_FILE_OPEN file_open;
- static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
- static __lowmem char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
- const struct tftp_options *tftp_opt;
- int i = 0;
- int err;
- int buffersize;
- int rrq_len;
- const uint8_t *timeout_ptr;
- uint32_t timeout;
- uint32_t oldtime;
- uint16_t tid;
- uint16_t opcode;
- uint16_t blk_num;
- uint32_t ip = 0;
- uint32_t opdata, *opdata_ptr;
- enum pxe_path_type path_type;
char fullpath[2*FILENAME_MAX];
- uint16_t server_port = TFTP_PORT; /* TFTP server port */
+#if GPXE
+ char urlsave[2*FILENAME_MAX];
+#endif
+ struct url_info url;
+ const struct url_scheme *us = NULL;
+ int redirect_count = 0;
+ bool found_scheme = false;
inode = file->inode = NULL;
-
- buf = rrq_packet_buf;
- *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
- buf += 2;
-
- path_type = pxe_path_type(filename);
- if (path_type == PXE_RELATIVE) {
- snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
- path_type = pxe_path_type(filename = fullpath);
- }
-
- switch (path_type) {
- case PXE_RELATIVE: /* Really shouldn't happen... */
- case PXE_URL:
- buf = stpcpy(buf, filename);
- ip = IPInfo.serverip; /* Default server */
- break;
-
- case PXE_HOMESERVER:
- buf = stpcpy(buf, filename+2);
- ip = IPInfo.serverip;
- break;
-
- case PXE_TFTP:
- np = strchr(filename, ':');
- buf = stpcpy(buf, np+2);
- if (parse_dotquad(filename, &ip) != np)
- ip = dns_resolv(filename);
- break;
-
- case PXE_URL_TFTP:
- np = filename + 7;
- while (*np && *np != '/' && *np != ':')
- np++;
- if (np > filename + 7) {
- if (parse_dotquad(filename + 7, &ip) != np)
- ip = dns_resolv(filename + 7);
- }
- if (*np == ':') {
- np++;
- server_port = 0;
- while (*np >= '0' && *np <= '9')
- server_port = server_port * 10 + *np++ - '0';
- server_port = server_port ? htons(server_port) : TFTP_PORT;
- }
- if (*np == '/')
- np++; /* Do *NOT* eat more than one slash here... */
- /*
- * The ; is because of a quirk in the TFTP URI spec (RFC
- * 3617); it is to be followed by TFTP modes, which we just ignore.
- */
- while (*np && *np != ';') {
- int v;
- if (*np == '%' && (v = hexbyte(np+1)) > 0) {
- *buf++ = v;
- np += 3;
- } else {
- *buf++ = *np++;
- }
- }
- *buf = '\0';
- break;
- }
- buf++; /* Point *past* the final NULL */
- memcpy(buf, rrq_tail, sizeof rrq_tail);
- buf += sizeof rrq_tail;
-
- rrq_len = buf - rrq_packet_buf;
-
- inode = allocate_socket(fs);
- if (!inode)
- return; /* Allocation failure */
- socket = PVT(inode);
+ while (filename) {
+ if (redirect_count++ > 5)
+ break;
+ strlcpy(fullpath, filename, sizeof fullpath);
#if GPXE
- if (path_type == PXE_URL) {
- if (has_gpxe) {
- file_open.Status = PXENV_STATUS_BAD_FUNC;
- file_open.FileName = FAR_PTR(rrq_packet_buf + 2);
- err = pxe_call(PXENV_FILE_OPEN, &file_open);
- if (err)
- goto done;
-
- socket->tftp_localport = -1;
- socket->tftp_remoteport = file_open.FileHandle;
- inode->size = -1;
- goto done;
- } else {
- static bool already = false;
- if (!already) {
- printf("URL syntax, but gPXE extensions not detected, "
- "trying plain TFTP...\n");
- already = true;
- }
+ strcpy(urlsave, fullpath);
+#endif
+ parse_url(&url, fullpath);
+ if (url.type == URL_SUFFIX) {
+ snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
+#if GPXE
+ strcpy(urlsave, fullpath);
+#endif
+ parse_url(&url, fullpath);
}
- }
-#endif /* GPXE */
- if (!ip)
- goto done; /* No server */
-
- timeout_ptr = TimeoutTable; /* Reset timeout */
-
-sendreq:
- timeout = *timeout_ptr++;
- if (!timeout) {
- free_socket(inode);
- return; /* No file available... */
- }
- oldtime = jiffies();
-
- socket->tftp_remoteip = ip;
- tid = socket->tftp_localport; /* TID(local port No) */
- udp_write.buffer = FAR_PTR(rrq_packet_buf);
- udp_write.ip = ip;
- udp_write.gw = gateway(udp_write.ip);
- udp_write.src_port = tid;
- udp_write.dst_port = server_port;
- udp_write.buffer_size = rrq_len;
- pxe_call(PXENV_UDP_WRITE, &udp_write);
-
- /* If the WRITE call fails, we let the timeout take care of it... */
-
-wait_pkt:
- for (;;) {
- buf = packet_buf;
- udp_read.status = 0;
- udp_read.buffer = FAR_PTR(buf);
- udp_read.buffer_size = PKTBUF_SIZE;
- udp_read.dest_ip = IPInfo.myip;
- udp_read.d_port = tid;
- err = pxe_call(PXENV_UDP_READ, &udp_read);
- if (err || udp_read.status) {
- uint32_t now = jiffies();
- if (now - oldtime >= timeout)
- goto sendreq;
- } else {
- /* Make sure the packet actually came from the server */
- if (udp_read.src_ip == socket->tftp_remoteip)
+ inode = allocate_socket(fs);
+ if (!inode)
+ return; /* Allocation failure */
+
+ url_set_ip(&url);
+
+ filename = NULL;
+ found_scheme = false;
+ for (us = url_schemes; us->name; us++) {
+ if (!strcmp(us->name, url.scheme)) {
+ if ((flags & ~us->ok_flags & OK_FLAGS_MASK) == 0)
+ us->open(&url, flags, inode, &filename);
+ found_scheme = true;
break;
+ }
}
- }
-
- socket->tftp_remoteport = udp_read.s_port;
- /* filesize <- -1 == unknown */
- inode->size = -1;
- /* Default blksize unless blksize option negotiated */
- socket->tftp_blksize = TFTP_BLOCKSIZE;
- buffersize = udp_read.buffer_size - 2; /* bytes after opcode */
- if (buffersize < 0)
- goto wait_pkt; /* Garbled reply */
-
- /*
- * Get the opcode type, and parse it
- */
- opcode = *(uint16_t *)packet_buf;
- switch (opcode) {
- case TFTP_ERROR:
- inode->size = 0;
- break; /* ERROR reply; don't try again */
-
- case TFTP_DATA:
- /*
- * If the server doesn't support any options, we'll get a
- * DATA reply instead of OACK. Stash the data in the file
- * buffer and go with the default value for all options...
- *
- * We got a DATA packet, meaning no options are
- * suported. Save the data away and consider the
- * length undefined, *unless* this is the only
- * data packet...
- */
- buffersize -= 2;
- if (buffersize < 0)
- goto wait_pkt;
- data = packet_buf + 2;
- blk_num = *(uint16_t *)data;
- data += 2;
- if (blk_num != htons(1))
- goto wait_pkt;
- socket->tftp_lastpkt = blk_num;
- if (buffersize > TFTP_BLOCKSIZE)
- goto err_reply; /* Corrupt */
- else if (buffersize < TFTP_BLOCKSIZE) {
- /*
- * This is the final EOF packet, already...
- * We know the filesize, but we also want to
- * ack the packet and set the EOF flag.
- */
- inode->size = buffersize;
- socket->tftp_goteof = 1;
- ack_packet(inode, blk_num);
- }
-
- socket->tftp_bytesleft = buffersize;
- socket->tftp_dataptr = socket->tftp_pktbuf;
- memcpy(socket->tftp_pktbuf, data, buffersize);
- break;
-
- case TFTP_OACK:
- /*
- * Now we need to parse the OACK packet to get the transfer
- * and packet sizes.
- */
-
- options = packet_buf + 2;
- p = options;
-
- while (buffersize) {
- const char *opt = p;
-
- /*
- * If we find an option which starts with a NUL byte,
- * (a null option), we're either seeing garbage that some
- * TFTP servers add to the end of the packet, or we have
- * no clue how to parse the rest of the packet (what is
- * an option name and what is a value?) In either case,
- * discard the rest.
- */
- if (!*opt)
- goto done;
-
- while (buffersize) {
- if (!*p)
- break; /* Found a final null */
- *p++ |= 0x20;
- buffersize--;
- }
- if (!buffersize)
- break; /* Unterminated option */
-
- /* Consume the terminal null */
- p++;
- buffersize--;
-
- if (!buffersize)
- break; /* No option data */
-
- /*
- * Parse option pointed to by options; guaranteed to be
- * null-terminated
- */
- tftp_opt = tftp_options;
- for (i = 0; i < tftp_nopts; i++) {
- if (!strcmp(opt, tftp_opt->str_ptr))
- break;
- tftp_opt++;
- }
- if (i == tftp_nopts)
- goto err_reply; /* Non-negotitated option returned,
- no idea what it means ...*/
-
- /* get the address of the filed that we want to write on */
- opdata_ptr = (uint32_t *)((char *)inode + tftp_opt->offset);
- opdata = 0;
-
- /* do convert a number-string to decimal number, just like atoi */
- while (buffersize--) {
- uint8_t d = *p++;
- if (d == '\0')
- break; /* found a final null */
- d -= '0';
- if (d > 9)
- goto err_reply; /* Not a decimal digit */
- opdata = opdata*10 + d;
- }
- *opdata_ptr = opdata;
- }
- break;
+ /* filename here is set on a redirect */
+ }
- default:
- printf("TFTP unknown opcode %d\n", ntohs(opcode));
- goto err_reply;
+ if (!found_scheme) {
+#if GPXE
+ /* No URL scheme found, hand it to GPXE */
+ gpxe_open(inode, urlsave);
+#endif
}
-done:
- if (!inode->size) {
+ if (inode->size) {
+ file->inode = inode;
+ file->inode->mode = (flags & O_DIRECTORY) ? DT_DIR : DT_REG;
+ } else {
free_socket(inode);
- return;
}
- file->inode = inode;
- return;
-err_reply:
- /* Build the TFTP error packet */
- tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
- printf("TFTP server sent an incomprehesible reply\n");
- kaboom();
+ return;
}
static int __pxe_chdir(struct fs_info *fs, const char *src,
- enum pxe_path_type path_type)
+ enum url_type url_type)
{
- if (path_type == PXE_RELATIVE)
+ if (url_type == URL_SUFFIX)
strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
- else if (path_type == PXE_HOMESERVER)
+ else if (url_type == URL_OLD_TFTP)
snprintf(fs->cwd_name, sizeof fs->cwd_name, "::%s", src);
else
strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
@@ -1031,7 +429,7 @@ static void get_prefix(void)
}
printf("TFTP prefix: %s\n", path_prefix);
- __pxe_chdir(this_fs, path_prefix, PXE_HOMESERVER);
+ __pxe_chdir(this_fs, path_prefix, URL_OLD_TFTP);
}
/*
@@ -1040,10 +438,8 @@ static void get_prefix(void)
static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
size_t bufsize)
{
- enum pxe_path_type path_type = pxe_path_type(src);
-
return snprintf(dst, bufsize, "%s%s",
- path_type == PXE_RELATIVE ? fs->cwd_name : "", src);
+ url_type(src) == URL_SUFFIX ? fs->cwd_name : "", src);
}
/*
@@ -1052,9 +448,7 @@ static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
static int pxe_chdir(struct fs_info *fs, const char *src)
{
/* The cwd for PXE is just a text prefix */
- enum pxe_path_type path_type = pxe_path_type(src);
-
- __pxe_chdir(fs, src, path_type);
+ __pxe_chdir(fs, src, url_type(src));
dprintf("cwd = \"%s\"\n", fs->cwd_name);
return 0;
@@ -1078,7 +472,7 @@ static int pxe_open_config(struct com32_filedata *filedata)
get_prefix();
if (DHCPMagic & 0x02) {
/* We got a DHCP option, try it first */
- if (open_file(ConfigName, filedata) >= 0)
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
}
@@ -1088,23 +482,23 @@ static int pxe_open_config(struct com32_filedata *filedata)
config_file = stpcpy(ConfigName, cfgprefix);
/* Try loading by UUID */
- if (have_uuid) {
- strcpy(config_file, UUID_str);
- if (open_file(ConfigName, filedata) >= 0)
+ if (sysappend_strings[SYSAPPEND_SYSUUID]) {
+ strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8);
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
}
/* Try loading by MAC address */
- strcpy(config_file, MAC_str);
- if (open_file(ConfigName, filedata) >= 0)
+ strcpy(config_file, sysappend_strings[SYSAPPEND_BOOTIF]+7);
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
/* Nope, try hexadecimal IP prefixes... */
- uchexbytes(config_file, (uint8_t *)&IPInfo.myip, 4);
+ sprintf(config_file, "%08X", ntohl(IPInfo.myip));
last = &config_file[8];
while (tries) {
*last = '\0'; /* Zero-terminate string */
- if (open_file(ConfigName, filedata) >= 0)
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
last--; /* Drop one character */
tries--;
@@ -1112,7 +506,7 @@ static int pxe_open_config(struct com32_filedata *filedata)
/* Final attempt: "default" string */
strcpy(config_file, default_str);
- if (open_file(ConfigName, filedata) >= 0)
+ if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
return 0;
printf("%-68s\n", "Unable to locate configuration file");
@@ -1124,43 +518,17 @@ static int pxe_open_config(struct com32_filedata *filedata)
*/
static void make_bootif_string(void)
{
+ static char bootif_str[7+3*(MAC_MAX+1)];
const uint8_t *src;
- char *dst = BOOTIFStr;
+ char *dst = bootif_str;
int i;
dst += sprintf(dst, "BOOTIF=%02x", MAC_type);
src = MAC;
for (i = MAC_len; i; i--)
dst += sprintf(dst, "-%02x", *src++);
-}
-/*
- * Generate the SYSUUID string, if we have one...
- */
-static void make_sysuuid_string(void)
-{
- static const uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
- const uint8_t *src = uuid;
- const uint8_t *uuid_ptr = uuid_dashes;
- char *dst;
- SYSUUIDStr[0] = '\0'; /* If nothing there... */
-
- /* Try loading by UUID */
- if (have_uuid) {
- dst = stpcpy(SYSUUIDStr, "SYSUUID=");
-
- while (*uuid_ptr) {
- int len = *uuid_ptr;
-
- lchexbytes(dst, src, len);
- dst += len * 2;
- src += len;
- uuid_ptr++;
- *dst++ = '-';
- }
- /* Remove last dash and zero-terminate */
- *--dst = '\0';
- }
+ sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str;
}
/*
@@ -1168,21 +536,22 @@ static void make_sysuuid_string(void)
* option into IPOption based on DHCP information in IPInfo.
*
*/
-char __bss16 IPOption[3+4*16];
-
static void genipopt(void)
{
- char *p = IPOption;
+ static char ip_option[3+4*16];
const uint32_t *v = &IPInfo.myip;
+ char *p;
int i;
- p = stpcpy(p, "ip=");
+ p = stpcpy(ip_option, "ip=");
for (i = 0; i < 4; i++) {
p += gendotquad(p, *v++);
*p++ = ':';
}
*--p = '\0';
+
+ sysappend_strings[SYSAPPEND_IP] = ip_option;
}
@@ -1190,6 +559,7 @@ static void genipopt(void)
static void ip_init(void)
{
uint32_t ip = IPInfo.myip;
+ char dot_quad_buf[16];
genipopt();
gendotquad(dot_quad_buf, ip);
@@ -1199,20 +569,6 @@ static void ip_init(void)
}
/*
- * Print the IPAPPEND strings, in order
- */
-static void print_ipappend(void)
-{
- size_t i;
-
- for (i = 0; i < (size_t)numIPAppends; i++) {
- const char *p = (const char *)(size_t)IPAppends[i];
- if (*p)
- printf("%s\n", p);
- }
-}
-
-/*
* Validity check on possible !PXE structure in buf
* return 1 for success, 0 for failure.
*
@@ -1413,6 +769,15 @@ static int pxe_init(bool quiet)
real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
+ /* Probe UNDI information */
+ pxe_call(PXENV_UNDI_GET_INFORMATION, &pxe_undi_info);
+ pxe_call(PXENV_UNDI_GET_IFACE_INFO, &pxe_undi_iface);
+
+ printf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n",
+ pxe_undi_info.BaseIo, pxe_undi_info.IntNumber,
+ pxe_undi_info.MaxTranUnit, pxe_undi_info.HwType,
+ pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags);
+
return 0;
}
@@ -1437,29 +802,13 @@ static void gpxe_init(void)
}
/*
- * Initialize UDP stack
- *
- */
-static void udp_init(void)
-{
- int err;
- static __lowmem struct s_PXENV_UDP_OPEN udp_open;
- udp_open.src_ip = IPInfo.myip;
- err = pxe_call(PXENV_UDP_OPEN, &udp_open);
- if (err || udp_open.status) {
- printf("Failed to initialize UDP stack ");
- printf("%d\n", udp_open.status);
- kaboom();
- }
-}
-
-
-/*
* Network-specific initialization
*/
static void network_init(void)
{
+ int err;
int pkt_len;
+ int i;
struct bootp_t *bp;
const size_t dhcp_max_packet = 4096;
@@ -1514,9 +863,12 @@ static void network_init(void)
lfree(bp);
make_bootif_string();
- make_sysuuid_string();
+ /* If DMI and DHCP disagree, which one should we set? */
+ if (have_uuid)
+ sysappend_set_uuid(uuid);
ip_init();
- print_ipappend();
+ /* print_sysappend(); */
+ http_bake_cookies();
/*
* Check to see if we got any PXELINUX-specific DHCP options; in particular,
@@ -1525,7 +877,20 @@ static void network_init(void)
if ((DHCPMagic & 1) == 0)
DHCPMagic = 0;
- udp_init();
+ /* Initialize lwip */
+ tcpip_init(NULL, NULL);
+
+ /* Start up the undi driver for lwip */
+ err = undiif_start(IPInfo.myip, IPInfo.netmask, IPInfo.gateway);
+ if (err) {
+ printf("undiif driver failed to start: %d\n", err);
+ kaboom();
+ }
+
+ for (i = 0; i < DNS_MAX_SERVERS; i++) {
+ /* Transfer the DNS information to lwip */
+ dns_setserver(i, (struct ip_addr *)&dns_server[i]);
+ }
}
/*
@@ -1536,9 +901,8 @@ static int pxe_fs_init(struct fs_info *fs)
{
(void)fs; /* drop the compile warning message */
- /* This block size is actually arbitrary... */
- fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
- fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
+ /* Prepare for handling pxe interrupts */
+ pxe_init_isr();
/* This block size is actually arbitrary... */
fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
@@ -1600,9 +964,9 @@ static inline bool is_efi(const struct efi_struct *efi)
return (efi->magic == EFI_MAGIC) && (efi->len >= 83);
}
+#if 0
static void install_int18_hack(void)
{
-#if 0
static const uint8_t int18_hack[] =
{
0xcd, 0x18, /* int $0x18 */
@@ -1638,30 +1002,8 @@ static void install_int18_hack(void)
*(uint16_t *)(dst+46) = InitStack.seg;
}
}
-#endif
-}
-
-int reset_pxe(void)
-{
- static __lowmem struct s_PXENV_UDP_CLOSE udp_close;
- extern void gpxe_unload(void);
- int err = 0;
-
- pxe_idle_cleanup();
-
- pxe_call(PXENV_UDP_CLOSE, &udp_close);
-
- if (gpxe_funcs & 0x80) {
- /* gPXE special unload implemented */
- call16(gpxe_unload, &zero_regs, NULL);
-
- /* Locate the actual vendor stack... */
- err = pxe_init(true);
- }
-
- install_int18_hack();
- return err;
}
+#endif
/*
* This function unloads the PXE and UNDI stacks and
@@ -1670,6 +1012,12 @@ int reset_pxe(void)
__export void unload_pxe(uint16_t flags)
{
/* PXE unload sequences */
+ /*
+ * iPXE does:
+ * UNDI_SHUTDOWN, UNDI_CLEANUP, STOP_UNDI
+ * Older Syslinux did:
+ * UDP_CLOSE, UNDI_SHUTDOWN, UNLOAD_STACK, STOP_UNDI/UNDI_CLEANUP
+ */
static const uint8_t new_api_unload[] = {
PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
};
@@ -1689,6 +1037,7 @@ __export void unload_pxe(uint16_t flags)
uint16_t Status; /* All calls have this as the first member */
} unload_call;
+ dprintf("Called unload_pxe()...\n");
dprintf("FBM before unload = %d\n", bios_fbm());
err = reset_pxe();
@@ -1707,7 +1056,8 @@ __export void unload_pxe(uint16_t flags)
memset(&unload_call, 0, sizeof unload_call);
err = pxe_call(api, &unload_call);
if (err || unload_call.Status != PXENV_STATUS_SUCCESS) {
- dprintf("PXE unload API call %04x failed\n", api);
+ printf("PXE unload API call %04x failed: 0x%x\n",
+ api, unload_call.Status);
goto cant_free;
}
}
@@ -1739,6 +1089,17 @@ cant_free:
return;
}
+static int pxe_readdir(struct file *file, struct dirent *dirent)
+{
+ struct inode *inode = file->inode;
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ if (socket->ops->readdir)
+ return socket->ops->readdir(inode, dirent);
+ else
+ return -1; /* No such operation */
+}
+
const struct fs_ops pxe_fs_ops = {
.fs_name = "pxe",
.fs_flags = FS_NODEV,
@@ -1751,4 +1112,5 @@ const struct fs_ops pxe_fs_ops = {
.mangle_name = pxe_mangle_name,
.chdir_start = pxe_chdir_start,
.open_config = pxe_open_config,
+ .readdir = pxe_readdir,
};
diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
index d4377581..4a43a9db 100644
--- a/core/fs/pxe/pxe.h
+++ b/core/fs/pxe/pxe.h
@@ -1,7 +1,7 @@
/* -----------------------------------------------------------------------
*
* Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
- * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,67 +21,21 @@
#define PXE_H
#include <syslinux/pxe_api.h>
-#include "fs.h" /* For MAX_OPEN, should go away */
+#include "fs.h" /* Mostly for FILENAME_MAX */
/*
* Some basic defines...
*/
-#define TFTP_PORT htons(69) /* Default TFTP port */
-#define TFTP_BLOCKSIZE_LG2 9
-#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2)
-#define PKTBUF_SIZE 2048 /* */
+#define PKTBUF_SIZE 2048 /* Used mostly by the gPXE backend */
#define is_digit(c) (((c) >= '0') && ((c) <= '9'))
-static inline bool is_hex(char c)
-{
- return (c >= '0' && c <= '9') ||
- (c >= 'A' && c <= 'F') ||
- (c >= 'a' && c <= 'f');
-}
-
-static inline int hexval(char c)
-{
- return (c >= 'A') ? (c & ~0x20) - 'A' + 10 : (c - '0');
-}
-
-/*
- * TFTP operation codes
- */
-#define TFTP_RRQ htons(1) // Read rest
-#define TFTP_WRQ htons(2) // Write rest
-#define TFTP_DATA htons(3) // Data packet
-#define TFTP_ACK htons(4) // ACK packet
-#define TFTP_ERROR htons(5) // ERROR packet
-#define TFTP_OACK htons(6) // OACK packet
-
-/*
- * TFTP error codes
- */
-#define TFTP_EUNDEF htons(0) // Unspecified error
-#define TFTP_ENOTFOUND htons(1) // File not found
-#define TFTP_EACCESS htons(2) // Access violation
-#define TFTP_ENOSPACE htons(3) // Disk full
-#define TFTP_EBADOP htons(4) // Invalid TFTP operation
-#define TFTP_EBADID htons(5) // Unknown transfer
-#define TFTP_EEXISTS htons(6) // File exists
-#define TFTP_ENOUSER htons(7) // No such user
-#define TFTP_EOPTNEG htons(8) // Option negotiation failure
-
-
#define BOOTP_OPTION_MAGIC htonl(0x63825363)
#define MAC_MAX 32
-/* Defines for DNS */
-#define DNS_PORT htons(53) /* Default DNS port */
-#define DNS_MAX_PACKET 512 /* Defined by protocol */
-#define DNS_MAX_SERVERS 4 /* Max no of DNS servers */
-
-
/*
- * structures
+ * structures
*/
-
struct pxenv_t {
uint8_t signature[6]; /* PXENV+ */
uint16_t version;
@@ -149,22 +103,32 @@ struct bootp_t {
uint8_t options[1260]; /* Vendor options */
} __attribute__ ((packed));
+struct netconn;
+struct netbuf;
/*
* Our inode private information -- this includes the packet buffer!
*/
+struct pxe_conn_ops {
+ void (*fill_buffer)(struct inode *inode);
+ void (*close)(struct inode *inode);
+ int (*readdir)(struct inode *inode, struct dirent *dirent);
+};
+
struct pxe_pvt_inode {
- uint16_t tftp_localport; /* Local port number (0=not in us)*/
+ struct netconn *conn; /* lwip network connection */
+ struct netbuf *buf; /* lwip cached buffer */
uint16_t tftp_remoteport; /* Remote port number */
- uint32_t tftp_remoteip; /* Remote IP address */
- uint32_t tftp_filepos; /* bytes downloaded (includeing buffer) */
+ uint32_t tftp_filepos; /* bytes downloaded (including buffer) */
uint32_t tftp_blksize; /* Block size for this connection(*) */
uint16_t tftp_bytesleft; /* Unclaimed data bytes */
- uint16_t tftp_lastpkt; /* Sequence number of last packet (NBO) */
+ uint16_t tftp_lastpkt; /* Sequence number of last packet (HBO) */
char *tftp_dataptr; /* Pointer to available data */
uint8_t tftp_goteof; /* 1 if the EOF packet received */
uint8_t tftp_unused[3]; /* Currently unused */
- char tftp_pktbuf[PKTBUF_SIZE];
-} __attribute__ ((packed));
+ char *tftp_pktbuf; /* Packet buffer */
+ struct inode *ctl; /* Control connection (for FTP) */
+ const struct pxe_conn_ops *ops;
+};
#define PVT(i) ((struct pxe_pvt_inode *)((i)->pvt))
@@ -184,6 +148,9 @@ struct ip_info {
*/
extern struct ip_info IPInfo;
+extern t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
+extern t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface;
+
extern uint8_t MAC[];
extern char BOOTIFStr[];
extern uint8_t MAC_len;
@@ -196,9 +163,6 @@ extern char boot_file[];
extern char path_prefix[];
extern char LocalDomain[];
-extern char IPOption[];
-extern char dot_quad_buf[];
-
extern uint32_t dns_server[];
extern uint16_t APIVer;
@@ -211,39 +175,63 @@ extern bool have_uuid;
extern uint8_t uuid_type;
extern uint8_t uuid[];
-extern const uint8_t TimeoutTable[];
-
/*
- * Compute the suitable gateway for a specific route -- too many
- * vendor PXE stacks don't do this correctly...
+ * functions
*/
-static inline uint32_t gateway(uint32_t ip)
-{
- if ((ip ^ IPInfo.myip) & IPInfo.netmask)
- return IPInfo.gateway;
- else
- return 0;
-}
-/*
- * functions
- */
+/* pxeisr.inc */
+extern uint8_t pxe_irq_vector;
+extern void pxe_isr(void);
+extern far_ptr_t pxe_irq_chain;
+extern void pxe_poll(void);
+
+/* isr.c */
+void pxe_init_isr(void);
+void pxe_start_isr(void);
+int reset_pxe(void);
/* pxe.c */
+struct url_info;
bool ip_ok(uint32_t);
+int pxe_getc(struct inode *inode);
+void free_socket(struct inode *inode);
+
+/* undiif.c */
+int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw);
+void undiif_input(t_PXENV_UNDI_ISR *isr);
/* dhcp_options.c */
void parse_dhcp(const void *, size_t);
-/* dnsresolv.c */
-int dns_mangle(char **, const char *);
-
/* idle.c */
void pxe_idle_init(void);
void pxe_idle_cleanup(void);
-/* socknum.c */
-uint16_t get_port(void);
-void free_port(uint16_t port);
+/* tftp.c */
+void tftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir);
+
+/* gpxeurl.c */
+void gpxe_open(struct inode *inode, const char *url);
+#define GPXE 0
+
+/* http.c */
+void http_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir);
+
+/* http_readdir.c */
+int http_readdir(struct inode *inode, struct dirent *dirent);
+
+/* ftp.c */
+void ftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir);
+
+/* ftp_readdir.c */
+int ftp_readdir(struct inode *inode, struct dirent *dirent);
+
+/* tcp.c */
+void tcp_close_file(struct inode *inode);
+void tcp_fill_buffer(struct inode *inode);
+const struct pxe_conn_ops tcp_conn_ops;
#endif /* pxe.h */
diff --git a/core/fs/pxe/tcp.c b/core/fs/pxe/tcp.c
new file mode 100644
index 00000000..ec7679e7
--- /dev/null
+++ b/core/fs/pxe/tcp.c
@@ -0,0 +1,78 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * tcp.c
+ *
+ * Common operations for TCP-based network protocols
+ */
+
+#include <lwip/api.h>
+#include "pxe.h"
+#include "../../../version.h"
+#include "url.h"
+
+void tcp_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ if (socket->conn) {
+ netconn_delete(socket->conn);
+ socket->conn = NULL;
+ }
+ if (socket->buf) {
+ netbuf_delete(socket->buf);
+ socket->buf = NULL;
+ }
+}
+
+void tcp_fill_buffer(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ void *data;
+ u16_t len;
+ err_t err;
+
+ /* Clean up or advance an inuse netbuf */
+ if (socket->buf) {
+ if (netbuf_next(socket->buf) < 0) {
+ netbuf_delete(socket->buf);
+ socket->buf = NULL;
+ }
+ }
+ /* If needed get a new netbuf */
+ if (!socket->buf) {
+ err = netconn_recv(socket->conn, &(socket->buf));
+ if (!socket->buf || err) {
+ socket->tftp_goteof = 1;
+ if (inode->size == -1)
+ inode->size = socket->tftp_filepos;
+ socket->ops->close(inode);
+ return;
+ }
+ }
+ /* Report the current fragment of the netbuf */
+ err = netbuf_data(socket->buf, &data, &len);
+ if (err) {
+ printf("netbuf_data err: %d\n", err);
+ kaboom();
+ }
+ socket->tftp_dataptr = data;
+ socket->tftp_filepos += len;
+ socket->tftp_bytesleft = len;
+ return;
+}
+
+const struct pxe_conn_ops tcp_conn_ops = {
+ .fill_buffer = tcp_fill_buffer,
+ .close = tcp_close_file,
+};
diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c
new file mode 100644
index 00000000..f6ea2974
--- /dev/null
+++ b/core/fs/pxe/tftp.c
@@ -0,0 +1,499 @@
+#include <minmax.h>
+#include <lwip/api.h>
+#include "pxe.h"
+#include "url.h"
+#include "tftp.h"
+
+static const uint8_t TimeoutTable[] = {
+ 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
+ 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
+};
+struct tftp_options {
+ const char *str_ptr; /* string pointer */
+ size_t offset; /* offset into socket structre */
+};
+struct tftp_packet {
+ uint16_t opcode;
+ uint16_t serial;
+ char data[];
+};
+
+#define IFIELD(x) offsetof(struct inode, x)
+#define PFIELD(x) (offsetof(struct inode, pvt) + \
+ offsetof(struct pxe_pvt_inode, x))
+
+static const struct tftp_options tftp_options[] =
+{
+ { "tsize", IFIELD(size) },
+ { "blksize", PFIELD(tftp_blksize) },
+};
+static const int tftp_nopts = sizeof tftp_options / sizeof tftp_options[0];
+
+static void tftp_error(struct inode *file, uint16_t errnum,
+ const char *errstr);
+
+static void tftp_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ if (!socket->tftp_goteof) {
+ tftp_error(inode, 0, "No error, file close");
+ }
+ if (socket->conn) {
+ netconn_delete(socket->conn);
+ socket->conn = NULL;
+ }
+}
+
+/*
+ * Send a UDP packet.
+ */
+static void udp_send(struct netconn *conn, const void *data, size_t len)
+{
+ struct netbuf *nbuf;
+ void *pbuf;
+ int err;
+
+ nbuf = netbuf_new();
+ if (!nbuf) {
+ printf("netbuf allocation error\n");
+ return;
+ }
+
+ pbuf = netbuf_alloc(nbuf, len);
+ if (!pbuf) {
+ printf("pbuf allocation error\n");
+ goto out;
+ }
+
+ memcpy(pbuf, data, len);
+
+ err = netconn_send(conn, nbuf);
+ if (err) {
+ printf("netconn_send error %d\n", err);
+ goto out;
+ }
+
+out:
+ netbuf_delete(nbuf);
+}
+
+/**
+ * Send an ERROR packet. This is used to terminate a connection.
+ *
+ * @inode: Inode structure
+ * @errnum: Error number (network byte order)
+ * @errstr: Error string (included in packet)
+ */
+static void tftp_error(struct inode *inode, uint16_t errnum,
+ const char *errstr)
+{
+ static struct {
+ uint16_t err_op;
+ uint16_t err_num;
+ char err_msg[64];
+ } __packed err_buf;
+ int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ err_buf.err_op = TFTP_ERROR;
+ err_buf.err_num = errnum;
+ memcpy(err_buf.err_msg, errstr, len);
+ err_buf.err_msg[len] = '\0';
+
+ udp_send(socket->conn, &err_buf, 4 + len + 1);
+}
+
+/**
+ * Send ACK packet. This is a common operation and so is worth canning.
+ *
+ * @param: inode, Inode pointer
+ * @param: ack_num, Packet # to ack (host byte order)
+ *
+ */
+static void ack_packet(struct inode *inode, uint16_t ack_num)
+{
+ static uint16_t ack_packet_buf[2];
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ /* Packet number to ack */
+ ack_packet_buf[0] = TFTP_ACK;
+ ack_packet_buf[1] = htons(ack_num);
+
+ udp_send(socket->conn, ack_packet_buf, 4);
+}
+
+/*
+ * Get a fresh packet if the buffer is drained, and we haven't hit
+ * EOF yet. The buffer should be filled immediately after draining!
+ */
+static void tftp_get_packet(struct inode *inode)
+{
+ uint16_t last_pkt;
+ const uint8_t *timeout_ptr;
+ uint8_t timeout;
+ uint16_t buffersize;
+ uint16_t serial;
+ jiffies_t oldtime;
+ struct tftp_packet *pkt = NULL;
+ struct netbuf *nbuf;
+ u16_t nbuf_len;
+ struct pxe_pvt_inode *socket = PVT(inode);
+ err_t err;
+
+ /*
+ * Start by ACKing the previous packet; this should cause
+ * the next packet to be sent.
+ */
+ timeout_ptr = TimeoutTable;
+ timeout = *timeout_ptr++;
+ oldtime = jiffies();
+
+ ack_again:
+ ack_packet(inode, socket->tftp_lastpkt);
+
+ while (timeout) {
+ err = netconn_recv(socket->conn, &nbuf);
+ if (!nbuf || err) {
+ jiffies_t now = jiffies();
+
+ if (now-oldtime >= timeout) {
+ oldtime = now;
+ timeout = *timeout_ptr++;
+ if (!timeout)
+ break;
+ goto ack_again;
+ }
+ continue;
+ }
+
+ netbuf_first(nbuf);
+ nbuf_len = 0;
+ nbuf_len = netbuf_len(nbuf);
+ if (nbuf_len <= socket->tftp_blksize + 4)
+ netbuf_copy(nbuf, socket->tftp_pktbuf, nbuf_len);
+ else
+ nbuf_len = 0; /* invalid packet */
+ netbuf_delete(nbuf);
+ if (nbuf_len < 4) /* Bad size for a DATA packet */
+ continue;
+
+ pkt = (struct tftp_packet *)(socket->tftp_pktbuf);
+ if (pkt->opcode != TFTP_DATA) /* Not a data packet */
+ continue;
+
+ /* If goes here, recevie OK, break */
+ break;
+ }
+
+ /* time runs out */
+ if (timeout == 0)
+ kaboom();
+
+ last_pkt = socket->tftp_lastpkt;
+ last_pkt++;
+ serial = ntohs(pkt->serial);
+ if (serial != last_pkt) {
+ /*
+ * Wrong packet, ACK the packet and try again.
+ * This is presumably because the ACK got lost,
+ * so the server just resent the previous packet.
+ */
+#if 0
+ printf("Wrong packet, wanted %04x, got %04x\n", \
+ htons(last_pkt), htons(*(uint16_t *)(data+2)));
+#endif
+ goto ack_again;
+ }
+
+ /* It's the packet we want. We're also EOF if the size < blocksize */
+ socket->tftp_lastpkt = last_pkt; /* Update last packet number */
+ buffersize = nbuf_len - 4; /* Skip TFTP header */
+ socket->tftp_dataptr = socket->tftp_pktbuf + 4;
+ socket->tftp_filepos += buffersize;
+ socket->tftp_bytesleft = buffersize;
+ if (buffersize < socket->tftp_blksize) {
+ /* it's the last block, ACK packet immediately */
+ ack_packet(inode, serial);
+
+ /* Make sure we know we are at end of file */
+ inode->size = socket->tftp_filepos;
+ socket->tftp_goteof = 1;
+ tftp_close_file(inode);
+ }
+}
+
+const struct pxe_conn_ops tftp_conn_ops = {
+ .fill_buffer = tftp_get_packet,
+ .close = tftp_close_file,
+};
+
+/**
+ * Open a TFTP connection to the server
+ *
+ * @param:inode, the inode to store our state in
+ * @param:ip, the ip to contact to get the file
+ * @param:filename, the file we wanna open
+ *
+ * @out: open_file_t structure, stores in file->open_file
+ * @out: the lenght of this file, stores in file->file_len
+ *
+ */
+void tftp_open(struct url_info *url, int flags, struct inode *inode,
+ const char **redir)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ char *buf;
+ struct netbuf *nbuf;
+ u16_t nbuf_len;
+ char *p;
+ char *options;
+ char *data;
+ static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
+ char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
+ char reply_packet_buf[PKTBUF_SIZE];
+ const struct tftp_options *tftp_opt;
+ int i = 0;
+ int err;
+ int buffersize;
+ int rrq_len;
+ const uint8_t *timeout_ptr;
+ jiffies_t timeout;
+ jiffies_t oldtime;
+ uint16_t opcode;
+ uint16_t blk_num;
+ uint32_t opdata, *opdata_ptr;
+ struct ip_addr addr;
+
+ (void)redir; /* TFTP does not redirect */
+ (void)flags;
+
+ if (url->type != URL_OLD_TFTP) {
+ /*
+ * The TFTP URL specification allows the TFTP to end with a
+ * ;mode= which we just ignore.
+ */
+ url_unescape(url->path, ';');
+ }
+
+ if (!url->port)
+ url->port = TFTP_PORT;
+
+ socket->ops = &tftp_conn_ops;
+ socket->conn = netconn_new(NETCONN_UDP);
+ if (!socket->conn)
+ return;
+
+ socket->conn->recv_timeout = 15; /* A 15 ms recv timeout... */
+ err = netconn_bind(socket->conn, NULL, 0);
+ if (err) {
+ printf("netconn_bind error %d\n", err);
+ return;
+ }
+
+ buf = rrq_packet_buf;
+ *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
+ buf += 2;
+
+ buf = stpcpy(buf, url->path);
+
+ buf++; /* Point *past* the final NULL */
+ memcpy(buf, rrq_tail, sizeof rrq_tail);
+ buf += sizeof rrq_tail;
+
+ rrq_len = buf - rrq_packet_buf;
+
+ timeout_ptr = TimeoutTable; /* Reset timeout */
+
+sendreq:
+ netconn_disconnect(socket->conn);
+ timeout = *timeout_ptr++;
+ if (!timeout)
+ return; /* No file available... */
+ oldtime = jiffies();
+
+ addr.addr = url->ip;
+ netconn_connect(socket->conn, &addr, url->port);
+ udp_send(socket->conn, rrq_packet_buf, rrq_len);
+
+ /* If the WRITE call fails, we let the timeout take care of it... */
+wait_pkt:
+ netconn_disconnect(socket->conn);
+ for (;;) {
+ err = netconn_recv(socket->conn, &nbuf);
+ if (!nbuf || err) {
+ jiffies_t now = jiffies();
+ if (now - oldtime >= timeout)
+ goto sendreq;
+ } else {
+ /* Make sure the packet actually came from the server */
+ bool ok_source;
+ ok_source = netbuf_fromaddr(nbuf)->addr == url->ip;
+ nbuf_len = netbuf_len(nbuf);
+ if (nbuf_len <= PKTBUF_SIZE)
+ netbuf_copy(nbuf, reply_packet_buf, nbuf_len);
+ else
+ nbuf_len = 0; /* impossible mtu < PKTBUF_SIZE */
+ netbuf_delete(nbuf);
+ if (ok_source)
+ break;
+ }
+ }
+
+ netconn_connect(socket->conn, netbuf_fromaddr(nbuf), netbuf_fromport(nbuf));
+
+ /* filesize <- -1 == unknown */
+ inode->size = -1;
+ socket->tftp_blksize = TFTP_BLOCKSIZE;
+ buffersize = nbuf_len - 2; /* bytes after opcode */
+ if (buffersize < 0)
+ goto wait_pkt; /* Garbled reply */
+
+ /*
+ * Get the opcode type, and parse it
+ */
+ opcode = *(uint16_t *)reply_packet_buf;
+ switch (opcode) {
+ case TFTP_ERROR:
+ inode->size = 0;
+ goto done; /* ERROR reply; don't try again */
+
+ case TFTP_DATA:
+ /*
+ * If the server doesn't support any options, we'll get a
+ * DATA reply instead of OACK. Stash the data in the file
+ * buffer and go with the default value for all options...
+ *
+ * We got a DATA packet, meaning no options are
+ * suported. Save the data away and consider the
+ * length undefined, *unless* this is the only
+ * data packet...
+ */
+ buffersize -= 2;
+ if (buffersize < 0)
+ goto wait_pkt;
+ data = reply_packet_buf + 2;
+ blk_num = ntohs(*(uint16_t *)data);
+ data += 2;
+ if (blk_num != 1)
+ goto wait_pkt;
+ socket->tftp_lastpkt = blk_num;
+ if (buffersize > TFTP_BLOCKSIZE)
+ goto err_reply; /* Corrupt */
+
+ socket->tftp_pktbuf = malloc(TFTP_BLOCKSIZE + 4);
+ if (!socket->tftp_pktbuf)
+ goto err_reply; /* Internal error */
+
+ if (buffersize < TFTP_BLOCKSIZE) {
+ /*
+ * This is the final EOF packet, already...
+ * We know the filesize, but we also want to
+ * ack the packet and set the EOF flag.
+ */
+ inode->size = buffersize;
+ socket->tftp_goteof = 1;
+ ack_packet(inode, blk_num);
+ }
+
+ socket->tftp_bytesleft = buffersize;
+ socket->tftp_dataptr = socket->tftp_pktbuf;
+ memcpy(socket->tftp_pktbuf, data, buffersize);
+ goto done;
+
+ case TFTP_OACK:
+ /*
+ * Now we need to parse the OACK packet to get the transfer
+ * and packet sizes.
+ */
+
+ options = reply_packet_buf + 2;
+ p = options;
+
+ while (buffersize) {
+ const char *opt = p;
+
+ /*
+ * If we find an option which starts with a NUL byte,
+ * (a null option), we're either seeing garbage that some
+ * TFTP servers add to the end of the packet, or we have
+ * no clue how to parse the rest of the packet (what is
+ * an option name and what is a value?) In either case,
+ * discard the rest.
+ */
+ if (!*opt)
+ goto done;
+
+ while (buffersize) {
+ if (!*p)
+ break; /* Found a final null */
+ *p++ |= 0x20;
+ buffersize--;
+ }
+ if (!buffersize)
+ break; /* Unterminated option */
+
+ /* Consume the terminal null */
+ p++;
+ buffersize--;
+
+ if (!buffersize)
+ break; /* No option data */
+
+ /*
+ * Parse option pointed to by options; guaranteed to be
+ * null-terminated
+ */
+ tftp_opt = tftp_options;
+ for (i = 0; i < tftp_nopts; i++) {
+ if (!strcmp(opt, tftp_opt->str_ptr))
+ break;
+ tftp_opt++;
+ }
+ if (i == tftp_nopts)
+ goto err_reply; /* Non-negotitated option returned,
+ no idea what it means ...*/
+
+ /* get the address of the filed that we want to write on */
+ opdata_ptr = (uint32_t *)((char *)inode + tftp_opt->offset);
+ opdata = 0;
+
+ /* do convert a number-string to decimal number, just like atoi */
+ while (buffersize--) {
+ uint8_t d = *p++;
+ if (d == '\0')
+ break; /* found a final null */
+ d -= '0';
+ if (d > 9)
+ goto err_reply; /* Not a decimal digit */
+ opdata = opdata*10 + d;
+ }
+ *opdata_ptr = opdata;
+ }
+
+ if (socket->tftp_blksize < 64 || socket->tftp_blksize > PKTBUF_SIZE)
+ goto err_reply;
+
+ /* Parsing successful, allocate buffer */
+ socket->tftp_pktbuf = malloc(socket->tftp_blksize + 4);
+ if (!socket->tftp_pktbuf)
+ goto err_reply;
+ else
+ goto done;
+
+ default:
+ printf("TFTP unknown opcode %d\n", ntohs(opcode));
+ goto err_reply;
+ }
+
+err_reply:
+ /* Build the TFTP error packet */
+ tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
+ inode->size = 0;
+
+done:
+ if (!inode->size) {
+ netconn_delete(socket->conn);
+ socket->conn = NULL;
+ }
+ return;
+}
diff --git a/core/fs/pxe/tftp.h b/core/fs/pxe/tftp.h
new file mode 100644
index 00000000..114c221f
--- /dev/null
+++ b/core/fs/pxe/tftp.h
@@ -0,0 +1,54 @@
+/* -----------------------------------------------------------------------
+ *
+ * Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * tftp.h
+ */
+#ifndef PXE_TFTP_H
+#define PXE_TFTP_H
+
+/*
+ * TFTP default port number
+ */
+#define TFTP_PORT 69
+
+/*
+ * TFTP default block size
+ */
+#define TFTP_BLOCKSIZE_LG2 9
+#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2)
+
+/*
+ * TFTP operation codes
+ */
+#define TFTP_RRQ htons(1) // Read rest
+#define TFTP_WRQ htons(2) // Write rest
+#define TFTP_DATA htons(3) // Data packet
+#define TFTP_ACK htons(4) // ACK packet
+#define TFTP_ERROR htons(5) // ERROR packet
+#define TFTP_OACK htons(6) // OACK packet
+
+/*
+ * TFTP error codes
+ */
+#define TFTP_EUNDEF htons(0) // Unspecified error
+#define TFTP_ENOTFOUND htons(1) // File not found
+#define TFTP_EACCESS htons(2) // Access violation
+#define TFTP_ENOSPACE htons(3) // Disk full
+#define TFTP_EBADOP htons(4) // Invalid TFTP operation
+#define TFTP_EBADID htons(5) // Unknown transfer
+#define TFTP_EEXISTS htons(6) // File exists
+#define TFTP_ENOUSER htons(7) // No such user
+#define TFTP_EOPTNEG htons(8) // Option negotiation failure
+
+#endif /* PXE_TFTP_H */
diff --git a/core/fs/pxe/url.h b/core/fs/pxe/url.h
new file mode 100644
index 00000000..53984f3a
--- /dev/null
+++ b/core/fs/pxe/url.h
@@ -0,0 +1,33 @@
+/*
+ * url.h
+ */
+
+#ifndef CORE_PXE_URL_H
+#define CORE_PXE_URL_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+enum url_type {
+ URL_NORMAL, /* It is a full URL */
+ URL_OLD_TFTP, /* It's a ::-style TFTP path */
+ URL_SUFFIX /* Prepend the pathname prefix */
+};
+
+struct url_info {
+ char *scheme;
+ char *user;
+ char *passwd;
+ char *host;
+ uint32_t ip; /* Placeholder field not set by parse_url() */
+ unsigned int port;
+ char *path; /* Includes query */
+ enum url_type type;
+};
+
+enum url_type url_type(const char *url);
+void parse_url(struct url_info *ui, char *url);
+size_t url_escape_unsafe(char *output, const char *input, size_t bufsize);
+char *url_unescape(char *buffer, char terminator);
+
+#endif /* CORE_PXE_URL_H */
diff --git a/core/fs/pxe/urlparse.c b/core/fs/pxe/urlparse.c
new file mode 100644
index 00000000..6b73ddb6
--- /dev/null
+++ b/core/fs/pxe/urlparse.c
@@ -0,0 +1,223 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * urlparse.c
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "url.h"
+
+/*
+ * Return the type of a URL without modifying the string
+ */
+enum url_type url_type(const char *url)
+{
+ const char *q;
+
+ q = strchr(url, ':');
+ if (!q)
+ return URL_SUFFIX;
+
+ if (q[1] == '/' && q[2] == '/')
+ return URL_NORMAL;
+
+ if (q[1] == ':')
+ return URL_OLD_TFTP;
+
+ return URL_SUFFIX;
+}
+
+/*
+ * Decompose a URL into its components. This is done in-place;
+ * this routine does not allocate any additional storage. Freeing the
+ * original buffer frees all storage used.
+ */
+void parse_url(struct url_info *ui, char *url)
+{
+ char *p = url;
+ char *q, *r, *s;
+ int c;
+
+ memset(ui, 0, sizeof *ui);
+
+ q = strchr(p, ':');
+ if (q && (q[1] == '/' && q[2] == '/')) {
+ ui->type = URL_NORMAL;
+
+ ui->scheme = p;
+ *q = '\0';
+ p = q+3;
+
+ q = strchr(p, '/');
+ if (q) {
+ *q = '\0';
+ ui->path = q+1;
+ q = strchr(q+1, '#');
+ if (q)
+ *q = '\0';
+ } else {
+ ui->path = "";
+ }
+
+ r = strchr(p, '@');
+ if (r) {
+ ui->user = p;
+ *r = '\0';
+ s = strchr(p, ':');
+ if (s) {
+ *s = '\0';
+ ui->passwd = s+1;
+ }
+ p = r+1;
+ }
+
+ ui->host = p;
+ r = strchr(p, ':');
+ if (r) {
+ *r++ = '\0';
+ ui->port = 0;
+ while ((c = *r++)) {
+ c -= '0';
+ if (c > 9)
+ break;
+ ui->port = ui->port * 10 + c;
+ }
+ }
+ } else if (q && q[1] == ':') {
+ *q = '\0';
+ ui->scheme = "tftp";
+ ui->host = p;
+ ui->path = q+2;
+ ui->type = URL_OLD_TFTP;
+ } else {
+ ui->path = p;
+ ui->type = URL_SUFFIX;
+ }
+}
+
+/*
+ * Escapes unsafe characters in a URL.
+ * This does *not* escape things like query characters!
+ * Returns the number of characters in the total output.
+ */
+size_t url_escape_unsafe(char *output, const char *input, size_t bufsize)
+{
+ static const char uchexchar[] = "0123456789ABCDEF";
+ const char *p;
+ unsigned char c;
+ char *q;
+ size_t n = 0;
+
+ q = output;
+ for (p = input; (c = *p); p++) {
+ if (c <= ' ' || c > '~') {
+ if (++n < bufsize) *q++ = '%';
+ if (++n < bufsize) *q++ = uchexchar[c >> 4];
+ if (++n < bufsize) *q++ = uchexchar[c & 15];
+ } else {
+ if (++n < bufsize) *q++ = c;
+ }
+ }
+
+ *q = '\0';
+ return n;
+}
+
+static int hexdigit(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ c |= 0x20;
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return -1;
+}
+
+/*
+ * Unescapes a buffer, optionally ending at an *unescaped* terminator
+ * (like ; for TFTP). The unescaping is done in-place.
+ *
+ * If a terminator is reached, return a pointer to the first character
+ * after the terminator.
+ */
+char *url_unescape(char *buffer, char terminator)
+{
+ char *p = buffer;
+ char *q = buffer;
+ unsigned char c;
+ int x, y;
+
+ while ((c = *p)) {
+ if (c == terminator) {
+ *q = '\0';
+ return p;
+ }
+ p++;
+ if (c == '%') {
+ x = hexdigit(p[0]);
+ if (x >= 0) {
+ y = hexdigit(p[1]);
+ if (y >= 0) {
+ *q++ = (x << 4) + y;
+ p += 2;
+ continue;
+ }
+ }
+ }
+ *q++ = c;
+ }
+ *q = '\0';
+ return NULL;
+}
+
+#ifdef URL_TEST
+
+int main(int argc, char *argv[])
+{
+ int i;
+ struct url_info url;
+
+ for (i = 1; i < argc; i++) {
+ parse_url(&url, argv[i]);
+ printf("scheme: %s\n"
+ "user: %s\n"
+ "passwd: %s\n"
+ "host: %s\n"
+ "port: %d\n"
+ "path: %s\n"
+ "type: %d\n",
+ url.scheme, url.user, url.passwd, url.host, url.port,
+ url.path, url.type);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/core/fs/readdir.c b/core/fs/readdir.c
index e2d593fc..546a704a 100644
--- a/core/fs/readdir.c
+++ b/core/fs/readdir.c
@@ -1,3 +1,4 @@
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/dirent.h>
@@ -12,7 +13,7 @@ __export DIR *opendir(const char *path)
int rv;
struct file *file;
- rv = searchdir(path);
+ rv = searchdir(path, O_RDONLY|O_DIRECTORY);
if (rv < 0)
return NULL;
diff --git a/core/head.inc b/core/head.inc
index 71eb5744..286b9b4e 100644
--- a/core/head.inc
+++ b/core/head.inc
@@ -34,5 +34,6 @@
%include "tracers.inc"
%include "stack.inc"
%include "io.inc"
+%include "vkernel.inc"
%endif ; _HEAD_INC
diff --git a/core/idle.c b/core/idle.c
index 9514df88..a089b088 100644
--- a/core/idle.c
+++ b/core/idle.c
@@ -1,5 +1,5 @@
-/* -*- fundamental -*- ---------------------------------------------------
- *
+/* ----------------------------------------------------------------------- *
+ *
* Copyright 2008 H. Peter Anvin - All Rights Reserved
* Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
*
@@ -24,7 +24,7 @@
#define TICKS_TO_IDLE 4 /* Also in idle.inc */
-extern uint32_t _IdleTimer;
+extern jiffies_t _IdleTimer;
__export uint16_t NoHalt = 0;
int (*idle_hook_func)(void);
diff --git a/core/include/core.h b/core/include/core.h
index d54495bb..d35bd038 100644
--- a/core/include/core.h
+++ b/core/include/core.h
@@ -11,6 +11,9 @@
#include <com32.h>
#include <errno.h>
#include <syslinux/pmapi.h>
+#include <syslinux/sysappend.h>
+#include <kaboom.h>
+#include <timer.h>
extern char core_xfer_buf[65536];
extern char core_cache_buf[65536];
@@ -42,6 +45,10 @@ extern uint8_t FlowIgnore;
/* diskstart.inc isolinux.asm*/
extern void getlinsec(void);
+/* pm.inc */
+void core_pm_null_hook(void);
+extern void (*core_pm_hook)(void);
+
/* getc.inc */
extern void core_open(void);
@@ -60,6 +67,12 @@ extern void *zalloc(size_t);
extern void free(void *);
extern void mem_init(void);
+/* sysappend.c */
+extern void print_sysappend(void);
+extern const char *sysappend_strings[SYSAPPEND_MAX];
+extern uint32_t SysAppends;
+extern void sysappend_set_uuid(const uint8_t *uuid);
+
void __cdecl core_intcall(uint8_t, const com32sys_t *, com32sys_t *);
void __cdecl core_farcall(uint32_t, const com32sys_t *, com32sys_t *);
int __cdecl core_cfarcall(uint32_t, const void *, uint32_t);
@@ -74,31 +87,6 @@ void call16(void (*)(void), const com32sys_t *, com32sys_t *);
#define __bss16 __attribute__((nocommon,section(".bss16")))
/*
- * Section for very large aligned objects, not zeroed on startup
- */
-#define __hugebss __attribute__((nocommon,section(".hugebss"),aligned(4096)))
-
-/*
- * Death! The macro trick is to avoid symbol conflict with
- * the real-mode symbol kaboom.
- */
-__noreturn _kaboom(void);
-#define kaboom() _kaboom()
-
-/*
- * Basic timer function...
- */
-extern volatile uint32_t __jiffies, __ms_timer;
-static inline uint32_t jiffies(void)
-{
- return __jiffies;
-}
-static inline uint32_t ms_timer(void)
-{
- return __ms_timer;
-}
-
-/*
* Helper routine to return a specific set of flags
*/
static inline void set_flags(com32sys_t *regs, uint32_t flags)
@@ -125,7 +113,11 @@ extern void cleanup_hardware(void);
extern void sirq_cleanup(void);
extern void adjust_screen(void);
-extern void execute(const char *cmdline, uint32_t type);
+extern void execute(const char *cmdline, uint32_t type, bool sysappend);
extern void load_kernel(const char *cmdline);
+extern void dmi_init(void);
+
+extern void do_sysappend(char *buf);
+
#endif /* CORE_H */
diff --git a/core/include/ctype.h b/core/include/ctype.h
index 048a77d6..6c7f57f4 100644
--- a/core/include/ctype.h
+++ b/core/include/ctype.h
@@ -22,9 +22,17 @@ static inline int tolower(int c)
return c;
}
-static inline int isspace(int c)
+static inline int isspace(int ch)
{
- return c <= ' ';
+ int space = 0;
+ if ((ch == ' ') ||
+ (ch == '\f') ||
+ (ch == '\n') ||
+ (ch == '\r') ||
+ (ch == '\t') ||
+ (ch == '\v'))
+ space = 1;
+ return space;
}
#endif /* CTYPE_H */
diff --git a/core/include/fs.h b/core/include/fs.h
index 5c13f5b5..c7d0fd75 100644
--- a/core/include/fs.h
+++ b/core/include/fs.h
@@ -12,12 +12,9 @@
#include "disk.h"
/*
- * Maximum number of open files. This is *currently* constrained by the
- * fact that PXE needs to be able to fit all its packet buffers into a
- * 64K segment; this should be fixed by moving the packet buffers to high
- * memory.
+ * Maximum number of open files.
*/
-#define MAX_OPEN_LG2 5
+#define MAX_OPEN_LG2 7
#define MAX_OPEN (1 << MAX_OPEN_LG2)
#define FILENAME_MAX_LG2 8
@@ -56,7 +53,7 @@ struct fs_ops {
enum fs_flags fs_flags;
int (*fs_init)(struct fs_info *);
- void (*searchdir)(const char *, struct file *);
+ void (*searchdir)(const char *, int, struct file *);
uint32_t (*getfssec)(struct file *, char *, int, bool *);
void (*close_file)(struct file *);
void (*mangle_name)(char *, const char *);
@@ -191,10 +188,10 @@ extern char *PATH;
void pm_mangle_name(com32sys_t *);
void pm_searchdir(com32sys_t *);
void mangle_name(char *, const char *);
-int searchdir(const char *name);
+int searchdir(const char *name, int flags);
void _close_file(struct file *);
size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors);
-int open_file(const char *name, struct com32_filedata *filedata);
+int open_file(const char *name, int flags, struct com32_filedata *filedata);
void pm_open_file(com32sys_t *);
void close_file(uint16_t handle);
void pm_close_file(com32sys_t *);
diff --git a/core/include/kaboom.h b/core/include/kaboom.h
new file mode 100644
index 00000000..4a763be9
--- /dev/null
+++ b/core/include/kaboom.h
@@ -0,0 +1,11 @@
+#ifndef KABOOM_H
+#define KABOOM_H
+
+/*
+ * Death! The macro trick is to avoid symbol conflict with
+ * the real-mode symbol kaboom.
+ */
+__noreturn _kaboom(void);
+#define kaboom() _kaboom()
+
+#endif /* KABOOM_H */
diff --git a/core/include/mbox.h b/core/include/mbox.h
new file mode 100644
index 00000000..3c35ce4e
--- /dev/null
+++ b/core/include/mbox.h
@@ -0,0 +1,59 @@
+/*
+ * mbox.h
+ *
+ * Simple thread mailbox interface
+ */
+
+#ifndef _MBOX_H
+#define _MBOX_H
+
+#include "thread.h"
+
+/*
+ * If a mailbox is allocated statically (as a struct mailbox), this
+ * is the number of slots it gets.
+ */
+#define MAILBOX_STATIC_SIZE 512
+
+struct mailbox {
+ struct semaphore prod_sem; /* Producer semaphore (empty slots) */
+ struct semaphore cons_sem; /* Consumer semaphore (data slots) */
+ struct semaphore head_sem; /* Head pointer semaphore */
+ struct semaphore tail_sem; /* Tail pointer semaphore */
+ void **wrap; /* Where pointers wrap */
+ void **head; /* Head pointer */
+ void **tail; /* Tail pointer */
+
+ void *data[MAILBOX_STATIC_SIZE]; /* Data array */
+};
+
+/* The number of bytes for an mailbox of size s */
+#define MBOX_BYTES(s) (sizeof(struct mailbox) + \
+ ((s)-MAILBOX_STATIC_SIZE)*sizeof(void *))
+
+void mbox_init(struct mailbox *mbox, size_t size);
+int mbox_post(struct mailbox *mbox, void *msg, mstime_t timeout);
+mstime_t mbox_fetch(struct mailbox *mbox, void **msg, mstime_t timeout);
+
+/*
+ * This marks a mailbox object as unusable; it will remain unusable
+ * until sem_init() is called on it again. This DOES NOT clear the
+ * list of blocked processes on this mailbox!
+ *
+ * It is also possible to mark the mailbox invalid by zeroing its
+ * memory structure.
+ */
+static inline void mbox_set_invalid(struct mailbox *mbox)
+{
+ sem_set_invalid(&mbox->prod_sem);
+}
+
+/*
+ * Ask if a mailbox object has been initialized.
+ */
+static inline bool mbox_is_valid(struct mailbox *mbox)
+{
+ return sem_is_valid(&mbox->prod_sem);
+}
+
+#endif /* _MBOX_H */
diff --git a/core/include/thread.h b/core/include/thread.h
new file mode 100644
index 00000000..6bfdfaa7
--- /dev/null
+++ b/core/include/thread.h
@@ -0,0 +1,115 @@
+#ifndef _THREAD_H
+#define _THREAD_H
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <timer.h>
+#include <sys/cpu.h>
+
+/* The idle thread runs at this priority */
+#define IDLE_THREAD_PRIORITY INT_MAX
+
+/* This priority should normally be used for hardware-polling threads */
+#define POLL_THREAD_PRIORITY (INT_MAX-1)
+
+struct semaphore;
+
+struct thread_list {
+ struct thread_list *next, *prev;
+};
+
+/*
+ * Stack frame used by __switch_to, see thread_asm.S
+ */
+struct thread_stack {
+ int errno;
+ uint16_t rmsp, rmss;
+ uint32_t edi, esi, ebp, ebx;
+ void (*eip)(void);
+};
+
+struct thread_block {
+ struct thread_list list;
+ struct thread *thread;
+ struct semaphore *semaphore;
+ mstime_t block_time;
+ mstime_t timeout;
+ bool timed_out;
+};
+
+#define THREAD_MAGIC 0x3568eb7d
+
+struct thread {
+ struct thread_stack *esp; /* Must be first; stack pointer */
+ unsigned int thread_magic;
+ const char *name; /* Name (for debugging) */
+ struct thread_list list;
+ struct thread_block *blocked;
+ void *stack, *rmstack; /* Stacks, iff allocated by malloc/lmalloc */
+ void *pvt; /* For the benefit of lwIP */
+ int prio;
+};
+
+extern void (*sched_hook_func)(void);
+
+void __thread_process_timeouts(void);
+void __schedule(void);
+void __switch_to(struct thread *);
+void thread_yield(void);
+
+extern struct thread *__current;
+static inline struct thread *current(void)
+{
+ return __current;
+}
+
+struct semaphore {
+ int count;
+ struct thread_list list;
+};
+
+#define DECLARE_INIT_SEMAPHORE(sem, cnt) \
+ struct semaphore sem = { \
+ .count = (cnt), \
+ .list = { \
+ .next = &sem.list, \
+ .prev = &sem.list \
+ } \
+ }
+
+mstime_t sem_down(struct semaphore *, mstime_t);
+void sem_up(struct semaphore *);
+void sem_init(struct semaphore *, int);
+
+/*
+ * This marks a semaphore object as unusable; it will remain unusable
+ * until sem_init() is called on it again. This DOES NOT clear the
+ * list of blocked processes on this semaphore!
+ *
+ * It is also possible to mark the semaphore invalid by zeroing its
+ * memory structure.
+ */
+static inline void sem_set_invalid(struct semaphore *sem)
+{
+ sem->list.next = NULL;
+}
+
+/*
+ * Ask if a semaphore object has been initialized.
+ */
+static inline bool sem_is_valid(struct semaphore *sem)
+{
+ return !!sem->list.next;
+}
+
+struct thread *start_thread(const char *name, size_t stack_size, int prio,
+ void (*start_func)(void *), void *func_arg);
+void __exit_thread(void);
+void kill_thread(struct thread *);
+
+void start_idle_thread(void);
+void test_thread(void);
+
+#endif /* _THREAD_H */
diff --git a/core/include/timer.h b/core/include/timer.h
new file mode 100644
index 00000000..1d66ba73
--- /dev/null
+++ b/core/include/timer.h
@@ -0,0 +1,21 @@
+#ifndef TIMER_H
+#define TIMER_H
+
+/*
+ * Basic timer function...
+ */
+typedef uint32_t jiffies_t;
+extern volatile jiffies_t __jiffies, __ms_timer;
+static inline jiffies_t jiffies(void)
+{
+ return __jiffies;
+}
+
+typedef uint32_t mstime_t;
+typedef int32_t mstimediff_t;
+static inline mstime_t ms_timer(void)
+{
+ return __ms_timer;
+}
+
+#endif /* TIMER_H */
diff --git a/core/init.c b/core/init.c
index ab2e271d..85bab775 100644
--- a/core/init.c
+++ b/core/init.c
@@ -77,6 +77,14 @@ void init(com32sys_t *regs __unused)
adjust_screen();
+ /* Init the memory subsystem */
+ mem_init();
+
/* CPU-dependent initialization and related checks. */
check_escapes();
+
+ /*
+ * Scan the DMI tables for interesting information.
+ */
+ dmi_init();
}
diff --git a/core/isolinux.asm b/core/isolinux.asm
index b292d8f9..b494eb4d 100644
--- a/core/isolinux.asm
+++ b/core/isolinux.asm
@@ -35,23 +35,6 @@ SECTOR_SIZE equ (1 << SECTOR_SHIFT)
ROOT_DIR_WORD equ 0x002F
-;
-; The following structure is used for "virtual kernels"; i.e. LILO-style
-; option labels. The options we permit here are `kernel' and `append
-; Since there is no room in the bottom 64K for all of these, we
-; stick them in high memory and copy them down before we need them.
-;
- struc vkernel
-vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
-vk_rname: resb FILENAME_MAX ; Real name
-vk_appendlen: resw 1
-vk_type: resb 1 ; Type of file
- alignb 4
-vk_append: resb max_cmd_len+1 ; Command line
- alignb 4
-vk_end: equ $ ; Should be <= vk_size
- endstruc
-
; ---------------------------------------------------------------------------
; BEGIN CODE
; ---------------------------------------------------------------------------
@@ -1213,17 +1196,6 @@ FuncFlag resb 1 ; Escape sequences received from keyboard
KernelType resb 1 ; Kernel type, from vkernel, if known
global KernelName
KernelName resb FILENAME_MAX ; Mangled name for kernel
- section .data16
- global IPAppends, numIPAppends
-%if IS_PXELINUX
- extern IPOption
- alignz 2
-IPAppends dw IPOption
-numIPAppends equ ($-IPAppends)/2
-%else
-IPAppends equ 0
-numIPAppends equ 0
-%endif
section .text16
;
diff --git a/core/keywords b/core/keywords
index 7f585b48..8af0095f 100644
--- a/core/keywords
+++ b/core/keywords
@@ -34,6 +34,8 @@ onerror
noescape
nocomplete
nohalt
+sysappend
+sendcookies
f0
f1
f2
diff --git a/core/keywords.inc b/core/keywords.inc
index 08d77c64..d91ca4ff 100644
--- a/core/keywords.inc
+++ b/core/keywords.inc
@@ -92,9 +92,11 @@ keywd_table:
keyword f0, pc_filename, FKeyN(10)
keyword f11, pc_filename, FKeyN(11)
keyword f12, pc_filename, FKeyN(12)
+ keyword ipappend, pc_sysappend
+ keyword sysappend, pc_sysappend
+ keyword localboot, pc_localboot
%if IS_PXELINUX
- keyword ipappend, pc_ipappend
+ keyword sendcookies, pc_sendcookies
%endif
- keyword localboot, pc_localboot
keywd_count equ ($-keywd_table)/keywd_size
diff --git a/core/layout.inc b/core/layout.inc
index be797ede..53ca783d 100644
--- a/core/layout.inc
+++ b/core/layout.inc
@@ -65,6 +65,7 @@ STACK32_LEN equ 64*1024
section .config write progbits align=4
section .replacestub exec write progbits align=16
section .gentextnr exec write nobits align=16
+ section .stack16 write nobits align=16
; Use .bss16 for things that doesn't have to be in low memory;
; .earlybss should be used for things that absolutely have
diff --git a/core/lwip/CHANGELOG b/core/lwip/CHANGELOG
new file mode 100644
index 00000000..6e27a66b
--- /dev/null
+++ b/core/lwip/CHANGELOG
@@ -0,0 +1,3050 @@
+HISTORY
+
+(CVS HEAD)
+
+ * [Enter new changes just after this line - do not remove this line]
+
+ ++ New features:
+
+
+ ++ Bugfixes:
+
+
+
+
+(STABLE-1.4.0)
+
+ ++ New features:
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp_impl.h, tcp_in.c, tcp_out.c: Removed 'dataptr' from 'struct tcp_seg' and
+ calculate it in tcp_zero_window_probe (the only place where it was used).
+
+ 2010-11-21: Simon Goldschmidt
+ * dhcp.c/.h: Added a function to deallocate the struct dhcp from a netif
+ (fixes bug #31525).
+
+ 2010-07-12: Simon Goldschmidt (patch by Stephane Lesage)
+ * ip.c, udp.c/.h, pbuf.h, sockets.c: task #10495: Added support for
+ IP_MULTICAST_LOOP at socket- and raw-API level.
+
+ 2010-06-16: Simon Goldschmidt
+ * ip.c: Added an optional define (LWIP_IP_ACCEPT_UDP_PORT) that can allow
+ link-layer-addressed UDP traffic to be received while a netif is down (just
+ like DHCP during configuration)
+
+ 2010-05-22: Simon Goldschmidt
+ * many many files: bug #27352: removed packing from ip_addr_t, the packed
+ version is now only used in protocol headers. Added global storage for
+ current src/dest IP address while in input functions.
+
+ 2010-05-16: Simon Goldschmidt
+ * def.h: task #10391: Add preprocessor-macros for compile-time htonl
+ calculation (and use them throughout the stack where applicable)
+
+ 2010-05-16: Simon Goldschmidt
+ * opt.h, memp_std.h, memp.c, ppp_oe.h/.c: PPPoE now uses its own MEMP pool
+ instead of the heap (moved struct pppoe_softc from ppp_oe.c to ppp_oe.h)
+
+ 2010-05-16: Simon Goldschmidt
+ * opt.h, memp_std.h, dns.h/.c: DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses its own
+ MEMP pool instead of the heap
+
+ 2010-05-13: Simon Goldschmidt
+ * tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly), added
+ new option SO_REUSE_RXTOALL to pass received UDP broadcast/multicast
+ packets to more than one pcb.
+
+ 2010-05-02: Simon Goldschmidt
+ * netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending
+ UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1
+
+ 2010-04-30: Simon Goldschmidt
+ * udp.h/.c, pbuf.h/.c: task #6849: added udp_send(_to/_if) functions that
+ take a precalculated checksum, added pbuf_fill_chksum() to copy data
+ into a pbuf and at the same time calculating the checksum for that data
+
+ 2010-04-29: Simon Goldschmidt
+ * ip_addr.h, etharp.h/.c, autoip.c: Create overridable macros for copying
+ 2-byte-aligned IP addresses and MAC addresses
+
+ 2010-04-28: Patch by Bill Auerbach
+ * ip.c: Inline generating IP checksum to save a function call
+
+ 2010-04-14: Simon Goldschmidt
+ * tcpip.h/.c, timers.c: Added an overridable define to get informed when the
+ tcpip_thread processes messages or timeouts to implement a watchdog.
+
+ 2010-03-28: Simon Goldschmidt
+ * ip_frag.c: create a new (contiguous) PBUF_RAM for every outgoing
+ fragment if LWIP_NETIF_TX_SINGLE_PBUF==1
+
+ 2010-03-27: Simon Goldschmidt
+ * etharp.c: Speedup TX by moving code from find_entry to etharp_output/
+ etharp_query to prevent unnecessary function calls (inspired by
+ patch #7135).
+
+ 2010-03-20: Simon Goldschmidt
+ * opt.h, tcpip.c/.h: Added an option to disable tcpip_(un)timeout code
+ since the linker cannot do this automatically to save space.
+
+ 2010-03-20: Simon Goldschmidt
+ * opt.h, etharp.c/.h: Added support for static ARP table entries
+
+ 2010-03-14: Simon Goldschmidt
+ * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum
+ when creating TCP segments, not when (re-)transmitting them.
+
+ 2010-03-07: Simon Goldschmidt
+ * sockets.c: bug #28775 (select/event_callback: only check select_cb_list
+ on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code.
+ This should speed up receiving data on sockets as the select code in
+ event_callback is only executed when select is waiting.
+
+ 2010-03-06: Simon Goldschmidt
+ * tcp_out.c: task #7013 (Create option to have all packets delivered to
+ netif->output in one piece): Always copy to try to create single pbufs
+ in tcp_write.
+
+ 2010-03-06: Simon Goldschmidt
+ * api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv
+ by not allocating a netbuf): added function netconn_recv_tcp_pbuf()
+ for tcp netconns to receive pbufs, not netbufs; use that function
+ for tcp sockets.
+
+ 2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt
+ * opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040:
+ Work on tcp_enqueue: Don't waste memory when chaining segments,
+ added option TCP_OVERSIZE to prevent creating many small pbufs when
+ calling tcp_write with many small blocks of data. Instead, pbufs are
+ allocated larger than needed and the space is used for later calls to
+ tcp_write.
+
+ 2010-02-21: Simon Goldschmidt
+ * stats.c/.h: Added const char* name to mem- and memp-stats for easier
+ debugging.
+
+ 2010-02-21: Simon Goldschmidt
+ * tcp.h (and usages), added tcp_impl.h: Splitted API and internal
+ implementation of tcp to make API usage cleare to application programmers
+
+ 2010-02-14: Simon Goldschmidt/Stephane Lesage
+ * ip_addr.h: Improved some defines working on ip addresses, added faster
+ macro to copy addresses that cannot be NULL
+
+ 2010-02-13: Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c, sockets.c: task #7865 (implement non-
+ blocking send operation)
+
+ 2010-02-12: Simon Goldschmidt
+ * sockets.c/.h: Added a minimal version of posix fctl() to have a
+ standardised way to set O_NONBLOCK for nonblocking sockets.
+
+ 2010-02-12: Simon Goldschmidt
+ * dhcp.c/.h, autoip.c/.h: task #10139 (Prefer statically allocated
+ memory): added autoip_set_struct() and dhcp_set_struct() to let autoip
+ and dhcp work with user-allocated structs instead of callin mem_malloc
+
+ 2010-02-12: Simon Goldschmidt/Jeff Barber
+ * tcp.c/h: patch #6865 (SO_REUSEADDR for TCP): if pcb.so_options has
+ SOF_REUSEADDR set, allow binding to endpoint in TIME_WAIT
+
+ 2010-02-12: Simon Goldschmidt
+ * sys layer: task #10139 (Prefer statically allocated memory): converted
+ mbox and semaphore functions to take pointers to sys_mbox_t/sys_sem_t;
+ converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
+ task #7212: Add Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX
+ to let sys.h use binary semaphores instead of mutexes - as before)
+
+ 2010-02-09: Simon Goldschmidt (Simon Kallweit)
+ * timers.c/.h: Added function sys_restart_timeouts() from patch #7085
+ (Restart system timeout handling)
+
+ 2010-02-09: Simon Goldschmidt
+ * netif.c/.h, removed loopif.c/.h: task #10153 (Integrate loopif into
+ netif.c) - loopif does not have to be created by the port any more,
+ just define LWIP_HAVE_LOOPIF to 1.
+
+ 2010-02-08: Simon Goldschmidt
+ * inet.h, ip_addr.c/.h: Added reentrant versions of inet_ntoa/ipaddr_ntoa
+ inet_ntoa_r/ipaddr_ntoa_r
+
+ 2010-02-08: Simon Goldschmidt
+ * netif.h: Added netif_s/get_igmp_mac_filter() macros
+
+ 2010-02-05: Simon Goldschmidt
+ * netif.h: Added function-like macros to get/set the hostname on a netif
+
+ 2010-02-04: Simon Goldschmidt
+ * nearly every file: Replaced struct ip_addr by typedef ip_addr_t to
+ make changing the actual implementation behind the typedef easier.
+
+ 2010-02-01: Simon Goldschmidt
+ * opt.h, memp_std.h, dns.h, netdb.c, memp.c: Let netdb use a memp pool
+ for allocating memory when getaddrinfo() is called.
+
+ 2010-01-31: Simon Goldschmidt
+ * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse
+ them once instead of parsing for every option. This also removes
+ the need for mem_malloc from dhcp_recv and makes it possible to
+ correctly retrieve the BOOTP file.
+
+ 2010-01-30: simon Goldschmidt
+ * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect
+ the sockets array.
+
+ 2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
+ * api.h, api_msg.c, sockets.c: Added except set support in select
+ (patch #6860)
+
+ 2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
+ * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c:
+ Add non-blocking support for connect (partly from patch #6860),
+ plus many cleanups in socket & netconn API.
+
+ 2010-01-27: Simon Goldschmidt
+ * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding
+ to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605
+
+ 2010-01-26: Simon Goldschmidt
+ * snmp: Use memp pools for snmp instead of the heap; added 4 new pools.
+
+ 2010-01-14: Simon Goldschmidt
+ * ppp.c/.h: Fixed bug #27856: PPP: Set netif link- and status-callback
+ by adding ppp_set_netif_statuscallback()/ppp_set_netif_linkcallback()
+
+ 2010-01-13: Simon Goldschmidt
+ * mem.c: The heap now may be moved to user-defined memory by defining
+ LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
+ (patch #6966 and bug #26133)
+
+ 2010-01-10: Simon Goldschmidt (Bill Auerbach)
+ * opt.h, memp.c: patch #6822 (Add option to place memory pools in
+ separate arrays)
+
+ 2010-01-10: Simon Goldschmidt
+ * init.c, igmp.c: patch #6463 (IGMP - Adding Random Delay): added define
+ LWIP_RAND() for lwip-wide randomization (to be defined in cc.h)
+
+ 2009-12-31: Simon Goldschmidt
+ * tcpip.c, init.c, memp.c, sys.c, memp_std.h, sys.h, tcpip.h
+ added timers.c/.h: Separated timer implementation from semaphore/mbox
+ implementation, moved timer implementation to timers.c/.h, timers are
+ now only called from tcpip_thread or by explicitly checking them.
+ (TASK#7235)
+
+ 2009-12-27: Simon Goldschmidt
+ * opt.h, etharp.h/.c, init.c, tcpip.c: Added an additional option
+ LWIP_ETHERNET to support ethernet without ARP (necessary for pure PPPoE)
+
+
+ ++ Bugfixes:
+
+ 2011-04-20: Simon Goldschmidt
+ * sys_arch.txt: sys_arch_timeouts() is not needed any more.
+
+ 2011-04-13: Simon Goldschmidt
+ * tcp.c, udp.c: Fixed bug #33048 (Bad range for IP source port numbers) by
+ using ports in the IANA private/dynamic range (49152 through 65535).
+
+ 2011-03-29: Simon Goldschmidt, patch by Emil Lhungdahl:
+ * etharp.h/.c: Fixed broken VLAN support.
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp.c: Fixed bug #32926 (TCP_RMV(&tcp_bound_pcbs) is called on unbound tcp
+ pcbs) by checking if the pcb was bound (local_port != 0).
+
+ 2011-03-27: Simon Goldschmidt
+ * ppp.c: Fixed bug #32280 (ppp: a pbuf is freed twice)
+
+ 2011-03-27: Simon Goldschmidt
+ * sockets.c: Fixed bug #32906: lwip_connect+lwip_send did not work for udp and
+ raw pcbs with LWIP_TCPIP_CORE_LOCKING==1.
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #32820 (Outgoing TCP connections created before route
+ is present never times out) by starting retransmission timer before checking
+ route.
+
+ 2011-03-22: Simon Goldschmidt
+ * ppp.c: Fixed bug #32648 (PPP code crashes when terminating a link) by only
+ calling sio_read_abort() if the file descriptor is valid.
+
+ 2011-03-14: Simon Goldschmidt
+ * err.h/.c, sockets.c, api_msg.c: fixed bug #31748 (Calling non-blocking connect
+ more than once can render a socket useless) since it mainly involves changing
+ "FATAL" classification of error codes: ERR_USE and ERR_ISCONN just aren't fatal.
+
+ 2011-03-13: Simon Goldschmidt
+ * sockets.c: fixed bug #32769 (ESHUTDOWN is linux-specific) by fixing
+ err_to_errno_table (ERR_CLSD: ENOTCONN instead of ESHUTDOWN), ERR_ISCONN:
+ use EALRADY instead of -1
+
+ 2011-03-13: Simon Goldschmidt
+ * api_lib.c: netconn_accept: return ERR_ABRT instead of ERR_CLSD if the
+ connection has been aborted by err_tcp (since this is not a normal closing
+ procedure).
+
+ 2011-03-13: Simon Goldschmidt
+ * tcp.c: tcp_bind: return ERR_VAL instead of ERR_ISCONN when trying to bind
+ with pcb->state != CLOSED
+
+ 2011-02-17: Simon Goldschmidt
+ * rawapi.txt: Fixed bug #32561 tcp_poll argument definition out-of-order in
+ documentation
+
+ 2011-02-17: Simon Goldschmidt
+ * many files: Added missing U/UL modifiers to fix 16-bit-arch portability.
+
+ 2011-01-24: Simon Goldschmidt
+ * sockets.c: Fixed bug #31741: lwip_select seems to have threading problems
+
+ 2010-12-02: Simon Goldschmidt
+ * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal.
+
+ 2010-11-23: Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c, sockets.c: netconn.recv_avail is only used for
+ LWIP_SO_RCVBUF and ioctl/FIONREAD.
+
+ 2010-11-23: Simon Goldschmidt
+ * etharp.c: Fixed bug #31720: ARP-queueing: RFC 1122 recommends to queue at
+ least 1 packet -> ARP_QUEUEING==0 now queues the most recent packet.
+
+ 2010-11-23: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #30577: tcp_input: don't discard ACK-only packets after
+ refusing 'refused_data' again.
+
+ 2010-11-22: Simon Goldschmidt
+ * sockets.c: Fixed bug #31590: getsockopt(... SO_ERROR ...) gives EINPROGRESS
+ after a successful nonblocking connection.
+
+ 2010-11-22: Simon Goldschmidt
+ * etharp.c: Fixed bug #31722: IP packets sent with an AutoIP source addr
+ must be sent link-local
+
+ 2010-11-22: Simon Goldschmidt
+ * timers.c: patch #7329: tcp_timer_needed prototype was ifdef'ed out for
+ LWIP_TIMERS==0
+
+ 2010-11-20: Simon Goldschmidt
+ * sockets.c: Fixed bug #31170: lwip_setsockopt() does not set socket number
+
+ 2010-11-20: Simon Goldschmidt
+ * sockets.h: Fixed bug #31304: Changed SHUT_RD, SHUT_WR and SHUT_RDWR to
+ resemble other stacks.
+
+ 2010-11-20: Simon Goldschmidt
+ * dns.c: Fixed bug #31535: TCP_SND_QUEUELEN must be at least 2 or else
+ no-copy TCP writes will never succeed.
+
+ 2010-11-20: Simon Goldschmidt
+ * dns.c: Fixed bug #31701: Error return value from dns_gethostbyname() does
+ not match documentation: return ERR_ARG instead of ERR_VAL if not
+ initialized or wrong argument.
+
+ 2010-10-20: Simon Goldschmidt
+ * sockets.h: Fixed bug #31385: sizeof(struct sockaddr) is 30 but should be 16
+
+ 2010-10-05: Simon Goldschmidt
+ * dhcp.c: Once again fixed #30038: DHCP/AutoIP cooperation failed when
+ replugging the network cable after an AutoIP address was assigned.
+
+ 2010-08-10: Simon Goldschmidt
+ * tcp.c: Fixed bug #30728: tcp_new_port() did not check listen pcbs
+
+ 2010-08-03: Simon Goldschmidt
+ * udp.c, raw.c: Don't chain empty pbufs when sending them (fixes bug #30625)
+
+ 2010-08-01: Simon Goldschmidt (patch by Greg Renda)
+ * ppp.c: Applied patch #7264 (PPP protocols are rejected incorrectly on big
+ endian architectures)
+
+ 2010-07-28: Simon Goldschmidt
+ * api_lib.c, api_msg.c, sockets.c, mib2.c: Fixed compilation with TCP or UDP
+ disabled.
+
+ 2010-07-27: Simon Goldschmidt
+ * tcp.c: Fixed bug #30565 (tcp_connect() check bound list): that check did no
+ harm but never did anything
+
+ 2010-07-21: Simon Goldschmidt
+ * ip.c: Fixed invalid fix for bug #30402 (CHECKSUM_GEN_IP_INLINE does not
+ add IP options)
+
+ 2010-07-16: Kieran Mansley
+ * msg_in.c: Fixed SNMP ASN constant defines to not use ! operator
+
+ 2010-07-10: Simon Goldschmidt
+ * ip.c: Fixed bug #30402: CHECKSUM_GEN_IP_INLINE does not add IP options
+
+ 2010-06-30: Simon Goldschmidt
+ * api_msg.c: fixed bug #30300 (shutdown parameter was not initialized in
+ netconn_delete)
+
+ 2010-06-28: Kieran Mansley
+ * timers.c remove unportable printing of C function pointers
+
+ 2010-06-24: Simon Goldschmidt
+ * init.c, timers.c/.h, opt.h, memp_std.h: From patch #7221: added flag
+ NO_SYS_NO_TIMERS to drop timer support for NO_SYS==1 for easier upgrading
+
+ 2010-06-24: Simon Goldschmidt
+ * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly
+ implemented shutdown at socket level.
+
+ 2010-06-21: Simon Goldschmidt
+ * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has
+ problems with zero-copy DMA MACs) by adding custom pbufs and implementing
+ custom pbufs that reference other (original) pbufs. Additionally set
+ IP_FRAG_USES_STATIC_BUF=0 as default to be on the safe side.
+
+ 2010-06-15: Simon Goldschmidt
+ * dhcp.c: Fixed bug #29970: DHCP endian issue parsing option responses
+
+ 2010-06-14: Simon Goldschmidt
+ * autoip.c: Fixed bug #30039: AutoIP does not reuse previous addresses
+
+ 2010-06-12: Simon Goldschmidt
+ * dhcp.c: Fixed bug #30038: dhcp_network_changed doesn't reset AUTOIP coop
+ state
+
+ 2010-05-17: Simon Goldschmidt
+ * netdb.c: Correctly NULL-terminate h_addr_list
+
+ 2010-05-16: Simon Goldschmidt
+ * def.h/.c: changed the semantics of LWIP_PREFIX_BYTEORDER_FUNCS to prevent
+ "symbol already defined" i.e. when linking to winsock
+
+ 2010-05-05: Simon Goldschmidt
+ * def.h, timers.c: Fixed bug #29769 (sys_check_timeouts: sys_now() may
+ overflow)
+
+ 2010-04-21: Simon Goldschmidt
+ * api_msg.c: Fixed bug #29617 (sometime cause stall on delete listening
+ connection)
+
+ 2010-03-28: Luca Ceresoli
+ * ip_addr.c/.h: patch #7143: Add a few missing const qualifiers
+
+ 2010-03-27: Luca Ceresoli
+ * mib2.c: patch #7130: remove meaningless const qualifiers
+
+ 2010-03-26: Simon Goldschmidt
+ * tcp_out.c: Make LWIP_NETIF_TX_SINGLE_PBUF work for TCP, too
+
+ 2010-03-26: Simon Goldschmidt
+ * various files: Fixed compiling with different options disabled (TCP/UDP),
+ triggered by bug #29345; don't allocate acceptmbox if LWIP_TCP is disabled
+
+ 2010-03-25: Simon Goldschmidt
+ * sockets.c: Fixed bug #29332: lwip_select() processes readset incorrectly
+
+ 2010-03-25: Simon Goldschmidt
+ * tcp_in.c, test_tcp_oos.c: Fixed bug #29080: Correctly handle remote side
+ overrunning our rcv_wnd in ooseq case.
+
+ 2010-03-22: Simon Goldschmidt
+ * tcp.c: tcp_listen() did not copy the pcb's prio.
+
+ 2010-03-19: Simon Goldschmidt
+ * snmp_msg.c: Fixed bug #29256: SNMP Trap address was not correctly set
+
+ 2010-03-14: Simon Goldschmidt
+ * opt.h, etharp.h: Fixed bug #29148 (Incorrect PBUF_POOL_BUFSIZE for ports
+ where ETH_PAD_SIZE > 0) by moving definition of ETH_PAD_SIZE to opt.h
+ and basing PBUF_LINK_HLEN on it.
+
+ 2010-03-08: Simon Goldschmidt
+ * netif.c, ipv4/ip.c: task #10241 (AutoIP: don't break existing connections
+ when assiging routable address): when checking incoming packets and
+ aborting existing connection on address change, filter out link-local
+ addresses.
+
+ 2010-03-06: Simon Goldschmidt
+ * sockets.c: Fixed LWIP_NETIF_TX_SINGLE_PBUF for LWIP_TCPIP_CORE_LOCKING
+
+ 2010-03-06: Simon Goldschmidt
+ * ipv4/ip.c: Don't try to forward link-local addresses
+
+ 2010-03-06: Simon Goldschmidt
+ * etharp.c: Fixed bug #29087: etharp: don't send packets for LinkLocal-
+ addresses to gw
+
+ 2010-03-05: Simon Goldschmidt
+ * dhcp.c: Fixed bug #29072: Correctly set ciaddr based on message-type
+ and state.
+
+ 2010-03-05: Simon Goldschmidt
+ * api_msg.c: Correctly set TCP_WRITE_FLAG_MORE when netconn_write is split
+ into multiple calls to tcp_write.
+
+ 2010-02-21: Simon Goldschmidt
+ * opt.h, mem.h, dns.c: task #10140: Remove DNS_USES_STATIC_BUF (keep
+ the implementation of DNS_USES_STATIC_BUF==1)
+
+ 2010-02-20: Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement
+ close() vs. shutdown(). Now the application does not get any more
+ recv callbacks after calling tcp_close(). Added tcp_shutdown().
+
+ 2010-02-19: Simon Goldschmidt
+ * mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent
+ confusion with realloc()
+
+ 2010-02-15: Simon Goldschmidt/Stephane Lesage
+ * netif.c/.h: Link status does not depend on LWIP_NETIF_LINK_CALLBACK
+ (fixes bug #28899)
+
+ 2010-02-14: Simon Goldschmidt
+ * netif.c: Fixed bug #28877 (Duplicate ARP gratuitous packet with
+ LWIP_NETIF_LINK_CALLBACK set on) by only sending if both link- and
+ admin-status of a netif are up
+
+ 2010-02-14: Simon Goldschmidt
+ * opt.h: Disable ETHARP_TRUST_IP_MAC by default since it slows down packet
+ reception and is not really necessary
+
+ 2010-02-14: Simon Goldschmidt
+ * etharp.c/.h: Fixed ARP input processing: only add a new entry if a
+ request was directed as us (RFC 826, Packet Reception), otherwise
+ only update existing entries; internalized some functions
+
+ 2010-02-14: Simon Goldschmidt
+ * netif.h, etharp.c, tcpip.c: Fixed bug #28183 (ARP and TCP/IP cannot be
+ disabled on netif used for PPPoE) by adding a new netif flag
+ (NETIF_FLAG_ETHERNET) that tells the stack the device is an ethernet
+ device but prevents usage of ARP (so that ethernet_input can be used
+ for PPPoE).
+
+ 2010-02-12: Simon Goldschmidt
+ * netif.c: netif_set_link_up/down: only do something if the link state
+ actually changes
+
+ 2010-02-12: Simon Goldschmidt/Stephane Lesage
+ * api_msg.c: Fixed bug #28865 (Cannot close socket/netconn in non-blocking
+ connect)
+
+ 2010-02-12: Simon Goldschmidt
+ * mem.h: Fixed bug #28866 (mem_realloc function defined in mem.h)
+
+ 2010-02-09: Simon Goldschmidt
+ * api_lib.c, api_msg.c, sockets.c, api.h, api_msg.h: Fixed bug #22110
+ (recv() makes receive window update for data that wasn't received by
+ application)
+
+ 2010-02-09: Simon Goldschmidt/Stephane Lesage
+ * sockets.c: Fixed bug #28853 (lwip_recvfrom() returns 0 on receive time-out
+ or any netconn_recv() error)
+
+ 2010-02-09: Simon Goldschmidt
+ * ppp.c: task #10154 (PPP: Update snmp in/out counters for tx/rx packets)
+
+ 2010-02-09: Simon Goldschmidt
+ * netif.c: For loopback packets, adjust the stats- and snmp-counters
+ for the loopback netif.
+
+ 2010-02-08: Simon Goldschmidt
+ * igmp.c/.h, ip.h: Moved most defines from igmp.h to igmp.c for clarity
+ since they are not used anywhere else.
+
+ 2010-02-08: Simon Goldschmidt (Stéphane Lesage)
+ * igmp.c, igmp.h, stats.c, stats.h: Improved IGMP stats
+ (patch from bug #28798)
+
+ 2010-02-08: Simon Goldschmidt (Stéphane Lesage)
+ * igmp.c: Fixed bug #28798 (Error in "Max Response Time" processing) and
+ another bug when LWIP_RAND() returns zero.
+
+ 2010-02-04: Simon Goldschmidt
+ * nearly every file: Use macros defined in ip_addr.h (some of them new)
+ to work with IP addresses (preparation for bug #27352 - Change ip_addr
+ from struct to typedef (u32_t) - and better code).
+
+ 2010-01-31: Simon Goldschmidt
+ * netif.c: Don't call the link-callback from netif_set_up/down() since
+ this invalidly retriggers DHCP.
+
+ 2010-01-29: Simon Goldschmidt
+ * ip_addr.h, inet.h, def.h, inet.c, def.c, more: Cleanly separate the
+ portability file inet.h and its contents from the stack: moved htonX-
+ functions to def.h (and the new def.c - they are not ipv4 dependent),
+ let inet.h depend on ip_addr.h and not the other way round.
+ This fixes bug #28732.
+
+ 2010-01-28: Kieran Mansley
+ * tcp.c: Ensure ssthresh >= 2*MSS
+
+ 2010-01-27: Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c: Fixed bug #27871: Calling tcp_abort() in recv
+ callback can lead to accessing unallocated memory. As a consequence,
+ ERR_ABRT means the application has called tcp_abort()!
+
+ 2010-01-25: Simon Goldschmidt
+ * snmp_structs.h, msg_in.c: Partly fixed bug #22070 (MIB_OBJECT_WRITE_ONLY
+ not implemented in SNMP): write-only or not-accessible are still
+ returned by getnext (though not by get)
+
+ 2010-01-24: Simon Goldschmidt
+ * snmp: Renamed the private mib node from 'private' to 'mib_private' to
+ not use reserved C/C++ keywords
+
+ 2010-01-23: Simon Goldschmidt
+ * sockets.c: Fixed bug #28716: select() returns 0 after waiting for less
+ than 1 ms
+
+ 2010-01-21: Simon Goldschmidt
+ * tcp.c, api_msg.c: Fixed bug #28651 (tcp_connect: no callbacks called
+ if tcp_enqueue fails) both in raw- and netconn-API
+
+ 2010-01-19: Simon Goldschmidt
+ * api_msg.c: Fixed bug #27316: netconn: Possible deadlock in err_tcp
+
+ 2010-01-18: Iordan Neshev/Simon Goldschmidt
+ * src/netif/ppp: reorganised PPP sourcecode to 2.3.11 including some
+ bugfix backports from 2.4.x.
+
+ 2010-01-18: Simon Goldschmidt
+ * mem.c: Fixed bug #28679: mem_realloc calculates mem_stats wrong
+
+ 2010-01-17: Simon Goldschmidt
+ * api_lib.c, api_msg.c, (api_msg.h, api.h, sockets.c, tcpip.c):
+ task #10102: "netconn: clean up conn->err threading issues" by adding
+ error return value to struct api_msg_msg
+
+ 2010-01-17: Simon Goldschmidt
+ * api.h, api_lib.c, sockets.c: Changed netconn_recv() and netconn_accept()
+ to return err_t (bugs #27709 and #28087)
+
+ 2010-01-14: Simon Goldschmidt
+ * ...: Use typedef for function prototypes throughout the stack.
+
+ 2010-01-13: Simon Goldschmidt
+ * api_msg.h/.c, api_lib.c: Fixed bug #26672 (close connection when receive
+ window = 0) by correctly draining recvmbox/acceptmbox
+
+ 2010-01-11: Simon Goldschmidt
+ * pap.c: Fixed bug #13315 (PPP PAP authentication can result in
+ erroneous callbacks) by copying the code from recent pppd
+
+ 2010-01-10: Simon Goldschmidt
+ * raw.c: Fixed bug #28506 (raw_bind should filter received packets)
+
+ 2010-01-10: Simon Goldschmidt
+ * tcp.h/.c: bug #28127 (remove call to tcp_output() from tcp_ack(_now)())
+
+ 2010-01-08: Simon Goldschmidt
+ * sockets.c: Fixed bug #28519 (lwip_recvfrom bug with len > 65535)
+
+ 2010-01-08: Simon Goldschmidt
+ * dns.c: Copy hostname for DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1 since string
+ passed to dns_local_addhost() might be volatile
+
+ 2010-01-07: Simon Goldschmidt
+ * timers.c, tcp.h: Call tcp_timer_needed() with NO_SYS==1, too
+
+ 2010-01-06: Simon Goldschmidt
+ * netdb.h: Fixed bug #28496: missing include guards in netdb.h
+
+ 2009-12-31: Simon Goldschmidt
+ * many ppp files: Reorganised PPP source code from ucip structure to pppd
+ structure to easily compare our code against the pppd code (around v2.3.1)
+
+ 2009-12-27: Simon Goldschmidt
+ * tcp_in.c: Another fix for bug #28241 (ooseq processing) and adapted
+ unit test
+
+
+(STABLE-1.3.2)
+
+ ++ New features:
+
+ 2009-10-27 Simon Goldschmidt/Stephan Lesage
+ * netifapi.c/.h: Added netifapi_netif_set_addr()
+
+ 2009-10-07 Simon Goldschmidt/Fabian Koch
+ * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to
+ support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO)
+
+ 2009-08-26 Simon Goldschmidt/Simon Kallweit
+ * slipif.c/.h: bug #26397: SLIP polling support
+
+ 2009-08-25 Simon Goldschmidt
+ * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN),
+ New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK.
+
+ 2009-08-25 Simon Goldschmidt
+ * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*)
+
+ 2009-08-24 Jakob Stoklund Olesen
+ * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond
+ to netif_set_link_up().
+
+ 2009-08-23 Simon Goldschmidt
+ * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state
+ to a human-readable string.
+
+ ++ Bugfixes:
+
+ 2009-12-24: Kieran Mansley
+ * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing
+ (BUG#28241)
+
+ 2009-12-06: Simon Goldschmidt
+ * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can
+ be statically allocated (like in ucip)
+
+ 2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev)
+ * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT
+
+ 2009-12-03: Simon Goldschmidt
+ * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit
+ could have non-zero length
+
+ 2009-12-02: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting
+ tcp_input_pcb until after calling the pcb's callbacks
+
+ 2009-11-29: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of-
+ sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code
+
+ 2009-11-29: Simon Goldschmidt
+ * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by
+ queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty
+
+ 2009-11-26: Simon Goldschmidt
+ * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending
+ segment
+
+ 2009-11-26: Simon Goldschmidt
+ * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle
+ algorithm at PCB level
+
+ 2009-11-22: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent
+
+ 2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach)
+ * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when
+ reusing time-wait pcb
+
+ 2009-11-20: Simon Goldschmidt (patch by Albert Bartel)
+ * sockets.c: Fixed bug #28062: Data received directly after accepting
+ does not wake up select
+
+ 2009-11-11: Simon Goldschmidt
+ * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo)
+
+ 2009-10-30: Simon Goldschmidt
+ * opt.h: Increased default value for TCP_MSS to 536, updated default
+ value for TCP_WND to 4*TCP_MSS to keep delayed ACK working.
+
+ 2009-10-28: Kieran Mansley
+ * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code
+ to follow algorithm from TCP/IP Illustrated
+
+ 2009-10-27: Kieran Mansley
+ * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK
+
+ 2009-10-25: Simon Goldschmidt
+ * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if
+ pcb->recv is NULL to keep rcv_wnd correct)
+
+ 2009-10-25: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state
+
+ 2009-10-23: Simon Goldschmidt (David Empson)
+ * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes
+
+ 2009-10-21: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and
+ trailing 1 byte len (SYN/FIN)
+
+ 2009-10-21: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #27315: zero window probe and FIN
+
+ 2009-10-19: Simon Goldschmidt
+ * dhcp.c/.h: Minor code simplification (don't store received pbuf, change
+ conditional code to assert where applicable), check pbuf length before
+ testing for valid reply
+
+ 2009-10-19: Simon Goldschmidt
+ * dhcp.c: Removed most calls to udp_connect since they aren't necessary
+ when using udp_sendto_if() - always stay connected to IP_ADDR_ANY.
+
+ 2009-10-16: Simon Goldschmidt
+ * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop
+ valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is
+ enabled
+
+ 2009-10-15: Simon Goldschmidt (Oleg Tyshev)
+ * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit
+
+ 2009-10-15: Simon Goldschmidt
+ * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv()
+ timeout
+
+ 2009-10-15: Simon Goldschmidt
+ * autoip.c: Fixed bug #27704: autoip starts with wrong address
+ LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead
+ of network byte order
+
+ 2009-10-11 Simon Goldschmidt (Jörg Kesten)
+ * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments
+ which are not consecutive when retransmitting unacked segments
+
+ 2009-10-09 Simon Goldschmidt
+ * opt.h: Fixed default values of some stats to only be enabled if used
+ Fixes bug #27338: sys_stats is defined when NO_SYS = 1
+
+ 2009-08-30 Simon Goldschmidt
+ * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK
+ function" by checking for loopback before calling ip_frag
+
+ 2009-08-25 Simon Goldschmidt
+ * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0
+
+ 2009-08-23 Simon Goldschmidt
+ * ppp.c: bug #27078: Possible memory leak in pppInit()
+
+ 2009-08-23 Simon Goldschmidt
+ * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result
+ is error.
+
+ 2009-08-23 Simon Goldschmidt
+ * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF
+ Fixed wrong parenthesis, added check in init.c
+
+ 2009-08-23 Simon Goldschmidt
+ * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms
+
+ 2009-08-23 Simon Goldschmidt
+ * many ppp files: bug #27267: Added include to string.h where needed
+
+ 2009-08-23 Simon Goldschmidt
+ * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian)
+
+
+(STABLE-1.3.1)
+
+ ++ New features:
+
+ 2009-05-10 Simon Goldschmidt
+ * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option
+ LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only
+ one pbuf to help MACs that don't support scatter-gather DMA.
+
+ 2009-05-09 Simon Goldschmidt
+ * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming
+ ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+
+ 2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen
+ * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive
+ extended info about the currently received packet.
+
+ 2009-04-27 Simon Goldschmidt
+ * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1
+
+ 2009-04-25 Simon Goldschmidt
+ * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next
+ bigger malloc pool if one is empty (only usable with MEM_USE_POOLS).
+
+ 2009-04-21 Simon Goldschmidt
+ * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static
+ hosts table. New configuration options DNS_LOCAL_HOSTLIST and
+ DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined
+ as an external function for lookup.
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique
+
+ 2009-03-31 Kieran Mansley
+ * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for
+ TCP timestamp options, off by default. Rework tcp_enqueue() to
+ take option flags rather than specified option data
+
+ 2009-02-18 Simon Goldschmidt
+ * cc.h: Added printf formatter for size_t: SZT_F
+
+ 2009-02-16 Simon Goldschmidt (patch by Rishi Khan)
+ * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast
+ pings
+
+ 2009-02-12 Simon Goldschmidt
+ * init.h: Added LWIP_VERSION to get the current version of the stack
+
+ 2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler)
+ * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead
+ of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc
+ is otherwise used)
+
+ 2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach)
+ * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial()
+ is only used by UDPLITE at present, so conditionalise it.
+
+ 2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli)
+ * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP
+ "seed" address. This should reduce AUTOIP conflicts if
+ LWIP_AUTOIP_CREATE_SEED_ADDR is overridden.
+
+ 2008-10-02 Jonathan Larmour and Rishi Khan
+ * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking
+ socket.
+
+ 2008-06-30 Simon Goldschmidt
+ * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from
+ interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows
+ mem_free to run between mem_malloc iterations. Added illegal counter for
+ mem stats.
+
+ 2008-06-27 Simon Goldschmidt
+ * stats.h/.c, some other files: patch #6483: stats module improvement:
+ Added defines to display each module's statistic individually, added stats
+ defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter.
+
+ 2008-06-17 Simon Goldschmidt
+ * err.h: patch #6459: Made err_t overridable to use a more efficient type
+ (define LWIP_ERR_T in cc.h)
+
+ 2008-06-17 Simon Goldschmidt
+ * slipif.c: patch #6480: Added a configuration option for slipif for symmetry
+ to loopif
+
+ 2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli)
+ * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly
+ modified version of patch # 6370: Moved loopif code to netif.c so that
+ loopback traffic is supported on all netifs (all local IPs).
+ Added option to limit loopback packets for each netifs.
+
+
+ ++ Bugfixes:
+ 2009-08-12 Kieran Mansley
+ * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when
+ out of window or out of order properly
+
+ 2009-08-12 Kieran Mansley
+ * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1
+
+ 2009-07-28 Simon Goldschmidt
+ * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s
+
+ 2009-07-27 Kieran Mansley
+ * api.h api_msg.h netdb.h sockets.h: add missing #include directives
+
+ 2009-07-09 Kieran Mansley
+ * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for
+ recv_avail and don't increment counters until message successfully
+ sent to mbox
+
+ 2009-06-25 Kieran Mansley
+ * api_msg.c api.h: BUG26722: initialise netconn write variables
+ in netconn_alloc
+
+ 2009-06-25 Kieran Mansley
+ * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set
+
+ 2009-06-25 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct
+ simultaneous close behaviour, and make snd_nxt have the same meaning
+ as in the RFCs.
+
+ 2009-05-12 Simon Goldschmidt
+ * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on
+ arp_table / uses etharp_query" by adding etharp_gratuitous()
+
+ 2009-05-12 Simon Goldschmidt
+ * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options
+ to the IP header (used by igmp_ip_output_if)
+
+ 2009-05-06 Simon Goldschmidt
+ * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if
+ defined) for SWAP_BYTES_IN_WORD to speed up checksumming.
+
+ 2009-05-05 Simon Goldschmidt
+ * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select()
+ to crash
+
+ 2009-05-04 Simon Goldschmidt
+ * init.c: snmp was not initialized in lwip_init()
+
+ 2009-05-04 Frédéric Bernon
+ * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled.
+
+ 2009-05-03 Simon Goldschmidt
+ * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full
+ (and unsent->next == NULL)
+
+ 2009-05-02 Simon Goldschmidt
+ * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after
+ 1.3.0 in CVS only) - fixes compilation of ppp_oe.c
+
+ 2009-05-02 Simon Goldschmidt
+ * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields
+
+ 2009-05-01 Simon Goldschmidt
+ * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets
+
+ 2009-05-01 Simon Goldschmidt
+ * ppp.c: bug #24228: Memory corruption with PPP and DHCP
+
+ 2009-04-29 Frédéric Bernon
+ * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the
+ SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception
+ of broadcast packets even when this option wasn't set. Port maintainers
+ which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h.
+ If you want this option also filter broadcast on recv operations, you also
+ have to set IP_SOF_BROADCAST_RECV=1 in opt.h.
+
+ 2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen
+ * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and
+ DHCP/AUTOIP cooperation
+
+ 2009-04-25 Simon Goldschmidt, Oleg Tyshev
+ * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd
+ Fixed by sorting the unsent and unacked queues (segments are inserted at the
+ right place in tcp_output and tcp_rexmit).
+
+ 2009-04-25 Simon Goldschmidt
+ * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation
+ when debugging": memp_sizes contained the wrong sizes (including sanity
+ regions); memp pools for MEM_USE_POOLS were too small
+
+ 2009-04-24 Simon Goldschmidt, Frédéric Bernon
+ * inet.c: patch #6765: Fix a small problem with the last changes (incorrect
+ behavior, with with ip address string not ended by a '\0', a space or a
+ end of line)
+
+ 2009-04-19 Simon Goldschmidt
+ * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails,
+ pcb->err is called, not pcb->connected (with an error code).
+
+ 2009-04-19 Simon Goldschmidt
+ * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with
+ no-copy-tcpwrite": deallocate option data, only concat segments with same flags
+
+ 2009-04-19 Simon Goldschmidt
+ * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated
+ in the header pbuf, not the data pbuf)
+
+ 2009-04-18 Simon Goldschmidt
+ * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore()
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in
+
+ 2009-04-15 Simon Goldschmidt
+ * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function
+ ip_hinted_output() (for smaller code mainly)
+
+ 2009-04-15 Simon Goldschmidt
+ * inet.c: patch #6765: Supporting new line characters in inet_aton()
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option;
+ Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu
+ is big enough in dhcp_start
+
+ 2009-04-15 Simon Goldschmidt
+ * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c: bug #26121: set_errno can be overridden
+
+ 2009-04-09 Kieran Mansley (patch from Luca Ceresoli <lucaceresoli>)
+ * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when
+ LWIP_TCP==0
+
+ 2009-04-09 Kieran Mansley (patch from Roy Lee <roylee17>)
+ * tcp.h: Patch#6802 Add do-while-clauses to those function like
+ macros in tcp.h
+
+ 2009-03-31 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window
+ updates are calculated and sent (BUG20515)
+
+ * tcp_in.c: cope with SYN packets received during established states,
+ and retransmission of initial SYN.
+
+ * tcp_out.c: set push bit correctly when tcp segments are merged
+
+ 2009-03-27 Kieran Mansley
+ * tcp_out.c set window correctly on probes (correcting change made
+ yesterday)
+
+ 2009-03-26 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping
+ connections where no reset required (bug #25622)
+
+ * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes
+ (bug #20779)
+
+ 2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach)
+ * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be
+ too small depending on MEM_ALIGNMENT
+
+ 2009-02-16 Simon Goldschmidt
+ * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard;
+ converted size argument of netconn_write to 'size_t'
+
+ 2009-02-16 Simon Goldschmidt
+ * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host
+ by moving accept callback function pointer to TCP_PCB_COMMON
+
+ 2009-02-12 Simon Goldschmidt
+ * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size"
+ option)
+
+ 2009-02-11 Simon Goldschmidt
+ * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start)
+
+ 2009-02-11 Simon Goldschmidt
+ * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize:
+ RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv())
+
+ 2009-02-10 Simon Goldschmidt
+ * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD:
+ Accepts_pending is decrease on a corresponding listen pcb when a connection
+ in state SYN_RCVD is close.
+
+ 2009-01-28 Jonathan Larmour
+ * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run
+ out of pool pbufs.
+
+ 2008-12-19 Simon Goldschmidt
+ * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2
+
+ 2008-12-10 Tamas Somogyi, Frédéric Bernon
+ * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and
+ port uses deleted netbuf.
+
+ 2008-10-18 Simon Goldschmidt
+ * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length
+ in tcp_parseopt
+
+ 2008-10-15 Simon Goldschmidt
+ * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers
+ by packing the struct ip_reass_helper.
+
+ 2008-10-03 David Woodhouse, Jonathan Larmour
+ * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address.
+
+ 2008-10-02 Jonathan Larmour
+ * dns.c: Hard-code structure sizes, to avoid issues on some compilers where
+ padding is included.
+
+ 2008-09-30 Jonathan Larmour
+ * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an
+ assertion check that addrlen isn't NULL.
+
+ 2008-09-30 Jonathan Larmour
+ * tcp.c: Fix bug #24227, wrong error message in tcp_bind.
+
+ 2008-08-26 Simon Goldschmidt
+ * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and
+ inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h
+
+ 2008-08-14 Simon Goldschmidt
+ * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when
+ tcp_close returns != ERR_OK)
+
+ 2008-07-08 Frédéric Bernon
+ * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters
+ in macros, mainly if MEM_STATS=0 and MEMP_STATS=0).
+
+ 2008-06-24 Jonathan Larmour
+ * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused
+ if tcp_seg_copy fails.
+
+ 2008-06-17 Simon Goldschmidt
+ * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations)
+ and created defines for swapping bytes and folding u32 to u16.
+
+ 2008-05-30 Kieran Mansley
+ * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd
+ rather than rcv_ann_wnd when deciding if packets are in-window.
+ Contributed by <arasmussen@consultant.datasys.swri.edu>
+
+ 2008-05-30 Kieran Mansley
+ * mem.h: Fix BUG#23254. Change macro definition of mem_* to allow
+ passing as function pointers when MEM_LIBC_MALLOC is defined.
+
+ 2008-05-09 Jonathan Larmour
+ * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to
+ stop it being treated as a fatal error.
+
+ 2008-04-15 Simon Goldschmidt
+ * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP
+ (flag now cleared)
+
+ 2008-03-27 Simon Goldschmidt
+ * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free
+ from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1
+ in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs
+ or heap memory from interrupt context
+
+ 2008-03-26 Simon Goldschmidt
+ * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote
+ host sent a zero mss as TCP option.
+
+
+(STABLE-1.3.0)
+
+ ++ New features:
+
+ 2008-03-10 Jonathan Larmour
+ * inet_chksum.c: Allow choice of one of the sample algorithms to be
+ made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM.
+
+ 2008-01-22 Frédéric Bernon
+ * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in
+ TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names.
+
+ 2008-01-14 Frédéric Bernon
+ * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable
+ to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the
+ tcp_recv callback (see rawapi.txt).
+
+ 2008-01-14 Frédéric Bernon, Marc Chaland
+ * ip.c: Integrate patch #6369" ip_input : checking before realloc".
+
+ 2008-01-12 Frédéric Bernon
+ * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
+ netconn::sem per netconn::op_completed like suggested for the task #7490
+ "Add return value to sys_mbox_post".
+
+ 2008-01-12 Frédéric Bernon
+ * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE,
+ DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues
+ sizes), like suggested for the task #7490 "Add return value to sys_mbox_post".
+
+ 2008-01-10 Frédéric Bernon
+ * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490
+ "Add return value to sys_mbox_post". tcpip_callback is always defined as
+ "blocking" ("block" parameter = 1).
+
+ 2008-01-10 Frédéric Bernon
+ * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
+ netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490
+ "Add return value to sys_mbox_post".
+
+ 2008-01-05 Frédéric Bernon
+ * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h:
+ Introduce changes for task #7490 "Add return value to sys_mbox_post" with some
+ modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which
+ indicate the number of pointers query by the mailbox. There is three defines
+ in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the
+ netconn::acceptmbox. Port maintainers, you can decide to just add this new
+ parameter in your implementation, but to ignore it to keep the previous behavior.
+ The new sys_mbox_trypost function return a value to know if the mailbox is
+ full or if the message is posted. Take a look to sys_arch.txt for more details.
+ This new function is used in tcpip_input (so, can be called in an interrupt
+ context since the function is not blocking), and in recv_udp and recv_raw.
+
+ 2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
+ * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c,
+ tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the
+ "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add
+ documentation in the rawapi.txt file.
+
+ 2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer
+
+ 2007-12-31 Frédéric Bernon, Luca Ceresoli
+ * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets
+ in autoip". The change in etharp_raw could be removed, since all calls to
+ etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be
+ wrong in the future.
+
+ 2007-12-30 Frédéric Bernon, Tom Evans
+ * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address
+ Filtering" reported by Tom Evans.
+
+ 2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
+ * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c,
+ sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API
+ applications have to call 'tcp_accepted(pcb)' in their accept callback to
+ keep accepting new connections.
+
+ 2007-12-13 Frédéric Bernon
+ * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result"
+ by err_t type. Add a new err_t code "ERR_INPROGRESS".
+
+ 2007-12-12 Frédéric Bernon
+ * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles
+ are the one which have ram usage.
+
+ 2007-12-05 Frédéric Bernon
+ * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static
+ set of variables (=0) or a local one (=1). In this last case, your port should
+ provide a function "struct hostent* sys_thread_hostent( struct hostent* h)"
+ which have to do a copy of "h" and return a pointer ont the "per-thread" copy.
+
+ 2007-12-03 Simon Goldschmidt
+ * ip.c: ip_input: check if a packet is for inp first before checking all other
+ netifs on netif_list (speeds up packet receiving in most cases)
+
+ 2007-11-30 Simon Goldschmidt
+ * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access
+ UDP: move a (connected) pcb selected for input to the front of the list of
+ pcbs so that it is found faster next time. Same for RAW pcbs that have eaten
+ a packet.
+
+ 2007-11-28 Simon Goldschmidt
+ * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS
+
+ 2007-11-25 Simon Goldschmidt
+ * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy
+ algorithm.
+
+ 2007-11-24 Simon Goldschmidt
+ * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c
+ to the new file netdb.c; included lwip_getaddrinfo.
+
+ 2007-11-21 Simon Goldschmidt
+ * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss
+ based on the MTU of the netif used to send. Enabled by default. Disable by
+ setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492.
+
+ 2007-11-19 Frédéric Bernon
+ * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name
+ received match the name query), implement DNS_USES_STATIC_BUF (the place where
+ copy dns payload to parse the response), return an error if there is no place
+ for a new query, and fix some minor problems.
+
+ 2007-11-16 Simon Goldschmidt
+ * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c
+ removed files: core/inet.c, core/inet6.c
+ Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into
+ inet and chksum part; changed includes in all lwIP files as appropriate
+
+ 2007-11-16 Simon Goldschmidt
+ * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential
+ dns resolver function for netconn api (netconn_gethostbyname) and socket api
+ (gethostbyname/gethostbyname_r).
+
+ 2007-11-15 Jim Pettinato, Frédéric Bernon
+ * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name
+ requests with RAW api interface. Initialization is done in lwip_init() with
+ build time options. DNS timer is added in tcpip_thread context. DHCP can set
+ DNS server ip addresses when options are received. You need to set LWIP_DNS=1
+ in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get
+ some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo"
+ list with points to improve.
+
+ 2007-11-06 Simon Goldschmidt
+ * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly
+ enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status
+ for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined.
+
+ 2007-11-06 Simon Goldschmidt
+ * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include
+ core header files in api.h (ip/tcp/udp/raw.h) to hide the internal
+ implementation from netconn api applications.
+
+ 2007-11-03 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP &
+ RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled
+ by default). Netconn API users can use the netconn_recv_bufsize macro to access
+ it. This is a first release which have to be improve for TCP. Note it used the
+ netconn::recv_avail which need to be more "thread-safe" (note there is already
+ the problem for FIONREAD with lwip_ioctl/ioctlsocket).
+
+ 2007-11-01 Frédéric Bernon, Marc Chaland
+ * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c:
+ Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api
+ layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api
+ layer. This option enable to delayed TCP PUSH flag on multiple "write" calls.
+ Note that previous "copy" parameter for "write" APIs is now called "apiflags".
+
+ 2007-10-24 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than
+ TCP_EVENT_xxx macros to get a code more readable. It could also help to remove
+ some code (like we have talk in "patch #5919 : Create compile switch to remove
+ select code"), but it could be done later.
+
+ 2007-10-08 Simon Goldschmidt
+ * many files: Changed initialization: many init functions are not needed any
+ more since we now rely on the compiler initializing global and static
+ variables to zero!
+
+ 2007-10-06 Simon Goldschmidt
+ * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY
+ to enqueue the received pbufs so that multiple packets can be reassembled
+ simultaneously and no static reassembly buffer is needed.
+
+ 2007-10-05 Simon Goldschmidt
+ * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so
+ all netifs (or ports) can use it.
+
+ 2007-10-05 Frédéric Bernon
+ * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the
+ common function to reduce a little bit the footprint (for all functions using
+ only the "netif" parameter).
+
+ 2007-10-03 Frédéric Bernon
+ * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down,
+ netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce
+ a little bit the footprint (for all functions using only the "netif" parameter).
+
+ 2007-09-15 Frédéric Bernon
+ * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF
+ option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for
+ netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for
+ IP_MULTICAST_TTL and IP_MULTICAST_IF.
+
+ 2007-09-10 Frédéric Bernon
+ * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles
+ even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime()
+ each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can
+ decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but
+ call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime()
+ or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro.
+ This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside
+ snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only
+ when it's queried (any direct call to "sysuptime" is changed by a call to
+ snmp_get_sysuptime).
+
+ 2007-09-09 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP,
+ and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags
+ if you want IGMP on an interface. igmp_stop() is now called inside netif_remove().
+ igmp_report_groups() is now called inside netif_set_link_up() (need to have
+ LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait
+ the next query message to receive the matching multicast streams).
+
+ 2007-09-08 Frédéric Bernon
+ * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains
+ IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change).
+ Use this new field to access to common pcb fields (ttl, tos, so_options, etc...).
+ Enable to access to these fields with LWIP_TCP=0.
+
+ 2007-09-05 Frédéric Bernon
+ * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h,
+ ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option
+ LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default).
+ Be careful, disabling ICMP make your product non-compliant to RFC1122, but
+ help to reduce footprint, and to reduce "visibility" on the Internet.
+
+ 2007-09-05 Frédéric Bernon, Bill Florac
+ * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list
+ for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new
+ parameters have to be provided: a task name, and a task stack size. For this
+ one, since it's platform dependant, you could define the best one for you in
+ your lwipopts.h. For port maintainers, you can just add these new parameters
+ in your sys_arch.c file, and but it's not mandatory, use them in your OS
+ specific functions.
+
+ 2007-09-05 Frédéric Bernon
+ * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings
+ inside init.c for task #7142 "Sanity check user-configurable values".
+
+ 2007-09-04 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by
+ memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the
+ value). It will avoid potential fragmentation problems, use a counter to know
+ how many times a group is used on an netif, and free it when all applications
+ leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity
+ check if LWIP_IGMP!=0).
+
+ 2007-09-03 Frédéric Bernon
+ * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement".
+ Initialize igmp_mac_filter to NULL in netif_add (this field should be set in
+ the netif's "init" function). Use the "imr_interface" field (for socket layer)
+ and/or the "interface" field (for netconn layer), for join/leave operations.
+ The igmp_join/leavegroup first parameter change from a netif to an ipaddr.
+ This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany).
+
+ 2007-08-30 Frédéric Bernon
+ * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions
+ from api/api_lib". Now netbuf API is independant of netconn, and can be used
+ with other API (application based on raw API, or future "socket2" API). Ports
+ maintainers just have to add src/api/netbuf.c in their makefile/projects.
+
+ 2007-08-30 Frédéric Bernon, Jonathan Larmour
+ * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check
+ user-configurable values".
+
+ 2007-08-29 Frédéric Bernon
+ * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start.
+ igmp_start is call inside netif_add. Now, igmp initialization is in the same
+ spirit than the others modules. Modify some IGMP debug traces.
+
+ 2007-08-29 Frédéric Bernon
+ * Add init.h, init.c, Change opt.h, tcpip.c: Task #7213 "Add a lwip_init function"
+ Add lwip_init function to regroup all modules initializations, and to provide
+ a place to add code for task #7142 "Sanity check user-configurable values".
+ Ports maintainers should remove direct initializations calls from their code,
+ and add init.c in their makefiles. Note that lwip_init() function is called
+ inside tcpip_init, but can also be used by raw api users since all calls are
+ disabled when matching options are disabled. Also note that their is new options
+ in opt.h, you should configure in your lwipopts.h (they are enabled per default).
+
+ 2007-08-26 Marc Boucher
+ * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL
+ since they can under certain circumstances be called with an invalid conn
+ pointer after the connection has been closed (and conn has been freed).
+
+ 2007-08-25 Frédéric Bernon (Artem Migaev's Patch)
+ * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up".
+ Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set.
+
+ 2007-08-22 Frédéric Bernon
+ * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK
+ to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release.
+
+ 2007-08-22 Frédéric Bernon
+ * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT &
+ ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the
+ name is tcpip_input (we keep the name of 1.2.0 function).
+
+ 2007-08-17 Jared Grubb
+ * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool
+ settings into new memp_std.h and optional user file lwippools.h. This adds
+ more dynamic mempools, and allows the user to create an arbitrary number of
+ mempools for mem_malloc.
+
+ 2007-08-16 Marc Boucher
+ * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function;
+ otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely
+ close the connection.
+
+ 2007-08-16 Marc Boucher
+ * sockets.c: lwip_accept(): check netconn_peer() error return.
+
+ 2007-08-16 Marc Boucher
+ * mem.c, mem.h: Added mem_calloc().
+
+ 2007-08-16 Marc Boucher
+ * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT)
+ for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG
+ and starving other message types.
+ Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API
+
+ 2007-08-16 Marc Boucher
+ * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf
+ type and flgs (later renamed to flags).
+ Use enum pbuf_flag as pbuf_type. Renumber PBUF_FLAG_*.
+ Improved lwip_recvfrom(). TCP push now propagated.
+
+ 2007-08-16 Marc Boucher
+ * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global
+ provided by etharp.
+
+ 2007-08-16 Marc Boucher
+ * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h,
+ etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c:
+ Added PPPoE support and various PPP improvements.
+
+ 2007-07-25 Simon Goldschmidt
+ * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial,
+ making netbuf_copy_partial use this function.
+
+ 2007-07-25 Simon Goldschmidt
+ * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with
+ 2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and
+ other stacks.
+
+ 2007-07-13 Jared Grubb (integrated by Frédéric Bernon)
+ * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add
+ a link callback in the netif struct, and functions to handle it. Be carefull
+ for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c)
+ if you want to be sure to be compatible with future changes...
+
+ 2007-06-30 Frédéric Bernon
+ * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions.
+
+ 2007-06-21 Simon Goldschmidt
+ * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both
+ LWIP_AUTOIP =0 and =1 to remove redundant code.
+
+ 2007-06-21 Simon Goldschmidt
+ * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option
+ MEM_USE_POOLS to use 4 pools with different sized elements instead of a
+ heap. This both prevents memory fragmentation and gives a higher speed
+ at the cost of more memory consumption. Turned off by default.
+
+ 2007-06-21 Simon Goldschmidt
+ * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of
+ netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into
+ int to be able to send a bigger buffer than 64K with one time (mainly
+ used from lwip_send).
+
+ 2007-06-21 Simon Goldschmidt
+ * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write
+ into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too.
+
+ 2007-06-21 Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in
+ netconn_write from api_lib.c to api_msg.c to also prevent multiple context-
+ changes on low memory or empty send-buffer.
+
+ 2007-06-18 Simon Goldschmidt
+ * etharp.c, etharp.h: Changed etharp to use a defined hardware address length
+ of 6 to avoid loading netif->hwaddr_len every time (since this file is only
+ used for ethernet and struct eth_addr already had a defined length of 6).
+
+ 2007-06-17 Simon Goldschmidt
+ * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets
+ to disable UDP checksum generation on transmit.
+
+ 2007-06-13 Frédéric Bernon, Simon Goldschmidt
+ * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid
+ pointers or parameters, and let the possibility to redefined it in cc.h. Use
+ this macro to check "conn" parameter in api_msg.c functions.
+
+ 2007-06-11 Simon Goldschmidt
+ * sockets.c, sockets.h: Added UDP lite support for sockets
+
+ 2007-06-10 Simon Goldschmidt
+ * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled
+ by default) to switch off UDP-Lite support if not needed (reduces udp.c code
+ size)
+
+ 2007-06-09 Dominik Spies (integrated by Frédéric Bernon)
+ * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h:
+ AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and
+ LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt
+ (see TODO mark in the source code).
+
+ 2007-06-09 Simon Goldschmidt
+ * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for
+ etharp_output() to match netif->output so etharp_output() can be used
+ directly as netif->output to save one function call.
+
+ 2007-06-08 Simon Goldschmidt
+ * netif.h, ethernetif.c, slipif.c, loopif.c: Added define
+ NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables,
+ added initialization of those to ethernetif, slipif and loopif.
+
+ 2007-05-18 Simon Goldschmidt
+ * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF
+ (defaulting to off for now) that can be set to 0 to send fragmented
+ packets by passing PBUF_REFs down the stack.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP
+ connections, such present in patch #5959.
+
+ 2007-05-23 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx
+ code in only one part...
+
+ 2007-05-18 Simon Goldschmidt
+ * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp
+ elements to overflow. This is achieved by adding some bytes before and after
+ each pool element (increasing their size, of course), filling them with a
+ prominent value and checking them on freeing the element.
+ Set it to 2 to also check every element in every pool each time memp_malloc()
+ or memp_free() is called (slower but more helpful).
+
+ 2007-05-10 Simon Goldschmidt
+ * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for
+ PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce
+ code size.
+
+ 2007-05-11 Frédéric Bernon
+ * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c:
+ Include a function pointer instead of a table index in the message to reduce
+ footprint. Disable some part of lwip_send and lwip_sendto if some options are
+ not set (LWIP_TCP, LWIP_UDP, LWIP_RAW).
+
+ 2007-05-10 Simon Goldschmidt
+ * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus
+ \ extern "C" {' in all header files. Now you can write your application using
+ the lwIP stack in C++ and simply #include the core files. Note I have left
+ out the netif/ppp/*h header files for now, since I don't know which files are
+ included by applications and which are for internal use only.
+
+ 2007-05-09 Simon Goldschmidt
+ * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library
+ memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for
+ situations where some compilers might inline the copy and save a function
+ call. Also replaced all calls to memcpy() with calls to (S)MEMCPY().
+
+ 2007-05-08 Simon Goldschmidt
+ * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc())
+ to be overriden in case the C-library malloc implementation is not protected
+ against concurrent access.
+
+ 2007-05-04 Simon Goldschmidt (Atte Kojo)
+ * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending
+ multiple packets to the same host.
+
+ 2007-05-04 Frédéric Bernon, Jonathan Larmour
+ * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible
+ to corrupt remote addr/port connection state". Reduce problems "not enought memory" with
+ netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between
+ sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function.
+ Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct,
+ these fields are now renamed "addr" & "port".
+
+ 2007-04-11 Jonathan Larmour
+ * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new
+ sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return
+ with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro
+ by the port in sys_arch.h if desired.
+
+ 2007-04-06 Frédéric Bernon, Simon Goldschmidt
+ * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API
+ allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp
+ clients, using new functions from netifapi.h. Disable as default (no port change to do).
+
+ 2007-04-05 Frédéric Bernon
+ * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant.
+
+ 2007-04-04 Simon Goldschmidt
+ * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x)
+ use this for and architecture-independent form to tell the compiler you intentionally
+ are not using this variable. Can be overriden in cc.h.
+
+ 2007-03-28 Frédéric Bernon
+ * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to
+ define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded
+ string, point on one of your's ethernetif field, or alloc a string you will free yourself).
+ It will be used by DHCP to register a client hostname, but can also be use when you call
+ snmp_set_sysname.
+
+ 2007-03-28 Frédéric Bernon
+ * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to
+ initialize a network interface's flag with. It tell this interface is an ethernet
+ device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility
+ Support for IPv4" section 4.6) when interface is "up" with netif_set_up().
+
+ 2007-03-26 Frédéric Bernon, Jonathan Larmour
+ * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build
+ time if you only use PPP or SLIP. The default is enable. Note we don't have to call
+ etharp_init in your port's initilization sequence if you use tcpip.c, because this call
+ is done in tcpip_init function.
+
+ 2007-03-22 Frédéric Bernon
+ * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the
+ new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in
+ your lwipopts.h. More, unused counters are not defined in the stats structs, and not
+ display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined
+ but never used. Fix msg_in.c with the correct #if test for a stat display.
+
+ 2007-03-21 Kieran Mansley
+ * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com).
+ Provides callback on netif up/down state change.
+
+ 2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds
+ * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c,
+ ip.c, netif.h, tcpip.c, opt.h:
+ New configuration option LWIP_IGMP to enable IGMP processing. Based on only one
+ filter per all network interfaces. Declare a new function in netif to enable to
+ control the MAC filter (to reduce lwIP traffic processing).
+
+ 2007-03-11 Frédéric Bernon
+ * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can
+ be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this
+ unless you know what you're doing (default are RFC1122 compliant). Note
+ that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds.
+
+ 2007-03-08 Frédéric Bernon
+ * tcp.h: Keepalive values can be configured at compile time, but don't change
+ this unless you know what you're doing (default are RFC1122 compliant).
+
+ 2007-03-08 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h:
+ Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO
+ on UDP sockets/netconn.
+
+ 2007-03-08 Simon Goldschmidt
+ * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time.
+
+ 2007-03-06 Frédéric Bernon
+ * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h:
+ Implement SO_RCVTIMEO on UDP sockets/netconn.
+
+ 2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt)
+ * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated
+ on the stack and remove the API msg type from memp
+
+ 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
+ * sockets.h, sockets.c: Move socket initialization to new
+ lwip_socket_init() function.
+ NOTE: this changes the API with ports. Ports will have to be
+ updated to call lwip_socket_init() now.
+
+ 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
+ * api_lib.c: Use memcpy in netbuf_copy_partial.
+
+
+ ++ Bug fixes:
+
+ 2008-03-17 Frédéric Bernon, Ed Kerekes
+ * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have
+ some problems to fill the IP header on some targets, use now the
+ ip.h macros to do it).
+
+ 2008-03-13 Frédéric Bernon
+ * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using
+ (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a
+ TCP connection caused a crash. Note that using (lwip_)recvfrom
+ like this is a bit slow and that using (lwip)getpeername is the
+ good lwip way to do it (so, using recv is faster on tcp sockets).
+
+ 2008-03-12 Frédéric Bernon, Jonathan Larmour
+ * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's
+ recv_raw() does not consume data", and the ping sample (with
+ LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom
+ returned the IP payload, without the IP header).
+
+ 2008-03-04 Jonathan Larmour
+ * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors
+ and/or warnings on some systems where mem_size_t and size_t differ.
+ * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc.
+
+ 2008-03-04 Kieran Mansley (contributions by others)
+ * Numerous small compiler error/warning fixes from contributions to
+ mailing list after 1.3.0 release candidate made.
+
+ 2008-01-25 Cui hengbin (integrated by Frédéric Bernon)
+ * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures.
+
+ 2008-01-15 Kieran Mansley
+ * tcp_out.c: BUG20511. Modify persist timer to start when we are
+ prevented from sending by a small send window, not just a zero
+ send window.
+
+ 2008-01-09 Jonathan Larmour
+ * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid
+ conflict with Linux system headers.
+
+ 2008-01-06 Jonathan Larmour
+ * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP
+ address entirely on receiving a DHCPNAK, and restarting discovery.
+
+ 2007-12-21 Simon Goldschmidt
+ * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail
+ is not protected" by using new macros for interlocked access to modify/test
+ netconn->recv_avail.
+
+ 2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev)
+ * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state)
+
+ 2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling
+ of silly window avoidance and prevent lwIP from shrinking the window)
+
+ 2007-12-04 Simon Goldschmidt
+ * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last
+ data packet was lost): add assert that all segment lists are empty in
+ tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED
+ state from LAST_ACK in tcp_process
+
+ 2007-12-02 Simon Goldschmidt
+ * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET
+ If including <sys/time.h> for system-struct timeval, LWIP_TIMEVAL_PRIVATE now
+ has to be set to 0 in lwipopts.h
+
+ 2007-12-02 Simon Goldschmidt
+ * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always
+ allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen
+ netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox.
+ This is a fix for thread-safety and allocates all items needed for a netconn
+ when the netconn is created.
+
+ 2007-11-30 Simon Goldschmidt
+ * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple
+ netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed
+ to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same
+ port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address)
+
+ 2007-11-27 Simon Goldschmidt
+ * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by
+ letting ip_route only use netifs that are up.
+
+ 2007-11-27 Simon Goldschmidt
+ * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF
+ and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and
+ sockets block most operations once they have seen a fatal error.
+
+ 2007-11-27 Simon Goldschmidt
+ * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the
+ netif to send as an argument (to be able to send on netifs that are down).
+
+ 2007-11-26 Simon Goldschmidt
+ * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs
+ arrive out-of-order
+
+ 2007-11-21 Simon Goldschmidt
+ * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early
+ Fixed the nagle algorithm; nagle now also works for all raw API applications
+ and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY'
+
+ 2007-11-12 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most
+ of the netconn_peer and netconn_addr processing is done inside tcpip_thread
+ context in do_getaddr.
+
+ 2007-11-10 Simon Goldschmidt
+ * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can
+ happen any time). Now the packet simply isn't enqueued when out of memory.
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or
+ TCP_MSS if that is smaller) as long as no MSS option is received from the
+ remote host.
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN)
+ is now based on TCP_MSS instead of pcb->mss (on passive open now effectively
+ sending our configured TCP_MSS instead of the one received).
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was
+ calculated based on the configured TCP_MSS, not on the MSS option received
+ with SYN+ACK.
+
+ 2007-10-09 Simon Goldschmidt
+ * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too
+ short and also was generated wrong if checksum coverage != tot_len;
+ receive: checksum was calculated wrong if checksum coverage != tot_len
+
+ 2007-10-08 Simon Goldschmidt
+ * mem.c: lfree was not updated in mem_realloc!
+
+ 2007-10-07 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential
+ crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT:
+ this change cause an API breakage for netconn_addr, since a parameter
+ type change. Any compiler should cause an error without any changes in
+ yours netconn_peer calls (so, it can't be a "silent change"). It also
+ reduce a little bit the footprint for socket layer (lwip_getpeername &
+ lwip_getsockname use now a common lwip_getaddrname function since
+ netconn_peer & netconn_addr have the same parameters).
+
+ 2007-09-20 Simon Goldschmidt
+ * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state)
+ by checking tcp_tw_pcbs also
+
+ 2007-09-19 Simon Goldschmidt
+ * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies)
+
+ 2007-09-15 Mike Kleshov
+ * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used)
+
+ 2007-09-06 Frédéric Bernon
+ * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove
+ it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which
+ already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h"
+ if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h.
+
+ 2007-08-30 Frédéric Bernon
+ * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces,
+ and fix some coding style.
+
+ 2007-08-28 Frédéric Bernon
+ * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any
+ kind of packets. These packets are considered like Ethernet packets (payload
+ pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets
+ are considered like IP packets (payload pointing to iphdr).
+
+ 2007-08-27 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error
+ problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state
+ and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT).
+
+ 2007-08-24 Kieran Mansley
+ * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy
+ compiler (Paradigm C++)
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement.
+ Introduce IGMP_STATS to centralize statistics management.
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast
+ packet on a udp pcb binded on an netif's IP address, and not on "any".
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement.
+ This is mainly on using lookup/lookfor, and some coding styles...
+
+ 2007-07-26 Frédéric Bernon (and "thedoctor")
+ * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages.
+
+ 2007-07-25 Simon Goldschmidt
+ * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if
+ tcp_output fails in tcp_close, the code in do_close_internal gets simpler
+ (tcp_output is called again later from tcp timers).
+
+ 2007-07-25 Simon Goldschmidt
+ * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old
+ copy_from_pbuf, which illegally modified the given pbuf.
+
+ 2007-07-25 Simon Goldschmidt
+ * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs:
+ changed snd_queuelen++ to snd_queuelen += pbuf_clen(p).
+
+ 2007-07-24 Simon Goldschmidt
+ * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the
+ correct state (must be CLOSED).
+
+ 2007-07-13 Thomas Taranowski (commited by Jared Grubb)
+ * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed
+ allocation. It now returns NULL.
+
+ 2007-07-13 Frédéric Bernon
+ * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in
+ all error cases.
+
+ 2007-07-13 Frédéric Bernon
+ * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed,
+ because current code doesn't follow rawapi.txt documentation.
+
+ 2007-07-13 Kieran Mansley
+ * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in
+ out of sequence processing of received packets
+
+ 2007-07-03 Simon Goldschmidt
+ * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an
+ assumption is made that this pbuf is in one piece (i.e. not chained). These
+ assumptions clash with the possibility of converting to fully pool-based
+ pbuf implementations, where PBUF_RAM pbufs might be chained.
+
+ 2007-07-03 Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems
+ when closing tcp netconns: removed conn->sem, less context switches when
+ closing, both netconn_close and netconn_delete should safely close tcp
+ connections.
+
+ 2007-07-02 Simon Goldschmidt
+ * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c,
+ tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off)
+ to cache ARP table indices with each pcb instead of single-entry cache for
+ the complete stack.
+
+ 2007-07-02 Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent
+ warnings when assigning to smaller types.
+
+ 2007-06-28 Simon Goldschmidt
+ * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing.
+
+ 2007-06-28 Simon Goldschmidt
+ * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if
+ a segment contained chained pbufs)
+
+ 2007-06-28 Frédéric Bernon
+ * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute
+ a "pseudo-random" value based on netif's MAC and some autoip fields. It's always
+ possible to define this macro in your own lwipopts.h to always use C library's
+ rand(). Note that autoip_create_rand_addr doesn't use this macro.
+
+ 2007-06-28 Frédéric Bernon
+ * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option
+ LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications
+ in api_lib/api_msg (use pointers and not type with table, etc...)
+
+ 2007-06-26 Simon Goldschmidt
+ * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines.
+
+ 2007-06-25 Simon Goldschmidt
+ * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload
+ for udp packets with no matching pcb.
+
+ 2007-06-25 Simon Goldschmidt
+ * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match
+ could get udp input packets if the remote side matched.
+
+ 2007-06-13 Simon Goldschmidt
+ * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get
+ changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0.
+
+ 2007-06-13 Simon Goldschmidt
+ * api_msg.c: pcb_new sets conn->err if protocol is not implemented
+ -> netconn_new_..() does not allocate a new connection for unsupported
+ protocols.
+
+ 2007-06-13 Frédéric Bernon, Simon Goldschmidt
+ * api_lib.c: change return expression in netconn_addr and netconn_peer, because
+ conn->err was reset to ERR_OK without any reasons (and error was lost)...
+
+ 2007-06-13 Frédéric Bernon, Matthias Weisser
+ * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename
+ MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid
+ some macro names collision with some OS macros.
+
+ 2007-06-11 Simon Goldschmidt
+ * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0,
+ create checksum over the complete packet. On RX, if it's < 8 (and not 0),
+ discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both
+ UDP & UDP Lite.
+
+ 2007-06-11 Srinivas Gollakota & Oleg Tyshev
+ * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags"
+ where TCP flags wasn't initialized in tcp_keepalive.
+
+ 2007-06-03 Simon Goldschmidt
+ * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function
+ registered, p->payload was modified without modifying p->len if sending
+ icmp_dest_unreach() (had no negative effect but was definitively wrong).
+
+ 2007-06-03 Simon Goldschmidt
+ * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp
+ re-used the input pbuf even if that didn't have enough space to include the
+ link headers. Now the space is tested and a new pbuf is allocated for the
+ echo response packet if the echo request pbuf isn't big enough.
+
+ 2007-06-01 Simon Goldschmidt
+ * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only
+ allocated by do_listen if success) and netconn_accept errors handling. In
+ most of api_lib functions, we replace some errors checkings like "if (conn==NULL)"
+ by ASSERT, except for netconn_delete.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return
+ an error code if it's impossible to fetch a pbuf on a TCP connection (and not
+ directly close the recvmbox).
+
+ 2007-05-22 Simon Goldschmidt
+ * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of
+ bound but unconnected (and non-listening) tcp_pcbs.
+
+ 2007-05-22 Frédéric Bernon
+ * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only
+ used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of
+ sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features
+ like "sys_timeout" in their application threads.
+
+ 2007-05-22 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see
+ which parameters are used by which do_xxx function, and to avoid "misusing"
+ parameters (patch #5938).
+
+ 2007-05-22 Simon Goldschmidt
+ * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938:
+ changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto
+ is only 8 bits wide. This affects the api, as there, the protocol was
+ u16_t, too.
+
+ 2007-05-18 Simon Goldschmidt
+ * memp.c: addition to patch #5913: smaller pointer was returned but
+ memp_memory was the same size -> did not save memory.
+
+ 2007-05-16 Simon Goldschmidt
+ * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns
+ != ERR_OK.
+
+ 2007-05-16 Simon Goldschmidt
+ * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same
+ as the one of the netif used for sending to prevent sending from old
+ addresses after a netif address gets changed (partly fixes bug #3168).
+
+ 2007-05-16 Frédéric Bernon
+ * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work
+ with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in
+ tcpip_init) because we have to be sure that network interfaces are already
+ added (mac filter is updated only in igmp_init for the moment).
+
+ 2007-05-16 Simon Goldschmidt
+ * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls
+ into sys_arch_sem_wait calls to prevent timers from running while waiting
+ for the heap. This fixes bug #19167.
+
+ 2007-05-13 Simon Goldschmidt
+ * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines
+ for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from
+ tcp.h to sockets.h.
+
+ 2007-05-07 Simon Goldschmidt
+ * mem.c: Another attempt to fix bug #17922.
+
+ 2007-05-04 Simon Goldschmidt
+ * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy()
+ implementation so that it can be reused (don't allocate the target
+ pbuf inside pbuf_copy()).
+
+ 2007-05-04 Simon Goldschmidt
+ * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem
+ to save a little RAM (next pointer of memp is not used while not in pool).
+
+ 2007-05-03 "maq"
+ * sockets.c: Fix ioctl FIONREAD when some data remains from last recv.
+ (patch #3574).
+
+ 2007-04-23 Simon Goldschmidt
+ * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results
+ in NULL reference for incoming TCP packets". Loopif has to be configured
+ (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input()
+ (multithreading environments, e.g. netif->input() = tcpip_input()) or
+ putting packets on a list that is fed to the stack by calling loopif_poll()
+ (single-thread / NO_SYS / polling environment where e.g.
+ netif->input() = ip_input).
+
+ 2007-04-17 Jonathan Larmour
+ * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold
+ the difference between two u16_t's.
+ * sockets.h: FD_SETSIZE needs to match number of sockets, which is
+ MEMP_NUM_NETCONN in sockets.c right now.
+
+ 2007-04-12 Jonathan Larmour
+ * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580).
+
+ 2007-04-12 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission
+ timer is reset to fix bug#19434, with help from Oleg Tyshev.
+
+ 2007-04-11 Simon Goldschmidt
+ * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than
+ previously thought need to be copied (everything but PBUF_ROM!). Cleaned up
+ pbuf.c: removed functions no needed any more (by etharp).
+
+ 2007-04-11 Kieran Mansley
+ * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix
+ "Constant is long" warnings with 16bit compilers. Contributed by
+ avatar@mmlab.cse.yzu.edu.tw
+
+ 2007-04-05 Frédéric Bernon, Jonathan Larmour
+ * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on
+ the mailbox is active". Now, the post is only done during a connect, and do_send,
+ do_write and do_join_leave_group don't do anything if a previous error was signaled.
+
+ 2007-04-03 Frédéric Bernon
+ * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output
+ packets. See patch #5834.
+
+ 2007-03-30 Frédéric Bernon
+ * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add
+ missing pcb allocations checking (in do_bind, and for each raw_new). Fix style.
+
+ 2007-03-30 Frédéric Bernon
+ * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with
+ others environment defines (these were too "generic").
+
+ 2007-03-28 Frédéric Bernon
+ * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call
+ result and can cause a crash. lwip_send now check netbuf_ref result.
+
+ 2007-03-28 Simon Goldschmidt
+ * sockets.c Remove "#include <errno.h>" from sockets.c to avoid multiple
+ definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is
+ defined. This is the way it should have been already (looking at
+ doc/sys_arch.txt)
+
+ 2007-03-28 Kieran Mansley
+ * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS +
+ IP and TCP headers *and* physical link headers
+
+ 2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov)
+ * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause
+ to send some garbage. It is not a definitive solution, but the patch does solve
+ the problem for most cases.
+
+ 2007-03-22 Frédéric Bernon
+ * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used).
+
+ 2007-03-22 Frédéric Bernon
+ * api_lib.c: somes resources couldn't be freed if there was errors during
+ netconn_new_with_proto_and_callback.
+
+ 2007-03-22 Frédéric Bernon
+ * ethernetif.c: update netif->input calls to check return value. In older ports,
+ it's a good idea to upgrade them, even if before, there could be another problem
+ (access to an uninitialized mailbox).
+
+ 2007-03-21 Simon Goldschmidt
+ * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed
+ by casting to unsigned).
+
+ 2007-03-21 Frédéric Bernon
+ * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from
+ api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a
+ dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call.
+ Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a
+ faster and more reliable communication between api_lib and tcpip.
+
+ 2007-03-21 Frédéric Bernon
+ * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0.
+
+ 2007-03-21 Frédéric Bernon
+ * api_msg.c, igmp.c, igmp.h: Fix C++ style comments
+
+ 2007-03-21 Kieran Mansley
+ * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS +
+ IP and TCP headers
+
+ 2007-03-21 Kieran Mansley
+ * Fix all uses of pbuf_header to check the return value. In some
+ cases just assert if it fails as I'm not sure how to fix them, but
+ this is no worse than before when they would carry on regardless
+ of the failure.
+
+ 2007-03-21 Kieran Mansley
+ * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and
+ comment out missing header include in icmp.c
+
+ 2007-03-20 Frédéric Bernon
+ * memp.h, stats.c: Fix stats_display function where memp_names table wasn't
+ synchronized with memp.h.
+
+ 2007-03-20 Frédéric Bernon
+ * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input,
+ tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with
+ network interfaces. Also fix a compiler warning.
+
+ 2007-03-20 Kieran Mansley
+ * udp.c: Only try and use pbuf_header() to make space for headers if
+ not a ROM or REF pbuf.
+
+ 2007-03-19 Frédéric Bernon
+ * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg()
+ and api_msg_post().
+
+ 2007-03-19 Frédéric Bernon
+ * Remove unimplemented "memp_realloc" function from memp.h.
+
+ 2007-03-11 Simon Goldschmidt
+ * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused
+ memory corruption.
+
+ 2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251
+ (missing `const' qualifier in socket functions), to get more compatible to
+ standard POSIX sockets.
+
+ 2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov)
+ * sockets.c: Add asserts inside bind, connect and sendto to check input
+ parameters. Remove excessive set_errno() calls after get_socket(), because
+ errno is set inside of get_socket(). Move last sock_set_errno() inside
+ lwip_close.
+
+ 2007-03-09 Simon Goldschmidt
+ * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory
+ was allocated too small.
+
+ 2007-03-06 Simon Goldschmidt
+ * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect
+ the stack from concurrent access.
+
+ 2007-03-06 Frédéric Bernon, Dmitry Potapov
+ * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy
+ call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input().
+
+ 2007-03-06 Simon Goldschmidt
+ * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files
+ if IP_FRAG == 0 and IP_REASSEMBLY == 0
+
+ 2007-03-06 Frédéric Bernon, Simon Goldschmidt
+ * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration
+ option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput.
+ Allow to do ARP processing for incoming packets inside tcpip_thread
+ (protecting ARP layer against concurrent access). You can also disable
+ old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0.
+ Older ports have to use tcpip_ethinput.
+
+ 2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * err.h, err.c: fixed compiler warning "initialization dircards qualifiers
+ from pointer target type"
+
+ 2007-03-05 Frédéric Bernon
+ * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES,
+ ETHARP_TRUST_IP_MAC, review SO_REUSE)
+
+ 2007-03-04 Frédéric Bernon
+ * api_msg.c: Remove some compiler warnings : parameter "pcb" was never
+ referenced.
+
+ 2007-03-04 Frédéric Bernon
+ * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from
+ Dmitry Potapov).
+ The api_msg struct stay on the stack (not moved to netconn struct).
+
+ 2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if
+ SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available)
+ Also fixed cast warning in pbuf_alloc()
+
+ 2007-03-04 Simon Goldschmidt
+ * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt
+ existing pbuf chain when enqueuing multiple pbufs to a pending ARP request
+
+ 2007-03-03 Frédéric Bernon
+ * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;"
+ It is static, and never used in udp.c except udp_init().
+
+ 2007-03-02 Simon Goldschmidt
+ * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from
+ tcpip_thread() to tcpip_init(). This way, raw API connections can be
+ initialized before tcpip_thread is running (e.g. before OS is started)
+
+ 2007-03-02 Frédéric Bernon
+ * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call
+ interval.
+
+ 2007-02-28 Kieran Mansley
+ * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved
+ outside the region of the pbuf by pbuf_header()
+
+ 2007-02-28 Kieran Mansley
+ * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero
+ when supplied timeout is also non-zero
+
+(STABLE-1.2.0)
+
+ 2006-12-05 Leon Woestenberg
+ * CHANGELOG: Mention STABLE-1.2.0 release.
+
+ ++ New features:
+
+ 2006-12-01 Christiaan Simons
+ * mem.h, opt.h: Added MEM_LIBC_MALLOC option.
+ Note this is a workaround. Currently I have no other options left.
+
+ 2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour)
+ * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define
+ to include/lwip/opt.h.
+ * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL.
+ Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h.
+ * opt.h: Add above new options.
+
+ 2006-08-18 Christiaan Simons
+ * tcp_{in,out}.c: added SNMP counters.
+ * ipv4/ip.c: added SNMP counters.
+ * ipv4/ip_frag.c: added SNMP counters.
+
+ 2006-08-08 Christiaan Simons
+ * etharp.{c,h}: added etharp_find_addr() to read
+ (stable) ethernet/IP address pair from ARP table
+
+ 2006-07-14 Christiaan Simons
+ * mib_structs.c: added
+ * include/lwip/snmp_structs.h: added
+ * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct
+
+ 2006-07-06 Christiaan Simons
+ * snmp/asn1_{enc,dec}.c added
+ * snmp/mib2.c added
+ * snmp/msg_{in,out}.c added
+ * include/lwip/snmp_asn1.h added
+ * include/lwip/snmp_msg.h added
+ * doc/snmp_agent.txt added
+
+ 2006-03-29 Christiaan Simons
+ * inet.c, inet.h: Added platform byteswap support.
+ Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and
+ optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros.
+
+ ++ Bug fixes:
+
+ 2006-11-30 Christiaan Simons
+ * dhcp.c: Fixed false triggers of request_timeout.
+
+ 2006-11-28 Christiaan Simons
+ * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags.
+
+ 2006-10-11 Christiaan Simons
+ * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h:
+ Partially accepted patch #5449 for ANSI C compatibility / build fixes.
+ * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol
+ identifier from 170 to 136 (bug #17574).
+
+ 2006-10-10 Christiaan Simons
+ * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice.
+
+ 2006-08-17 Christiaan Simons
+ * udp.c: Fixed bug #17200, added check for broadcast
+ destinations for PCBs bound to a unicast address.
+
+ 2006-08-07 Christiaan Simons
+ * api_msg.c: Flushing TCP output in do_close() (bug #15926).
+
+ 2006-06-27 Christiaan Simons
+ * api_msg.c: Applied patch for cold case (bug #11135).
+ In accept_function() ensure newconn->callback is always initialized.
+
+ 2006-06-15 Christiaan Simons
+ * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748),
+ facilitate printing of mem_size_t and u16_t statistics.
+
+ 2006-06-14 Christiaan Simons
+ * api_msg.c: Applied patch #5146 to handle allocation failures
+ in accept() by Kevin Lawson.
+
+ 2006-05-26 Christiaan Simons
+ * api_lib.c: Removed conn->sem creation and destruction
+ from netconn_write() and added sys_sem_new to netconn_new_*.
+
+(STABLE-1_1_1)
+
+ 2006-03-03 Christiaan Simons
+ * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap
+ access and added pbuf_alloc() return value checks.
+
+ 2006-01-01 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is
+ now handled by the checksum routine properly.
+
+ 2006-02-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * pbuf.c: Fix alignment; pbuf_init() would not work unless
+ pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.)
+
+ 2005-12-20 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch
+ submitted by Mitrani Hiroshi.
+
+ 2005-12-15 Christiaan Simons
+ * inet.c: Disabled the added summing routine to preserve code space.
+
+ 2005-12-14 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson.
+ Added Curt McDowell's optimized checksumming routine for future
+ inclusion. Need to create test case for unaliged, aligned, odd,
+ even length combination of cases on various endianess machines.
+
+ 2005-12-09 Christiaan Simons
+ * inet.c: Rewrote standard checksum routine in proper portable C.
+
+ 2005-11-25 Christiaan Simons
+ * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only.
+ * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t,
+ u32_t, s32_t typedefs. This solves most debug word-length assumes.
+
+ 2005-07-17 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * inet.c: Fixed unaligned 16-bit access in the standard checksum
+ routine by Peter Jolasson.
+ * slipif.c: Fixed implementation assumption of single-pbuf datagrams.
+
+ 2005-02-04 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch.
+ * tcp_{out|in}.c: Applied patch fixing unaligned access.
+
+ 2005-01-04 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement.
+
+ 2005-01-03 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * udp.c: UDP pcb->recv() was called even when it was NULL.
+
+(STABLE-1_1_0)
+
+ 2004-12-28 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.*: Disabled multiple packets on the ARP queue.
+ This clashes with TCP queueing.
+
+ 2004-11-28 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.*: Fixed race condition from ARP request to ARP timeout.
+ Halved the ARP period, doubled the period counts.
+ ETHARP_MAX_PENDING now should be at least 2. This prevents
+ the counter from reaching 0 right away (which would allow
+ too little time for ARP responses to be received).
+
+ 2004-11-25 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * dhcp.c: Decline messages were not multicast but unicast.
+ * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD.
+ Do not try hard to insert arbitrary packet's source address,
+ etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD.
+ etharp_query() now always DOES call ETHARP_TRY_HARD so that users
+ querying an address will see it appear in the cache (DHCP could
+ suffer from this when a server invalidly gave an in-use address.)
+ * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are
+ comparing network addresses (identifiers), not the network masks
+ themselves.
+ * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given
+ IP address actually belongs to the network of the given interface.
+
+ 2004-11-24 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state.
+
+(STABLE-1_1_0-RC1)
+
+ 2004-10-16 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately,
+ even if one is already pending, if the rcv_wnd is above a threshold
+ (currently TCP_WND/2). This avoids waiting for a timer to expire to send a
+ delayed ACK in order to open the window if the stack is only receiving data.
+
+ 2004-09-12 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp*.*: Retransmit time-out handling improvement by Sam Jansen.
+
+ 2004-08-20 Tony Mountifield <tony@softins.co.uk>
+ * etharp.c: Make sure the first pbuf queued on an ARP entry
+ is properly ref counted.
+
+ 2004-07-27 Tony Mountifield <tony@softins.co.uk>
+ * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler
+ warnings about comparison.
+ * pbuf.c: Stopped compiler complaining of empty if statement
+ when LWIP_DEBUGF() empty. Closed an unclosed comment.
+ * tcp.c: Stopped compiler complaining of empty if statement
+ when LWIP_DEBUGF() empty.
+ * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons().
+ * inet.c: Added a couple of casts to quiet the compiler.
+ No need to test isascii(c) before isdigit(c) or isxdigit(c).
+
+ 2004-07-22 Tony Mountifield <tony@softins.co.uk>
+ * inet.c: Made data types consistent in inet_ntoa().
+ Added casts for return values of checksum routines, to pacify compiler.
+ * ip_frag.c, tcp_out.c, sockets.c, pbuf.c
+ Small corrections to some debugging statements, to pacify compiler.
+
+ 2004-07-21 Tony Mountifield <tony@softins.co.uk>
+ * etharp.c: Removed spurious semicolon and added missing end-of-comment.
+ * ethernetif.c Updated low_level_output() to match prototype for
+ netif->linkoutput and changed low_level_input() similarly for consistency.
+ * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype
+ of raw_recv() in raw.h and so avoid compiler error.
+ * sockets.c: Added trivial (int) cast to keep compiler happier.
+ * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros.
+
+(STABLE-1_0_0)
+
+ ++ Changes:
+
+ 2004-07-05 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure
+ your cc.h file defines this either 1 or 0. If non-defined,
+ defaults to 1.
+ * .c: Added <string.h> and <errno.h> includes where used.
+ * etharp.c: Made some array indices unsigned.
+
+ 2004-06-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * netif.*: Added netif_set_up()/down().
+ * dhcp.c: Changes to restart program flow.
+
+ 2004-05-07 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.c: In find_entry(), instead of a list traversal per candidate, do a
+ single-pass lookup for different candidates. Should exploit locality.
+
+ 2004-04-29 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp*.c: Cleaned up source comment documentation for Doxygen processing.
+ * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC.
+ * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by
+ the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option.
+
+ ++ Bug fixes:
+
+ 2004-04-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution
+ suggested by Timmy Brolin. Fix for 32-bit processors that cannot access
+ non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix
+ is to prefix the 14-bit Ethernet headers with two padding bytes.
+
+ 2004-04-23 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * ip_addr.c: Fix in the ip_addr_isbroadcast() check.
+ * etharp.c: Fixed the case where the packet that initiates the ARP request
+ is not queued, and gets lost. Fixed the case where the packets destination
+ address is already known; we now always queue the packet and perform an ARP
+ request.
+
+(STABLE-0_7_0)
+
+ ++ Bug fixes:
+
+ * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition.
+ * Fixed TCP bug in dequeueing of FIN from out of order segment queue.
+ * Fixed two possible NULL references in rare cases.
+
+(STABLE-0_6_6)
+
+ ++ Bug fixes:
+
+ * Fixed DHCP which did not include the IP address in DECLINE messages.
+
+ ++ Changes:
+
+ * etharp.c has been hauled over a bit.
+
+(STABLE-0_6_5)
+
+ ++ Bug fixes:
+
+ * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic.
+ * Packets sent from ARP queue had invalid source hardware address.
+
+ ++ Changes:
+
+ * Pass-by ARP requests do now update the cache.
+
+ ++ New features:
+
+ * No longer dependent on ctype.h.
+ * New socket options.
+ * Raw IP pcb support.
+
+(STABLE-0_6_4)
+
+ ++ Bug fixes:
+
+ * Some debug formatters and casts fixed.
+ * Numereous fixes in PPP.
+
+ ++ Changes:
+
+ * DEBUGF now is LWIP_DEBUGF
+ * pbuf_dechain() has been re-enabled.
+ * Mentioned the changed use of CVS branches in README.
+
+(STABLE-0_6_3)
+
+ ++ Bug fixes:
+
+ * Fixed pool pbuf memory leak in pbuf_alloc().
+ Occured if not enough PBUF_POOL pbufs for a packet pbuf chain.
+ Reported by Savin Zlobec.
+
+ * PBUF_POOL chains had their tot_len field not set for non-first
+ pbufs. Fixed in pbuf_alloc().
+
+ ++ New features:
+
+ * Added PPP stack contributed by Marc Boucher
+
+ ++ Changes:
+
+ * Now drops short packets for ICMP/UDP/TCP protocols. More robust.
+
+ * ARP queueuing now queues the latest packet instead of the first.
+ This is the RFC recommended behaviour, but can be overridden in
+ lwipopts.h.
+
+(0.6.2)
+
+ ++ Bugfixes:
+
+ * TCP has been fixed to deal with the new use of the pbuf->ref
+ counter.
+
+ * DHCP dhcp_inform() crash bug fixed.
+
+ ++ Changes:
+
+ * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed
+ pbuf_refresh(). This has sped up pbuf pool operations considerably.
+ Implemented by David Haas.
+
+(0.6.1)
+
+ ++ New features:
+
+ * The packet buffer implementation has been enhanced to support
+ zero-copy and copy-on-demand for packet buffers which have their
+ payloads in application-managed memory.
+ Implemented by David Haas.
+
+ Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy
+ if an outgoing packet can be directly sent on the link, or perform
+ a copy-on-demand when necessary.
+
+ The application can safely assume the packet is sent, and the RAM
+ is available to the application directly after calling udp_send()
+ or similar function.
+
+ ++ Bugfixes:
+
+ * ARP_QUEUEING should now correctly work for all cases, including
+ PBUF_REF.
+ Implemented by Leon Woestenberg.
+
+ ++ Changes:
+
+ * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer
+ to a '0.0.0.0' IP address.
+
+ * The packet buffer implementation is changed. The pbuf->ref counter
+ meaning has changed, and several pbuf functions have been
+ adapted accordingly.
+
+ * netif drivers have to be changed to set the hardware address length field
+ that must be initialized correctly by the driver (hint: 6 for Ethernet MAC).
+ See the contrib/ports/c16x cs8900 driver as a driver example.
+
+ * netif's have a dhcp field that must be initialized to NULL by the driver.
+ See the contrib/ports/c16x cs8900 driver as a driver example.
+
+(0.5.x) This file has been unmaintained up to 0.6.1. All changes are
+ logged in CVS but have not been explained here.
+
+(0.5.3) Changes since version 0.5.2
+
+ ++ Bugfixes:
+
+ * memp_malloc(MEMP_API_MSG) could fail with multiple application
+ threads because it wasn't protected by semaphores.
+
+ ++ Other changes:
+
+ * struct ip_addr now packed.
+
+ * The name of the time variable in arp.c has been changed to ctime
+ to avoid conflicts with the time() function.
+
+(0.5.2) Changes since version 0.5.1
+
+ ++ New features:
+
+ * A new TCP function, tcp_tmr(), now handles both TCP timers.
+
+ ++ Bugfixes:
+
+ * A bug in tcp_parseopt() could cause the stack to hang because of a
+ malformed TCP option.
+
+ * The address of new connections in the accept() function in the BSD
+ socket library was not handled correctly.
+
+ * pbuf_dechain() did not update the ->tot_len field of the tail.
+
+ * Aborted TCP connections were not handled correctly in all
+ situations.
+
+ ++ Other changes:
+
+ * All protocol header structs are now packed.
+
+ * The ->len field in the tcp_seg structure now counts the actual
+ amount of data, and does not add one for SYN and FIN segments.
+
+(0.5.1) Changes since version 0.5.0
+
+ ++ New features:
+
+ * Possible to run as a user process under Linux.
+
+ * Preliminary support for cross platform packed structs.
+
+ * ARP timer now implemented.
+
+ ++ Bugfixes:
+
+ * TCP output queue length was badly initialized when opening
+ connections.
+
+ * TCP delayed ACKs were not sent correctly.
+
+ * Explicit initialization of BSS segment variables.
+
+ * read() in BSD socket library could drop data.
+
+ * Problems with memory alignment.
+
+ * Situations when all TCP buffers were used could lead to
+ starvation.
+
+ * TCP MSS option wasn't parsed correctly.
+
+ * Problems with UDP checksum calculation.
+
+ * IP multicast address tests had endianess problems.
+
+ * ARP requests had wrong destination hardware address.
+
+ ++ Other changes:
+
+ * struct eth_addr changed from u16_t[3] array to u8_t[6].
+
+ * A ->linkoutput() member was added to struct netif.
+
+ * TCP and UDP ->dest_* struct members where changed to ->remote_*.
+
+ * ntoh* macros are now null definitions for big endian CPUs.
+
+(0.5.0) Changes since version 0.4.2
+
+ ++ New features:
+
+ * Redesigned operating system emulation layer to make porting easier.
+
+ * Better control over TCP output buffers.
+
+ * Documenation added.
+
+ ++ Bugfixes:
+
+ * Locking issues in buffer management.
+
+ * Bugfixes in the sequential API.
+
+ * IP forwarding could cause memory leakage. This has been fixed.
+
+ ++ Other changes:
+
+ * Directory structure somewhat changed; the core/ tree has been
+ collapsed.
+
+(0.4.2) Changes since version 0.4.1
+
+ ++ New features:
+
+ * Experimental ARP implementation added.
+
+ * Skeleton Ethernet driver added.
+
+ * Experimental BSD socket API library added.
+
+ ++ Bugfixes:
+
+ * In very intense situations, memory leakage could occur. This has
+ been fixed.
+
+ ++ Other changes:
+
+ * Variables named "data" and "code" have been renamed in order to
+ avoid name conflicts in certain compilers.
+
+ * Variable++ have in appliciable cases been translated to ++variable
+ since some compilers generate better code in the latter case.
+
+(0.4.1) Changes since version 0.4
+
+ ++ New features:
+
+ * TCP: Connection attempts time out earlier than data
+ transmissions. Nagle algorithm implemented. Push flag set on the
+ last segment in a burst.
+
+ * UDP: experimental support for UDP-Lite extensions.
+
+ ++ Bugfixes:
+
+ * TCP: out of order segments were in some cases handled incorrectly,
+ and this has now been fixed. Delayed acknowledgements was broken
+ in 0.4, has now been fixed. Binding to an address that is in use
+ now results in an error. Reset connections sometimes hung an
+ application; this has been fixed.
+
+ * Checksum calculation sometimes failed for chained pbufs with odd
+ lengths. This has been fixed.
+
+ * API: a lot of bug fixes in the API. The UDP API has been improved
+ and tested. Error reporting and handling has been
+ improved. Logical flaws and race conditions for incoming TCP
+ connections has been found and removed.
+
+ * Memory manager: alignment issues. Reallocating memory sometimes
+ failed, this has been fixed.
+
+ * Generic library: bcopy was flawed and has been fixed.
+
+ ++ Other changes:
+
+ * API: all datatypes has been changed from generic ones such as
+ ints, to specified ones such as u16_t. Functions that return
+ errors now have the correct type (err_t).
+
+ * General: A lot of code cleaned up and debugging code removed. Many
+ portability issues have been fixed.
+
+ * The license was changed; the advertising clause was removed.
+
+ * C64 port added.
+
+ * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri
+ Kosunen, Mikael Caleres, and Frits Wilmink for reporting and
+ fixing bugs!
+
+(0.4) Changes since version 0.3.1
+
+ * Memory management has been radically changed; instead of
+ allocating memory from a shared heap, memory for objects that are
+ rapidly allocated and deallocated is now kept in pools. Allocation
+ and deallocation from those memory pools is very fast. The shared
+ heap is still present but is used less frequently.
+
+ * The memory, memory pool, and packet buffer subsystems now support
+ 4-, 2-, or 1-byte alignment.
+
+ * "Out of memory" situations are handled in a more robust way.
+
+ * Stack usage has been reduced.
+
+ * Easier configuration of lwIP parameters such as memory usage,
+ TTLs, statistics gathering, etc. All configuration parameters are
+ now kept in a single header file "lwipopts.h".
+
+ * The directory structure has been changed slightly so that all
+ architecture specific files are kept under the src/arch
+ hierarchy.
+
+ * Error propagation has been improved, both in the protocol modules
+ and in the API.
+
+ * The code for the RTXC architecture has been implemented, tested
+ and put to use.
+
+ * Bugs have been found and corrected in the TCP, UDP, IP, API, and
+ the Internet checksum modules.
+
+ * Bugs related to porting between a 32-bit and a 16-bit architecture
+ have been found and corrected.
+
+ * The license has been changed slightly to conform more with the
+ original BSD license, including the advertisement clause.
+
+(0.3.1) Changes since version 0.3
+
+ * Fix of a fatal bug in the buffer management. Pbufs with allocated
+ RAM never returned the RAM when the pbuf was deallocated.
+
+ * TCP congestion control, window updates and retransmissions did not
+ work correctly. This has now been fixed.
+
+ * Bugfixes in the API.
+
+(0.3) Changes since version 0.2
+
+ * New and improved directory structure. All include files are now
+ kept in a dedicated include/ directory.
+
+ * The API now has proper error handling. A new function,
+ netconn_err(), now returns an error code for the connection in
+ case of errors.
+
+ * Improvements in the memory management subsystem. The system now
+ keeps a pointer to the lowest free memory block. A new function,
+ mem_malloc2() tries to allocate memory once, and if it fails tries
+ to free some memory and retry the allocation.
+
+ * Much testing has been done with limited memory
+ configurations. lwIP now does a better job when overloaded.
+
+ * Some bugfixes and improvements to the buffer (pbuf) subsystem.
+
+ * Many bugfixes in the TCP code:
+
+ - Fixed a bug in tcp_close().
+
+ - The TCP receive window was incorrectly closed when out of
+ sequence segments was received. This has been fixed.
+
+ - Connections are now timed-out of the FIN-WAIT-2 state.
+
+ - The initial congestion window could in some cases be too
+ large. This has been fixed.
+
+ - The retransmission queue could in some cases be screwed up. This
+ has been fixed.
+
+ - TCP RST flag now handled correctly.
+
+ - Out of sequence data was in some cases never delivered to the
+ application. This has been fixed.
+
+ - Retransmitted segments now contain the correct acknowledgment
+ number and advertised window.
+
+ - TCP retransmission timeout backoffs are not correctly computed
+ (ala BSD). After a number of retransmissions, TCP now gives up
+ the connection.
+
+ * TCP connections now are kept on three lists, one for active
+ connections, one for listening connections, and one for
+ connections that are in TIME-WAIT. This greatly speeds up the fast
+ timeout processing for sending delayed ACKs.
+
+ * TCP now provides proper feedback to the application when a
+ connection has been successfully set up.
+
+ * More comments have been added to the code. The code has also been
+ somewhat cleaned up.
+
+(0.2) Initial public release.
diff --git a/core/lwip/COPYING b/core/lwip/COPYING
new file mode 100644
index 00000000..e23898b5
--- /dev/null
+++ b/core/lwip/COPYING
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2001, 2002 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>
+ *
+ */
+
+
diff --git a/core/lwip/FILES b/core/lwip/FILES
new file mode 100644
index 00000000..66253196
--- /dev/null
+++ b/core/lwip/FILES
@@ -0,0 +1,4 @@
+src/ - The source code for the lwIP TCP/IP stack.
+doc/ - The documentation for lwIP.
+
+See also the FILES file in each subdirectory.
diff --git a/core/lwip/README b/core/lwip/README
new file mode 100644
index 00000000..a62cc4f3
--- /dev/null
+++ b/core/lwip/README
@@ -0,0 +1,89 @@
+INTRODUCTION
+
+lwIP is a small independent implementation of the TCP/IP protocol
+suite that has been developed by Adam Dunkels at the Computer and
+Networks Architectures (CNA) lab at the Swedish Institute of Computer
+Science (SICS).
+
+The focus of the lwIP TCP/IP implementation is to reduce the RAM usage
+while still having a full scale TCP. This making lwIP suitable for use
+in embedded systems with tens of kilobytes of free RAM and room for
+around 40 kilobytes of code ROM.
+
+FEATURES
+
+ * IP (Internet Protocol) including packet forwarding over multiple network
+ interfaces
+ * ICMP (Internet Control Message Protocol) for network maintenance and debugging
+ * IGMP (Internet Group Management Protocol) for multicast traffic management
+ * UDP (User Datagram Protocol) including experimental UDP-lite extensions
+ * TCP (Transmission Control Protocol) with congestion control, RTT estimation
+ and fast recovery/fast retransmit
+ * Specialized raw/native API for enhanced performance
+ * Optional Berkeley-like socket API
+ * DNS (Domain names resolver)
+ * SNMP (Simple Network Management Protocol)
+ * DHCP (Dynamic Host Configuration Protocol)
+ * AUTOIP (for IPv4, conform with RFC 3927)
+ * PPP (Point-to-Point Protocol)
+ * ARP (Address Resolution Protocol) for Ethernet
+
+LICENSE
+
+lwIP is freely available under a BSD license.
+
+DEVELOPMENT
+
+lwIP has grown into an excellent TCP/IP stack for embedded devices,
+and developers using the stack often submit bug fixes, improvements,
+and additions to the stack to further increase its usefulness.
+
+Development of lwIP is hosted on Savannah, a central point for
+software development, maintenance and distribution. Everyone can
+help improve lwIP by use of Savannah's interface, CVS and the
+mailing list. A core team of developers will commit changes to the
+CVS source tree.
+
+The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and
+contributions (such as platform ports) are in the 'contrib' module.
+
+See doc/savannah.txt for details on CVS server access for users and
+developers.
+
+Last night's CVS tar ball can be downloaded from:
+ http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING]
+
+The current CVS trees are web-browsable:
+ http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/
+ http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/
+
+Submit patches and bugs via the lwIP project page:
+ http://savannah.nongnu.org/projects/lwip/
+
+
+DOCUMENTATION
+
+The original out-dated homepage of lwIP and Adam Dunkels' papers on
+lwIP are at the official lwIP home page:
+ http://www.sics.se/~adam/lwip/
+
+Self documentation of the source code is regularly extracted from the
+current CVS sources and is available from this web page:
+ http://www.nongnu.org/lwip/
+
+There is now a constantly growin wiki about lwIP at
+ http://lwip.wikia.com/wiki/LwIP_Wiki
+
+Also, there are mailing lists you can subscribe at
+ http://savannah.nongnu.org/mail/?group=lwip
+plus searchable archives:
+ http://lists.nongnu.org/archive/html/lwip-users/
+ http://lists.nongnu.org/archive/html/lwip-devel/
+
+Reading Adam's papers, the files in docs/, browsing the source code
+documentation and browsing the mailing list archives is a good way to
+become familiar with the design of lwIP.
+
+Adam Dunkels <adam@sics.se>
+Leon Woestenberg <leon.woestenberg@gmx.net>
+
diff --git a/core/lwip/UPGRADING b/core/lwip/UPGRADING
new file mode 100644
index 00000000..6501107a
--- /dev/null
+++ b/core/lwip/UPGRADING
@@ -0,0 +1,144 @@
+This file lists major changes between release versions that require
+ports or applications to be changed. Use it to update a port or an
+application written for an older version of lwIP to correctly work
+with newer versions.
+
+
+(CVS HEAD)
+
+ * [Enter new changes just after this line - do not remove this line]
+
+ ++ Application changes:
+
+ * Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for
+ compatibility to old applications, but will be removed in the future).
+
+ * Renamed mem_realloc() to mem_trim() to prevent confusion with realloc()
+
+ +++ Raw API:
+ * Changed the semantics of tcp_close() (since it was rather a
+ shutdown before): Now the application does *NOT* get any calls to the recv
+ callback (aside from NULL/closed) after calling tcp_close()
+
+ * When calling tcp_abort() from a raw API TCP callback function,
+ make sure you return ERR_ABRT to prevent accessing unallocated memory.
+ (ERR_ABRT now means the applicaiton has called tcp_abort!)
+
+ +++ Netconn API:
+ * Changed netconn_receive() and netconn_accept() to return
+ err_t, not a pointer to new data/netconn.
+
+ +++ Socket API:
+ * LWIP_SO_RCVTIMEO: when accept() or recv() time out, they
+ now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT.
+
+ * Added a minimal version of posix fctl() to have a
+ standardised way to set O_NONBLOCK for nonblocking sockets.
+
+ +++ all APIs:
+ * correctly implemented SO(F)_REUSEADDR
+
+ ++ Port changes
+
+ +++ new files:
+
+ * Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h:
+
+ * Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains
+ the actual application programmer's API
+
+ * Separated timer implementation from sys.h/.c, moved to timers.h/.c;
+ Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you
+ still want to use your own timer implementation for NO_SYS==0 (as before).
+
+ +++ sys layer:
+
+ * Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/
+ sys_sem_t;
+
+ * Converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
+
+ * Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use
+ binary semaphores instead of mutexes - as before)
+
+ +++ new options:
+
+ * Don't waste memory when chaining segments, added option TCP_OVERSIZE to
+ prevent creating many small pbufs when calling tcp_write with many small
+ blocks of data. Instead, pbufs are allocated larger than needed and the
+ space is used for later calls to tcp_write.
+
+ * Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs
+ in tcp_write/udp_send.
+
+ * Added an additional option LWIP_ETHERNET to support ethernet without ARP
+ (necessary for pure PPPoE)
+
+ * Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may
+ be used to place these pools into user-defined memory by using external
+ declaration.
+
+ * Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT
+
+ +++ new pools:
+
+ * Netdb uses a memp pool for allocating memory when getaddrinfo() is called,
+ so MEMP_NUM_NETDB has to be set accordingly.
+
+ * DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so
+ MEMP_NUM_LOCALHOSTLIST has to be set accordingly.
+
+ * Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have
+ to be set accordingly.
+
+ * PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES
+ has to be set accordingly
+
+ * Integrated loopif into netif.c - loopif does not have to be created by the
+ port any more, just define LWIP_HAVE_LOOPIF to 1.
+
+ * Added define LWIP_RAND() for lwip-wide randomization (needs to be defined
+ in cc.h, e.g. used by igmp)
+
+ * Added printf-formatter X8_F to printf u8_t as hex
+
+ * The heap now may be moved to user-defined memory by defining
+ LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
+
+ * added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work
+ with user-allocated structs instead of calling mem_malloc
+
+ * Added const char* name to mem- and memp-stats for easier debugging.
+
+ * Calculate the TCP/UDP checksum while copying to only fetch data once:
+ Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum
+
+ * Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to
+ more than one pcb.
+
+ * Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned
+ off any more, if this is set to 0, only one packet (the most recent one) is
+ queued (like demanded by RFC 1122).
+
+
+ ++ Major bugfixes/improvements
+
+ * Implemented tcp_shutdown() to only shut down one end of a connection
+ * Implemented shutdown() at socket- and netconn-level
+ * Added errorset support to select() + improved select speed overhead
+ * Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x)
+ * Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1
+ * Use macros defined in ip_addr.h to work with IP addresses
+ * Implemented many nonblocking socket/netconn functions
+ * Fixed ARP input processing: only add a new entry if a request was directed as us
+ * mem_realloc() to mem_trim() to prevent confusion with realloc()
+ * Some improvements for AutoIP (don't route/forward link-local addresses, don't break
+ existing connections when assigning a routable address)
+ * Correctly handle remote side overrunning our rcv_wnd in ooseq case
+ * Removed packing from ip_addr_t, the packed version is now only used in protocol headers
+ * Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0
+ * Added support for static ARP table entries
+
+(STABLE-1.3.2)
+
+ * initial version of this file
diff --git a/core/lwip/doc/FILES b/core/lwip/doc/FILES
new file mode 100644
index 00000000..05d356f4
--- /dev/null
+++ b/core/lwip/doc/FILES
@@ -0,0 +1,6 @@
+savannah.txt - How to obtain the current development source code.
+contrib.txt - How to contribute to lwIP as a developer.
+rawapi.txt - The documentation for the core API of lwIP.
+ Also provides an overview about the other APIs and multithreading.
+snmp_agent.txt - The documentation for the lwIP SNMP agent.
+sys_arch.txt - The documentation for a system abstraction layer of lwIP.
diff --git a/core/lwip/doc/contrib.txt b/core/lwip/doc/contrib.txt
new file mode 100644
index 00000000..39596fca
--- /dev/null
+++ b/core/lwip/doc/contrib.txt
@@ -0,0 +1,63 @@
+1 Introduction
+
+This document describes some guidelines for people participating
+in lwIP development.
+
+2 How to contribute to lwIP
+
+Here is a short list of suggestions to anybody working with lwIP and
+trying to contribute bug reports, fixes, enhancements, platform ports etc.
+First of all as you may already know lwIP is a volunteer project so feedback
+to fixes or questions might often come late. Hopefully the bug and patch tracking
+features of Savannah help us not lose users' input.
+
+2.1 Source code style:
+
+1. do not use tabs.
+2. indentation is two spaces per level (i.e. per tab).
+3. end debug messages with a trailing newline (\n).
+4. one space between keyword and opening bracket.
+5. no space between function and opening bracket.
+6. one space and no newline before opening curly braces of a block.
+7. closing curly brace on a single line.
+8. spaces surrounding assignment and comparisons.
+9. don't initialize static and/or global variables to zero, the compiler takes care of that.
+10. use current source code style as further reference.
+
+2.2 Source code documentation style:
+
+1. JavaDoc compliant and Doxygen compatible.
+2. Function documentation above functions in .c files, not .h files.
+ (This forces you to synchronize documentation and implementation.)
+3. Use current documentation style as further reference.
+
+2.3 Bug reports and patches:
+
+1. Make sure you are reporting bugs or send patches against the latest
+ sources. (From the latest release and/or the current CVS sources.)
+2. If you think you found a bug make sure it's not already filed in the
+ bugtracker at Savannah.
+3. If you have a fix put the patch on Savannah. If it is a patch that affects
+ both core and arch specific stuff please separate them so that the core can
+ be applied separately while leaving the other patch 'open'. The prefered way
+ is to NOT touch archs you can't test and let maintainers take care of them.
+ This is a good way to see if they are used at all - the same goes for unix
+ netifs except tapif.
+4. Do not file a bug and post a fix to it to the patch area. Either a bug report
+ or a patch will be enough.
+ If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area.
+5. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two)
+ can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded
+ as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead
+ for reporting a compiler warning fix.
+6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other
+ trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you
+ change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than
+ if it's not to the point and long :) so the chances for it to be applied are greater.
+
+2.4 Platform porters:
+
+1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and
+ you think it could benefit others[1] you might want discuss this on the mailing list. You
+ can also ask for CVS access to submit and maintain your port in the contrib CVS module.
+ \ No newline at end of file
diff --git a/core/lwip/doc/rawapi.txt b/core/lwip/doc/rawapi.txt
new file mode 100644
index 00000000..c727da99
--- /dev/null
+++ b/core/lwip/doc/rawapi.txt
@@ -0,0 +1,505 @@
+Raw TCP/IP interface for lwIP
+
+Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons
+
+lwIP provides three Application Program's Interfaces (APIs) for programs
+to use for communication with the TCP/IP code:
+* low-level "core" / "callback" or "raw" API.
+* higher-level "sequential" API.
+* BSD-style socket API.
+
+The sequential API provides a way for ordinary, sequential, programs
+to use the lwIP stack. It is quite similar to the BSD socket API. The
+model of execution is based on the blocking open-read-write-close
+paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP
+code and the application program must reside in different execution
+contexts (threads).
+
+The socket API is a compatibility API for existing applications,
+currently it is built on top of the sequential API. It is meant to
+provide all functions needed to run socket API applications running
+on other platforms (e.g. unix / windows etc.). However, due to limitations
+in the specification of this API, there might be incompatibilities
+that require small modifications of existing programs.
+
+** Threading
+
+lwIP started targeting single-threaded environments. When adding multi-
+threading support, instead of making the core thread-safe, another
+approach was chosen: there is one main thread running the lwIP core
+(also known as the "tcpip_thread"). The raw API may only be used from
+this thread! Application threads using the sequential- or socket API
+communicate with this main thread through message passing.
+
+ As such, the list of functions that may be called from
+ other threads or an ISR is very limited! Only functions
+ from these API header files are thread-safe:
+ - api.h
+ - netbuf.h
+ - netdb.h
+ - netifapi.h
+ - sockets.h
+ - sys.h
+
+ Additionaly, memory (de-)allocation functions may be
+ called from multiple threads (not ISR!) with NO_SYS=0
+ since they are protected by SYS_LIGHTWEIGHT_PROT and/or
+ semaphores.
+
+ Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1
+ and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1,
+ pbuf_free() may also be called from another thread or
+ an ISR (since only then, mem_free - for PBUF_RAM - may
+ be called from an ISR: otherwise, the HEAP is only
+ protected by semaphores).
+
+
+** The remainder of this document discusses the "raw" API. **
+
+The raw TCP/IP interface allows the application program to integrate
+better with the TCP/IP code. Program execution is event based by
+having callback functions being called from within the TCP/IP
+code. The TCP/IP code and the application program both run in the same
+thread. The sequential API has a much higher overhead and is not very
+well suited for small systems since it forces a multithreaded paradigm
+on the application.
+
+The raw TCP/IP interface is not only faster in terms of code execution
+time but is also less memory intensive. The drawback is that program
+development is somewhat harder and application programs written for
+the raw TCP/IP interface are more difficult to understand. Still, this
+is the preferred way of writing applications that should be small in
+code size and memory usage.
+
+Both APIs can be used simultaneously by different application
+programs. In fact, the sequential API is implemented as an application
+program using the raw TCP/IP interface.
+
+--- Callbacks
+
+Program execution is driven by callbacks. Each callback is an ordinary
+C function that is called from within the TCP/IP code. Every callback
+function is passed the current TCP or UDP connection state as an
+argument. Also, in order to be able to keep program specific state,
+the callback functions are called with a program specified argument
+that is independent of the TCP/IP state.
+
+The function for setting the application connection state is:
+
+- void tcp_arg(struct tcp_pcb *pcb, void *arg)
+
+ Specifies the program specific state that should be passed to all
+ other callback functions. The "pcb" argument is the current TCP
+ connection control block, and the "arg" argument is the argument
+ that will be passed to the callbacks.
+
+
+--- TCP connection setup
+
+The functions used for setting up connections is similar to that of
+the sequential API and of the BSD socket API. A new TCP connection
+identifier (i.e., a protocol control block - PCB) is created with the
+tcp_new() function. This PCB can then be either set to listen for new
+incoming connections or be explicitly connected to another host.
+
+- struct tcp_pcb *tcp_new(void)
+
+ Creates a new connection identifier (PCB). If memory is not
+ available for creating the new pcb, NULL is returned.
+
+- err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr,
+ u16_t port)
+
+ Binds the pcb to a local IP address and port number. The IP address
+ can be specified as IP_ADDR_ANY in order to bind the connection to
+ all local IP addresses.
+
+ If another connection is bound to the same port, the function will
+ return ERR_USE, otherwise ERR_OK is returned.
+
+- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)
+
+ Commands a pcb to start listening for incoming connections. When an
+ incoming connection is accepted, the function specified with the
+ tcp_accept() function will be called. The pcb will have to be bound
+ to a local port with the tcp_bind() function.
+
+ The tcp_listen() function returns a new connection identifier, and
+ the one passed as an argument to the function will be
+ deallocated. The reason for this behavior is that less memory is
+ needed for a connection that is listening, so tcp_listen() will
+ reclaim the memory needed for the original connection and allocate a
+ new smaller memory block for the listening connection.
+
+ tcp_listen() may return NULL if no memory was available for the
+ listening connection. If so, the memory associated with the pcb
+ passed as an argument to tcp_listen() will not be deallocated.
+
+- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+
+ Same as tcp_listen, but limits the number of outstanding connections
+ in the listen queue to the value specified by the backlog argument.
+ To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h.
+
+- void tcp_accepted(struct tcp_pcb *pcb)
+
+ Inform lwIP that an incoming connection has been accepted. This would
+ usually be called from the accept callback. This allows lwIP to perform
+ housekeeping tasks, such as allowing further incoming connections to be
+ queued in the listen backlog.
+
+- void tcp_accept(struct tcp_pcb *pcb,
+ err_t (* accept)(void *arg, struct tcp_pcb *newpcb,
+ err_t err))
+
+ Specified the callback function that should be called when a new
+ connection arrives on a listening connection.
+
+- err_t tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr,
+ u16_t port, err_t (* connected)(void *arg,
+ struct tcp_pcb *tpcb,
+ err_t err));
+
+ Sets up the pcb to connect to the remote host and sends the
+ initial SYN segment which opens the connection.
+
+ The tcp_connect() function returns immediately; it does not wait for
+ the connection to be properly setup. Instead, it will call the
+ function specified as the fourth argument (the "connected" argument)
+ when the connection is established. If the connection could not be
+ properly established, either because the other host refused the
+ connection or because the other host didn't answer, the "err"
+ callback function of this pcb (registered with tcp_err, see below)
+ will be called.
+
+ The tcp_connect() function can return ERR_MEM if no memory is
+ available for enqueueing the SYN segment. If the SYN indeed was
+ enqueued successfully, the tcp_connect() function returns ERR_OK.
+
+
+--- Sending TCP data
+
+TCP data is sent by enqueueing the data with a call to
+tcp_write(). When the data is successfully transmitted to the remote
+host, the application will be notified with a call to a specified
+callback function.
+
+- err_t tcp_write(struct tcp_pcb *pcb, void *dataptr, u16_t len,
+ u8_t copy)
+
+ Enqueues the data pointed to by the argument dataptr. The length of
+ the data is passed as the len parameter. The copy argument is either
+ 0 or 1 and indicates whether the new memory should be allocated for
+ the data to be copied into. If the argument is 0, no new memory
+ should be allocated and the data should only be referenced by
+ pointer.
+
+ The tcp_write() function will fail and return ERR_MEM if the length
+ of the data exceeds the current send buffer size or if the length of
+ the queue of outgoing segment is larger than the upper limit defined
+ in lwipopts.h. The number of bytes available in the output queue can
+ be retrieved with the tcp_sndbuf() function.
+
+ The proper way to use this function is to call the function with at
+ most tcp_sndbuf() bytes of data. If the function returns ERR_MEM,
+ the application should wait until some of the currently enqueued
+ data has been successfully received by the other host and try again.
+
+- void tcp_sent(struct tcp_pcb *pcb,
+ err_t (* sent)(void *arg, struct tcp_pcb *tpcb,
+ u16_t len))
+
+ Specifies the callback function that should be called when data has
+ successfully been received (i.e., acknowledged) by the remote
+ host. The len argument passed to the callback function gives the
+ amount bytes that was acknowledged by the last acknowledgment.
+
+
+--- Receiving TCP data
+
+TCP data reception is callback based - an application specified
+callback function is called when new data arrives. When the
+application has taken the data, it has to call the tcp_recved()
+function to indicate that TCP can advertise increase the receive
+window.
+
+- void tcp_recv(struct tcp_pcb *pcb,
+ err_t (* recv)(void *arg, struct tcp_pcb *tpcb,
+ struct pbuf *p, err_t err))
+
+ Sets the callback function that will be called when new data
+ arrives. The callback function will be passed a NULL pbuf to
+ indicate that the remote host has closed the connection. If
+ there are no errors and the callback function is to return
+ ERR_OK, then it must free the pbuf. Otherwise, it must not
+ free the pbuf so that lwIP core code can store it.
+
+- void tcp_recved(struct tcp_pcb *pcb, u16_t len)
+
+ Must be called when the application has received the data. The len
+ argument indicates the length of the received data.
+
+
+--- Application polling
+
+When a connection is idle (i.e., no data is either transmitted or
+received), lwIP will repeatedly poll the application by calling a
+specified callback function. This can be used either as a watchdog
+timer for killing connections that have stayed idle for too long, or
+as a method of waiting for memory to become available. For instance,
+if a call to tcp_write() has failed because memory wasn't available,
+the application may use the polling functionality to call tcp_write()
+again when the connection has been idle for a while.
+
+- void tcp_poll(struct tcp_pcb *pcb,
+ err_t (* poll)(void *arg, struct tcp_pcb *tpcb),
+ u8_t interval)
+
+ Specifies the polling interval and the callback function that should
+ be called to poll the application. The interval is specified in
+ number of TCP coarse grained timer shots, which typically occurs
+ twice a second. An interval of 10 means that the application would
+ be polled every 5 seconds.
+
+
+--- Closing and aborting connections
+
+- err_t tcp_close(struct tcp_pcb *pcb)
+
+ Closes the connection. The function may return ERR_MEM if no memory
+ was available for closing the connection. If so, the application
+ should wait and try again either by using the acknowledgment
+ callback or the polling functionality. If the close succeeds, the
+ function returns ERR_OK.
+
+ The pcb is deallocated by the TCP code after a call to tcp_close().
+
+- void tcp_abort(struct tcp_pcb *pcb)
+
+ Aborts the connection by sending a RST (reset) segment to the remote
+ host. The pcb is deallocated. This function never fails.
+
+ ATTENTION: When calling this from one of the TCP callbacks, make
+ sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+ or you will risk accessing deallocated memory or memory leaks!
+
+
+If a connection is aborted because of an error, the application is
+alerted of this event by the err callback. Errors that might abort a
+connection are when there is a shortage of memory. The callback
+function to be called is set using the tcp_err() function.
+
+- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg,
+ err_t err))
+
+ The error callback function does not get the pcb passed to it as a
+ parameter since the pcb may already have been deallocated.
+
+
+--- Lower layer TCP interface
+
+TCP provides a simple interface to the lower layers of the
+system. During system initialization, the function tcp_init() has
+to be called before any other TCP function is called. When the system
+is running, the two timer functions tcp_fasttmr() and tcp_slowtmr()
+must be called with regular intervals. The tcp_fasttmr() should be
+called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and
+tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds.
+
+
+--- UDP interface
+
+The UDP interface is similar to that of TCP, but due to the lower
+level of complexity of UDP, the interface is significantly simpler.
+
+- struct udp_pcb *udp_new(void)
+
+ Creates a new UDP pcb which can be used for UDP communication. The
+ pcb is not active until it has either been bound to a local address
+ or connected to a remote address.
+
+- void udp_remove(struct udp_pcb *pcb)
+
+ Removes and deallocates the pcb.
+
+- err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr,
+ u16_t port)
+
+ Binds the pcb to a local address. The IP-address argument "ipaddr"
+ can be IP_ADDR_ANY to indicate that it should listen to any local IP
+ address. The function currently always return ERR_OK.
+
+- err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr,
+ u16_t port)
+
+ Sets the remote end of the pcb. This function does not generate any
+ network traffic, but only set the remote address of the pcb.
+
+- err_t udp_disconnect(struct udp_pcb *pcb)
+
+ Remove the remote end of the pcb. This function does not generate
+ any network traffic, but only removes the remote address of the pcb.
+
+- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
+
+ Sends the pbuf p. The pbuf is not deallocated.
+
+- void udp_recv(struct udp_pcb *pcb,
+ void (* recv)(void *arg, struct udp_pcb *upcb,
+ struct pbuf *p,
+ struct ip_addr *addr,
+ u16_t port),
+ void *recv_arg)
+
+ Specifies a callback function that should be called when a UDP
+ datagram is received.
+
+
+--- System initalization
+
+A truly complete and generic sequence for initializing the lwip stack
+cannot be given because it depends on the build configuration (lwipopts.h)
+and additional initializations for your runtime environment (e.g. timers).
+
+We can give you some idea on how to proceed when using the raw API.
+We assume a configuration using a single Ethernet netif and the
+UDP and TCP transport layers, IPv4 and the DHCP client.
+
+Call these functions in the order of appearance:
+
+- stats_init()
+
+ Clears the structure where runtime statistics are gathered.
+
+- sys_init()
+
+ Not of much use since we set the NO_SYS 1 option in lwipopts.h,
+ to be called for easy configuration changes.
+
+- lwip_mem_init()
+
+ Initializes the dynamic memory heap defined by MEM_SIZE.
+
+- memp_init()
+
+ Initializes the memory pools defined by MEMP_NUM_x.
+
+- pbuf_init()
+
+ Initializes the pbuf memory pool defined by PBUF_POOL_SIZE.
+
+- etharp_init()
+
+ Initializes the ARP table and queue.
+ Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval
+ after this initialization.
+
+- ip_init()
+
+ Doesn't do much, it should be called to handle future changes.
+
+- udp_init()
+
+ Clears the UDP PCB list.
+
+- tcp_init()
+
+ Clears the TCP PCB list and clears some internal TCP timers.
+ Note: you must call tcp_fasttmr() and tcp_slowtmr() at the
+ predefined regular intervals after this initialization.
+
+- netif_add(struct netif *netif, struct ip_addr *ipaddr,
+ struct ip_addr *netmask, struct ip_addr *gw,
+ void *state, err_t (* init)(struct netif *netif),
+ err_t (* input)(struct pbuf *p, struct netif *netif))
+
+ Adds your network interface to the netif_list. Allocate a struct
+ netif and pass a pointer to this structure as the first argument.
+ Give pointers to cleared ip_addr structures when using DHCP,
+ or fill them with sane numbers otherwise. The state pointer may be NULL.
+
+ The init function pointer must point to a initialization function for
+ your ethernet netif interface. The following code illustrates it's use.
+
+ err_t netif_if_init(struct netif *netif)
+ {
+ u8_t i;
+
+ for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i];
+ init_my_eth_device();
+ return ERR_OK;
+ }
+
+ For ethernet drivers, the input function pointer must point to the lwip
+ function ethernet_input() declared in "netif/etharp.h". Other drivers
+ must use ip_input() declared in "lwip/ip.h".
+
+- netif_set_default(struct netif *netif)
+
+ Registers the default network interface.
+
+- netif_set_up(struct netif *netif)
+
+ When the netif is fully configured this function must be called.
+
+- dhcp_start(struct netif *netif)
+
+ Creates a new DHCP client for this interface on the first call.
+ Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
+ the predefined regular intervals after starting the client.
+
+ You can peek in the netif->dhcp struct for the actual DHCP status.
+
+
+--- Optimalization hints
+
+The first thing you want to optimize is the lwip_standard_checksum()
+routine from src/core/inet.c. You can override this standard
+function with the #define LWIP_CHKSUM <your_checksum_routine>.
+
+There are C examples given in inet.c or you might want to
+craft an assembly function for this. RFC1071 is a good
+introduction to this subject.
+
+Other significant improvements can be made by supplying
+assembly or inline replacements for htons() and htonl()
+if you're using a little-endian architecture.
+#define LWIP_PLATFORM_BYTESWAP 1
+#define LWIP_PLATFORM_HTONS(x) <your_htons>
+#define LWIP_PLATFORM_HTONL(x) <your_htonl>
+
+Check your network interface driver if it reads at
+a higher speed than the maximum wire-speed. If the
+hardware isn't serviced frequently and fast enough
+buffer overflows are likely to occur.
+
+E.g. when using the cs8900 driver, call cs8900if_service(ethif)
+as frequently as possible. When using an RTOS let the cs8900 interrupt
+wake a high priority task that services your driver using a binary
+semaphore or event flag. Some drivers might allow additional tuning
+to match your application and network.
+
+For a production release it is recommended to set LWIP_STATS to 0.
+Note that speed performance isn't influenced much by simply setting
+high values to the memory options.
+
+For more optimization hints take a look at the lwIP wiki.
+
+--- Zero-copy MACs
+
+To achieve zero-copy on transmit, the data passed to the raw API must
+remain unchanged until sent. Because the send- (or write-)functions return
+when the packets have been enqueued for sending, data must be kept stable
+after that, too.
+
+This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions
+must *not* be reused by the application unless their ref-count is 1.
+
+For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too,
+but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while
+PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change).
+
+Also, data passed to tcp_write without the copy-flag must not be changed!
+
+Therefore, be careful which type of PBUF you use and if you copy TCP data
+or not!
diff --git a/core/lwip/doc/savannah.txt b/core/lwip/doc/savannah.txt
new file mode 100644
index 00000000..409905b1
--- /dev/null
+++ b/core/lwip/doc/savannah.txt
@@ -0,0 +1,135 @@
+Daily Use Guide for using Savannah for lwIP
+
+Table of Contents:
+
+1 - Obtaining lwIP from the CVS repository
+2 - Committers/developers CVS access using SSH (to be written)
+3 - Merging from DEVEL branch to main trunk (stable branch)
+4 - How to release lwIP
+
+
+
+1 Obtaining lwIP from the CVS repository
+----------------------------------------
+
+To perform an anonymous CVS checkout of the main trunk (this is where
+bug fixes and incremental enhancements occur), do this:
+
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip
+
+Or, obtain a stable branch (updated with bug fixes only) as follows:
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+ -r STABLE-0_7 -d lwip-0.7 lwip
+
+Or, obtain a specific (fixed) release as follows:
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+ -r STABLE-0_7_0 -d lwip-0.7.0 lwip
+
+3 Committers/developers CVS access using SSH
+--------------------------------------------
+
+The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption.
+As such, CVS commits to the server occur through a SSH tunnel for project members.
+To create a SSH2 key pair in UNIX-like environments, do this:
+
+ssh-keygen -t dsa
+
+Under Windows, a recommended SSH client is "PuTTY", freely available with good
+documentation and a graphic user interface. Use its key generator.
+
+Now paste the id_dsa.pub contents into your Savannah account public key list. Wait
+a while so that Savannah can update its configuration (This can take minutes).
+
+Try to login using SSH:
+
+ssh -v your_login@cvs.sv.gnu.org
+
+If it tells you:
+
+Authenticating with public key "your_key_name"...
+Server refused to allocate pty
+
+then you could login; Savannah refuses to give you a shell - which is OK, as we
+are allowed to use SSH for CVS only. Now, you should be able to do this:
+
+export CVS_RSH=ssh
+cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip
+
+after which you can edit your local files with bug fixes or new features and
+commit them. Make sure you know what you are doing when using CVS to make
+changes on the repository. If in doubt, ask on the lwip-members mailing list.
+
+(If SSH asks about authenticity of the host, you can check the key
+ fingerprint against http://savannah.nongnu.org/cvs/?group=lwip)
+
+
+3 Merging from DEVEL branch to main trunk (stable)
+--------------------------------------------------
+
+Merging is a delicate process in CVS and requires the
+following disciplined steps in order to prevent conflicts
+in the future. Conflicts can be hard to solve!
+
+Merging from branch A to branch B requires that the A branch
+has a tag indicating the previous merger. This tag is called
+'merged_from_A_to_B'. After merging, the tag is moved in the
+A branch to remember this merger for future merge actions.
+
+IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE
+REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE
+MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME).
+
+Merge all changes in DEVEL since our last merge to main:
+
+In the working copy of the main trunk:
+cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL
+
+(This will apply the changes between 'merged_from_DEVEL_to_main'
+and 'DEVEL' to your work set of files)
+
+We can now commit the merge result.
+cvs commit -R -m "Merged from DEVEL to main."
+
+If this worked out OK, we now move the tag in the DEVEL branch
+to this merge point, so we can use this point for future merges:
+
+cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip
+
+4 How to release lwIP
+---------------------
+
+First, checkout a clean copy of the branch to be released. Tag this set with
+tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example).
+
+Login CVS using pserver authentication, then export a clean copy of the
+tagged tree. Export is similar to a checkout, except that the CVS metadata
+is not created locally.
+
+export CVS_RSH=ssh
+cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
+ -r STABLE-0_6_3 -d lwip-0.6.3 lwip
+
+Archive this directory using tar, gzip'd, bzip2'd and zip'd.
+
+tar czvf lwip-0.6.3.tar.gz lwip-0.6.3
+tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3
+zip -r lwip-0.6.3.zip lwip-0.6.3
+
+Now, sign the archives with a detached GPG binary signature as follows:
+
+gpg -b lwip-0.6.3.tar.gz
+gpg -b lwip-0.6.3.tar.bz2
+gpg -b lwip-0.6.3.zip
+
+Upload these files using anonymous FTP:
+ncftp ftp://savannah.gnu.org/incoming/savannah/lwip
+
+ncftp>mput *0.6.3.*
+
+Additionally, you may post a news item on Savannah, like this:
+
+A new 0.6.3 release is now available here:
+http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3
+
+You will have to submit this via the user News interface, then approve
+this via the Administrator News interface. \ No newline at end of file
diff --git a/core/lwip/doc/snmp_agent.txt b/core/lwip/doc/snmp_agent.txt
new file mode 100644
index 00000000..2653230f
--- /dev/null
+++ b/core/lwip/doc/snmp_agent.txt
@@ -0,0 +1,181 @@
+SNMPv1 agent for lwIP
+
+Author: Christiaan Simons
+
+This is a brief introduction how to use and configure the SNMP agent.
+Note the agent uses the raw-API UDP interface so you may also want to
+read rawapi.txt to gain a better understanding of the SNMP message handling.
+
+0 Agent Capabilities
+====================
+
+SNMPv1 per RFC1157
+ This is an old(er) standard but is still widely supported.
+ For SNMPv2c and v3 have a greater complexity and need many
+ more lines of code. IMHO this breaks the idea of "lightweight IP".
+
+ Note the S in SNMP stands for "Simple". Note that "Simple" is
+ relative. SNMP is simple compared to the complex ISO network
+ management protocols CMIP (Common Management Information Protocol)
+ and CMOT (CMip Over Tcp).
+
+MIB II per RFC1213
+ The standard lwIP stack management information base.
+ This is a required MIB, so this is always enabled.
+ When builing lwIP without TCP, the mib-2.tcp group is omitted.
+ The groups EGP, CMOT and transmission are disabled by default.
+
+ Most mib-2 objects are not writable except:
+ sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
+ Writing to or changing the ARP and IP address and route
+ tables is not possible.
+
+ Note lwIP has a very limited notion of IP routing. It currently
+ doen't have a route table and doesn't have a notion of the U,G,H flags.
+ Instead lwIP uses the interface list with only one default interface
+ acting as a single gateway interface (G) for the default route.
+
+ The agent returns a "virtual table" with the default route 0.0.0.0
+ for the default interface and network routes (no H) for each
+ network interface in the netif_list.
+ All routes are considered to be up (U).
+
+Loading additional MIBs
+ MIBs can only be added in compile-time, not in run-time.
+ There is no MIB compiler thus additional MIBs must be hand coded.
+
+Large SNMP message support
+ The packet decoding and encoding routines are designed
+ to use pbuf-chains. Larger payloads than the minimum
+ SNMP requirement of 484 octets are supported if the
+ PBUF_POOL_SIZE and IP_REASS_BUFSIZE are set to match your
+ local requirement.
+
+1 Building the Agent
+====================
+
+First of all you'll need to add the following define
+to your local lwipopts.h:
+
+#define LWIP_SNMP 1
+
+and add the source files in lwip/src/core/snmp
+and some snmp headers in lwip/src/include/lwip to your makefile.
+
+Note you'll might need to adapt you network driver to update
+the mib2 variables for your interface.
+
+2 Running the Agent
+===================
+
+The following function calls must be made in your program to
+actually get the SNMP agent running.
+
+Before starting the agent you should supply pointers
+to non-volatile memory for sysContact, sysLocation,
+and snmpEnableAuthenTraps. You can do this by calling
+
+snmp_set_syscontact()
+snmp_set_syslocation()
+snmp_set_snmpenableauthentraps()
+
+Additionally you may want to set
+
+snmp_set_sysdescr()
+snmp_set_sysobjid() (if you have a private MIB)
+snmp_set_sysname()
+
+Also before starting the agent you need to setup
+one or more trap destinations using these calls:
+
+snmp_trap_dst_enable();
+snmp_trap_dst_ip_set();
+
+In the lwIP initialisation sequence call snmp_init() just after
+the call to udp_init().
+
+Exactly every 10 msec the SNMP uptime timestamp must be updated with
+snmp_inc_sysuptime(). You should call this from a timer interrupt
+or a timer signal handler depending on your runtime environment.
+
+An alternative way to update the SNMP uptime timestamp is to do a call like
+snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but call to
+a lower frequency). Another one is to not call snmp_inc_sysuptime() or
+snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro.
+This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside
+snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only
+when it's queried (any function which need "sysuptime" have to call
+snmp_get_sysuptime).
+
+
+3 Private MIBs
+==============
+
+If want to extend the agent with your own private MIB you'll need to
+add the following define to your local lwipopts.h:
+
+#define SNMP_PRIVATE_MIB 1
+
+You must provide the private_mib.h and associated files yourself.
+Note we don't have a "MIB compiler" that generates C source from a MIB,
+so you're required to do some serious coding if you enable this!
+
+Note the lwIP enterprise ID (26381) is assigned to the lwIP project,
+ALL OBJECT IDENTIFIERS LIVING UNDER THIS ID ARE ASSIGNED BY THE lwIP
+MAINTAINERS!
+
+If you need to create your own private MIB you'll need
+to apply for your own enterprise ID with IANA: http://www.iana.org/numbers.html
+
+You can set it by passing a struct snmp_obj_id to the agent
+using snmp_set_sysobjid(&my_object_id), just before snmp_init().
+
+Note the object identifiers for thes MIB-2 and your private MIB
+tree must be kept in sorted ascending (lexicographical) order.
+This to ensure correct getnext operation.
+
+An example for a private MIB is part of the "minimal Unix" project:
+contrib/ports/unix/proj/minimal/lwip_prvmib.c
+
+The next chapter gives a more detailed description of the
+MIB-2 tree and the optional private MIB.
+
+4 The Gory Details
+==================
+
+4.0 Object identifiers and the MIB tree.
+
+We have three distinct parts for all object identifiers:
+
+The prefix
+ .iso.org.dod.internet
+
+the middle part
+ .mgmt.mib-2.ip.ipNetToMediaTable.ipNetToMediaEntry.ipNetToMediaPhysAddress
+
+and the index part
+ .1.192.168.0.1
+
+Objects located above the .internet hierarchy aren't supported.
+Currently only the .mgmt sub-tree is available and
+when the SNMP_PRIVATE_MIB is enabled the .private tree
+becomes available too.
+
+Object identifiers from incoming requests are checked
+for a matching prefix, middle part and index part
+or are expanded(*) for GetNext requests with short
+or inexisting names in the request.
+(* we call this "expansion" but this also
+resembles the "auto-completion" operation)
+
+The middle part is usually located in ROM (const)
+to preserve precious RAM on small microcontrollers.
+However RAM location is possible for a dynamically
+changing private tree.
+
+The index part is handled by functions which in
+turn use dynamically allocated index trees from RAM.
+These trees are updated by e.g. the etharp code
+when new entries are made or removed form the ARP cache.
+
+/** @todo more gory details */
diff --git a/core/lwip/doc/sys_arch.txt b/core/lwip/doc/sys_arch.txt
new file mode 100644
index 00000000..4eb93078
--- /dev/null
+++ b/core/lwip/doc/sys_arch.txt
@@ -0,0 +1,216 @@
+sys_arch interface for lwIP 0.6++
+
+Author: Adam Dunkels
+
+The operating system emulation layer provides a common interface
+between the lwIP code and the underlying operating system kernel. The
+general idea is that porting lwIP to new architectures requires only
+small changes to a few header files and a new sys_arch
+implementation. It is also possible to do a sys_arch implementation
+that does not rely on any underlying operating system.
+
+The sys_arch provides semaphores and mailboxes to lwIP. For the full
+lwIP functionality, multiple threads support can be implemented in the
+sys_arch, but this is not required for the basic lwIP
+functionality. Previous versions of lwIP required the sys_arch to
+implement timer scheduling as well but as of lwIP 0.5 this is
+implemented in a higher layer.
+
+In addition to the source file providing the functionality of sys_arch,
+the OS emulation layer must provide several header files defining
+macros used throughout lwip. The files required and the macros they
+must define are listed below the sys_arch description.
+
+Semaphores can be either counting or binary - lwIP works with both
+kinds. Mailboxes are used for message passing and can be implemented
+either as a queue which allows multiple messages to be posted to a
+mailbox, or as a rendez-vous point where only one message can be
+posted at a time. lwIP works with both kinds, but the former type will
+be more efficient. A message in a mailbox is just a pointer, nothing
+more.
+
+Semaphores are represented by the type "sys_sem_t" which is typedef'd
+in the sys_arch.h file. Mailboxes are equivalently represented by the
+type "sys_mbox_t". lwIP does not place any restrictions on how
+sys_sem_t or sys_mbox_t are represented internally.
+
+The following functions must be implemented by the sys_arch:
+
+- void sys_init(void)
+
+ Is called to initialize the sys_arch layer.
+
+- sys_sem_t sys_sem_new(u8_t count)
+
+ Creates and returns a new semaphore. The "count" argument specifies
+ the initial state of the semaphore.
+
+- void sys_sem_free(sys_sem_t sem)
+
+ Deallocates a semaphore.
+
+- void sys_sem_signal(sys_sem_t sem)
+
+ Signals a semaphore.
+
+- u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)
+
+ Blocks the thread while waiting for the semaphore to be
+ signaled. If the "timeout" argument is non-zero, the thread should
+ only be blocked for the specified time (measured in
+ milliseconds). If the "timeout" argument is zero, the thread should be
+ blocked until the semaphore is signalled.
+
+ If the timeout argument is non-zero, the return value is the number of
+ milliseconds spent waiting for the semaphore to be signaled. If the
+ semaphore wasn't signaled within the specified time, the return value is
+ SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
+ (i.e., it was already signaled), the function may return zero.
+
+ Notice that lwIP implements a function with a similar name,
+ sys_sem_wait(), that uses the sys_arch_sem_wait() function.
+
+- sys_mbox_t sys_mbox_new(int size)
+
+ Creates an empty mailbox for maximum "size" elements. Elements stored
+ in mailboxes are pointers. You have to define macros "_MBOX_SIZE"
+ in your lwipopts.h, or ignore this parameter in your implementation
+ and use a default size.
+
+- void sys_mbox_free(sys_mbox_t mbox)
+
+ Deallocates a mailbox. If there are messages still present in the
+ mailbox when the mailbox is deallocated, it is an indication of a
+ programming error in lwIP and the developer should be notified.
+
+- void sys_mbox_post(sys_mbox_t mbox, void *msg)
+
+ Posts the "msg" to the mailbox. This function have to block until
+ the "msg" is really posted.
+
+- err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg)
+
+ Try to post the "msg" to the mailbox. Returns ERR_MEM if this one
+ is full, else, ERR_OK if the "msg" is posted.
+
+- u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout)
+
+ Blocks the thread until a message arrives in the mailbox, but does
+ not block the thread longer than "timeout" milliseconds (similar to
+ the sys_arch_sem_wait() function). If "timeout" is 0, the thread should
+ be blocked until a message arrives. The "msg" argument is a result
+ parameter that is set by the function (i.e., by doing "*msg =
+ ptr"). The "msg" parameter maybe NULL to indicate that the message
+ should be dropped.
+
+ The return values are the same as for the sys_arch_sem_wait() function:
+ Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
+ timeout.
+
+ Note that a function with a similar name, sys_mbox_fetch(), is
+ implemented by lwIP.
+
+- u32_t sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg)
+
+ This is similar to sys_arch_mbox_fetch, however if a message is not
+ present in the mailbox, it immediately returns with the code
+ SYS_MBOX_EMPTY. On success 0 is returned.
+
+ To allow for efficient implementations, this can be defined as a
+ function-like macro in sys_arch.h instead of a normal function. For
+ example, a naive implementation could be:
+ #define sys_arch_mbox_tryfetch(mbox,msg) \
+ sys_arch_mbox_fetch(mbox,msg,1)
+ although this would introduce unnecessary delays.
+
+If threads are supported by the underlying operating system and if
+such functionality is needed in lwIP, the following function will have
+to be implemented as well:
+
+- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio)
+
+ Starts a new thread named "name" with priority "prio" that will begin its
+ execution in the function "thread()". The "arg" argument will be passed as an
+ argument to the thread() function. The stack size to used for this thread is
+ the "stacksize" parameter. The id of the new thread is returned. Both the id
+ and the priority are system dependent.
+
+- sys_prot_t sys_arch_protect(void)
+
+ This optional function does a "fast" critical region protection and returns
+ the previous protection level. This function is only called during very short
+ critical regions. An embedded system which supports ISR-based drivers might
+ want to implement this function by disabling interrupts. Task-based systems
+ might want to implement this by using a mutex or disabling tasking. This
+ function should support recursive calls from the same task or interrupt. In
+ other words, sys_arch_protect() could be called while already protected. In
+ that case the return value indicates that it is already protected.
+
+ sys_arch_protect() is only required if your port is supporting an operating
+ system.
+
+- void sys_arch_unprotect(sys_prot_t pval)
+
+ This optional function does a "fast" set of critical region protection to the
+ value specified by pval. See the documentation for sys_arch_protect() for
+ more information. This function is only required if your port is supporting
+ an operating system.
+
+Note:
+
+Be carefull with using mem_malloc() in sys_arch. When malloc() refers to
+mem_malloc() you can run into a circular function call problem. In mem.c
+lwip_mem_init() tries to allcate a semaphore using mem_malloc, which of course
+can't be performed when sys_arch uses mem_malloc.
+
+-------------------------------------------------------------------------------
+Additional files required for the "OS support" emulation layer:
+-------------------------------------------------------------------------------
+
+cc.h - Architecture environment, some compiler specific, some
+ environment specific (probably should move env stuff
+ to sys_arch.h.)
+
+ Typedefs for the types used by lwip -
+ u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t
+
+ Compiler hints for packing lwip's structures -
+ PACK_STRUCT_FIELD(x)
+ PACK_STRUCT_STRUCT
+ PACK_STRUCT_BEGIN
+ PACK_STRUCT_END
+
+ Platform specific diagnostic output -
+ LWIP_PLATFORM_DIAG(x) - non-fatal, print a message.
+ LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution.
+ Portability defines for printf formatters:
+ U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F
+
+ "lightweight" synchronization mechanisms -
+ SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable.
+ SYS_ARCH_PROTECT(x) - enter protection mode.
+ SYS_ARCH_UNPROTECT(x) - leave protection mode.
+
+ If the compiler does not provide memset() this file must include a
+ definition of it, or include a file which defines it.
+
+ This file must either include a system-local <errno.h> which defines
+ the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO
+ to make lwip/arch.h define the codes which are used throughout.
+
+
+perf.h - Architecture specific performance measurement.
+ Measurement calls made throughout lwip, these can be defined to nothing.
+ PERF_START - start measuring something.
+ PERF_STOP(x) - stop measuring something, and record the result.
+
+sys_arch.h - Tied to sys_arch.c
+
+ Arch dependent types for the following objects:
+ sys_sem_t, sys_mbox_t, sys_thread_t,
+ And, optionally:
+ sys_prot_t
+
+ Defines to set vars of sys_mbox_t and sys_sem_t to NULL.
+ SYS_MBOX_NULL NULL
+ SYS_SEM_NULL NULL
diff --git a/core/lwip/src/FILES b/core/lwip/src/FILES
new file mode 100644
index 00000000..952aeabb
--- /dev/null
+++ b/core/lwip/src/FILES
@@ -0,0 +1,13 @@
+api/ - The code for the high-level wrapper API. Not needed if
+ you use the lowel-level call-back/raw API.
+
+core/ - The core of the TPC/IP stack; protocol implementations,
+ memory and buffer management, and the low-level raw API.
+
+include/ - lwIP include files.
+
+netif/ - Generic network interface device drivers are kept here,
+ as well as the ARP module.
+
+For more information on the various subdirectories, check the FILES
+file in each directory.
diff --git a/core/lwip/src/api/api_lib.c b/core/lwip/src/api/api_lib.c
new file mode 100644
index 00000000..b1a9e525
--- /dev/null
+++ b/core/lwip/src/api/api_lib.c
@@ -0,0 +1,740 @@
+/**
+ * @file
+ * Sequential API External module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <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 aborted */
+ NETCONN_SET_SAFE_ERR(conn, ERR_ABRT);
+ return ERR_ABRT;
+ }
+#if TCP_LISTEN_BACKLOG
+ /* Let the stack know that we have accepted the connection. */
+ msg.function = do_recv;
+ msg.msg.conn = conn;
+ /* don't care for the return value of do_recv */
+ TCPIP_APIMSG(&msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+ *new_conn = newconn;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(new_conn);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Receive data: actual implementation that doesn't care whether pbuf or netbuf
+ * is received
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf/netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+static err_t
+netconn_recv_data(struct netconn *conn, void **new_buf)
+{
+ void *buf = NULL;
+ u16_t len;
+ err_t err;
+#if LWIP_TCP
+ struct api_msg msg;
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+
+ err = conn->last_err;
+ if (ERR_IS_FATAL(err)) {
+ /* don't recv on fatal errors: this might block the application task
+ waiting on recvmbox forever! */
+ /* @todo: this does not allow us to fetch data that has been put into recvmbox
+ before the fatal error occurred - is that a problem? */
+ return err;
+ }
+
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+
+#if LWIP_TCP
+ if (conn->type == NETCONN_TCP) {
+ if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
+ /* Let the stack know that we have taken the data. */
+ /* TODO: Speedup: Don't block and wait for the answer here
+ (to prevent multiple thread-switches). */
+ msg.function = do_recv;
+ msg.msg.conn = conn;
+ if (buf != NULL) {
+ msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len;
+ } else {
+ msg.msg.msg.r.len = 1;
+ }
+ /* don't care for the return value of do_recv */
+ TCPIP_APIMSG(&msg);
+ }
+
+ /* If we are closed, we indicate that we no longer wish to use the socket */
+ if (buf == NULL) {
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+ /* Avoid to lose any previous error code */
+ NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
+ return ERR_CLSD;
+ }
+ len = ((struct pbuf *)buf)->tot_len;
+ }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+ else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+#if (LWIP_UDP || LWIP_RAW)
+ {
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ len = netbuf_len((struct netbuf *)buf);
+ }
+#endif /* (LWIP_UDP || LWIP_RAW) */
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_DEC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
+
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+}
+
+/**
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ * ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
+{
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
+ netconn_type(conn) == NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf);
+}
+
+/**
+ * Receive data (in form of a netbuf containing a packet buffer) from a netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+err_t
+netconn_recv(struct netconn *conn, struct netbuf **new_buf)
+{
+#if LWIP_TCP
+ struct netbuf *buf = NULL;
+ err_t err;
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+
+#if LWIP_TCP
+ if (conn->type == NETCONN_TCP) {
+ struct pbuf *p = NULL;
+ /* This is not a listening netconn, since recvmbox is set */
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_MEM);
+ return ERR_MEM;
+ }
+
+ err = netconn_recv_data(conn, (void **)&p);
+ if (err != ERR_OK) {
+ memp_free(MEMP_NETBUF, buf);
+ return err;
+ }
+ LWIP_ASSERT("p != NULL", p != NULL);
+
+ buf->p = p;
+ buf->ptr = p;
+ buf->port = 0;
+ ip_addr_set_any(&buf->addr);
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+ } else
+#endif /* LWIP_TCP */
+ {
+#if (LWIP_UDP || LWIP_RAW)
+ return netconn_recv_data(conn, (void **)new_buf);
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+}
+
+/**
+ * TCP: update the receive window: by calling this, the application
+ * tells the stack that it has processed data and is able to accept
+ * new data.
+ * ATTENTION: use with care, this is mainly used for sockets!
+ * Can only be used when calling netconn_set_noautorecved(conn, 1) before.
+ *
+ * @param conn the netconn for which to update the receive window
+ * @param length amount of data processed (ATTENTION: this must be accurate!)
+ */
+void
+netconn_recved(struct netconn *conn, u32_t length)
+{
+#if LWIP_TCP
+ if ((conn != NULL) && (conn->type == NETCONN_TCP) &&
+ (netconn_get_noautorecved(conn))) {
+ struct api_msg msg;
+ /* Let the stack know that we have taken the data. */
+ /* TODO: Speedup: Don't block and wait for the answer here
+ (to prevent multiple thread-switches). */
+ msg.function = do_recv;
+ msg.msg.conn = conn;
+ msg.msg.msg.r.len = length;
+ /* don't care for the return value of do_recv */
+ TCPIP_APIMSG(&msg);
+ }
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(length);
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Send data (in form of a netbuf) to a specific remote IP address and port.
+ * Only to be used for UDP and RAW netconns (not TCP).
+ *
+ * @param conn the netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @param addr the remote IP address to which to send the data
+ * @param port the remote port to which to send the data
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port)
+{
+ if (buf != NULL) {
+ ip_addr_set(&buf->addr, addr);
+ buf->port = port;
+ return netconn_send(conn, buf);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * Send data over a UDP or RAW netconn (that is already connected).
+ *
+ * @param conn the UDP or RAW netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_send(struct netconn *conn, struct netbuf *buf)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
+ msg.function = do_send;
+ msg.msg.conn = conn;
+ msg.msg.msg.b = buf;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Send data over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param dataptr pointer to the application buffer that contains the data to send
+ * @param size size of the application data to send
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apiflags)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;);
+ if (size == 0) {
+ return ERR_OK;
+ }
+
+ /* @todo: for non-blocking write, check if 'size' would ever fit into
+ snd_queue or snd_buf */
+ msg.function = do_write;
+ msg.msg.conn = conn;
+ msg.msg.msg.w.dataptr = dataptr;
+ msg.msg.msg.w.apiflags = apiflags;
+ msg.msg.msg.w.len = size;
+ /* For locking the core: this _can_ be delayed on low memory/low send buffer,
+ but if it is, this is done inside api_msg.c:do_write(), so we can use the
+ non-blocking version here. */
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Close ot shutdown a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close or shutdown
+ * @param how fully close or only shutdown one side?
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+static err_t
+netconn_close_shutdown(struct netconn *conn, u8_t how)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_close;
+ msg.msg.conn = conn;
+ /* shutting down both ends is the same as closing */
+ msg.msg.msg.sd.shut = how;
+ /* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close,
+ don't use TCPIP_APIMSG here */
+ err = tcpip_apimsg(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Close a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_close(struct netconn *conn)
+{
+ /* shutting down both ends is the same as closing */
+ return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
+}
+
+/**
+ * Shut down one or both sides of a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to shut down
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
+{
+ return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
+}
+
+#if LWIP_IGMP
+/**
+ * Join multicast groups for UDP netconns.
+ *
+ * @param conn the UDP netconn for which to change multicast addresses
+ * @param multiaddr IP address of the multicast group to join or leave
+ * @param netif_addr the IP address of the network interface on which to send
+ * the igmp message
+ * @param join_or_leave flag whether to send a join- or leave-message
+ * @return ERR_OK if the action was taken, any err_t on error
+ */
+err_t
+netconn_join_leave_group(struct netconn *conn,
+ ip_addr_t *multiaddr,
+ ip_addr_t *netif_addr,
+ enum netconn_igmp join_or_leave)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = do_join_leave_group;
+ msg.msg.conn = conn;
+ msg.msg.msg.jl.multiaddr = multiaddr;
+ msg.msg.msg.jl.netif_addr = netif_addr;
+ msg.msg.msg.jl.join_or_leave = join_or_leave;
+ err = TCPIP_APIMSG(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+/**
+ * Execute a DNS query, only one IP address is returned
+ *
+ * @param name a string representation of the DNS host name to query
+ * @param addr a preallocated ip_addr_t where to store the resolved IP address
+ * @return ERR_OK: resolving succeeded
+ * ERR_MEM: memory error, try again later
+ * ERR_ARG: dns client not initialized or invalid hostname
+ * ERR_VAL: dns server response was invalid
+ */
+err_t
+netconn_gethostbyname(const char *name, ip_addr_t *addr)
+{
+ struct dns_api_msg msg;
+ err_t err;
+ sys_sem_t sem;
+
+ LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
+
+ err = sys_sem_new(&sem, 0);
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ msg.name = name;
+ msg.addr = addr;
+ msg.err = &err;
+ msg.sem = &sem;
+
+ tcpip_callback(do_gethostbyname, &msg);
+ sys_sem_wait(&sem);
+ sys_sem_free(&sem);
+
+ return err;
+}
+#endif /* LWIP_DNS*/
+
+#endif /* LWIP_NETCONN */
diff --git a/core/lwip/src/api/api_msg.c b/core/lwip/src/api/api_msg.c
new file mode 100644
index 00000000..448f96dd
--- /dev/null
+++ b/core/lwip/src/api/api_msg.c
@@ -0,0 +1,1535 @@
+/**
+ * @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) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+ }
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ if (was_blocking) {
+ sys_sem_signal(&conn->op_completed);
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Called from netconn_connect.
+ *
+ * @param msg the api_msg_msg pointing to the connection and containing
+ * the IP address and port to connect to
+ */
+void
+do_connect(struct api_msg_msg *msg)
+{
+ if (msg->conn->pcb.tcp == NULL) {
+ /* This may happen when calling netconn_connect() a second time */
+ msg->err = ERR_CLSD;
+ } else {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ /* Prevent connect while doing any other action. */
+ if (msg->conn->state != NETCONN_NONE) {
+ msg->err = ERR_ISCONN;
+ } else {
+ setup_tcp(msg->conn);
+ msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr,
+ msg->msg.bc.port, do_connected);
+ if (msg->err == ERR_OK) {
+ u8_t non_blocking = netconn_is_nonblocking(msg->conn);
+ msg->conn->state = NETCONN_CONNECT;
+ SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
+ if (non_blocking) {
+ msg->err = ERR_INPROGRESS;
+ } else {
+ msg->conn->current_msg = msg;
+ /* sys_sem_signal() is called from do_connected (or err_tcp()),
+ * when the connection is established! */
+ return;
+ }
+ }
+ }
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
+ break;
+ }
+ }
+ sys_sem_signal(&msg->conn->op_completed);
+}
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Only used for UDP netconns.
+ * Called from netconn_disconnect.
+ *
+ * @param msg the api_msg_msg pointing to the connection to disconnect
+ */
+void
+do_disconnect(struct api_msg_msg *msg)
+{
+#if LWIP_UDP
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+ udp_disconnect(msg->conn->pcb.udp);
+ msg->err = ERR_OK;
+ } else
+#endif /* LWIP_UDP */
+ {
+ msg->err = ERR_VAL;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Set a TCP pcb contained in a netconn into listen mode
+ * Called from netconn_listen.
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_listen(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (msg->conn->type == NETCONN_TCP) {
+ if (msg->conn->state == NETCONN_NONE) {
+#if TCP_LISTEN_BACKLOG
+ struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+#else /* TCP_LISTEN_BACKLOG */
+ struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp);
+#endif /* TCP_LISTEN_BACKLOG */
+ if (lpcb == NULL) {
+ /* in this case, the old pcb is still allocated */
+ msg->err = ERR_MEM;
+ } else {
+ /* delete the recvmbox and allocate the acceptmbox */
+ if (sys_mbox_valid(&msg->conn->recvmbox)) {
+ /** @todo: should we drain the recvmbox here? */
+ sys_mbox_free(&msg->conn->recvmbox);
+ sys_mbox_set_invalid(&msg->conn->recvmbox);
+ }
+ msg->err = ERR_OK;
+ if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+ msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+ }
+ if (msg->err == ERR_OK) {
+ msg->conn->state = NETCONN_LISTEN;
+ msg->conn->pcb.tcp = lpcb;
+ tcp_arg(msg->conn->pcb.tcp, msg->conn);
+ tcp_accept(msg->conn->pcb.tcp, accept_function);
+ } else {
+ /* since the old pcb is already deallocated, free lpcb now */
+ tcp_close(lpcb);
+ msg->conn->pcb.tcp = NULL;
+ }
+ }
+ }
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a RAW or UDP pcb contained in a netconn
+ * Called from netconn_send
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_send(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (ip_addr_isany(&msg->msg.b->addr)) {
+ msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+ } else {
+ msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+ }
+ break;
+#endif
+#if LWIP_UDP
+ case NETCONN_UDP:
+#if LWIP_CHECKSUM_ON_COPY
+ if (ip_addr_isany(&msg->msg.b->addr)) {
+ msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ } else {
+ msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ &msg->msg.b->addr, msg->msg.b->port,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ }
+#else /* LWIP_CHECKSUM_ON_COPY */
+ if (ip_addr_isany(&msg->msg.b->addr)) {
+ msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+ } else {
+ msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ break;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Indicate data has been received from a TCP pcb contained in a netconn
+ * Called from netconn_recv
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_recv(struct api_msg_msg *msg)
+{
+ msg->err = ERR_OK;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (msg->conn->type == NETCONN_TCP) {
+#if TCP_LISTEN_BACKLOG
+ if (msg->conn->pcb.tcp->state == LISTEN) {
+ tcp_accepted(msg->conn->pcb.tcp);
+ } else
+#endif /* TCP_LISTEN_BACKLOG */
+ {
+ u32_t remaining = msg->msg.r.len;
+ do {
+ u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
+ tcp_recved(msg->conn->pcb.tcp, recved);
+ remaining -= recved;
+ }while(remaining != 0);
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * See if more data needs to be written from a previous call to netconn_write.
+ * Called initially from do_write. If the first call can't send all data
+ * (because of low memory or empty send-buffer), this function is called again
+ * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
+ * blocking application thread (waiting in netconn_write) is released.
+ *
+ * @param conn netconn (that is currently in state NETCONN_WRITE) to process
+ * @return ERR_OK
+ * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
+ */
+static err_t
+do_writemore(struct netconn *conn)
+{
+ err_t err = ERR_OK;
+ void *dataptr;
+ u16_t len, available;
+ u8_t write_finished = 0;
+ size_t diff;
+ u8_t dontblock = netconn_is_nonblocking(conn) ||
+ (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK);
+ u8_t apiflags = conn->current_msg->msg.w.apiflags;
+
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+ LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
+ conn->write_offset < conn->current_msg->msg.w.len);
+
+ dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
+ diff = conn->current_msg->msg.w.len - conn->write_offset;
+ if (diff > 0xffffUL) { /* max_u16_t */
+ len = 0xffff;
+#if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+#endif
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ } else {
+ len = (u16_t)diff;
+ }
+ available = tcp_sndbuf(conn->pcb.tcp);
+ if (available < len) {
+ /* don't try to write more than sendbuf */
+ len = available;
+#if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+#endif
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ if (dontblock && (len < conn->current_msg->msg.w.len)) {
+ /* failed to send all data at once -> nonblocking write not possible */
+ err = ERR_MEM;
+ }
+ if (err == ERR_OK) {
+ LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
+ err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+ }
+ if (dontblock && (err == ERR_MEM)) {
+ /* nonblocking write failed */
+ write_finished = 1;
+ err = ERR_WOULDBLOCK;
+ /* let poll_tcp check writable space to mark the pcb
+ writable again */
+ conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+ /* let select mark this pcb as non-writable. */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ } else {
+ /* if OK or memory error, check available space */
+ if (((err == ERR_OK) || (err == ERR_MEM)) &&
+ ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+ (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT))) {
+ /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+ let select mark this pcb as non-writable. */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ }
+
+ if (err == ERR_OK) {
+ conn->write_offset += len;
+ if (conn->write_offset == conn->current_msg->msg.w.len) {
+ /* everything was written */
+ write_finished = 1;
+ conn->write_offset = 0;
+ }
+ tcp_output(conn->pcb.tcp);
+ } else if (err == ERR_MEM) {
+ /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
+ we do NOT return to the application thread, since ERR_MEM is
+ only a temporary error! */
+
+ /* tcp_write returned ERR_MEM, try tcp_output anyway */
+ tcp_output(conn->pcb.tcp);
+
+ #if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+ #endif
+ } else {
+ /* On errors != ERR_MEM, we don't try writing any more but return
+ the error to the application thread. */
+ write_finished = 1;
+ }
+ }
+
+ if (write_finished) {
+ /* everything was written: set back connection state
+ and back to application task */
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+#if LWIP_TCPIP_CORE_LOCKING
+ if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0)
+#endif
+ {
+ sys_sem_signal(&conn->op_completed);
+ }
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ else
+ return ERR_MEM;
+#endif
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a TCP pcb contained in a netconn
+ * Called from netconn_write
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_write(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ if (msg->conn->type == NETCONN_TCP) {
+#if LWIP_TCP
+ if (msg->conn->state != NETCONN_NONE) {
+ /* netconn is connecting, closing or in blocking write */
+ msg->err = ERR_INPROGRESS;
+ } else if (msg->conn->pcb.tcp != NULL) {
+ msg->conn->state = NETCONN_WRITE;
+ /* set all the variables used by do_writemore */
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
+ msg->conn->current_msg = msg;
+ msg->conn->write_offset = 0;
+#if LWIP_TCPIP_CORE_LOCKING
+ msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED;
+ if (do_writemore(msg->conn) != ERR_OK) {
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(&msg->conn->op_completed, 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ do_writemore(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ /* for both cases: if do_writemore was called, don't ACK the APIMSG
+ since do_writemore ACKs it! */
+ return;
+ } else {
+ msg->err = ERR_CONN;
+ }
+#else /* LWIP_TCP */
+ msg->err = ERR_VAL;
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Return a connection's local or remote address
+ * Called from netconn_getaddr
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_getaddr(struct api_msg_msg *msg)
+{
+ if (msg->conn->pcb.ip != NULL) {
+ *(msg->msg.ad.ipaddr) = (msg->msg.ad.local ? msg->conn->pcb.ip->local_ip :
+ msg->conn->pcb.ip->remote_ip);
+
+ msg->err = ERR_OK;
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (msg->msg.ad.local) {
+ *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
+ } else {
+ /* return an error as connecting is only a helper for upper layers */
+ msg->err = ERR_CONN;
+ }
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ if (msg->msg.ad.local) {
+ *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
+ } else {
+ if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
+ msg->err = ERR_CONN;
+ } else {
+ *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+ }
+ }
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("invalid netconn_type", 0);
+ break;
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Close a TCP pcb contained in a netconn
+ * Called from netconn_close
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_close(struct api_msg_msg *msg)
+{
+#if LWIP_TCP
+ /* @todo: abort running write/connect? */
+ if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) {
+ /* this only happens for TCP netconns */
+ LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP);
+ msg->err = ERR_INPROGRESS;
+ } else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
+ if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) {
+ /* LISTEN doesn't support half shutdown */
+ msg->err = ERR_CONN;
+ } else {
+ if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
+ /* Drain and delete mboxes */
+ netconn_drain(msg->conn);
+ }
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->conn->current_msg = msg;
+ do_close_internal(msg->conn);
+ /* for tcp netconns, do_close_internal ACKs the message */
+ return;
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ msg->err = ERR_VAL;
+ }
+ sys_sem_signal(&msg->conn->op_completed);
+}
+
+#if LWIP_IGMP
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+do_join_leave_group(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = igmp_joingroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr);
+ } else {
+ msg->err = igmp_leavegroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr);
+ }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+/**
+ * Callback function that is called when DNS name is resolved
+ * (or on timeout). A waiting application thread is waked up by
+ * signaling the semaphore.
+ */
+static void
+do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+ LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0);
+ LWIP_UNUSED_ARG(name);
+
+ if (ipaddr == NULL) {
+ /* timeout or memory error */
+ *msg->err = ERR_VAL;
+ } else {
+ /* address was resolved */
+ *msg->err = ERR_OK;
+ *msg->addr = *ipaddr;
+ }
+ /* wake up the application task waiting in netconn_gethostbyname */
+ sys_sem_signal(msg->sem);
+}
+
+/**
+ * Execute a DNS query
+ * Called from netconn_gethostbyname
+ *
+ * @param arg the dns_api_msg pointing to the query
+ */
+void
+do_gethostbyname(void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+ *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg);
+ if (*msg->err != ERR_INPROGRESS) {
+ /* on error or immediate success, wake up the application
+ * task waiting in netconn_gethostbyname */
+ sys_sem_signal(msg->sem);
+ }
+}
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_NETCONN */
diff --git a/core/lwip/src/api/err.c b/core/lwip/src/api/err.c
new file mode 100644
index 00000000..92fa8b7d
--- /dev/null
+++ b/core/lwip/src/api/err.c
@@ -0,0 +1,75 @@
+/**
+ * @file
+ * Error Management module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <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 */
+ "Address in use.", /* ERR_USE -8 */
+ "Already connected.", /* ERR_ISCONN -9 */
+ "Connection aborted.", /* ERR_ABRT -10 */
+ "Connection reset.", /* ERR_RST -11 */
+ "Connection closed.", /* ERR_CLSD -12 */
+ "Not connected.", /* ERR_CONN -13 */
+ "Illegal argument.", /* ERR_ARG -14 */
+ "Low-level netif error.", /* ERR_IF -15 */
+};
+
+/**
+ * Convert an lwip internal error to a string representation.
+ *
+ * @param err an lwip internal err_t
+ * @return a string representation for err
+ */
+const char *
+lwip_strerr(err_t err)
+{
+ return err_strerr[-err];
+
+}
+
+#endif /* LWIP_DEBUG */
diff --git a/core/lwip/src/api/netbuf.c b/core/lwip/src/api/netbuf.c
new file mode 100644
index 00000000..9390c9ee
--- /dev/null
+++ b/core/lwip/src/api/netbuf.c
@@ -0,0 +1,245 @@
+/**
+ * @file
+ * Network buffer management
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <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!
+ *
+ * @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().
+ *
+ * @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.
+ *
+ * @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
+ *
+ * @param buf pointer to the netbuf which contains the packet buffer to free
+ */
+void
+netbuf_free(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = buf->ptr = NULL;
+}
+
+/**
+ * Let a netbuf reference existing (non-volatile) data.
+ *
+ * @param buf netbuf which should reference the data
+ * @param dataptr pointer to the data to reference
+ * @param size size of the data
+ * @return ERR_OK if data is referenced
+ * ERR_MEM if data couldn't be referenced due to lack of memory
+ */
+err_t
+netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
+{
+ LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+ if (buf->p == NULL) {
+ buf->ptr = NULL;
+ return ERR_MEM;
+ }
+ buf->p->payload = (void*)dataptr;
+ buf->p->len = buf->p->tot_len = size;
+ buf->ptr = buf->p;
+ return ERR_OK;
+}
+
+/**
+ * Chain one netbuf to another (@see pbuf_chain)
+ *
+ * @param head the first netbuf
+ * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
+ */
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+ LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;);
+ LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
+ pbuf_cat(head->p, tail->p);
+ head->ptr = head->p;
+ memp_free(MEMP_NETBUF, tail);
+}
+
+/**
+ * Get the data pointer and length of the data inside a netbuf.
+ *
+ * @param buf netbuf to get the data from
+ * @param dataptr pointer to a void pointer where to store the data pointer
+ * @param len pointer to an u16_t where the length of the data is stored
+ * @return ERR_OK if the information was retreived,
+ * ERR_BUF on error.
+ */
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+ LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
+
+ if (buf->ptr == NULL) {
+ return ERR_BUF;
+ }
+ *dataptr = buf->ptr->payload;
+ *len = buf->ptr->len;
+ return ERR_OK;
+}
+
+/**
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the next part.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ * @return -1 if there is no next part
+ * 1 if moved to the next part but now there is no next part
+ * 0 if moved to the next part and there are still more parts
+ */
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;);
+ if (buf->ptr->next == NULL) {
+ return -1;
+ }
+ buf->ptr = buf->ptr->next;
+ if (buf->ptr->next == NULL) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the beginning of the packet.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ */
+void
+netbuf_first(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ buf->ptr = buf->p;
+}
+
+#endif /* LWIP_NETCONN */
diff --git a/core/lwip/src/api/netdb.c b/core/lwip/src/api/netdb.c
new file mode 100644
index 00000000..a7e4e06b
--- /dev/null
+++ b/core/lwip/src/api/netdb.c
@@ -0,0 +1,352 @@
+/**
+ * @file
+ * API functions for name resolving
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/netdb.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/err.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
+
+#include <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 */
diff --git a/core/lwip/src/api/netifapi.c b/core/lwip/src/api/netifapi.c
new file mode 100644
index 00000000..43e47203
--- /dev/null
+++ b/core/lwip/src/api/netifapi.c
@@ -0,0 +1,160 @@
+/**
+ * @file
+ * Network Interface Sequential API module
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netifapi.h"
+#include "lwip/tcpip.h"
+
+/**
+ * Call netif_add() inside the tcpip_thread context.
+ */
+void
+do_netifapi_netif_add(struct netifapi_msg_msg *msg)
+{
+ if (!netif_add( msg->netif,
+ msg->msg.add.ipaddr,
+ msg->msg.add.netmask,
+ msg->msg.add.gw,
+ msg->msg.add.state,
+ msg->msg.add.init,
+ msg->msg.add.input)) {
+ msg->err = ERR_IF;
+ } else {
+ msg->err = ERR_OK;
+ }
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call netif_set_addr() inside the tcpip_thread context.
+ */
+void
+do_netifapi_netif_set_addr(struct netifapi_msg_msg *msg)
+{
+ netif_set_addr( msg->netif,
+ msg->msg.add.ipaddr,
+ msg->msg.add.netmask,
+ msg->msg.add.gw);
+ msg->err = ERR_OK;
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
+ * tcpip_thread context.
+ */
+void
+do_netifapi_netif_common(struct netifapi_msg_msg *msg)
+{
+ if (msg->msg.common.errtfunc != NULL) {
+ msg->err = msg->msg.common.errtfunc(msg->netif);
+ } else {
+ msg->err = ERR_OK;
+ msg->msg.common.voidfunc(msg->netif);
+ }
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call netif_add() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_add()
+ */
+err_t
+netifapi_netif_add(struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw,
+ void *state,
+ netif_init_fn init,
+ netif_input_fn input)
+{
+ struct netifapi_msg msg;
+ msg.function = do_netifapi_netif_add;
+ msg.msg.netif = netif;
+ msg.msg.msg.add.ipaddr = ipaddr;
+ msg.msg.msg.add.netmask = netmask;
+ msg.msg.msg.add.gw = gw;
+ msg.msg.msg.add.state = state;
+ msg.msg.msg.add.init = init;
+ msg.msg.msg.add.input = input;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+/**
+ * Call netif_set_addr() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_set_addr()
+ */
+err_t
+netifapi_netif_set_addr(struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw)
+{
+ struct netifapi_msg msg;
+ msg.function = do_netifapi_netif_set_addr;
+ msg.msg.netif = netif;
+ msg.msg.msg.add.ipaddr = ipaddr;
+ msg.msg.msg.add.netmask = netmask;
+ msg.msg.msg.add.gw = gw;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+/**
+ * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
+ * way by running that function inside the tcpip_thread context.
+ *
+ * @note use only for functions where there is only "netif" parameter.
+ */
+err_t
+netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+ netifapi_errt_fn errtfunc)
+{
+ struct netifapi_msg msg;
+ msg.function = do_netifapi_netif_common;
+ msg.msg.netif = netif;
+ msg.msg.msg.common.voidfunc = voidfunc;
+ msg.msg.msg.common.errtfunc = errtfunc;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+#endif /* LWIP_NETIF_API */
diff --git a/core/lwip/src/api/sockets.c b/core/lwip/src/api/sockets.c
new file mode 100644
index 00000000..e36012ce
--- /dev/null
+++ b/core/lwip/src/api/sockets.c
@@ -0,0 +1,2347 @@
+/**
+ * @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. */
+ EADDRINUSE, /* ERR_USE -8 Address in use. */
+ EALREADY, /* ERR_ISCONN -9 Already connected. */
+ ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */
+ ECONNRESET, /* ERR_RST -11 Connection reset. */
+ ENOTCONN, /* ERR_CLSD -12 Connection closed. */
+ ENOTCONN, /* ERR_CONN -13 Not connected. */
+ EIO, /* ERR_ARG -14 Illegal argument. */
+ -1, /* ERR_IF -15 Low-level netif error */
+};
+
+#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 = &sock->conn->pcb.raw->remote_ip;
+ if (sock->conn->type == NETCONN_RAW) {
+ remote_port = 0;
+ } else {
+ remote_port = sock->conn->pcb.udp->remote_port;
+ }
+ }
+
+ 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=%"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);