www.pudn.com > lwip-1.3.0.rar > sockets.c


/** 
 * @file 
 * Sockets BSD-Like API module 
 * 
 */ 
 
/* 
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution. 
 * 3. The name of the author may not be used to endorse or promote products 
 *    derived from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE. 
 * 
 * This file is part of the lwIP TCP/IP stack. 
 * 
 * Author: Adam Dunkels  
 * 
 * Improved by Marc Boucher  and David Haas  
 * 
 */ 
 
#include "lwip/opt.h" 
 
#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ 
 
#include "lwip/sockets.h" 
#include "lwip/api.h" 
#include "lwip/sys.h" 
#include "lwip/igmp.h" 
#include "lwip/inet.h" 
#include "lwip/tcp.h" 
#include "lwip/raw.h" 
#include "lwip/udp.h" 
#include "lwip/tcpip.h" 
 
#include  
 
#define NUM_SOCKETS MEMP_NUM_NETCONN 
 
/** Contains all internal pointers and states used for a socket */ 
struct lwip_socket { 
  /** sockets currently are built on netconns, each socket has one netconn */ 
  struct netconn *conn; 
  /** data that was left from the previous read */ 
  struct netbuf *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 */ 
  u16_t rcvevent; 
  /** number of times data was received, set by event_callback(), 
      tested by select */ 
  u16_t sendevent; 
  /** socket flags (currently, only used for O_NONBLOCK) */ 
  u16_t flags; 
  /** last error that occurred on this socket */ 
  int err; 
}; 
 
/** Description for a task waiting in select */ 
struct lwip_select_cb { 
  /** Pointer to the next waiting task */ 
  struct lwip_select_cb *next; 
  /** 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_socket *sock; 
  /** socket index for which to change options */ 
  int s; 
  /** 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_socket sockets[NUM_SOCKETS]; 
/** The global list of tasks waiting for select */ 
static struct lwip_select_cb *select_cb_list; 
 
/** Semaphore protecting the sockets array */ 
static sys_sem_t socksem; 
/** Semaphore protecting select_cb_list */ 
static sys_sem_t selectsem; 
 
/** 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.            */ 
  EHOSTUNREACH,  /* ERR_RTE        -3      Routing problem.         */ 
  ECONNABORTED,  /* ERR_ABRT       -4      Connection aborted.      */ 
  ECONNRESET,    /* ERR_RST        -5      Connection reset.        */ 
  ESHUTDOWN,     /* ERR_CLSD       -6      Connection closed.       */ 
  ENOTCONN,      /* ERR_CONN       -7      Not connected.           */ 
  EINVAL,        /* ERR_VAL        -8      Illegal value.           */ 
  EIO,           /* ERR_ARG        -9      Illegal argument.        */ 
  EADDRINUSE,    /* ERR_USE        -10     Address in use.          */ 
  -1,            /* ERR_IF         -11     Low-level netif error    */ 
  -1,            /* ERR_ISCONN     -12     Already connected.       */ 
  ETIMEDOUT,     /* ERR_TIMEOUT    -13     Timeout                  */ 
  EINPROGRESS    /* ERR_INPROGRESS -14     Operation in progress    */ 
}; 
 
#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 
#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) 
 
/* 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) 
{ 
  socksem   = sys_sem_new(1); 
  selectsem = sys_sem_new(1); 
} 
 
/** 
 * Map a externally used socket index to the internal socket representation. 
 * 
 * @param s externally used socket index 
 * @return struct lwip_socket for the socket or NULL if not found 
 */ 
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; 
} 
 
/** 
 * Allocate a new socket for a given netconn. 
 * 
 * @param newconn the netconn for which to allocate a socket 
 * @return the index of the new socket; -1 on error 
 */ 
static int 
alloc_socket(struct netconn *newconn) 
{ 
  int i; 
 
  /* 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; 
} 
 
/* 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_socket *sock, *nsock; 
  struct netconn *newconn; 
  struct ip_addr naddr; 
  u16_t port; 
  int newsock; 
  struct sockaddr_in sin; 
  err_t err; 
 
  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); 
  sock = get_socket(s); 
  if (!sock) 
    return -1; 
 
  newconn = netconn_accept(sock->conn); 
  if (!newconn) { 
    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) failed, err=%d\n", s, sock->conn->err)); 
    sock_set_errno(sock, err_to_errno(sock->conn->err)); 
    return -1; 
  } 
 
  /* get the IP address and port of the remote host */ 
  err = netconn_peer(newconn, &naddr, &port); 
  if (err != ERR_OK) { 
    netconn_delete(newconn); 
    sock_set_errno(sock, err_to_errno(err)); 
    return -1; 
  } 
 
  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); 
 
  SMEMCPY(addr, &sin, *addrlen); 
 
  newsock = alloc_socket(newconn); 
  if (newsock == -1) { 
    netconn_delete(newconn); 
    sock_set_errno(sock, ENFILE); 
    return -1; 
  } 
  LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS)); 
  newconn->callback = event_callback; 
  nsock = &sockets[newsock]; 
  LWIP_ASSERT("invalid socket pointer", nsock != NULL); 
 
  sys_sem_wait(socksem); 
  /* 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! 
   */ 
  nsock->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) 
    return -1; 
 
  LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) && 
             ((((struct sockaddr_in *)name)->sin_family) == AF_INET)), 
             sock_set_errno(sock, err_to_errno(ERR_ARG)); 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)); 
 
  sock = get_socket(s); 
  if (!sock) { 
    return -1; 
  } 
 
  netconn_delete(sock->conn); 
 
  sys_sem_wait(socksem); 
  if (sock->lastdata) { 
    netbuf_delete(sock->lastdata); 
  } 
  sock->lastdata   = NULL; 
  sock->lastoffset = 0; 
  sock->conn       = NULL; 
  sock_set_errno(sock, 0); 
  sys_sem_signal(socksem); 
  return 0; 
} 
 
int 
lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) 
{ 
  struct lwip_socket *sock; 
  err_t err; 
 
  sock = get_socket(s); 
  if (!sock) 
    return -1; 
 
  LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) && 
             ((((struct sockaddr_in *)name)->sin_family) == AF_INET)), 
             sock_set_errno(sock, err_to_errno(ERR_ARG)); 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; 
} 
 
/** 
 * 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: need TCP_LISTEN_BACKLOG=1) 
 * @return 0 on success, non-zero on failure 
 */ 
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) 
    return -1; 
 
  /* limit the "backlog" parameter to fit in an u8_t */ 
  if (backlog < 0) { 
    backlog = 0; 
  } 
  if (backlog > 0xff) { 
    backlog = 0xff; 
  } 
 
  err = netconn_listen_with_backlog(sock->conn, 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, int len, unsigned int flags, 
        struct sockaddr *from, socklen_t *fromlen) 
{ 
  struct lwip_socket *sock; 
  struct netbuf      *buf; 
  u16_t               buflen, copylen, off = 0; 
  struct ip_addr     *addr; 
  u16_t               port; 
  u8_t                done = 0; 
 
  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %d, 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", (void*)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) || (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. */ 
      sock->lastdata = buf = netconn_recv(sock->conn); 
      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv netbuf=%p\n", (void*)buf)); 
 
      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, (((sock->conn->pcb.ip!=NULL) && (sock->conn->err==ERR_OK))?ETIMEDOUT:err_to_errno(sock->conn->err))); 
        return 0; 
      } 
    } 
 
    buflen = netbuf_len(buf); 
    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%d len=%d off=%d sock->lastoffset=%d\n", buflen, len, off, sock->lastoffset)); 
 
    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, (u8_t*)mem + off, copylen, sock->lastoffset); 
 
    off += copylen; 
 
    if (netconn_type(sock->conn) == NETCONN_TCP) { 
      len -= copylen; 
      if ( (len <= 0) || (buf->p->flags & PBUF_FLAG_PUSH) || !sock->rcvevent) { 
        done = 1; 
      } 
    } else { 
      done = 1; 
    } 
 
    /* 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 ((sock->conn->type == NETCONN_TCP) && (buflen - copylen > 0)) { 
        sock->lastdata = buf; 
        sock->lastoffset += copylen; 
        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", (void*)buf)); 
      } else { 
        sock->lastdata = NULL; 
        sock->lastoffset = 0; 
        LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", (void*)buf)); 
        netbuf_delete(buf); 
      } 
    } else { 
      done = 1; 
    } 
  } while (!done); 
 
  /* Check to see from where the data was.*/ 
  if (from && fromlen) { 
    struct sockaddr_in sin; 
 
    if (netconn_type(sock->conn) == NETCONN_TCP) { 
      addr = (struct ip_addr*)&(sin.sin_addr.s_addr); 
      netconn_getaddr(sock->conn, addr, &port, 0); 
    } else { 
      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); 
 
    SMEMCPY(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, off)); 
  } else { 
#if SOCKETS_DEBUG 
    struct sockaddr_in sin; 
 
    if (netconn_type(sock->conn) == NETCONN_TCP) { 
      addr = (struct ip_addr*)&(sin.sin_addr.s_addr); 
      netconn_getaddr(sock->conn, addr, &port, 0); 
    } else { 
      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, off)); 
#endif /*  SOCKETS_DEBUG */ 
  } 
 
  sock_set_errno(sock, 0); 
  return off; 
} 
 
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, const void *data, int size, unsigned int flags) 
{ 
  struct lwip_socket *sock; 
  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) 
    return -1; 
 
  if (sock->conn->type!=NETCONN_TCP) { 
#if (LWIP_UDP || LWIP_RAW) 
    return lwip_sendto(s, data, size, flags, NULL, 0); 
#else 
    sock_set_errno(sock, err_to_errno(ERR_ARG)); 
    return -1; 
#endif /* (LWIP_UDP || LWIP_RAW) */ 
  } 
 
  err = netconn_write(sock->conn, data, size, NETCONN_COPY | ((flags & MSG_MORE)?NETCONN_MORE:0)); 
 
  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d size=%d\n", s, err, size)); 
  sock_set_errno(sock, err_to_errno(err)); 
  return (err==ERR_OK?size:-1); 
} 
 
int 
lwip_sendto(int s, const void *data, int size, unsigned int flags, 
       struct sockaddr *to, socklen_t tolen) 
{ 
  struct lwip_socket *sock; 
  struct ip_addr remote_addr; 
  int err; 
#if !LWIP_TCPIP_CORE_LOCKING 
  struct netbuf buf; 
  u16_t remote_port; 
#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 
    sock_set_errno(sock, err_to_errno(ERR_ARG)); 
    return -1; 
#endif /* LWIP_TCP */ 
  } 
 
  LWIP_ASSERT("lwip_sendto: size must fit in u16_t", 
              ((size >= 0) && (size <= 0xffff))); 
  LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) || 
             ((tolen == sizeof(struct sockaddr_in)) && 
             ((((struct sockaddr_in *)to)->sin_family) == AF_INET))), 
             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); 
 
#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; 
   
    p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); 
    if (p == NULL) { 
      err = ERR_MEM; 
    } else { 
      p->payload = (void*)data; 
      p->len = p->tot_len = size; 
       
      remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr; 
       
      LOCK_TCPIP_CORE(); 
      if (sock->conn->type==NETCONN_RAW) { 
        err = sock->conn->err = raw_sendto(sock->conn->pcb.raw, p, &remote_addr); 
      } else { 
        err = sock->conn->err = udp_sendto(sock->conn->pcb.udp, p, &remote_addr, ntohs(((struct sockaddr_in *)to)->sin_port)); 
      } 
      UNLOCK_TCPIP_CORE(); 
       
      pbuf_free(p); 
    } 
  } 
#else 
  /* initialize a buffer */ 
  buf.p = buf.ptr = NULL; 
  if (to) { 
    remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr; 
    remote_port      = ntohs(((struct sockaddr_in *)to)->sin_port); 
    buf.addr         = &remote_addr; 
    buf.port         = remote_port; 
  } else { 
    remote_addr.addr = 0; 
    remote_port      = 0; 
    buf.addr         = NULL; 
    buf.port         = 0; 
  } 
 
  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", remote_port)); 
     
  /* make the buffer point to the data that should be sent */ 
  if ((err = netbuf_ref(&buf, data, size)) == ERR_OK) { 
    /* send the data */ 
    err = netconn_send(sock->conn, &buf); 
  } 
 
  /* deallocated the buffer */ 
  if (buf.p != NULL) { 
    pbuf_free(buf.p); 
  } 
#endif /* LWIP_TCPIP_CORE_LOCKING */ 
  sock_set_errno(sock, err_to_errno(err)); 
  return (err==ERR_OK?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)); 
    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(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, int 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; 
 *                out: set of sockets that had read events 
 * @param writeset in: set of sockets to check for write events; 
 *                 out: set of sockets that had write events 
 * @param exceptset not yet implemented 
 * @return number of sockets that had events (read+write) 
 */ 
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; 
} 
 
 
/** 
 * Processing exceptset is not yet implemented. 
 */ 
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 */ 
  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)); 
      if(msectimeout == 0) 
        msectimeout = 1; 
    } 
     
    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; 
} 
 
/** 
 * 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_socket *sock; 
  struct lwip_select_cb *scb; 
 
  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_sem_wait(socksem); 
      if (conn->socket < 0) { 
        if (evt == NETCONN_EVT_RCVPLUS) { 
          conn->socket--; 
        } 
        sys_sem_signal(socksem); 
        return; 
      } 
      sys_sem_signal(socksem); 
    } 
 
    sock = get_socket(s); 
    if (!sock) { 
      return; 
    } 
  } else { 
    return; 
  } 
 
  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; 
    default: 
      LWIP_ASSERT("unknown event", 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; 
    } 
  } 
} 
 
/** 
 * Unimplemented: Close one end of a full-duplex connection. 
 * Currently, the full connection is closed. 
 */ 
int 
lwip_shutdown(int s, int how) 
{ 
  LWIP_UNUSED_ARG(how); 
  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); 
  return lwip_close(s); /* XXX temporary hack until proper implementation */ 
} 
 
static int 
lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) 
{ 
  struct lwip_socket *sock; 
  struct sockaddr_in sin; 
  struct ip_addr 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=%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); 
 
  SMEMCPY(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_socket *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; 
#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; 
  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_socket *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: 
      if (sock->err == 0) { 
        sock_set_errno(sock, err_to_errno(sock->conn->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 = sock->conn->recv_timeout; 
      break; 
#endif /* LWIP_SO_RCVTIMEO */ 
#if LWIP_SO_RCVBUF 
    case SO_RCVBUF: 
      *(int *)optval = sock->conn->recv_bufsize; 
      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*/ 
    }  /* 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: 
      ((struct in_addr*) optval)->s_addr = sock->conn->pcb.udp->multicast_ip.addr; 
      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%x\n", 
                  s, *(u32_t *)optval)); 
      break; 
#endif /* LWIP_IGMP */ 
    }  /* switch (optname) */ 
    break; 
 
#if LWIP_TCP 
/* 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->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 */ 
 
    }  /* 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; 
    }  /* switch (optname) */ 
    break; 
#endif /* LWIP_UDP */ 
  } /* 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_socket *sock = get_socket(s); 
  int 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_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; 
  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_socket *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: 
      sock->conn->recv_timeout = ( *(int*)optval ); 
      break; 
#endif /* LWIP_SO_RCVTIMEO */ 
#if LWIP_SO_RCVBUF 
    case SO_RCVBUF: 
      sock->conn->recv_bufsize = ( *(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 */ 
    }  /* 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, ..) -> %u\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, ..)-> %u\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: 
      sock->conn->pcb.udp->multicast_ip.addr = ((struct in_addr*) optval)->s_addr; 
      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; 
        if(optname == IP_ADD_MEMBERSHIP){ 
          data->err = igmp_joingroup((struct ip_addr*)&(imr->imr_interface.s_addr), (struct ip_addr*)&(imr->imr_multiaddr.s_addr)); 
        } else { 
          data->err = igmp_leavegroup((struct ip_addr*)&(imr->imr_interface.s_addr), (struct ip_addr*)&(imr->imr_multiaddr.s_addr)); 
        } 
        if(data->err != ERR_OK) { 
          data->err = EADDRNOTAVAIL; 
        } 
      } 
      break; 
#endif /* LWIP_IGMP */ 
    }  /* switch (optname) */ 
    break; 
 
#if LWIP_TCP 
/* 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->keep_idle = (u32_t)(*(int*)optval); 
      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %lu\n", 
                  s, sock->conn->pcb.tcp->keep_idle)); 
      break; 
 
#if LWIP_TCP_KEEPALIVE 
    case TCP_KEEPIDLE: 
      sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval); 
      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %lu\n", 
                  s, sock->conn->pcb.tcp->keep_idle)); 
      break; 
    case TCP_KEEPINTVL: 
      sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval); 
      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %lu\n", 
                  s, sock->conn->pcb.tcp->keep_intvl)); 
      break; 
    case TCP_KEEPCNT: 
      sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval); 
      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %lu\n", 
                  s, sock->conn->pcb.tcp->keep_cnt)); 
      break; 
#endif /* LWIP_TCP_KEEPALIVE */ 
 
    }  /* switch (optname) */ 
    break; 
#endif /* LWIP_TCP*/ 
#if LWIP_UDP && LWIP_UDPLITE 
  /* Level: IPPROTO_UDPLITE */ 
  case IPPROTO_UDPLITE: 
    switch (optname) { 
    case UDPLITE_SEND_CSCOV: 
      if ((*(int*)optval != 0) && (*(int*)optval < 8)) { 
        /* don't allow illegal values! */ 
        sock->conn->pcb.udp->chksum_len_tx = 8; 
      } else { 
        sock->conn->pcb.udp->chksum_len_tx = *(int*)optval; 
      } 
      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n", 
                  s, (*(int*)optval)) ); 
      break; 
    case UDPLITE_RECV_CSCOV: 
      if ((*(int*)optval != 0) && (*(int*)optval < 8)) { 
        /* don't allow illegal values! */ 
        sock->conn->pcb.udp->chksum_len_rx = 8; 
      } else { 
        sock->conn->pcb.udp->chksum_len_rx = *(int*)optval; 
      } 
      LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n", 
                  s, (*(int*)optval)) ); 
      break; 
    }  /* switch (optname) */ 
    break; 
#endif /* LWIP_UDP */ 
  }  /* switch (level) */ 
  sys_sem_signal(sock->conn->op_completed); 
} 
 
int 
lwip_ioctl(int s, long cmd, void *argp) 
{ 
  struct lwip_socket *sock = get_socket(s); 
  u16_t buflen = 0; 
 
  if (!sock) 
    return -1; 
 
  switch (cmd) { 
  case FIONREAD: 
    if (!argp) { 
      sock_set_errno(sock, EINVAL); 
      return -1; 
    } 
 
    SYS_ARCH_GET(sock->conn->recv_avail, *((u16_t*)argp)); 
 
    /* Check if there is data left from the last recv operation. /maq 041215 */ 
    if (sock->lastdata) { 
      buflen = netbuf_len(sock->lastdata); 
      buflen -= sock->lastoffset; 
 
      *((u16_t*)argp) += buflen; 
    } 
 
    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; 
  } /* switch (cmd) */ 
} 
 
#endif /* LWIP_SOCKET */