initial commit of lk (little kernel) project
diff --git a/lib/lwip/src/api/api_lib.c b/lib/lwip/src/api/api_lib.c
new file mode 100644
index 0000000..3d83d1e
--- /dev/null
+++ b/lib/lwip/src/api/api_lib.c
@@ -0,0 +1,729 @@
+/*
+ * 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"
+#include "lwip/api.h"
+#include "lwip/api_msg.h"
+#include "lwip/memp.h"
+
+
+struct
+netbuf *netbuf_new(void)
+{
+  struct netbuf *buf;
+
+  buf = memp_malloc(MEMP_NETBUF);
+  if (buf != NULL) {
+    buf->p = NULL;
+    buf->ptr = NULL;
+    return buf;
+  } else {
+    return NULL;
+  }
+}
+
+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);
+  }
+}
+
+void *
+netbuf_alloc(struct netbuf *buf, u16_t size)
+{
+  /* 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;
+  }
+  buf->ptr = buf->p;
+  return buf->p->payload;
+}
+
+void
+netbuf_free(struct netbuf *buf)
+{
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = buf->ptr = NULL;
+}
+
+void
+netbuf_ref(struct netbuf *buf, void *dataptr, u16_t size)
+{
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+  buf->p->payload = dataptr;
+  buf->p->len = buf->p->tot_len = size;
+  buf->ptr = buf->p;
+}
+
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+  pbuf_chain(head->p, tail->p);
+  head->ptr = head->p;
+  memp_free(MEMP_NETBUF, tail);
+}
+
+u16_t
+netbuf_len(struct netbuf *buf)
+{
+  return buf->p->tot_len;
+}
+
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+  if (buf->ptr == NULL) {
+    return ERR_BUF;
+  }
+  *dataptr = buf->ptr->payload;
+  *len = buf->ptr->len;
+  return ERR_OK;
+}
+
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+  if (buf->ptr->next == NULL) {
+    return -1;
+  }
+  buf->ptr = buf->ptr->next;
+  if (buf->ptr->next == NULL) {
+    return 1;
+  }
+  return 0;
+}
+
+void
+netbuf_first(struct netbuf *buf)
+{
+  buf->ptr = buf->p;
+}
+
+void
+netbuf_copy_partial(struct netbuf *buf, void *dataptr, u16_t len, u16_t offset)
+{
+  struct pbuf *p;
+  u16_t i, left;
+
+  left = 0;
+
+  if(buf == NULL || dataptr == NULL) {
+    return;
+  }
+  
+  /* This implementation is bad. It should use bcopy
+     instead. */
+  for(p = buf->p; left < len && p != NULL; p = p->next) {
+    if (offset != 0 && offset >= p->len) {
+      offset -= p->len;
+    } else {    
+      for(i = offset; i < p->len; ++i) {
+  ((u8_t *)dataptr)[left] = ((u8_t *)p->payload)[i];
+  if (++left >= len) {
+    return;
+  }
+      }
+      offset = 0;
+    }
+  }
+}
+
+void
+netbuf_copy(struct netbuf *buf, void *dataptr, u16_t len)
+{
+  netbuf_copy_partial(buf, dataptr, len, 0);
+}
+
+struct ip_addr *
+netbuf_fromaddr(struct netbuf *buf)
+{
+  return buf->fromaddr;
+}
+
+u16_t
+netbuf_fromport(struct netbuf *buf)
+{
+  return buf->fromport;
+}
+
+struct
+netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u16_t proto,
+                                   void (*callback)(struct netconn *, enum netconn_evt, u16_t len))
+{
+  struct netconn *conn;
+  struct api_msg *msg;
+
+  conn = memp_malloc(MEMP_NETCONN);
+  if (conn == NULL) {
+    return NULL;
+  }
+  
+  conn->err = ERR_OK;
+  conn->type = t;
+  conn->pcb.tcp = NULL;
+
+  if ((conn->mbox = sys_mbox_new()) == SYS_MBOX_NULL) {
+    memp_free(MEMP_NETCONN, conn);
+    return NULL;
+  }
+  conn->recvmbox = SYS_MBOX_NULL;
+  conn->acceptmbox = SYS_MBOX_NULL;
+  conn->sem = SYS_SEM_NULL;
+  conn->state = NETCONN_NONE;
+  conn->socket = 0;
+  conn->callback = callback;
+  conn->recv_avail = 0;
+
+  if((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
+    memp_free(MEMP_NETCONN, conn);
+    return NULL;
+  }
+  
+  msg->type = API_MSG_NEWCONN;
+  msg->msg.msg.bc.port = proto; /* misusing the port field */
+  msg->msg.conn = conn;
+  api_msg_post(msg);  
+  sys_mbox_fetch(conn->mbox, NULL);
+  memp_free(MEMP_API_MSG, msg);
+
+  if ( conn->err != ERR_OK ) {
+    memp_free(MEMP_NETCONN, conn);
+    return NULL;
+  }
+
+  return conn;
+}
+
+
+struct
+netconn *netconn_new(enum netconn_type t)
+{
+  return netconn_new_with_proto_and_callback(t,0,NULL);
+}
+
+struct
+netconn *netconn_new_with_callback(enum netconn_type t,
+                                   void (*callback)(struct netconn *, enum netconn_evt, u16_t len))
+{
+  return netconn_new_with_proto_and_callback(t,0,callback);
+}
+
+
+err_t
+netconn_delete(struct netconn *conn)
+{
+  struct api_msg *msg;
+  void *mem;
+  
+  if (conn == NULL) {
+    return ERR_OK;
+  }
+  
+  if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
+    return ERR_MEM;
+  }
+  
+  msg->type = API_MSG_DELCONN;
+  msg->msg.conn = conn;
+  api_msg_post(msg);  
+  sys_mbox_fetch(conn->mbox, NULL);
+  memp_free(MEMP_API_MSG, msg);
+
+  /* Drain the recvmbox. */
+  if (conn->recvmbox != SYS_MBOX_NULL) {
+    while (sys_arch_mbox_fetch(conn->recvmbox, &mem, 1) != SYS_ARCH_TIMEOUT) {
+      if (conn->type == NETCONN_TCP) {
+        if(mem != NULL)
+          pbuf_free((struct pbuf *)mem);
+      } else {
+        netbuf_delete((struct netbuf *)mem);
+      }
+    }
+    sys_mbox_free(conn->recvmbox);
+    conn->recvmbox = SYS_MBOX_NULL;
+  }
+ 
+
+  /* Drain the acceptmbox. */
+  if (conn->acceptmbox != SYS_MBOX_NULL) {
+    while (sys_arch_mbox_fetch(conn->acceptmbox, &mem, 1) != SYS_ARCH_TIMEOUT) {
+      netconn_delete((struct netconn *)mem);
+    }
+    
+    sys_mbox_free(conn->acceptmbox);
+    conn->acceptmbox = SYS_MBOX_NULL;
+  }
+
+  sys_mbox_free(conn->mbox);
+  conn->mbox = SYS_MBOX_NULL;
+  if (conn->sem != SYS_SEM_NULL) {
+    sys_sem_free(conn->sem);
+  }
+  /*  conn->sem = SYS_SEM_NULL;*/
+  memp_free(MEMP_NETCONN, conn);
+  return ERR_OK;
+}
+
+enum netconn_type
+netconn_type(struct netconn *conn)
+{
+  return conn->type;
+}
+
+err_t
+netconn_peer(struct netconn *conn, struct ip_addr *addr,
+       u16_t *port)
+{
+  switch (conn->type) {
+  case NETCONN_RAW:
+    /* return an error as connecting is only a helper for upper layers */
+    return ERR_CONN;
+  case NETCONN_UDPLITE:
+  case NETCONN_UDPNOCHKSUM:
+  case NETCONN_UDP:
+    if (conn->pcb.udp == NULL ||
+  ((conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0))
+     return ERR_CONN;
+    *addr = (conn->pcb.udp->remote_ip);
+    *port = conn->pcb.udp->remote_port;
+    break;
+  case NETCONN_TCP:
+    if (conn->pcb.tcp == NULL)
+      return ERR_CONN;
+    *addr = (conn->pcb.tcp->remote_ip);
+    *port = conn->pcb.tcp->remote_port;
+    break;
+  }
+  return (conn->err = ERR_OK);
+}
+
+err_t
+netconn_addr(struct netconn *conn, struct ip_addr **addr,
+       u16_t *port)
+{
+  switch (conn->type) {
+  case NETCONN_RAW:
+    *addr = &(conn->pcb.raw->local_ip);
+    *port = conn->pcb.raw->protocol;
+    break;
+  case NETCONN_UDPLITE:
+  case NETCONN_UDPNOCHKSUM:
+  case NETCONN_UDP:
+    *addr = &(conn->pcb.udp->local_ip);
+    *port = conn->pcb.udp->local_port;
+    break;
+  case NETCONN_TCP:
+    *addr = &(conn->pcb.tcp->local_ip);
+    *port = conn->pcb.tcp->local_port;
+    break;
+  }
+  return (conn->err = ERR_OK);
+}
+
+err_t
+netconn_bind(struct netconn *conn, struct ip_addr *addr,
+      u16_t port)
+{
+  struct api_msg *msg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  if (conn->type != NETCONN_TCP &&
+     conn->recvmbox == SYS_MBOX_NULL) {
+    if ((conn->recvmbox = sys_mbox_new()) == SYS_MBOX_NULL) {
+      return ERR_MEM;
+    }
+  }
+  
+  if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
+    return (conn->err = ERR_MEM);
+  }
+  msg->type = API_MSG_BIND;
+  msg->msg.conn = conn;
+  msg->msg.msg.bc.ipaddr = addr;
+  msg->msg.msg.bc.port = port;
+  api_msg_post(msg);
+  sys_mbox_fetch(conn->mbox, NULL);
+  memp_free(MEMP_API_MSG, msg);
+  return conn->err;
+}
+
+
+err_t
+netconn_connect(struct netconn *conn, struct ip_addr *addr,
+       u16_t port)
+{
+  struct api_msg *msg;
+  
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+
+  if (conn->recvmbox == SYS_MBOX_NULL) {
+    if ((conn->recvmbox = sys_mbox_new()) == SYS_MBOX_NULL) {
+      return ERR_MEM;
+    }
+  }
+  
+  if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
+    return ERR_MEM;
+  }
+  msg->type = API_MSG_CONNECT;
+  msg->msg.conn = conn;  
+  msg->msg.msg.bc.ipaddr = addr;
+  msg->msg.msg.bc.port = port;
+  api_msg_post(msg);
+  sys_mbox_fetch(conn->mbox, NULL);
+  memp_free(MEMP_API_MSG, msg);
+  return conn->err;
+}
+
+err_t
+netconn_disconnect(struct netconn *conn)
+{
+  struct api_msg *msg;
+  
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
+    return ERR_MEM;
+  }
+  msg->type = API_MSG_DISCONNECT;
+  msg->msg.conn = conn;  
+  api_msg_post(msg);
+  sys_mbox_fetch(conn->mbox, NULL);
+  memp_free(MEMP_API_MSG, msg);
+  return conn->err;
+
+}
+
+err_t
+netconn_listen(struct netconn *conn)
+{
+  struct api_msg *msg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  if (conn->acceptmbox == SYS_MBOX_NULL) {
+    conn->acceptmbox = sys_mbox_new();
+    if (conn->acceptmbox == SYS_MBOX_NULL) {
+      return ERR_MEM;
+    }
+  }
+  
+  if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
+    return (conn->err = ERR_MEM);
+  }
+  msg->type = API_MSG_LISTEN;
+  msg->msg.conn = conn;
+  api_msg_post(msg);
+  sys_mbox_fetch(conn->mbox, NULL);
+  memp_free(MEMP_API_MSG, msg);
+  return conn->err;
+}
+
+struct netconn *
+netconn_accept(struct netconn *conn)
+{
+  struct netconn *newconn;
+  
+  if (conn == NULL) {
+    return NULL;
+  }
+  
+  sys_mbox_fetch(conn->acceptmbox, (void **)&newconn);
+  /* Register event with callback */
+  if (conn->callback)
+      (*conn->callback)(conn, NETCONN_EVT_RCVMINUS, 0);
+  
+  return newconn;
+}
+
+struct netbuf *
+netconn_recv(struct netconn *conn)
+{
+  struct api_msg *msg;
+  struct netbuf *buf;
+  struct pbuf *p;
+  u16_t len;
+    
+  if (conn == NULL) {
+    return NULL;
+  }
+  
+  if (conn->recvmbox == SYS_MBOX_NULL) {
+    conn->err = ERR_CONN;
+    return NULL;
+  }
+
+  if (conn->err != ERR_OK) {
+    return NULL;
+  }
+
+  if (conn->type == NETCONN_TCP) {
+    if (conn->pcb.tcp->state == LISTEN) {
+      conn->err = ERR_CONN;
+      return NULL;
+    }
+
+
+    buf = memp_malloc(MEMP_NETBUF);
+
+    if (buf == NULL) {
+      conn->err = ERR_MEM;
+      return NULL;
+    }
+    
+    sys_mbox_fetch(conn->recvmbox, (void **)&p);
+
+    if (p != NULL)
+    {
+        len = p->tot_len;
+        conn->recv_avail -= len;
+    }
+    else
+        len = 0;
+    
+    /* Register event with callback */
+      if (conn->callback)
+        (*conn->callback)(conn, NETCONN_EVT_RCVMINUS, len);
+
+    /* If we are closed, we indicate that we no longer wish to receive
+       data by setting conn->recvmbox to SYS_MBOX_NULL. */
+    if (p == NULL) {
+      memp_free(MEMP_NETBUF, buf);
+      sys_mbox_free(conn->recvmbox);
+      conn->recvmbox = SYS_MBOX_NULL;
+      return NULL;
+    }
+
+    buf->p = p;
+    buf->ptr = p;
+    buf->fromport = 0;
+    buf->fromaddr = NULL;
+
+    /* Let the stack know that we have taken the data. */
+    if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
+      conn->err = ERR_MEM;
+      return buf;
+    }
+    msg->type = API_MSG_RECV;
+    msg->msg.conn = conn;
+    if (buf != NULL) {
+      msg->msg.msg.len = buf->p->tot_len;
+    } else {
+      msg->msg.msg.len = 1;
+    }
+    api_msg_post(msg);
+
+    sys_mbox_fetch(conn->mbox, NULL);
+    memp_free(MEMP_API_MSG, msg);
+  } else {
+    sys_mbox_fetch(conn->recvmbox, (void **)&buf);
+  conn->recv_avail -= buf->p->tot_len;
+    /* Register event with callback */
+    if (conn->callback)
+        (*conn->callback)(conn, NETCONN_EVT_RCVMINUS, buf->p->tot_len);
+  }
+
+  
+
+    
+  LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv: received %p (err %d)\n", (void *)buf, conn->err));
+
+
+  return buf;
+}
+
+err_t
+netconn_send(struct netconn *conn, struct netbuf *buf)
+{
+  struct api_msg *msg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  if (conn->err != ERR_OK) {
+    return conn->err;
+  }
+
+  if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
+    return (conn->err = ERR_MEM);
+  }
+
+  LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %d bytes\n", buf->p->tot_len));
+  msg->type = API_MSG_SEND;
+  msg->msg.conn = conn;
+  msg->msg.msg.p = buf->p;
+  api_msg_post(msg);
+
+  sys_mbox_fetch(conn->mbox, NULL);
+  memp_free(MEMP_API_MSG, msg);
+  return conn->err;
+}
+
+err_t
+netconn_write(struct netconn *conn, void *dataptr, u16_t size, u8_t copy)
+{
+  struct api_msg *msg;
+  u16_t len;
+  
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  if (conn->err != ERR_OK) {
+    return conn->err;
+  }
+  
+  if (conn->sem == SYS_SEM_NULL) {
+    conn->sem = sys_sem_new(0);
+    if (conn->sem == SYS_SEM_NULL) {
+      return ERR_MEM;
+    }
+  }
+
+  if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
+    return (conn->err = ERR_MEM);
+  }
+  msg->type = API_MSG_WRITE;
+  msg->msg.conn = conn;
+        
+
+  conn->state = NETCONN_WRITE;
+  while (conn->err == ERR_OK && size > 0) {
+    msg->msg.msg.w.dataptr = dataptr;
+    msg->msg.msg.w.copy = copy;
+    
+    if (conn->type == NETCONN_TCP) {
+      if (tcp_sndbuf(conn->pcb.tcp) == 0) {
+  sys_sem_wait(conn->sem);
+  if (conn->err != ERR_OK) {
+    goto ret;
+  }
+      }
+      if (size > tcp_sndbuf(conn->pcb.tcp)) {
+  /* We cannot send more than one send buffer's worth of data at a
+     time. */
+  len = tcp_sndbuf(conn->pcb.tcp);
+      } else {
+  len = size;
+      }
+    } else {
+      len = size;
+    }
+    
+    LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write: writing %d bytes (%d)\n", len, copy));
+    msg->msg.msg.w.len = len;
+    api_msg_post(msg);
+    sys_mbox_fetch(conn->mbox, NULL);    
+    if (conn->err == ERR_OK) {
+      dataptr = (void *)((u8_t *)dataptr + len);
+      size -= len;
+    } else if (conn->err == ERR_MEM) {
+      conn->err = ERR_OK;
+      sys_sem_wait(conn->sem);
+    } else {
+      goto ret;
+    }
+  }
+ ret:
+  memp_free(MEMP_API_MSG, msg);
+  conn->state = NETCONN_NONE;
+  if (conn->sem != SYS_SEM_NULL) {
+    sys_sem_free(conn->sem);
+    conn->sem = SYS_SEM_NULL;
+  }
+  
+  return conn->err;
+}
+
+err_t
+netconn_close(struct netconn *conn)
+{
+  struct api_msg *msg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  if ((msg = memp_malloc(MEMP_API_MSG)) == NULL) {
+    return (conn->err = ERR_MEM);
+  }
+
+  conn->state = NETCONN_CLOSE;
+ again:
+  msg->type = API_MSG_CLOSE;
+  msg->msg.conn = conn;
+  api_msg_post(msg);
+  sys_mbox_fetch(conn->mbox, NULL);
+  if (conn->err == ERR_MEM &&
+     conn->sem != SYS_SEM_NULL) {
+    sys_sem_wait(conn->sem);
+    goto again;
+  }
+  conn->state = NETCONN_NONE;
+  memp_free(MEMP_API_MSG, msg);
+  return conn->err;
+}
+
+err_t
+netconn_err(struct netconn *conn)
+{
+  return conn->err;
+}
+
diff --git a/lib/lwip/src/api/api_msg.c b/lib/lwip/src/api/api_msg.c
new file mode 100644
index 0000000..0cbe626
--- /dev/null
+++ b/lib/lwip/src/api/api_msg.c
@@ -0,0 +1,800 @@
+/*
+ * 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"
+#include "lwip/arch.h"
+#include "lwip/api_msg.h"
+#include "lwip/memp.h"
+#include "lwip/sys.h"
+#include "lwip/tcpip.h"
+
+#if LWIP_RAW
+static u8_t
+recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+    struct ip_addr *addr)
+{
+  struct netbuf *buf;
+  struct netconn *conn;
+
+  conn = arg;
+  if (!conn) return 0;
+
+  if (conn->recvmbox != SYS_MBOX_NULL) {
+    if (!(buf = memp_malloc(MEMP_NETBUF))) {
+      return 0;
+    }
+    pbuf_ref(p);
+    buf->p = p;
+    buf->ptr = p;
+    buf->fromaddr = addr;
+    buf->fromport = pcb->protocol;
+
+    conn->recv_avail += p->tot_len;
+    /* Register event with callback */
+    if (conn->callback)
+        (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, p->tot_len);
+    sys_mbox_post(conn->recvmbox, buf);
+  }
+
+  return 0; /* do not eat the packet */
+}
+#endif
+#if LWIP_UDP
+static void
+recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+   struct ip_addr *addr, u16_t port)
+{
+  struct netbuf *buf;
+  struct netconn *conn;
+
+  conn = arg;
+  
+  if (conn == NULL) {
+    pbuf_free(p);
+    return;
+  }
+  if (conn->recvmbox != SYS_MBOX_NULL) {
+    buf = memp_malloc(MEMP_NETBUF);
+    if (buf == NULL) {
+      pbuf_free(p);
+      return;
+    } else {
+      buf->p = p;
+      buf->ptr = p;
+      buf->fromaddr = addr;
+      buf->fromport = port;
+    }
+
+  conn->recv_avail += p->tot_len;
+    /* Register event with callback */
+    if (conn->callback)
+        (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, p->tot_len);
+    sys_mbox_post(conn->recvmbox, buf);
+  }
+}
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+
+static err_t
+recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  struct netconn *conn;
+  u16_t len;
+  
+  conn = arg;
+
+  if (conn == NULL) {
+    pbuf_free(p);
+    return ERR_VAL;
+  }
+
+  if (conn->recvmbox != SYS_MBOX_NULL) {
+        
+    conn->err = err;
+    if (p != NULL) {
+        len = p->tot_len;
+        conn->recv_avail += len;
+    }
+    else
+        len = 0;
+    /* Register event with callback */
+    if (conn->callback)
+        (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, len);
+    sys_mbox_post(conn->recvmbox, p);
+  }  
+  return ERR_OK;
+}
+
+
+static err_t
+poll_tcp(void *arg, struct tcp_pcb *pcb)
+{
+  struct netconn *conn;
+
+  conn = arg;
+  if (conn != NULL &&
+     (conn->state == NETCONN_WRITE || conn->state == NETCONN_CLOSE) &&
+     conn->sem != SYS_SEM_NULL) {
+    sys_sem_signal(conn->sem);
+  }
+  return ERR_OK;
+}
+
+static err_t
+sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+  struct netconn *conn;
+
+  conn = arg;
+  if (conn != NULL && conn->sem != SYS_SEM_NULL) {
+    sys_sem_signal(conn->sem);
+  }
+
+  if (conn && conn->callback)
+      if (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT)
+          (*conn->callback)(conn, NETCONN_EVT_SENDPLUS, len);
+  
+  return ERR_OK;
+}
+
+static void
+err_tcp(void *arg, err_t err)
+{
+  struct netconn *conn;
+
+  conn = arg;
+
+  conn->pcb.tcp = NULL;
+
+  
+  conn->err = err;
+  if (conn->recvmbox != SYS_MBOX_NULL) {
+    /* Register event with callback */
+    if (conn->callback)
+      (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, 0);
+    sys_mbox_post(conn->recvmbox, NULL);
+  }
+  if (conn->mbox != SYS_MBOX_NULL) {
+    sys_mbox_post(conn->mbox, NULL);
+  }
+  if (conn->acceptmbox != SYS_MBOX_NULL) {
+     /* Register event with callback */
+    if (conn->callback)
+      (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, 0);
+    sys_mbox_post(conn->acceptmbox, NULL);
+  }
+  if (conn->sem != SYS_SEM_NULL) {
+    sys_sem_signal(conn->sem);
+  }
+}
+
+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);
+}
+
+static err_t
+accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+  sys_mbox_t mbox;
+  struct netconn *newconn;
+  struct netconn *conn;
+  
+#if API_MSG_DEBUG
+#if TCP_DEBUG
+  tcp_debug_print_state(newpcb->state);
+#endif /* TCP_DEBUG */
+#endif /* API_MSG_DEBUG */
+  conn = (struct netconn *)arg;
+  mbox = conn->acceptmbox;
+  newconn = memp_malloc(MEMP_NETCONN);
+  if (newconn == NULL) {
+    return ERR_MEM;
+  }
+  newconn->type = NETCONN_TCP;
+  newconn->pcb.tcp = newpcb;
+  setup_tcp(newconn);
+  newconn->recvmbox = sys_mbox_new();
+  if (newconn->recvmbox == SYS_MBOX_NULL) {
+    memp_free(MEMP_NETCONN, newconn);
+    return ERR_MEM;
+  }
+  newconn->mbox = sys_mbox_new();
+  if (newconn->mbox == SYS_MBOX_NULL) {
+    sys_mbox_free(newconn->recvmbox);
+    memp_free(MEMP_NETCONN, newconn);
+    return ERR_MEM;
+  }
+  newconn->sem = sys_sem_new(0);
+  if (newconn->sem == SYS_SEM_NULL) {
+    sys_mbox_free(newconn->recvmbox);
+    sys_mbox_free(newconn->mbox);
+    memp_free(MEMP_NETCONN, newconn);
+    return ERR_MEM;
+  }
+  newconn->acceptmbox = SYS_MBOX_NULL;
+  newconn->err = err;
+  /* Register event with callback */
+  if (conn->callback)
+  {
+    (*conn->callback)(conn, NETCONN_EVT_RCVPLUS, 0);
+    /* We have to set the callback here even though
+     * the new socket is unknown. Mark the socket as -1. */
+    newconn->callback = conn->callback;
+    newconn->socket = -1;
+  }
+  
+  sys_mbox_post(mbox, newconn);
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+static void
+do_newconn(struct api_msg_msg *msg)
+{
+   if(msg->conn->pcb.tcp != NULL) {
+   /* This "new" connection already has a PCB allocated. */
+   /* Is this an error condition? Should it be deleted? 
+      We currently just are happy and return. */
+     sys_mbox_post(msg->conn->mbox, NULL);
+     return;
+   }
+
+   msg->conn->err = ERR_OK;
+
+   /* Allocate a PCB for this connection */
+   switch(msg->conn->type) {
+#if LWIP_RAW
+   case NETCONN_RAW:
+      msg->conn->pcb.raw = raw_new(msg->msg.bc.port); /* misusing the port field */
+      raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+     break;
+#endif
+#if LWIP_UDP
+   case NETCONN_UDPLITE:
+      msg->conn->pcb.udp = udp_new();
+      if(msg->conn->pcb.udp == NULL) {
+         msg->conn->err = ERR_MEM;
+         break;
+      }
+      udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+      udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+      break;
+   case NETCONN_UDPNOCHKSUM:
+      msg->conn->pcb.udp = udp_new();
+      if(msg->conn->pcb.udp == NULL) {
+         msg->conn->err = ERR_MEM;
+         break;
+      }
+      udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+      udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+      break;
+   case NETCONN_UDP:
+      msg->conn->pcb.udp = udp_new();
+      if(msg->conn->pcb.udp == NULL) {
+         msg->conn->err = ERR_MEM;
+         break;
+      }
+      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->conn->err = ERR_MEM;
+         break;
+      }
+      setup_tcp(msg->conn);
+      break;
+#endif
+   }
+   
+  
+  sys_mbox_post(msg->conn->mbox, NULL);
+}
+
+
+static void
+do_delconn(struct api_msg_msg *msg)
+{
+  if (msg->conn->pcb.tcp != NULL) {
+    switch (msg->conn->type) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      raw_remove(msg->conn->pcb.raw);
+      break;
+#endif
+#if LWIP_UDP
+    case NETCONN_UDPLITE:
+      /* FALLTHROUGH */
+    case NETCONN_UDPNOCHKSUM:
+      /* FALLTHROUGH */
+    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:
+      if (msg->conn->pcb.tcp->state == LISTEN) {
+  tcp_arg(msg->conn->pcb.tcp, NULL);
+  tcp_accept(msg->conn->pcb.tcp, NULL);  
+  tcp_close(msg->conn->pcb.tcp);
+      } else {
+  tcp_arg(msg->conn->pcb.tcp, NULL);
+  tcp_sent(msg->conn->pcb.tcp, NULL);
+  tcp_recv(msg->conn->pcb.tcp, NULL);  
+  tcp_poll(msg->conn->pcb.tcp, NULL, 0);
+  tcp_err(msg->conn->pcb.tcp, NULL);
+  if (tcp_close(msg->conn->pcb.tcp) != ERR_OK) {
+    tcp_abort(msg->conn->pcb.tcp);
+  }
+      }
+#endif
+    default:  
+    break;
+    }
+  }
+  /* Trigger select() in socket layer */
+  if (msg->conn->callback)
+  {
+      (*msg->conn->callback)(msg->conn, NETCONN_EVT_RCVPLUS, 0);
+      (*msg->conn->callback)(msg->conn, NETCONN_EVT_SENDPLUS, 0);
+  }
+  
+  if (msg->conn->mbox != SYS_MBOX_NULL) {
+    sys_mbox_post(msg->conn->mbox, NULL);
+  }
+}
+
+static void
+do_bind(struct api_msg_msg *msg)
+{
+  if (msg->conn->pcb.tcp == NULL) {
+    switch (msg->conn->type) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      msg->conn->pcb.raw = raw_new(msg->msg.bc.port); /* misusing the port field as protocol */
+      raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+      break;
+#endif
+#if LWIP_UDP
+    case NETCONN_UDPLITE:
+      msg->conn->pcb.udp = udp_new();
+      udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+      udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+      break;
+    case NETCONN_UDPNOCHKSUM:
+      msg->conn->pcb.udp = udp_new();
+      udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+      udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+      break;
+    case NETCONN_UDP:
+      msg->conn->pcb.udp = udp_new();
+      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();
+      setup_tcp(msg->conn);
+#endif /* LWIP_TCP */
+    default:  
+    break;
+    }
+  }
+  switch (msg->conn->type) {
+#if LWIP_RAW
+  case NETCONN_RAW:
+    msg->conn->err = raw_bind(msg->conn->pcb.raw,msg->msg.bc.ipaddr);
+    break;
+#endif
+#if LWIP_UDP
+  case NETCONN_UDPLITE:
+    /* FALLTHROUGH */
+  case NETCONN_UDPNOCHKSUM:
+    /* FALLTHROUGH */
+  case NETCONN_UDP:
+    msg->conn->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->conn->err = tcp_bind(msg->conn->pcb.tcp,
+            msg->msg.bc.ipaddr, msg->msg.bc.port);
+#endif /* LWIP_TCP */
+  default:
+    break;
+  }
+  sys_mbox_post(msg->conn->mbox, NULL);
+}
+#if LWIP_TCP
+
+static err_t
+do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+  struct netconn *conn;
+
+  conn = arg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  
+  conn->err = err;
+  if (conn->type == NETCONN_TCP && err == ERR_OK) {
+    setup_tcp(conn);
+  }    
+  sys_mbox_post(conn->mbox, NULL);
+  return ERR_OK;
+}
+#endif  
+
+static void
+do_connect(struct api_msg_msg *msg)
+{
+  if (msg->conn->pcb.tcp == NULL) {
+    switch (msg->conn->type) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      msg->conn->pcb.raw = raw_new(msg->msg.bc.port); /* misusing the port field as protocol */
+      raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+      break;
+#endif
+#if LWIP_UDP
+    case NETCONN_UDPLITE:
+      msg->conn->pcb.udp = udp_new();
+      if (msg->conn->pcb.udp == NULL) {
+  msg->conn->err = ERR_MEM;
+  sys_mbox_post(msg->conn->mbox, NULL);
+  return;
+      }
+      udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+      udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+      break;
+    case NETCONN_UDPNOCHKSUM:
+      msg->conn->pcb.udp = udp_new();
+      if (msg->conn->pcb.udp == NULL) {
+  msg->conn->err = ERR_MEM;
+  sys_mbox_post(msg->conn->mbox, NULL);
+  return;
+      }
+      udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+      udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+      break;
+    case NETCONN_UDP:
+      msg->conn->pcb.udp = udp_new();
+      if (msg->conn->pcb.udp == NULL) {
+  msg->conn->err = ERR_MEM;
+  sys_mbox_post(msg->conn->mbox, NULL);
+  return;
+      }
+      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->conn->err = ERR_MEM;
+  sys_mbox_post(msg->conn->mbox, NULL);
+  return;
+      }
+#endif
+    default:
+      break;
+    }
+  }
+  switch (msg->conn->type) {
+#if LWIP_RAW
+  case NETCONN_RAW:
+    raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
+    sys_mbox_post(msg->conn->mbox, NULL);
+    break;
+#endif
+#if LWIP_UDP
+  case NETCONN_UDPLITE:
+    /* FALLTHROUGH */
+  case NETCONN_UDPNOCHKSUM:
+    /* FALLTHROUGH */
+  case NETCONN_UDP:
+    udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+    sys_mbox_post(msg->conn->mbox, NULL);
+    break;
+#endif 
+#if LWIP_TCP      
+  case NETCONN_TCP:
+    /*    tcp_arg(msg->conn->pcb.tcp, msg->conn);*/
+    setup_tcp(msg->conn);
+    tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port,
+    do_connected);
+    /*tcp_output(msg->conn->pcb.tcp);*/
+#endif
+
+  default:
+    break;
+  }
+}
+
+static void
+do_disconnect(struct api_msg_msg *msg)
+{
+
+  switch (msg->conn->type) {
+#if LWIP_RAW
+  case NETCONN_RAW:
+    /* Do nothing as connecting is only a helper for upper lwip layers */
+    break;
+#endif
+#if LWIP_UDP
+  case NETCONN_UDPLITE:
+    /* FALLTHROUGH */
+  case NETCONN_UDPNOCHKSUM:
+    /* FALLTHROUGH */
+  case NETCONN_UDP:
+    udp_disconnect(msg->conn->pcb.udp);
+    break;
+#endif 
+  case NETCONN_TCP:
+    break;
+  }
+  sys_mbox_post(msg->conn->mbox, NULL);
+}
+
+
+static void
+do_listen(struct api_msg_msg *msg)
+{
+  if (msg->conn->pcb.tcp != NULL) {
+    switch (msg->conn->type) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: listen RAW: cannot listen for RAW.\n"));
+      break;
+#endif
+#if LWIP_UDP
+    case NETCONN_UDPLITE:
+      /* FALLTHROUGH */
+    case NETCONN_UDPNOCHKSUM:
+      /* FALLTHROUGH */
+    case NETCONN_UDP:
+      LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: listen UDP: cannot listen for UDP.\n"));
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP      
+    case NETCONN_TCP:
+      msg->conn->pcb.tcp = tcp_listen(msg->conn->pcb.tcp);
+      if (msg->conn->pcb.tcp == NULL) {
+  msg->conn->err = ERR_MEM;
+      } else {
+  if (msg->conn->acceptmbox == SYS_MBOX_NULL) {
+    msg->conn->acceptmbox = sys_mbox_new();
+    if (msg->conn->acceptmbox == SYS_MBOX_NULL) {
+      msg->conn->err = ERR_MEM;
+      break;
+    }
+  }
+  tcp_arg(msg->conn->pcb.tcp, msg->conn);
+  tcp_accept(msg->conn->pcb.tcp, accept_function);
+      }
+#endif
+    default:
+      break;
+    }
+  }
+  sys_mbox_post(msg->conn->mbox, NULL);
+}
+
+static void
+do_accept(struct api_msg_msg *msg)
+{
+  if (msg->conn->pcb.tcp != NULL) {
+    switch (msg->conn->type) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: accept RAW: cannot accept for RAW.\n"));
+      break;
+#endif
+#if LWIP_UDP
+    case NETCONN_UDPLITE:
+      /* FALLTHROUGH */
+    case NETCONN_UDPNOCHKSUM:
+      /* FALLTHROUGH */
+    case NETCONN_UDP:    
+      LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: accept UDP: cannot accept for UDP.\n"));
+      break;
+#endif /* LWIP_UDP */
+    case NETCONN_TCP:
+      break;
+    }
+  }
+}
+
+static void
+do_send(struct api_msg_msg *msg)
+{
+  if (msg->conn->pcb.tcp != NULL) {
+    switch (msg->conn->type) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      raw_send(msg->conn->pcb.raw, msg->msg.p);
+      break;
+#endif
+#if LWIP_UDP
+    case NETCONN_UDPLITE:
+      /* FALLTHROUGH */
+    case NETCONN_UDPNOCHKSUM:
+      /* FALLTHROUGH */
+    case NETCONN_UDP:
+      udp_send(msg->conn->pcb.udp, msg->msg.p);
+      break;
+#endif /* LWIP_UDP */
+    case NETCONN_TCP:
+      break;
+    }
+  }
+  sys_mbox_post(msg->conn->mbox, NULL);
+}
+
+static void
+do_recv(struct api_msg_msg *msg)
+{
+#if LWIP_TCP
+  if (msg->conn->pcb.tcp != NULL) {
+    if (msg->conn->type == NETCONN_TCP) {
+      tcp_recved(msg->conn->pcb.tcp, msg->msg.len);
+    }
+  }
+#endif  
+  sys_mbox_post(msg->conn->mbox, NULL);
+}
+
+static void
+do_write(struct api_msg_msg *msg)
+{
+#if LWIP_TCP  
+  err_t err;
+#endif  
+  if (msg->conn->pcb.tcp != NULL) {
+    switch (msg->conn->type) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      msg->conn->err = ERR_VAL;
+      break;
+#endif
+#if LWIP_UDP 
+    case NETCONN_UDPLITE:
+      /* FALLTHROUGH */
+    case NETCONN_UDPNOCHKSUM:
+      /* FALLTHROUGH */
+    case NETCONN_UDP:
+      msg->conn->err = ERR_VAL;
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP 
+    case NETCONN_TCP:      
+      err = tcp_write(msg->conn->pcb.tcp, msg->msg.w.dataptr,
+                      msg->msg.w.len, msg->msg.w.copy);
+      /* This is the Nagle algorithm: inhibit the sending of new TCP
+   segments when new outgoing data arrives from the user if any
+   previously transmitted data on the connection remains
+   unacknowledged. */
+      if(err == ERR_OK && (msg->conn->pcb.tcp->unacked == NULL || (msg->conn->pcb.tcp->flags & TF_NODELAY)) ) {
+  tcp_output(msg->conn->pcb.tcp);
+      }
+      msg->conn->err = err;
+      if (msg->conn->callback)
+          if (err == ERR_OK)
+          {
+              if (tcp_sndbuf(msg->conn->pcb.tcp) <= TCP_SNDLOWAT)
+                  (*msg->conn->callback)(msg->conn, NETCONN_EVT_SENDMINUS, msg->msg.w.len);
+          }
+#endif
+    default:
+      break;
+    }
+  }
+  sys_mbox_post(msg->conn->mbox, NULL);
+}
+
+static void
+do_close(struct api_msg_msg *msg)
+{
+  err_t err;
+
+  err = ERR_OK;
+
+  if (msg->conn->pcb.tcp != NULL) {
+    switch (msg->conn->type) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      break;
+#endif
+#if LWIP_UDP
+    case NETCONN_UDPLITE:
+      /* FALLTHROUGH */
+    case NETCONN_UDPNOCHKSUM:
+      /* FALLTHROUGH */
+    case NETCONN_UDP:
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case NETCONN_TCP:
+      if (msg->conn->pcb.tcp->state == LISTEN) {
+  err = tcp_close(msg->conn->pcb.tcp);
+      }
+      msg->conn->err = err;      
+#endif
+    default:      
+      break;
+    }
+  }
+  sys_mbox_post(msg->conn->mbox, NULL);
+}
+
+typedef void (* api_msg_decode)(struct api_msg_msg *msg);
+static api_msg_decode decode[API_MSG_MAX] = {
+  do_newconn,
+  do_delconn,
+  do_bind,
+  do_connect,
+  do_disconnect,
+  do_listen,
+  do_accept,
+  do_send,
+  do_recv,
+  do_write,
+  do_close
+  };
+void
+api_msg_input(struct api_msg *msg)
+{  
+  decode[msg->type](&(msg->msg));
+}
+
+void
+api_msg_post(struct api_msg *msg)
+{
+  tcpip_apimsg(msg);
+}
+
+
+
diff --git a/lib/lwip/src/api/err.c b/lib/lwip/src/api/err.c
new file mode 100644
index 0000000..b582d88
--- /dev/null
+++ b/lib/lwip/src/api/err.c
@@ -0,0 +1,59 @@
+/*
+ * 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 char *err_strerr[] = {"Ok.",
+           "Out of memory error.",
+           "Buffer error.",
+           "Connection aborted.",
+           "Connection reset.",
+           "Connection closed.",
+           "Not connected.",
+           "Illegal value.",
+           "Illegal argument.",
+           "Routing problem.",
+           "Address in use."
+};
+
+
+char *
+lwip_strerr(err_t err)
+{
+  return err_strerr[-err];
+
+}
+
+
+#endif /* LWIP_DEBUG */
diff --git a/lib/lwip/src/api/sockets.c b/lib/lwip/src/api/sockets.c
new file mode 100644
index 0000000..b7333d9
--- /dev/null
+++ b/lib/lwip/src/api/sockets.c
@@ -0,0 +1,1362 @@
+/*
+ * 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 <string.h>
+//#include <errno.h>
+
+#include "lwip/opt.h"
+#include "lwip/api.h"
+#include "lwip/arch.h"
+#include "lwip/sys.h"
+
+#include "lwip/sockets.h"
+
+#define NUM_SOCKETS MEMP_NUM_NETCONN
+
+struct lwip_socket {
+  struct netconn *conn;
+  struct netbuf *lastdata;
+  u16_t lastoffset;
+  u16_t rcvevent;
+  u16_t sendevent;
+  u16_t  flags;
+  int err;
+};
+
+struct lwip_select_cb
+{
+    struct lwip_select_cb *next;
+    fd_set *readset;
+    fd_set *writeset;
+    fd_set *exceptset;
+    int sem_signalled;
+    sys_sem_t sem;
+};
+
+static struct lwip_socket sockets[NUM_SOCKETS];
+static struct lwip_select_cb *select_cb_list = 0;
+
+static sys_sem_t socksem = 0;
+static sys_sem_t selectsem = 0;
+
+static void
+event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
+
+static int err_to_errno_table[11] = {
+    0,      /* ERR_OK    0      No error, everything OK. */
+    ENOMEM,    /* ERR_MEM  -1      Out of memory error.     */
+    ENOBUFS,    /* ERR_BUF  -2      Buffer error.            */
+    ECONNABORTED,  /* ERR_ABRT -3      Connection aborted.      */
+    ECONNRESET,    /* ERR_RST  -4      Connection reset.        */
+    ESHUTDOWN,    /* ERR_CLSD -5      Connection closed.       */
+    ENOTCONN,    /* ERR_CONN -6      Not connected.           */
+    EINVAL,    /* ERR_VAL  -7      Illegal value.           */
+    EIO,    /* ERR_ARG  -8      Illegal argument.        */
+    EHOSTUNREACH,  /* ERR_RTE  -9      Routing problem.         */
+    EADDRINUSE    /* ERR_USE  -10     Address in use.          */
+};
+
+#define ERR_TO_ERRNO_TABLE_SIZE \
+  (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0]))
+
+#define err_to_errno(err) \
+  (-(err) >= 0 && -(err) < ERR_TO_ERRNO_TABLE_SIZE ? \
+    err_to_errno_table[-(err)] : EIO)
+
+#ifdef ERRNO
+#define set_errno(err) errno = (err)
+#else
+#define set_errno(err)
+#endif
+
+#define sock_set_errno(sk, e) do { \
+      sk->err = (e); \
+      set_errno(sk->err); \
+} while (0)
+
+
+static struct lwip_socket *
+get_socket(int s)
+{
+  struct lwip_socket *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;
+}
+
+static int
+alloc_socket(struct netconn *newconn)
+{
+  int i;
+
+  if (!socksem)
+      socksem = sys_sem_new(1);
+
+  /* Protect socket array */
+  sys_sem_wait(socksem);
+
+  /* allocate a new socket identifier */
+  for(i = 0; i < NUM_SOCKETS; ++i) {
+    if (!sockets[i].conn) {
+      sockets[i].conn = newconn;
+      sockets[i].lastdata = NULL;
+      sockets[i].lastoffset = 0;
+      sockets[i].rcvevent = 0;
+      sockets[i].sendevent = 1; /* TCP send buf is empty */
+      sockets[i].flags = 0;
+      sockets[i].err = 0;
+      sys_sem_signal(socksem);
+      return i;
+    }
+  }
+  sys_sem_signal(socksem);
+  return -1;
+}
+
+int
+lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+  struct lwip_socket *sock;
+  struct netconn *newconn;
+  struct ip_addr naddr;
+  u16_t port;
+  int newsock;
+  struct sockaddr_in sin;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
+  sock = get_socket(s);
+  if (!sock) {
+    set_errno(EBADF);
+    return -1;
+  }
+
+  newconn = netconn_accept(sock->conn);
+
+  /* get the IP address and port of the remote host */
+  netconn_peer(newconn, &naddr, &port);
+
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_len = sizeof(sin);
+  sin.sin_family = AF_INET;
+  sin.sin_port = htons(port);
+  sin.sin_addr.s_addr = naddr.addr;
+
+  if (*addrlen > sizeof(sin))
+      *addrlen = sizeof(sin);
+
+  memcpy(addr, &sin, *addrlen);
+
+  newsock = alloc_socket(newconn);
+  if (newsock == -1) {
+    netconn_delete(newconn);
+  sock_set_errno(sock, ENOBUFS);
+  return -1;
+  }
+  newconn->callback = event_callback;
+  sock = get_socket(newsock);
+
+  sys_sem_wait(socksem);
+  sock->rcvevent += -1 - newconn->socket;
+  newconn->socket = newsock;
+  sys_sem_signal(socksem);
+
+  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=%u\n", port));
+
+  sock_set_errno(sock, 0);
+  return newsock;
+}
+
+int
+lwip_bind(int s, struct sockaddr *name, socklen_t namelen)
+{
+  struct lwip_socket *sock;
+  struct ip_addr local_addr;
+  u16_t local_port;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    set_errno(EBADF);
+    return -1;
+  }
+
+  local_addr.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr;
+  local_port = ((struct sockaddr_in *)name)->sin_port;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
+  ip_addr_debug_print(SOCKETS_DEBUG, &local_addr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u)\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_socket *sock;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
+  if (!socksem)
+      socksem = sys_sem_new(1);
+
+  /* We cannot allow multiple closes of the same socket. */
+  sys_sem_wait(socksem);
+
+  sock = get_socket(s);
+  if (!sock) {
+      sys_sem_signal(socksem);
+      set_errno(EBADF);
+      return -1;
+  }
+
+  netconn_delete(sock->conn);
+  if (sock->lastdata) {
+    netbuf_delete(sock->lastdata);
+  }
+  sock->lastdata = NULL;
+  sock->lastoffset = 0;
+  sock->conn = NULL;
+  sys_sem_signal(socksem);
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+int
+lwip_connect(int s, struct sockaddr *name, socklen_t namelen)
+{
+  struct lwip_socket *sock;
+  err_t err;
+
+  sock = get_socket(s);
+  if (!sock) {
+    set_errno(EBADF);
+    return -1;
+  }
+
+  if (((struct sockaddr_in *)name)->sin_family == AF_UNSPEC) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
+    err = netconn_disconnect(sock->conn);
+  } else {
+    struct ip_addr remote_addr;
+    u16_t remote_port;
+
+    remote_addr.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr;
+    remote_port = ((struct sockaddr_in *)name)->sin_port;
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
+    ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr);
+    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u)\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;
+}
+
+int
+lwip_listen(int s, int backlog)
+{
+  struct lwip_socket *sock;
+  err_t err;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
+  sock = get_socket(s);
+  if (!sock) {
+    set_errno(EBADF);
+    return -1;
+  }
+
+  err = netconn_listen(sock->conn);
+
+  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, int len, unsigned int flags,
+        struct sockaddr *from, socklen_t *fromlen)
+{
+  struct lwip_socket *sock;
+  struct netbuf *buf;
+  u16_t buflen, copylen;
+  struct ip_addr *addr;
+  u16_t port;
+
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %d, 0x%x, ..)\n", s, mem, len, flags));
+  sock = get_socket(s);
+  if (!sock) {
+    set_errno(EBADF);
+    return -1;
+  }
+
+  /* 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) || (sock->flags & O_NONBLOCK))
+  && !sock->rcvevent)
+    {
+      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. */
+    buf = netconn_recv(sock->conn);
+
+    if (!buf) {
+      /* We should really do some error checking here. */
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL!\n", s));
+      sock_set_errno(sock, 0);
+      return 0;
+    }
+  }
+
+  buflen = netbuf_len(buf);
+
+  buflen -= sock->lastoffset;
+
+  if (len > buflen) {
+    copylen = buflen;
+  } else {
+    copylen = len;
+  }
+
+  /* copy the contents of the received buffer into
+     the supplied memory pointer mem */
+  netbuf_copy_partial(buf, mem, copylen, sock->lastoffset);
+
+  /* Check to see from where the data was. */
+  if (from && fromlen) {
+    struct sockaddr_in sin;
+
+    addr = netbuf_fromaddr(buf);
+    port = netbuf_fromport(buf);
+
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_len = sizeof(sin);
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(port);
+    sin.sin_addr.s_addr = 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=%u len=%u\n", port, copylen));
+  } else {
+#if SOCKETS_DEBUG
+    addr = netbuf_fromaddr(buf);
+    port = netbuf_fromport(buf);
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
+    ip_addr_debug_print(SOCKETS_DEBUG, addr);
+    LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u len=%u\n", port, copylen));
+#endif
+
+  }
+
+  /* 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;
+  } else {
+    sock->lastdata = NULL;
+    sock->lastoffset = 0;
+    netbuf_delete(buf);
+  }
+
+
+  sock_set_errno(sock, 0);
+  return copylen;
+}
+
+int
+lwip_read(int s, void *mem, int len)
+{
+  return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+}
+
+int
+lwip_recv(int s, void *mem, int len, unsigned int flags)
+{
+  return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
+}
+
+int
+lwip_send(int s, void *data, int size, unsigned int flags)
+{
+  struct lwip_socket *sock;
+  struct netbuf *buf;
+  err_t err;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%d, flags=0x%x)\n", s, data, size, flags));
+
+  sock = get_socket(s);
+  if (!sock) {
+    set_errno(EBADF);
+    return -1;
+  }
+
+  switch (netconn_type(sock->conn)) {
+  case NETCONN_RAW:
+  case NETCONN_UDP:
+  case NETCONN_UDPLITE:
+  case NETCONN_UDPNOCHKSUM:
+    /* create a buffer */
+    buf = netbuf_new();
+
+    if (!buf) {
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) ENOBUFS\n", s));
+      sock_set_errno(sock, ENOBUFS);
+      return -1;
+    }
+
+    /* make the buffer point to the data that should
+       be sent */
+    netbuf_ref(buf, data, size);
+
+    /* send the data */
+    err = netconn_send(sock->conn, buf);
+
+    /* deallocated the buffer */
+    netbuf_delete(buf);
+    break;
+  case NETCONN_TCP:
+    err = netconn_write(sock->conn, data, size, NETCONN_COPY);
+    break;
+  default:
+    err = ERR_ARG;
+    break;
+  }
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d\n", s, err));
+    sock_set_errno(sock, err_to_errno(err));
+    return -1;
+  }
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) ok size=%d\n", s, size));
+  sock_set_errno(sock, 0);
+  return size;
+}
+
+int
+lwip_sendto(int s, void *data, int size, unsigned int flags,
+       struct sockaddr *to, socklen_t tolen)
+{
+  struct lwip_socket *sock;
+  struct ip_addr remote_addr, addr;
+  u16_t remote_port, port;
+  int ret,connected;
+
+  sock = get_socket(s);
+  if (!sock) {
+    set_errno(EBADF);
+    return -1;
+  }
+
+  /* get the peer if currently connected */
+  connected = (netconn_peer(sock->conn, &addr, &port) == ERR_OK);
+
+  remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr;
+  remote_port = ((struct sockaddr_in *)to)->sin_port;
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, size=%d, flags=0x%x to=", s, data, size, flags));
+  ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u\n", ntohs(remote_port)));
+
+  netconn_connect(sock->conn, &remote_addr, ntohs(remote_port));
+
+  ret = lwip_send(s, data, size, flags);
+
+  /* reset the remote address and port number
+     of the connection */
+  if (connected)
+    netconn_connect(sock->conn, &addr, port);
+  else
+  netconn_disconnect(sock->conn);
+  return ret;
+}
+
+int
+lwip_socket(int domain, int type, int protocol)
+{
+  struct netconn *conn;
+  int i;
+
+  /* create a netconn */
+  switch (type) {
+  case SOCK_RAW:
+    conn = netconn_new_with_proto_and_callback(NETCONN_RAW, 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(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));
+    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);
+
+  if (i == -1) {
+    netconn_delete(conn);
+  set_errno(ENOBUFS);
+  return -1;
+  }
+  conn->socket = i;
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
+  set_errno(0);
+  return i;
+}
+
+int
+lwip_write(int s, void *data, int size)
+{
+   return lwip_send(s, data, size, 0);
+}
+
+
+static int
+lwip_selscan(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset)
+{
+    int i, nready = 0;
+    fd_set lreadset, lwriteset, lexceptset;
+    struct lwip_socket *p_sock;
+
+    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++)
+    {
+        if (FD_ISSET(i, readset))
+        {
+            /* See if netconn of this socket is ready for read */
+            p_sock = get_socket(i);
+            if (p_sock && (p_sock->lastdata || p_sock->rcvevent))
+            {
+                FD_SET(i, &lreadset);
+		LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
+                nready++;
+            }
+        }
+        if (FD_ISSET(i, writeset))
+        {
+            /* See if netconn of this socket is ready for write */
+            p_sock = get_socket(i);
+            if (p_sock && p_sock->sendevent)
+            {
+                FD_SET(i, &lwriteset);
+		LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
+                nready++;
+            }
+        }
+    }
+    *readset = lreadset;
+    *writeset = lwriteset;
+    FD_ZERO(exceptset);
+
+    return nready;
+}
+
+
+
+int
+lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+               struct timeval *timeout)
+{
+    int i;
+    int nready;
+    fd_set lreadset, lwriteset, lexceptset;
+    u32_t msectimeout;
+    struct lwip_select_cb select_cb;
+    struct lwip_select_cb *p_selcb;
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%ld tvusec=%ld)\n", maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, timeout ? timeout->tv_sec : -1L, timeout ? timeout->tv_usec : -1L));
+
+    select_cb.next = 0;
+    select_cb.readset = readset;
+    select_cb.writeset = writeset;
+    select_cb.exceptset = exceptset;
+    select_cb.sem_signalled = 0;
+
+    /* Protect ourselves searching through the list */
+    if (!selectsem)
+        selectsem = sys_sem_new(1);
+    sys_sem_wait(selectsem);
+
+    if (readset)
+        lreadset = *readset;
+    else
+        FD_ZERO(&lreadset);
+    if (writeset)
+        lwriteset = *writeset;
+    else
+        FD_ZERO(&lwriteset);
+    if (exceptset)
+        lexceptset = *exceptset;
+    else
+        FD_ZERO(&lexceptset);
+
+    /* Go through each socket in each list to count number of sockets which
+       currently match */
+    nready = lwip_selscan(maxfdp1, &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)
+        {
+            sys_sem_signal(selectsem);
+            if (readset)
+                FD_ZERO(readset);
+            if (writeset)
+                FD_ZERO(writeset);
+            if (exceptset)
+                FD_ZERO(exceptset);
+
+	    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
+	    set_errno(0);
+
+            return 0;
+        }
+
+        /* 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.sem = sys_sem_new(0);
+        /* Note that we are still protected */
+        /* Put this select_cb on top of list */
+        select_cb.next = select_cb_list;
+        select_cb_list = &select_cb;
+
+        /* Now we can safely unprotect */
+        sys_sem_signal(selectsem);
+
+        /* Now just wait to be woken */
+        if (timeout == 0)
+            /* Wait forever */
+            msectimeout = 0;
+        else
+            msectimeout =  ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
+
+        i = sys_sem_wait_timeout(select_cb.sem, msectimeout);
+
+        /* Take us off the list */
+        sys_sem_wait(selectsem);
+        if (select_cb_list == &select_cb)
+            select_cb_list = select_cb.next;
+        else
+            for (p_selcb = select_cb_list; p_selcb; p_selcb = p_selcb->next)
+                if (p_selcb->next == &select_cb)
+                {
+                    p_selcb->next = select_cb.next;
+                    break;
+                }
+
+        sys_sem_signal(selectsem);
+
+        sys_sem_free(select_cb.sem);
+        if (i == 0)             /* Timeout */
+        {
+            if (readset)
+                FD_ZERO(readset);
+            if (writeset)
+                FD_ZERO(writeset);
+            if (exceptset)
+                FD_ZERO(exceptset);
+
+	    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
+	    set_errno(0);
+
+            return 0;
+        }
+
+        if (readset)
+            lreadset = *readset;
+        else
+            FD_ZERO(&lreadset);
+        if (writeset)
+            lwriteset = *writeset;
+        else
+            FD_ZERO(&lwriteset);
+        if (exceptset)
+            lexceptset = *exceptset;
+        else
+            FD_ZERO(&lexceptset);
+
+        /* See what's set */
+        nready = lwip_selscan(maxfdp1, &lreadset, &lwriteset, &lexceptset);
+    }
+    else
+        sys_sem_signal(selectsem);
+
+    if (readset)
+        *readset = lreadset;
+    if (writeset)
+        *writeset = lwriteset;
+    if (exceptset)
+        *exceptset = lexceptset;
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+    set_errno(0);
+
+    return nready;
+}
+
+
+static void
+event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
+{
+    int s;
+    struct lwip_socket *sock;
+    struct lwip_select_cb *scb;
+
+    /* 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. */
+            if (evt == NETCONN_EVT_RCVPLUS)
+                conn->socket--;
+            return;
+        }
+
+        sock = get_socket(s);
+        if (!sock)
+            return;
+    }
+    else
+        return;
+
+    if (!selectsem)
+        selectsem = sys_sem_new(1);
+
+    sys_sem_wait(selectsem);
+    /* 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;
+    }
+    sys_sem_signal(selectsem);
+
+    /* Now decide if anyone is waiting for this socket */
+    /* NOTE: This code is written this way to protect the select link list
+       but to avoid a deadlock situation by releasing socksem before
+       signalling for the select. This means we need to go through the 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. */
+    while (1)
+    {
+        sys_sem_wait(selectsem);
+        for (scb = select_cb_list; scb; scb = scb->next)
+        {
+            if (scb->sem_signalled == 0)
+            {
+                /* Test this select call for our socket */
+                if (scb->readset && FD_ISSET(s, scb->readset))
+                    if (sock->rcvevent)
+                        break;
+                if (scb->writeset && FD_ISSET(s, scb->writeset))
+                    if (sock->sendevent)
+                        break;
+            }
+        }
+        if (scb)
+        {
+            scb->sem_signalled = 1;
+            sys_sem_signal(selectsem);
+            sys_sem_signal(scb->sem);
+        } else {
+            sys_sem_signal(selectsem);
+            break;
+        }
+    }
+
+}
+
+
+
+
+int lwip_shutdown(int s, int how)
+{
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
+  return lwip_close(s); /* XXX temporary hack until proper implementation */
+}
+
+int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen)
+{
+  struct lwip_socket *sock;
+  struct sockaddr_in sin;
+  struct ip_addr naddr;
+
+  sock = get_socket(s);
+  if (!sock) {
+    set_errno(EBADF);
+    return -1;
+  }
+
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_len = sizeof(sin);
+  sin.sin_family = AF_INET;
+
+  /* get the IP address and port of the remote host */
+  netconn_peer(sock->conn, &naddr, &sin.sin_port);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getpeername(%d, addr=", s));
+  ip_addr_debug_print(SOCKETS_DEBUG, &naddr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%d)\n", sin.sin_port));
+
+  sin.sin_port = htons(sin.sin_port);
+  sin.sin_addr.s_addr = naddr.addr;
+
+  if (*namelen > sizeof(sin))
+      *namelen = sizeof(sin);
+
+  memcpy(name, &sin, *namelen);
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen)
+{
+  struct lwip_socket *sock;
+  struct sockaddr_in sin;
+  struct ip_addr *naddr;
+
+  sock = get_socket(s);
+  if (!sock) {
+    set_errno(EBADF);
+    return -1;
+  }
+
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_len = sizeof(sin);
+  sin.sin_family = AF_INET;
+
+  /* get the IP address and port of the remote host */
+  netconn_addr(sock->conn, &naddr, &sin.sin_port);
+
+  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockname(%d, addr=", s));
+  ip_addr_debug_print(SOCKETS_DEBUG, naddr);
+  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%d)\n", sin.sin_port));
+
+  sin.sin_port = htons(sin.sin_port);
+  sin.sin_addr.s_addr = naddr->addr;
+
+  if (*namelen > sizeof(sin))
+      *namelen = sizeof(sin);
+
+  memcpy(name, &sin, *namelen);
+  sock_set_errno(sock, 0);
+  return 0;
+}
+
+int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+  int err = 0;
+  struct lwip_socket *sock = get_socket(s);
+
+  if(!sock) {
+   	set_errno(EBADF);
+    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_OOBINLINE: */
+      /* UNIMPL case SO_RCVBUF: */
+      /* 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;
+
+      default:
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", s, optname));
+        err = ENOPROTOOPT;
+      }  /* switch */
+      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;
+
+      default:
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", s, optname));
+        err = ENOPROTOOPT;
+      }  /* switch */
+      break;
+         
+/* 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:
+        break;
+         
+      default:
+        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", s, optname));
+        err = ENOPROTOOPT;
+      }  /* switch */
+      break;
+
+/* 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( 0 != err ) {
+    sock_set_errno(sock, err);
+    return -1;
+  }
+   
+
+
+  /* Now do the actual option processing */
+
+  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.tcp->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 (sock->conn->type) {
+      case NETCONN_RAW:
+        *(int*)optval = SOCK_RAW;
+        break;
+      case NETCONN_TCP:
+        *(int*)optval = SOCK_STREAM;
+        break;
+      case NETCONN_UDP:
+      case NETCONN_UDPLITE:
+      case NETCONN_UDPNOCHKSUM:
+        *(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 */
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", s, *(int *)optval));
+      break;
+
+    case SO_ERROR:
+      *(int *)optval = sock->err;
+      sock->err = 0;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", s, *(int *)optval));
+      break;
+    }  /* switch */
+    break;
+
+/* Level: IPPROTO_IP */
+  case IPPROTO_IP:
+    switch( optname ) {
+    case IP_TTL:
+      *(int*)optval = sock->conn->pcb.tcp->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.tcp->tos;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", s, *(int *)optval));
+      break;
+    }  /* switch */
+    break;
+
+/* Level: IPPROTO_TCP */
+  case IPPROTO_TCP:
+    switch( optname ) {
+    case TCP_NODELAY:
+      *(int*)optval = (sock->conn->pcb.tcp->flags & TF_NODELAY);
+      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->keepalive;
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n", s, *(int *)optval));
+      break;
+    }  /* switch */
+    break;
+  }
+
+
+  sock_set_errno(sock, err);
+  return err ? -1 : 0;
+}
+
+int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+  struct lwip_socket *sock = get_socket(s);
+  int err = 0;
+
+  if(!sock) {
+   	set_errno(EBADF);
+    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 SO_OOBINLINE: */
+    /* UNIMPL case SO_RCVBUF: */
+    /* 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;
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", s, optname));
+      err = ENOPROTOOPT;
+    }  /* switch */
+    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;
+      default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", s, optname));
+      err = ENOPROTOOPT;
+    }  /* switch */
+    break;
+
+/* 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:
+      break;
+
+    default:
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", s, optname));
+      err = ENOPROTOOPT;
+    }  /* switch */
+    break;
+
+/* UNDEFINED LEVEL */      
+  default:
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", s, level, optname));
+    err = ENOPROTOOPT;
+  }  /* switch */
+
+
+  if( 0 != err ) {
+    sock_set_errno(sock, err);
+    return -1;
+  }
+
+
+
+  /* Now do the actual option processing */
+
+  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.tcp->so_options |= optname;
+      } else {
+        sock->conn->pcb.tcp->so_options &= ~optname;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", s, optname, (*(int*)optval?"on":"off")));
+      break;
+    }  /* switch */
+    break;
+
+/* Level: IPPROTO_IP */
+  case IPPROTO_IP:
+    switch( optname ) {
+    case IP_TTL:
+      sock->conn->pcb.tcp->ttl = (u8_t)(*(int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %u\n", s, sock->conn->pcb.tcp->ttl));
+      break;
+    case IP_TOS:
+      sock->conn->pcb.tcp->tos = (u8_t)(*(int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %u\n", s, sock->conn->pcb.tcp->tos));
+      break;
+    }  /* switch */
+    break;
+
+/* Level: IPPROTO_TCP */
+  case IPPROTO_TCP:
+    switch( optname ) {
+    case TCP_NODELAY:
+      if ( *(int*)optval ) {
+        sock->conn->pcb.tcp->flags |= TF_NODELAY;
+      } else {
+        sock->conn->pcb.tcp->flags &= ~TF_NODELAY;
+      }
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", s, (*(int *)optval)?"on":"off") );
+      break;
+    case TCP_KEEPALIVE:
+      sock->conn->pcb.tcp->keepalive = (u32_t)(*(int*)optval);
+      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %lu\n", s, sock->conn->pcb.tcp->keepalive));
+      break;
+    }  /* switch */
+    break;
+  }  /* switch */
+
+  sock_set_errno(sock, err);
+  return err ? -1 : 0;
+}
+
+int lwip_ioctl(int s, long cmd, void *argp)
+{
+  struct lwip_socket *sock = get_socket(s);
+
+  if(!sock) {
+   	set_errno(EBADF);
+    return -1;
+  }
+
+  switch (cmd) {
+  case FIONREAD:
+    if (!argp) {
+      sock_set_errno(sock, EINVAL);
+      return -1;
+    }
+
+    *((u16_t*)argp) = sock->conn->recv_avail;
+
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %u\n", s, argp, *((u16_t*)argp)));
+    sock_set_errno(sock, 0);
+    return 0;
+
+  case FIONBIO:
+    if (argp && *(u32_t*)argp)
+      sock->flags |= O_NONBLOCK;
+    else
+      sock->flags &= ~O_NONBLOCK;
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, !!(sock->flags & O_NONBLOCK)));
+    sock_set_errno(sock, 0);
+    return 0;
+
+  default:
+    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
+    sock_set_errno(sock, ENOSYS); /* not yet implemented */
+    return -1;
+  }
+}
+
diff --git a/lib/lwip/src/api/tcpip.c b/lib/lwip/src/api/tcpip.c
new file mode 100644
index 0000000..ce8a2ca
--- /dev/null
+++ b/lib/lwip/src/api/tcpip.c
@@ -0,0 +1,198 @@
+/*
+ * 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"
+
+#include "lwip/sys.h"
+
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+
+#include "lwip/ip.h"
+#include "lwip/ip_frag.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+
+#include "lwip/tcpip.h"
+
+static void (* tcpip_init_done)(void *arg) = NULL;
+static void *tcpip_init_done_arg;
+static sys_mbox_t mbox;
+
+#if LWIP_TCP
+static int tcpip_tcp_timer_active = 0;
+
+static void
+tcpip_tcp_timer(void *arg)
+{
+  (void)arg;
+
+  /* call TCP timer handler */
+  tcp_tmr();
+  /* timer still needed? */
+  if (tcp_active_pcbs || tcp_tw_pcbs) {
+    /* restart timer */
+    sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+  } else {
+    /* disable timer */
+    tcpip_tcp_timer_active = 0;
+  }
+}
+
+#if !NO_SYS
+void
+tcp_timer_needed(void)
+{
+  /* timer is off but needed again? */
+  if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
+    /* enable and start timer */
+    tcpip_tcp_timer_active = 1;
+    sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+  }
+}
+#endif /* !NO_SYS */
+#endif /* LWIP_TCP */
+
+#if IP_REASSEMBLY
+static void
+ip_timer(void *data)
+{
+  LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: ip_reass_tmr()\n"));
+  ip_reass_tmr();
+  sys_timeout(1000, ip_timer, NULL);
+}
+#endif
+
+static void
+tcpip_thread(void *arg)
+{
+  struct tcpip_msg *msg;
+
+  (void)arg;
+
+  ip_init();
+#if LWIP_UDP  
+  udp_init();
+#endif
+#if LWIP_TCP
+  tcp_init();
+#endif
+#if IP_REASSEMBLY
+  sys_timeout(1000, ip_timer, NULL);
+#endif
+  if (tcpip_init_done != NULL) {
+    tcpip_init_done(tcpip_init_done_arg);
+  }
+
+  while (1) {                          /* MAIN Loop */
+    sys_mbox_fetch(mbox, (void *)&msg);
+    switch (msg->type) {
+    case TCPIP_MSG_API:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
+      api_msg_input(msg->msg.apimsg);
+      break;
+    case TCPIP_MSG_INPUT:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: IP packet %p\n", (void *)msg));
+      ip_input(msg->msg.inp.p, msg->msg.inp.netif);
+      break;
+    case TCPIP_MSG_CALLBACK:
+      LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
+      msg->msg.cb.f(msg->msg.cb.ctx);
+      break;
+    default:
+      break;
+    }
+    memp_free(MEMP_TCPIP_MSG, msg);
+  }
+}
+
+err_t
+tcpip_input(struct pbuf *p, struct netif *inp)
+{
+  struct tcpip_msg *msg;
+  
+  msg = memp_malloc(MEMP_TCPIP_MSG);
+  if (msg == NULL) {
+    pbuf_free(p);    
+    return ERR_MEM;  
+  }
+  
+  msg->type = TCPIP_MSG_INPUT;
+  msg->msg.inp.p = p;
+  msg->msg.inp.netif = inp;
+  sys_mbox_post(mbox, msg);
+  return ERR_OK;
+}
+
+err_t
+tcpip_callback(void (*f)(void *ctx), void *ctx)
+{
+  struct tcpip_msg *msg;
+  
+  msg = memp_malloc(MEMP_TCPIP_MSG);
+  if (msg == NULL) {
+    return ERR_MEM;  
+  }
+  
+  msg->type = TCPIP_MSG_CALLBACK;
+  msg->msg.cb.f = f;
+  msg->msg.cb.ctx = ctx;
+  sys_mbox_post(mbox, msg);
+  return ERR_OK;
+}
+
+void
+tcpip_apimsg(struct api_msg *apimsg)
+{
+  struct tcpip_msg *msg;
+  msg = memp_malloc(MEMP_TCPIP_MSG);
+  if (msg == NULL) {
+    memp_free(MEMP_API_MSG, apimsg);
+    return;
+  }
+  msg->type = TCPIP_MSG_API;
+  msg->msg.apimsg = apimsg;
+  sys_mbox_post(mbox, msg);
+}
+
+void
+tcpip_init(void (* initfunc)(void *), void *arg)
+{
+  tcpip_init_done = initfunc;
+  tcpip_init_done_arg = arg;
+  mbox = sys_mbox_new();
+  sys_thread_new(tcpip_thread, NULL, TCPIP_THREAD_PRIO);
+}
+
+
+
+