[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Minios-devel] [UNIKRAFT/LIBMICROPYTHON PATCH 3/5] Add glue code.



Hi Vlad,

On 07.10.19, 12:59, "Minios-devel on behalf of Vlad-Andrei BĂDOIU (78692)" 
<minios-devel-bounces@xxxxxxxxxxxxxxxxxxxx on behalf of 
vlad_andrei.badoiu@xxxxxxxxxxxxxxx> wrote:

    Hey Felipe,
    
    Thank you for the patch. I have several comments:
    
    * The main.c file seems to be similar to the file unix/main.c. Wouldn't 
    it be cleaner
    
    to apply a patch on unix/main.c?
    
Yes, it would :), I'll fix it in v2.

    * The same applies for extmod/modlwip.c and extmod/modusocket.c .

For modlwip.c yes, I'll fix it. modusocket.c doesn't have a version under the 
unix/ sub-directory, only under other OSes (e.g,. Zephyr) so we better keep it 
as is.

-- Felipe
    
    Thanks!
    
    Vlad
    
    On 25.09.2019 11:07, Felipe Huici wrote:
    > Add code to run the main interpreter loop and to provide support for
    > lwip/sockets.
    >
    > Signed-off-by: Felipe Huici <felipe.huici@xxxxxxxxx>
    > ---
    >   extmod/modlwip.c       | 1410 
++++++++++++++++++++++++++++++++++++++++++++++++
    >   extmod/modusocket.c    |  624 +++++++++++++++++++++
    >   include/modlwip.h      |  111 ++++
    >   include/mpconfigport.h |  320 +++++++++++
    >   main.c                 |  543 +++++++++++++++++++
    >   5 files changed, 3008 insertions(+)
    >   create mode 100644 extmod/modlwip.c
    >   create mode 100644 extmod/modusocket.c
    >   create mode 100644 include/modlwip.h
    >   create mode 100644 include/mpconfigport.h
    >   create mode 100644 main.c
    >
    > diff --git a/extmod/modlwip.c b/extmod/modlwip.c
    > new file mode 100644
    > index 0000000..3e085f7
    > --- /dev/null
    > +++ b/extmod/modlwip.c
    > @@ -0,0 +1,1410 @@
    > +/*
    > + * This file is part of the MicroPython project, http://micropython.org/
    > + *
    > + * The MIT License (MIT)
    > + *
    > + * Copyright (c) 2013, 2014 Damien P. George
    > + * Copyright (c) 2015 Galen Hazelwood
    > + * Copyright (c) 2015-2017 Paul Sokolovsky
    > + *
    > + * Permission is hereby granted, free of charge, to any person obtaining 
a copy
    > + * of this software and associated documentation files (the "Software"), 
to deal
    > + * in the Software without restriction, including without limitation the 
rights
    > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or 
sell
    > + * copies of the Software, and to permit persons to whom the Software is
    > + * furnished to do so, subject to the following conditions:
    > + *
    > + * The above copyright notice and this permission notice shall be 
included in
    > + * all copies or substantial portions of the Software.
    > + *
    > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR
    > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY,
    > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
SHALL THE
    > + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
ARISING FROM,
    > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
DEALINGS IN
    > + * THE SOFTWARE.
    > + */
    > +
    > +#include <string.h>
    > +#include <stdio.h>
    > +
    > +#include <uk/print.h>
    > +#include <netif/uknetdev.h>
    > +
    > +#include "lwip/init.h"
    > +#include "lwip/timeouts.h"
    > +#include "lwip/tcp.h"
    > +#include "lwip/udp.h"
    > +#include "lwip/dns.h"
    > +#include "lwip/igmp.h"
    > +
    > +#include "py/nlr.h"
    > +#include "py/objlist.h"
    > +#include "py/runtime.h"
    > +#include "py/stream.h"
    > +#include "py/mperrno.h"
    > +#include "py/mphal.h"
    > +
    > +#include "lib/netutils/netutils.h"
    > +
    > +#define DEBUG_printf uk_pr_debug
    > +
    > +// All socket options should be globally distinct,
    > +// because we ignore option levels for efficiency.
    > +#define IP_ADD_MEMBERSHIP 0x400
    > +
    > +// For compatibilily with older lwIP versions.
    > +#ifndef ip_set_option
    > +#define ip_set_option(pcb, opt)   ((pcb)->so_options |= (opt))
    > +#endif
    > +#ifndef ip_reset_option
    > +#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt))
    > +#endif
    > +
    > +#ifdef MICROPY_PY_LWIP_SLIP
    > +#include "netif/slipif.h"
    > +#include "lwip/sio.h"
    > +#endif
    > +
    > +#ifdef MICROPY_PY_LWIP_SLIP
    > 
+/******************************************************************************/
    > +// Slip object for modlwip. Requires a serial driver for the port that 
supports
    > +// the lwip serial callback functions.
    > +
    > +typedef struct _lwip_slip_obj_t {
    > +    mp_obj_base_t base;
    > +    struct netif lwip_netif;
    > +} lwip_slip_obj_t;
    > +
    > +// Slip object is unique for now. Possibly can fix this later. FIXME
    > +STATIC lwip_slip_obj_t lwip_slip_obj;
    > +
    > +// Declare these early.
    > +void mod_lwip_register_poll(void (*poll)(void *arg), void *poll_arg);
    > +void mod_lwip_deregister_poll(void (*poll)(void *arg), void *poll_arg);
    > +
    > +STATIC void slip_lwip_poll(void *netif) {
    > +    slipif_poll((struct netif*)netif);
    > +}
    > +
    > +STATIC const mp_obj_type_t lwip_slip_type;
    > +
    > +// lwIP SLIP callback functions
    > +sio_fd_t sio_open(u8_t dvnum) {
    > +    // We support singleton SLIP interface, so just return any truish 
value.
    > +    return (sio_fd_t)1;
    > +}
    > +
    > +void sio_send(u8_t c, sio_fd_t fd) {
    > +    mp_obj_type_t *type = mp_obj_get_type(MP_STATE_VM(lwip_slip_stream));
    > +    int error;
    > +    type->stream_p->write(MP_STATE_VM(lwip_slip_stream), &c, 1, &error);
    > +}
    > +
    > +u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len) {
    > +    mp_obj_type_t *type = mp_obj_get_type(MP_STATE_VM(lwip_slip_stream));
    > +    int error;
    > +    mp_uint_t out_sz = 
type->stream_p->read(MP_STATE_VM(lwip_slip_stream), data, len, &error);
    > +    if (out_sz == MP_STREAM_ERROR) {
    > +        if (mp_is_nonblocking_error(error)) {
    > +            return 0;
    > +        }
    > +        // Can't do much else, can we?
    > +        return 0;
    > +    }
    > +    return out_sz;
    > +}
    > +
    > +// constructor lwip.slip(device=integer, iplocal=string, ipremote=string)
    > +STATIC mp_obj_t lwip_slip_make_new(mp_obj_t type_in, size_t n_args, 
size_t n_kw, const mp_obj_t *args) {
    > +    mp_arg_check_num(n_args, n_kw, 3, 3, false);
    > +
    > +    lwip_slip_obj.base.type = &lwip_slip_type;
    > +
    > +    MP_STATE_VM(lwip_slip_stream) = args[0];
    > +
    > +    ip_addr_t iplocal, ipremote;
    > +    if (!ipaddr_aton(mp_obj_str_get_str(args[1]), &iplocal)) {
    > +        mp_raise_ValueError("not a valid local IP");
    > +    }
    > +    if (!ipaddr_aton(mp_obj_str_get_str(args[2]), &ipremote)) {
    > +        mp_raise_ValueError("not a valid remote IP");
    > +    }
    > +
    > +    struct netif *n = &lwip_slip_obj.lwip_netif;
    > +    if (netif_add(n, &iplocal, IP_ADDR_BROADCAST, &ipremote, NULL, 
slipif_init, ip_input) == NULL) {
    > +       mp_raise_ValueError("out of memory");
    > +    }
    > +    netif_set_up(n);
    > +    netif_set_default(n);
    > +    mod_lwip_register_poll(slip_lwip_poll, n);
    > +
    > +    return (mp_obj_t)&lwip_slip_obj;
    > +}
    > +
    > +STATIC mp_obj_t lwip_slip_status(mp_obj_t self_in) {
    > +    // Null function for now.
    > +    return mp_const_none;
    > +}
    > +
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_1(lwip_slip_status_obj, lwip_slip_status);
    > +
    > +STATIC const mp_rom_map_elem_t lwip_slip_locals_dict_table[] = {
    > +    { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&lwip_slip_status_obj) },
    > +};
    > +
    > +STATIC MP_DEFINE_CONST_DICT(lwip_slip_locals_dict, 
lwip_slip_locals_dict_table);
    > +
    > +STATIC const mp_obj_type_t lwip_slip_type = {
    > +    { &mp_type_type },
    > +    .name = MP_QSTR_slip,
    > +    .make_new = lwip_slip_make_new,
    > +    .locals_dict = (mp_obj_dict_t*)&lwip_slip_locals_dict,
    > +};
    > +
    > +#endif // MICROPY_PY_LWIP_SLIP
    > +
    > 
+/******************************************************************************/
    > +// Table to convert lwIP err_t codes to socket errno codes, from the lwIP
    > +// socket API.
    > +
    > +// Extension to lwIP error codes
    > +#define _ERR_BADF -16
    > +// TODO: We just know that change happened somewhere between 1.4.0 and 
1.4.1,
    > +// investigate in more detail.
    > +#if LWIP_VERSION < 0x01040100
    > +static const int error_lookup_table[] = {
    > +    0,                /* ERR_OK          0      No error, everything OK. 
*/
    > +    MP_ENOMEM,        /* ERR_MEM        -1      Out of memory error.     
*/
    > +    MP_ENOBUFS,       /* ERR_BUF        -2      Buffer error.            
*/
    > +    MP_EWOULDBLOCK,   /* ERR_TIMEOUT    -3      Timeout                  
*/
    > +    MP_EHOSTUNREACH,  /* ERR_RTE        -4      Routing problem.         
*/
    > +    MP_EINPROGRESS,   /* ERR_INPROGRESS -5      Operation in progress    
*/
    > +    MP_EINVAL,        /* ERR_VAL        -6      Illegal value.           
*/
    > +    MP_EWOULDBLOCK,   /* ERR_WOULDBLOCK -7      Operation would block.   
*/
    > +
    > +    MP_ECONNABORTED,  /* ERR_ABRT       -8      Connection aborted.      
*/
    > +    MP_ECONNRESET,    /* ERR_RST        -9      Connection reset.        
*/
    > +    MP_ENOTCONN,      /* ERR_CLSD       -10     Connection closed.       
*/
    > +    MP_ENOTCONN,      /* ERR_CONN       -11     Not connected.           
*/
    > +    MP_EIO,           /* ERR_ARG        -12     Illegal argument.        
*/
    > +    MP_EADDRINUSE,    /* ERR_USE        -13     Address in use.          
*/
    > +    -1,               /* ERR_IF         -14     Low-level netif error    
*/
    > +    MP_EALREADY,      /* ERR_ISCONN     -15     Already connected.       
*/
    > +    MP_EBADF,         /* _ERR_BADF      -16     Closed socket (null pcb) 
*/
    > +};
    > +#else
    > +static const int error_lookup_table[] = {
    > +    0,                /* ERR_OK          0      No error, everything OK. 
*/
    > +    MP_ENOMEM,        /* ERR_MEM        -1      Out of memory error.     
*/
    > +    MP_ENOBUFS,       /* ERR_BUF        -2      Buffer error.            
*/
    > +    MP_EWOULDBLOCK,   /* ERR_TIMEOUT    -3      Timeout                  
*/
    > +    MP_EHOSTUNREACH,  /* ERR_RTE        -4      Routing problem.         
*/
    > +    MP_EINPROGRESS,   /* ERR_INPROGRESS -5      Operation in progress    
*/
    > +    MP_EINVAL,        /* ERR_VAL        -6      Illegal value.           
*/
    > +    MP_EWOULDBLOCK,   /* ERR_WOULDBLOCK -7      Operation would block.   
*/
    > +
    > +    MP_EADDRINUSE,    /* ERR_USE        -8      Address in use.          
*/
    > +    MP_EALREADY,      /* ERR_ISCONN     -9      Already connected.       
*/
    > +    MP_ECONNABORTED,  /* ERR_ABRT       -10     Connection aborted.      
*/
    > +    MP_ECONNRESET,    /* ERR_RST        -11     Connection reset.        
*/
    > +    MP_ENOTCONN,      /* ERR_CLSD       -12     Connection closed.       
*/
    > +    MP_ENOTCONN,      /* ERR_CONN       -13     Not connected.           
*/
    > +    MP_EIO,           /* ERR_ARG        -14     Illegal argument.        
*/
    > +    -1,               /* ERR_IF         -15     Low-level netif error    
*/
    > +    MP_EBADF,         /* _ERR_BADF      -16     Closed socket (null pcb) 
*/
    > +};
    > +#endif
    > +
    > 
+/*******************************************************************************/
    > +// The socket object provided by lwip.socket.
    > +
    > +#define MOD_NETWORK_AF_INET (2)
    > +#define MOD_NETWORK_AF_INET6 (10)
    > +
    > +#define MOD_NETWORK_SOCK_STREAM (1)
    > +#define MOD_NETWORK_SOCK_DGRAM (2)
    > +#define MOD_NETWORK_SOCK_RAW (3)
    > +
    > +typedef struct _lwip_socket_obj_t {
    > +    mp_obj_base_t base;
    > +
    > +    volatile union {
    > +        struct tcp_pcb *tcp;
    > +        struct udp_pcb *udp;
    > +    } pcb;
    > +    volatile union {
    > +        struct pbuf *pbuf;
    > +        struct tcp_pcb *connection;
    > +    } incoming;
    > +    mp_obj_t callback;
    > +    byte peer[4];
    > +    mp_uint_t peer_port;
    > +    mp_uint_t timeout;
    > +    uint16_t recv_offset;
    > +
    > +    uint8_t domain;
    > +    uint8_t type;
    > +
    > +    #define STATE_NEW 0
    > +    #define STATE_CONNECTING 1
    > +    #define STATE_CONNECTED 2
    > +    #define STATE_PEER_CLOSED 3
    > +    // Negative value is lwIP error
    > +    int8_t state;
    > +} lwip_socket_obj_t;
    > +
    > +static inline void poll_sockets(void) {
    > +#ifdef MICROPY_EVENT_POLL_HOOK
    > +    MICROPY_EVENT_POLL_HOOK;
    > +#else
    > +    mp_hal_delay_ms(1);
    > +#endif
    > +
    > +    /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
    > +    uknetdev_poll_all();
    > +    sys_check_timeouts();
    > +    /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
    > +}
    > +
    > 
+/*******************************************************************************/
    > +// Callback functions for the lwIP raw API.
    > +
    > +static inline void exec_user_callback(lwip_socket_obj_t *socket) {
    > +    if (socket->callback != MP_OBJ_NULL) {
    > +        mp_call_function_1_protected(socket->callback, socket);
    > +    }
    > +}
    > +
    > +// Callback for incoming UDP packets. We simply stash the packet and the 
source address,
    > +// in case we need it for recvfrom.
    > +STATIC void _lwip_udp_incoming(void *arg, struct udp_pcb *upcb, struct 
pbuf *p, ip_addr_t *addr, u16_t port) {
    > +    lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
    > +
    > +    if (socket->incoming.pbuf != NULL) {
    > +        // That's why they call it "unreliable". No room in the inn, 
drop the packet.
    > +        pbuf_free(p);
    > +    } else {
    > +        socket->incoming.pbuf = p;
    > +        socket->peer_port = (mp_uint_t)port;
    > +        memcpy(&socket->peer, addr, sizeof(socket->peer));
    > +    }
    > +}
    > +
    > +// Callback for general tcp errors.
    > +STATIC void _lwip_tcp_error(void *arg, err_t err) {
    > +    lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
    > +
    > +    // Pass the error code back via the connection variable.
    > +    socket->state = err;
    > +    // If we got here, the lwIP stack either has deallocated or will 
deallocate the pcb.
    > +    socket->pcb.tcp = NULL;
    > +}
    > +
    > +// Callback for tcp connection requests. Error code err is unused. (See 
tcp.h)
    > +STATIC err_t _lwip_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t 
err) {
    > +    lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
    > +
    > +    socket->state = STATE_CONNECTED;
    > +    DEBUG_printf("Socket %p connected\n", socket);
    > +    return ERR_OK;
    > +}
    > +
    > +// By default, a child socket of listen socket is created with recv
    > +// handler which discards incoming pbuf's. We don't want to do that,
    > +// so set this handler which requests lwIP to keep pbuf's and deliver
    > +// them later. We cannot cache pbufs in child socket on Python side,
    > +// until it is created in accept().
    > +STATIC err_t _lwip_tcp_recv_unaccepted(void *arg, struct tcp_pcb *pcb, 
struct pbuf *p, err_t err) {
    > +    return ERR_BUF;
    > +}
    > +
    > +// "Poll" (idle) callback to be called ASAP after accept callback
    > +// to execute Python callback function, as it can't be executed
    > +// from accept callback itself.
    > +STATIC err_t _lwip_tcp_accept_finished(void *arg, struct tcp_pcb *pcb)
    > +{
    > +    lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
    > +    tcp_poll(pcb, NULL, 0);
    > +    exec_user_callback(socket);
    > +    return ERR_OK;
    > +}
    > +
    > +// Callback for incoming tcp connections.
    > +STATIC err_t _lwip_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t 
err) {
    > +    lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
    > +    tcp_recv(newpcb, _lwip_tcp_recv_unaccepted);
    > +
    > +    if (socket->incoming.connection != NULL) {
    > +        DEBUG_printf("_lwip_tcp_accept: Tried to queue >1 pcb waiting 
for accept\n");
    > +        // We need to handle this better. This single-level structure 
makes the
    > +        // backlog setting kind of pointless. FIXME
    > +        return ERR_BUF;
    > +    } else {
    > +        socket->incoming.connection = newpcb;
    > +        if (socket->callback != MP_OBJ_NULL) {
    > +            // Schedule accept callback to be called when lwIP is done
    > +            // with processing this incoming connection on its side and
    > +            // is idle.
    > +            tcp_poll(newpcb, _lwip_tcp_accept_finished, 1);
    > +        }
    > + DEBUG_printf("Accepted new connection on socket %p\n", socket);
    > +        return ERR_OK;
    > +    }
    > +}
    > +
    > +// Callback for inbound tcp packets.
    > +STATIC err_t _lwip_tcp_recv(void *arg, struct tcp_pcb *tcpb, struct pbuf 
*p, err_t err) {
    > +    lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
    > +
    > +    if (p == NULL) {
    > +        // Other side has closed connection.
    > +        DEBUG_printf("_lwip_tcp_recv[%p]: other side closed 
connection\n", socket);
    > +        socket->state = STATE_PEER_CLOSED;
    > +        exec_user_callback(socket);
    > +        return ERR_OK;
    > +    }
    > +
    > +    if (socket->incoming.pbuf == NULL) {
    > +        socket->incoming.pbuf = p;
    > +    } else {
    > +        #ifdef SOCKET_SINGLE_PBUF
    > +        return ERR_BUF;
    > +        #else
    > +        pbuf_cat(socket->incoming.pbuf, p);
    > +        #endif
    > +    }
    > +
    > +    exec_user_callback(socket);
    > +
    > +    return ERR_OK;
    > +}
    > +
    > 
+/*******************************************************************************/
    > +// Functions for socket send/receive operations. Socket send/recv and 
friends call
    > +// these to do the work.
    > +
    > +// Helper function for send/sendto to handle UDP packets.
    > +STATIC mp_uint_t lwip_udp_send(lwip_socket_obj_t *socket, const byte 
*buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) {
    > +    if (len > 0xffff) {
    > +        // Any packet that big is probably going to fail the pbuf_alloc 
anyway, but may as well try
    > +        len = 0xffff;
    > +    }
    > +
    > +    // FIXME: maybe PBUF_ROM?
    > +    struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
    > +    if (p == NULL) {
    > +        *_errno = MP_ENOMEM;
    > +        return -1;
    > +    }
    > +
    > +    memcpy(p->payload, buf, len);
    > +
    > +    err_t err;
    > +    if (ip == NULL) {
    > +        err = udp_send(socket->pcb.udp, p);
    > +    } else {
    > +        ip_addr_t dest;
    > +        IP4_ADDR(&dest, ip[0], ip[1], ip[2], ip[3]);
    > +        err = udp_sendto(socket->pcb.udp, p, &dest, port);
    > +    }
    > +
    > +    pbuf_free(p);
    > +
    > +    // udp_sendto can return 1 on occasion for ESP8266 port.  It's not 
known why
    > +    // but it seems that the send actually goes through without error in 
this case.
    > +    // So we treat such cases as a success until further investigation.
    > +    if (err != ERR_OK && err != 1) {
    > +        *_errno = error_lookup_table[-err];
    > +        return -1;
    > +    }
    > +
    > +    return len;
    > +}
    > +
    > +// Helper function for recv/recvfrom to handle UDP packets
    > +STATIC mp_uint_t lwip_udp_receive(lwip_socket_obj_t *socket, byte *buf, 
mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) {
    > +
    > +    if (socket->incoming.pbuf == NULL) {
    > +        if (socket->timeout != -1) {
    > +            for (mp_uint_t retries = socket->timeout / 100; retries--;) {
    > +                mp_hal_delay_ms(100);
    > +                if (socket->incoming.pbuf != NULL) break;
    > +            }
    > +            if (socket->incoming.pbuf == NULL) {
    > +                *_errno = MP_ETIMEDOUT;
    > +                return -1;
    > +            }
    > +        } else {
    > +            while (socket->incoming.pbuf == NULL) {
    > +                poll_sockets();
    > +            }
    > +        }
    > +    }
    > +
    > +    if (ip != NULL) {
    > +        memcpy(ip, &socket->peer, sizeof(socket->peer));
    > +        *port = socket->peer_port;
    > +    }
    > +
    > +    struct pbuf *p = socket->incoming.pbuf;
    > +
    > +    u16_t result = pbuf_copy_partial(p, buf, ((p->tot_len > len) ? len : 
p->tot_len), 0);
    > +    pbuf_free(p);
    > +    socket->incoming.pbuf = NULL;
    > +
    > +    return (mp_uint_t) result;
    > +}
    > +
    > +// For use in stream virtual methods
    > +#define STREAM_ERROR_CHECK(socket) \
    > +        if (socket->state < 0) { \
    > +            *_errno = error_lookup_table[-socket->state]; \
    > +            return MP_STREAM_ERROR; \
    > +        } \
    > +        assert(socket->pcb.tcp);
    > +
    > +
    > +// Helper function for send/sendto to handle TCP packets
    > +STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte 
*buf, mp_uint_t len, int *_errno) {
    > +    // Check for any pending errors
    > +    STREAM_ERROR_CHECK(socket);
    > +
    > +    u16_t available = tcp_sndbuf(socket->pcb.tcp);
    > +
    > +    if (available == 0) {
    > +        // Non-blocking socket
    > +        if (socket->timeout == 0) {
    > +            *_errno = MP_EAGAIN;
    > +            return MP_STREAM_ERROR;
    > +        }
    > +
    > +        mp_uint_t start = mp_hal_ticks_ms();
    > +        // Assume that STATE_PEER_CLOSED may mean half-closed 
connection, where peer closed it
    > +        // sending direction, but not receiving. Consequently, check for 
both STATE_CONNECTED
    > +        // and STATE_PEER_CLOSED as normal conditions and still waiting 
for buffers to be sent.
    > +        // If peer fully closed socket, we would have socket->state set 
to ERR_RST (connection
    > +        // reset) by error callback.
    > +        // Avoid sending too small packets, so wait until at least 16 
bytes available
    > +        while (socket->state >= STATE_CONNECTED && (available = 
tcp_sndbuf(socket->pcb.tcp)) < 16) {
    > +            if (socket->timeout != -1 && mp_hal_ticks_ms() - start > 
socket->timeout) {
    > +                *_errno = MP_ETIMEDOUT;
    > +                return MP_STREAM_ERROR;
    > +            }
    > +            poll_sockets();
    > +        }
    > +
    > +        // While we waited, something could happen
    > +        STREAM_ERROR_CHECK(socket);
    > +    }
    > +
    > +    u16_t write_len = MIN(available, len);
    > +
    > +    err_t err = tcp_write(socket->pcb.tcp, buf, write_len, 
TCP_WRITE_FLAG_COPY);
    > +    DEBUG_printf("Write to socket %p @%p %u bytes: %d\n", socket, buf, 
write_len, err);
    > +
    > +    if (err != ERR_OK) {
    > +        *_errno = error_lookup_table[-err];
    > +        return MP_STREAM_ERROR;
    > +    }
    > +
    > +    return write_len;
    > +}
    > +
    > +// Helper function for recv/recvfrom to handle TCP packets
    > +STATIC mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, 
mp_uint_t len, int *_errno) {
    > +    // Check for any pending errors
    > +    STREAM_ERROR_CHECK(socket);
    > +
    > +    if (socket->incoming.pbuf == NULL) {
    > +
    > +        // Non-blocking socket
    > +        if (socket->timeout == 0) {
    > +            if (socket->state == STATE_PEER_CLOSED) {
    > +                return 0;
    > +            }
    > +            *_errno = MP_EAGAIN;
    > +            return -1;
    > +        }
    > +
    > +        mp_uint_t start = mp_hal_ticks_ms();
    > + DEBUG_printf("Wait for data on socket %p to receive...\n", socket);
    > +        while (socket->state == STATE_CONNECTED && socket->incoming.pbuf 
== NULL) {
    > +            if (socket->timeout != -1 && mp_hal_ticks_ms() - start > 
socket->timeout) {
    > +                *_errno = MP_ETIMEDOUT;
    > +                return -1;
    > +            }
    > +            poll_sockets();
    > +        }
    > + DEBUG_printf("Done\n");
    > +
    > +        if (socket->state == STATE_PEER_CLOSED) {
    > +            if (socket->incoming.pbuf == NULL) {
    > +                // socket closed and no data left in buffer
    > +                return 0;
    > +            }
    > +        } else if (socket->state != STATE_CONNECTED) {
    > +            assert(socket->state < 0);
    > +            *_errno = error_lookup_table[-socket->state];
    > +            return -1;
    > +        }
    > +    }
    > +
    > +    assert(socket->pcb.tcp != NULL);
    > +
    > +    struct pbuf *p = socket->incoming.pbuf;
    > +
    > +    mp_uint_t remaining = p->len - socket->recv_offset;
    > +    if (len > remaining) {
    > +        len = remaining;
    > +    }
    > +
    > +    memcpy(buf, (byte*)p->payload + socket->recv_offset, len);
    > +
    > +    remaining -= len;
    > +    if (remaining == 0) {
    > +        socket->incoming.pbuf = p->next;
    > +        // If we don't ref here, free() will free the entire chain,
    > +        // if we ref, it does what we need: frees 1st buf, and decrements
    > +        // next buf's refcount back to 1.
    > +        pbuf_ref(p->next);
    > +        pbuf_free(p);
    > +        socket->recv_offset = 0;
    > +    } else {
    > +        socket->recv_offset += len;
    > +    }
    > +    tcp_recved(socket->pcb.tcp, len);
    > +
    > +    return len;
    > +}
    > +
    > 
+/*******************************************************************************/
    > +// The socket functions provided by lwip.socket.
    > +
    > +STATIC const mp_obj_type_t lwip_socket_type;
    > +
    > +STATIC void lwip_socket_print(const mp_print_t *print, mp_obj_t self_in, 
mp_print_kind_t kind) {
    > +    lwip_socket_obj_t *self = self_in;
    > +    mp_printf(print, "<socket state=%d timeout=%d incoming=%p off=%d>", 
self->state, self->timeout,
    > +        self->incoming.pbuf, self->recv_offset);
    > +}
    > +
    > +// FIXME: Only supports two arguments at present
    > +STATIC mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, size_t 
n_args, size_t n_kw, const mp_obj_t *args) {
    > +    mp_arg_check_num(n_args, n_kw, 0, 4, false);
    > +
    > +    lwip_socket_obj_t *socket = 
m_new_obj_with_finaliser(lwip_socket_obj_t);
    > +    socket->base.type = (mp_obj_t)&lwip_socket_type;
    > +    socket->domain = MOD_NETWORK_AF_INET;
    > +    socket->type = MOD_NETWORK_SOCK_STREAM;
    > +    socket->callback = MP_OBJ_NULL;
    > +    if (n_args >= 1) {
    > +        socket->domain = mp_obj_get_int(args[0]);
    > +        if (n_args >= 2) {
    > +            socket->type = mp_obj_get_int(args[1]);
    > +        }
    > +    }
    > +
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM: socket->pcb.tcp = tcp_new(); break;
    > +        case MOD_NETWORK_SOCK_DGRAM: socket->pcb.udp = udp_new(); break;
    > +        //case MOD_NETWORK_SOCK_RAW: socket->pcb.raw = raw_new(); break;
    > +        default: mp_raise_OSError(MP_EINVAL);
    > +    }
    > +
    > +    if (socket->pcb.tcp == NULL) {
    > +        mp_raise_OSError(MP_ENOMEM);
    > +    }
    > +
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM: {
    > +            // Register the socket object as our callback argument.
    > +            tcp_arg(socket->pcb.tcp, (void*)socket);
    > +            // Register our error callback.
    > +            tcp_err(socket->pcb.tcp, _lwip_tcp_error);
    > +            break;
    > +        }
    > +        case MOD_NETWORK_SOCK_DGRAM: {
    > +            // Register our receive callback now. Since UDP sockets 
don't require binding or connection
    > +            // before use, there's no other good time to do it.
    > +            udp_recv(socket->pcb.udp, _lwip_udp_incoming, (void*)socket);
    > +            break;
    > +        }
    > +    }
    > +
    > +    socket->incoming.pbuf = NULL;
    > +    socket->timeout = -1;
    > +    socket->state = STATE_NEW;
    > +    socket->recv_offset = 0;
    > +    return socket;
    > +}
    > +
    > +STATIC mp_obj_t lwip_socket_close(mp_obj_t self_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +    bool socket_is_listener = false;
    > +    err_t err;
    > +
    > +    if (socket->pcb.tcp == NULL) {
    > +        return mp_const_none;
    > +    }
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM: {
    > +            if (socket->pcb.tcp->state == LISTEN) {
    > +                socket_is_listener = true;
    > +            } else {
    > +         /* Flush output queue */
    > +         tcp_output(socket->pcb.tcp);
    > +     }
    > +
    > +     err = tcp_close(socket->pcb.tcp);
    > +     DEBUG_printf("Closed socket %p: %d\n", socket, err);
    > +            if (err != ERR_OK) {
    > +                DEBUG_printf("lwip_close: had to call tcp_abort()\n");
    > +                tcp_abort(socket->pcb.tcp);
    > +            }
    > +            break;
    > +        }
    > +        case MOD_NETWORK_SOCK_DGRAM: udp_remove(socket->pcb.udp); break;
    > +        //case MOD_NETWORK_SOCK_RAW: raw_remove(socket->pcb.raw); break;
    > +    }
    > +    socket->pcb.tcp = NULL;
    > +    socket->state = _ERR_BADF;
    > +    if (socket->incoming.pbuf != NULL) {
    > +        if (!socket_is_listener) {
    > +            pbuf_free(socket->incoming.pbuf);
    > +        } else {
    > +     DEBUG_printf("Abort incoming connection request %p\n", 
socket->incoming.connection);
    > +            tcp_abort(socket->incoming.connection);
    > +        }
    > +        socket->incoming.pbuf = NULL;
    > +    }
    > +
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_1(lwip_socket_close_obj, 
lwip_socket_close);
    > +
    > +STATIC mp_obj_t lwip_socket_bind(mp_obj_t self_in, mp_obj_t addr_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +
    > +    uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
    > +    mp_uint_t port = netutils_parse_inet_addr(addr_in, ip, NETUTILS_BIG);
    > +
    > +    ip_addr_t bind_addr;
    > +    IP4_ADDR(&bind_addr, ip[0], ip[1], ip[2], ip[3]);
    > +
    > +    err_t err = ERR_ARG;
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM: {
    > +            err = tcp_bind(socket->pcb.tcp, &bind_addr, port);
    > +            break;
    > +        }
    > +        case MOD_NETWORK_SOCK_DGRAM: {
    > +            err = udp_bind(socket->pcb.udp, &bind_addr, port);
    > +            break;
    > +        }
    > +    }
    > +
    > +    if (err != ERR_OK) {
    > +        mp_raise_OSError(error_lookup_table[-err]);
    > +    }
    > +
    > +    DEBUG_printf("lwip_close: Bind socket %p\n", socket);
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_bind_obj, lwip_socket_bind);
    > +
    > +STATIC mp_obj_t lwip_socket_listen(mp_obj_t self_in, mp_obj_t 
backlog_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +    mp_int_t backlog = mp_obj_get_int(backlog_in);
    > +
    > +    if (socket->pcb.tcp == NULL) {
    > +        mp_raise_OSError(MP_EBADF);
    > +    }
    > +    if (socket->type != MOD_NETWORK_SOCK_STREAM) {
    > +        mp_raise_OSError(MP_EOPNOTSUPP);
    > +    }
    > +
    > +    struct tcp_pcb *new_pcb = tcp_listen_with_backlog(socket->pcb.tcp, 
(u8_t)backlog);
    > +    if (new_pcb == NULL) {
    > +        mp_raise_OSError(MP_ENOMEM);
    > +    }
    > +    socket->pcb.tcp = new_pcb;
    > +    tcp_accept(new_pcb, _lwip_tcp_accept);
    > +
    > +    DEBUG_printf("lwip_listen: Listen on socket %p\n", socket);
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_listen_obj, 
lwip_socket_listen);
    > +
    > +STATIC mp_obj_t lwip_socket_accept(mp_obj_t self_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +
    > +    if (socket->pcb.tcp == NULL) {
    > +        mp_raise_OSError(MP_EBADF);
    > +    }
    > +    if (socket->type != MOD_NETWORK_SOCK_STREAM) {
    > +        mp_raise_OSError(MP_EOPNOTSUPP);
    > +    }
    > +    // I need to do this because "tcp_accepted", later, is a macro.
    > +    struct tcp_pcb *listener = socket->pcb.tcp;
    > +    if (listener->state != LISTEN) {
    > +        mp_raise_OSError(MP_EINVAL);
    > +    }
    > +
    > +    // accept incoming connection
    > +    if (socket->incoming.connection == NULL) {
    > +        if (socket->timeout == 0) {
    > +            mp_raise_OSError(MP_EAGAIN);
    > +        } else if (socket->timeout != -1) {
    > +            for (mp_uint_t retries = socket->timeout / 100; retries--;) {
    > +                mp_hal_delay_ms(100);
    > +                if (socket->incoming.connection != NULL) break;
    > +            }
    > +            if (socket->incoming.connection == NULL) {
    > +                mp_raise_OSError(MP_ETIMEDOUT);
    > +            }
    > +        } else {
    > +     DEBUG_printf("Wait for connection on socket %p\n", socket);
    > +            while (socket->incoming.connection == NULL) {
    > +                poll_sockets();
    > +            }
    > +     DEBUG_printf("Done\n");
    > +        }
    > +    }
    > +
    > +    // create new socket object
    > +    lwip_socket_obj_t *socket2 = 
m_new_obj_with_finaliser(lwip_socket_obj_t);
    > +    socket2->base.type = (mp_obj_t)&lwip_socket_type;
    > +
    > +    // We get a new pcb handle...
    > +    socket2->pcb.tcp = socket->incoming.connection;
    > +    socket->incoming.connection = NULL;
    > +
    > +    // ...and set up the new socket for it.
    > +    socket2->domain = MOD_NETWORK_AF_INET;
    > +    socket2->type = MOD_NETWORK_SOCK_STREAM;
    > +    socket2->incoming.pbuf = NULL;
    > +    socket2->timeout = socket->timeout;
    > +    socket2->state = STATE_CONNECTED;
    > +    socket2->recv_offset = 0;
    > +    socket2->callback = MP_OBJ_NULL;
    > +    tcp_arg(socket2->pcb.tcp, (void*)socket2);
    > +    tcp_err(socket2->pcb.tcp, _lwip_tcp_error);
    > +    tcp_recv(socket2->pcb.tcp, _lwip_tcp_recv);
    > +    DEBUG_printf("New socket for endpoint connection: %p\n", socket2);
    > +
    > +    tcp_accepted(listener);
    > +
    > +    // make the return value
    > +    uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
    > +    memcpy(ip, &(socket2->pcb.tcp->remote_ip), sizeof(ip));
    > +    mp_uint_t port = (mp_uint_t)socket2->pcb.tcp->remote_port;
    > +    mp_obj_tuple_t *client = mp_obj_new_tuple(2, NULL);
    > +    client->items[0] = socket2;
    > +    client->items[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG);
    > +
    > +    return client;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_1(lwip_socket_accept_obj, 
lwip_socket_accept);
    > +
    > +STATIC mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +
    > +    if (socket->pcb.tcp == NULL) {
    > +        mp_raise_OSError(MP_EBADF);
    > +    }
    > +
    > +    // get address
    > +    uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
    > +    mp_uint_t port = netutils_parse_inet_addr(addr_in, ip, NETUTILS_BIG);
    > +
    > +    ip_addr_t dest;
    > +    IP4_ADDR(&dest, ip[0], ip[1], ip[2], ip[3]);
    > +
    > +    err_t err = ERR_ARG;
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM: {
    > +            if (socket->state != STATE_NEW) {
    > +                if (socket->state == STATE_CONNECTED) {
    > +                    mp_raise_OSError(MP_EISCONN);
    > +                } else {
    > +                    mp_raise_OSError(MP_EALREADY);
    > +                }
    > +            }
    > +            // Register our receive callback.
    > +            tcp_recv(socket->pcb.tcp, _lwip_tcp_recv);
    > +            socket->state = STATE_CONNECTING;
    > +            err = tcp_connect(socket->pcb.tcp, &dest, port, 
_lwip_tcp_connected);
    > +            if (err != ERR_OK) {
    > +                socket->state = STATE_NEW;
    > +                mp_raise_OSError(error_lookup_table[-err]);
    > +            }
    > +            socket->peer_port = (mp_uint_t)port;
    > +            memcpy(socket->peer, &dest, sizeof(socket->peer));
    > +            // And now we wait...
    > +            if (socket->timeout != -1) {
    > +                for (mp_uint_t retries = socket->timeout / 100; 
retries--;) {
    > +                    mp_hal_delay_ms(100);
    > +                    if (socket->state != STATE_CONNECTING) break;
    > +                }
    > +                if (socket->state == STATE_CONNECTING) {
    > +                    mp_raise_OSError(MP_EINPROGRESS);
    > +                }
    > +            } else {
    > +                while (socket->state == STATE_CONNECTING) {
    > +                    poll_sockets();
    > +                }
    > +            }
    > +            if (socket->state == STATE_CONNECTED) {
    > +               err = ERR_OK;
    > +            } else {
    > +               err = socket->state;
    > +            }
    > +            break;
    > +        }
    > +        case MOD_NETWORK_SOCK_DGRAM: {
    > +            err = udp_connect(socket->pcb.udp, &dest, port);
    > +            break;
    > +        }
    > +    }
    > +
    > +    if (err != ERR_OK) {
    > +        mp_raise_OSError(error_lookup_table[-err]);
    > +    }
    > +
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_connect_obj, 
lwip_socket_connect);
    > +
    > +STATIC void lwip_socket_check_connected(lwip_socket_obj_t *socket) {
    > +    if (socket->pcb.tcp == NULL) {
    > +        // not connected
    > +        int _errno = error_lookup_table[-socket->state];
    > +        socket->state = _ERR_BADF;
    > +        mp_raise_OSError(_errno);
    > +    }
    > +}
    > +
    > +STATIC mp_obj_t lwip_socket_send(mp_obj_t self_in, mp_obj_t buf_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +    int _errno;
    > +
    > +    lwip_socket_check_connected(socket);
    > +
    > +    mp_buffer_info_t bufinfo;
    > +    mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
    > +
    > +    mp_uint_t ret = 0;
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM: {
    > +            ret = lwip_tcp_send(socket, bufinfo.buf, bufinfo.len, 
&_errno);
    > +            break;
    > +        }
    > +        case MOD_NETWORK_SOCK_DGRAM: {
    > +            ret = lwip_udp_send(socket, bufinfo.buf, bufinfo.len, NULL, 
0, &_errno);
    > +            break;
    > +        }
    > +    }
    > +    if (ret == -1) {
    > +        mp_raise_OSError(_errno);
    > +    }
    > +
    > +    return mp_obj_new_int_from_uint(ret);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_send_obj, lwip_socket_send);
    > +
    > +STATIC mp_obj_t lwip_socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +    int _errno;
    > +
    > +    lwip_socket_check_connected(socket);
    > +
    > +    mp_int_t len = mp_obj_get_int(len_in);
    > +    vstr_t vstr;
    > +    vstr_init_len(&vstr, len);
    > +
    > +    mp_uint_t ret = 0;
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM: {
    > +            ret = lwip_tcp_receive(socket, (byte*)vstr.buf, len, 
&_errno);
    > +            break;
    > +        }
    > +        case MOD_NETWORK_SOCK_DGRAM: {
    > +            ret = lwip_udp_receive(socket, (byte*)vstr.buf, len, NULL, 
NULL, &_errno);
    > +            break;
    > +        }
    > +    }
    > +    if (ret == -1) {
    > +        mp_raise_OSError(_errno);
    > +    }
    > +
    > +    if (ret == 0) {
    > +        return mp_const_empty_bytes;
    > +    }
    > +    vstr.len = ret;
    > +    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recv_obj, lwip_socket_recv);
    > +
    > +STATIC mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, 
mp_obj_t addr_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +    int _errno;
    > +
    > +    lwip_socket_check_connected(socket);
    > +
    > +    mp_buffer_info_t bufinfo;
    > +    mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ);
    > +
    > +    uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
    > +    mp_uint_t port = netutils_parse_inet_addr(addr_in, ip, NETUTILS_BIG);
    > +
    > +    mp_uint_t ret = 0;
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM: {
    > +            ret = lwip_tcp_send(socket, bufinfo.buf, bufinfo.len, 
&_errno);
    > +            break;
    > +        }
    > +        case MOD_NETWORK_SOCK_DGRAM: {
    > +            ret = lwip_udp_send(socket, bufinfo.buf, bufinfo.len, ip, 
port, &_errno);
    > +            break;
    > +        }
    > +    }
    > +    if (ret == -1) {
    > +        mp_raise_OSError(_errno);
    > +    }
    > +
    > +    return mp_obj_new_int_from_uint(ret);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_3(lwip_socket_sendto_obj, 
lwip_socket_sendto);
    > +
    > +STATIC mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +    int _errno;
    > +
    > +    lwip_socket_check_connected(socket);
    > +
    > +    mp_int_t len = mp_obj_get_int(len_in);
    > +    vstr_t vstr;
    > +    vstr_init_len(&vstr, len);
    > +    byte ip[4];
    > +    mp_uint_t port;
    > +
    > +    mp_uint_t ret = 0;
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM: {
    > +            memcpy(ip, &socket->peer, sizeof(socket->peer));
    > +            port = (mp_uint_t) socket->peer_port;
    > +            ret = lwip_tcp_receive(socket, (byte*)vstr.buf, len, 
&_errno);
    > +            break;
    > +        }
    > +        case MOD_NETWORK_SOCK_DGRAM: {
    > +            ret = lwip_udp_receive(socket, (byte*)vstr.buf, len, ip, 
&port, &_errno);
    > +            break;
    > +        }
    > +    }
    > +    if (ret == -1) {
    > +        mp_raise_OSError(_errno);
    > +    }
    > +
    > +    mp_obj_t tuple[2];
    > +    if (ret == 0) {
    > +        tuple[0] = mp_const_empty_bytes;
    > +    } else {
    > +        vstr.len = ret;
    > +        tuple[0] = mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
    > +    }
    > +    tuple[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG);
    > +    return mp_obj_new_tuple(2, tuple);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recvfrom_obj, 
lwip_socket_recvfrom);
    > +
    > +STATIC mp_obj_t lwip_socket_sendall(mp_obj_t self_in, mp_obj_t buf_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +    lwip_socket_check_connected(socket);
    > +
    > +    int _errno;
    > +    mp_buffer_info_t bufinfo;
    > +    mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
    > +
    > +    mp_uint_t ret = 0;
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM: {
    > +            if (socket->timeout == 0) {
    > +                // Behavior of sendall() for non-blocking sockets isn't 
explicitly specified.
    > +                // But it's specified that "On error, an exception is 
raised, there is no
    > +                // way to determine how much data, if any, was 
successfully sent." Then, the
    > +                // most useful behavior is: check whether we will be 
able to send all of input
    > +                // data without EAGAIN, and if won't be, raise it 
without sending any.
    > +                if (bufinfo.len > tcp_sndbuf(socket->pcb.tcp)) {
    > +                    mp_raise_OSError(MP_EAGAIN);
    > +                }
    > +            }
    > +            // TODO: In CPython3.5, socket timeout should apply to the
    > +            // entire sendall() operation, not to individual send() 
chunks.
    > +            while (bufinfo.len != 0) {
    > +                ret = lwip_tcp_send(socket, bufinfo.buf, bufinfo.len, 
&_errno);
    > +                if (ret == -1) {
    > +                    mp_raise_OSError(_errno);
    > +                }
    > +                bufinfo.len -= ret;
    > +                bufinfo.buf = (char*)bufinfo.buf + ret;
    > +            }
    > +            break;
    > +        }
    > +        case MOD_NETWORK_SOCK_DGRAM:
    > +            mp_raise_NotImplementedError("");
    > +            break;
    > +    }
    > +
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_sendall_obj, 
lwip_socket_sendall);
    > +
    > +STATIC mp_obj_t lwip_socket_settimeout(mp_obj_t self_in, mp_obj_t 
timeout_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +    mp_uint_t timeout;
    > +    if (timeout_in == mp_const_none) {
    > +        timeout = -1;
    > +    } else {
    > +        #if MICROPY_PY_BUILTINS_FLOAT
    > +        timeout = 1000 * mp_obj_get_float(timeout_in);
    > +        #else
    > +        timeout = 1000 * mp_obj_get_int(timeout_in);
    > +        #endif
    > +    }
    > +    socket->timeout = timeout;
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_settimeout_obj, 
lwip_socket_settimeout);
    > +
    > +STATIC mp_obj_t lwip_socket_setblocking(mp_obj_t self_in, mp_obj_t 
flag_in) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +    bool val = mp_obj_is_true(flag_in);
    > +    if (val) {
    > +        socket->timeout = -1;
    > +    } else {
    > +        socket->timeout = 0;
    > +    }
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_setblocking_obj, 
lwip_socket_setblocking);
    > +
    > +STATIC mp_obj_t lwip_socket_setsockopt(mp_uint_t n_args, const mp_obj_t 
*args) {
    > +    (void)n_args; // always 4
    > +    lwip_socket_obj_t *socket = args[0];
    > +
    > +    int opt = mp_obj_get_int(args[2]);
    > +    if (opt == 20) {
    > +        if (args[3] == mp_const_none) {
    > +            socket->callback = MP_OBJ_NULL;
    > +        } else {
    > +            socket->callback = args[3];
    > +        }
    > +        return mp_const_none;
    > +    }
    > +
    > +    switch (opt) {
    > +        // level: SOL_SOCKET
    > +        case SOF_REUSEADDR: {
    > +            mp_int_t val = mp_obj_get_int(args[3]);
    > +            // Options are common for UDP and TCP pcb's.
    > +            if (val) {
    > +                ip_set_option(socket->pcb.tcp, SOF_REUSEADDR);
    > +            } else {
    > +                ip_reset_option(socket->pcb.tcp, SOF_REUSEADDR);
    > +            }
    > +            break;
    > +        }
    > +
    > +        // level: IPPROTO_IP
    > +        case IP_ADD_MEMBERSHIP: {
    > +            mp_buffer_info_t bufinfo;
    > +            mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ);
    > +            if (bufinfo.len != sizeof(ip_addr_t) * 2) {
    > +                mp_raise_ValueError(NULL);
    > +            }
    > +
    > +            // POSIX setsockopt has order: group addr, if addr, lwIP has 
it vice-versa
    > +            err_t err = igmp_joingroup((ip_addr_t*)bufinfo.buf + 1, 
bufinfo.buf);
    > +            if (err != ERR_OK) {
    > +                mp_raise_OSError(error_lookup_table[-err]);
    > +            }
    > +            break;
    > +        }
    > +
    > +        default:
    > +            printf("Warning: lwip.setsockopt() not implemented\n");
    > +    }
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_setsockopt_obj, 
4, 4, lwip_socket_setsockopt);
    > +
    > +STATIC mp_obj_t lwip_socket_makefile(mp_uint_t n_args, const mp_obj_t 
*args) {
    > +    (void)n_args;
    > +    return args[0];
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_makefile_obj, 1, 
3, lwip_socket_makefile);
    > +
    > +STATIC mp_uint_t lwip_socket_read(mp_obj_t self_in, void *buf, mp_uint_t 
size, int *errcode) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM:
    > +            return lwip_tcp_receive(socket, buf, size, errcode);
    > +        case MOD_NETWORK_SOCK_DGRAM:
    > +            return lwip_udp_receive(socket, buf, size, NULL, NULL, 
errcode);
    > +    }
    > +    // Unreachable
    > +    return MP_STREAM_ERROR;
    > +}
    > +
    > +STATIC mp_uint_t lwip_socket_write(mp_obj_t self_in, const void *buf, 
mp_uint_t size, int *errcode) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +
    > +    switch (socket->type) {
    > +        case MOD_NETWORK_SOCK_STREAM:
    > +            return lwip_tcp_send(socket, buf, size, errcode);
    > +        case MOD_NETWORK_SOCK_DGRAM:
    > +            return lwip_udp_send(socket, buf, size, NULL, 0, errcode);
    > +    }
    > +    // Unreachable
    > +    return MP_STREAM_ERROR;
    > +}
    > +
    > +STATIC mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, 
uintptr_t arg, int *errcode) {
    > +    lwip_socket_obj_t *socket = self_in;
    > +    mp_uint_t ret;
    > +
    > +    if (request == MP_STREAM_POLL) {
    > +        uintptr_t flags = arg;
    > +        ret = 0;
    > +
    > +        if (flags & MP_STREAM_POLL_RD && socket->incoming.pbuf != NULL) {
    > +            ret |= MP_STREAM_POLL_RD;
    > +        }
    > +
    > +        if (flags & MP_STREAM_POLL_WR && tcp_sndbuf(socket->pcb.tcp) > 
0) {
    > +            ret |= MP_STREAM_POLL_WR;
    > +        }
    > +
    > +        if (socket->state == STATE_PEER_CLOSED) {
    > +            // Peer-closed socket is both readable and writable: read 
will
    > +            // return EOF, write - error. Without this poll will hang on 
a
    > +            // socket which was closed by peer.
    > +            ret |= flags & (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR);
    > +        }
    > +
    > +    } else {
    > +        *errcode = MP_EINVAL;
    > +        ret = MP_STREAM_ERROR;
    > +    }
    > +
    > +    return ret;
    > +}
    > +
    > +STATIC const mp_rom_map_elem_t lwip_socket_locals_dict_table[] = {
    > +    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&lwip_socket_close_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&lwip_socket_close_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_bind), MP_ROM_PTR(&lwip_socket_bind_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&lwip_socket_listen_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_accept), MP_ROM_PTR(&lwip_socket_accept_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&lwip_socket_connect_obj) 
},
    > +    { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&lwip_socket_send_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&lwip_socket_recv_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_sendto), MP_ROM_PTR(&lwip_socket_sendto_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_recvfrom), 
MP_ROM_PTR(&lwip_socket_recvfrom_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_sendall), MP_ROM_PTR(&lwip_socket_sendall_obj) 
},
    > +    { MP_ROM_QSTR(MP_QSTR_settimeout), 
MP_ROM_PTR(&lwip_socket_settimeout_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_setblocking), 
MP_ROM_PTR(&lwip_socket_setblocking_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_setsockopt), 
MP_ROM_PTR(&lwip_socket_setsockopt_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_makefile), 
MP_ROM_PTR(&lwip_socket_makefile_obj) },
    > +
    > +    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) 
},
    > +    { MP_ROM_QSTR(MP_QSTR_readline), 
MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
    > +};
    > +STATIC MP_DEFINE_CONST_DICT(lwip_socket_locals_dict, 
lwip_socket_locals_dict_table);
    > +
    > +STATIC const mp_stream_p_t lwip_socket_stream_p = {
    > +    .read = lwip_socket_read,
    > +    .write = lwip_socket_write,
    > +    .ioctl = lwip_socket_ioctl,
    > +};
    > +
    > +STATIC const mp_obj_type_t lwip_socket_type = {
    > +    { &mp_type_type },
    > +    .name = MP_QSTR_socket,
    > +    .print = lwip_socket_print,
    > +    .make_new = lwip_socket_make_new,
    > +    .protocol = &lwip_socket_stream_p,
    > +    .locals_dict = (mp_obj_dict_t*)&lwip_socket_locals_dict,
    > +};
    > +
    > 
+/******************************************************************************/
    > +// Support functions for memory protection. lwIP has its own memory 
management
    > +// routines for its internal structures, and since they might be called 
in
    > +// interrupt handlers, they need some protection.
    > +/*
    > +sys_prot_t sys_arch_protect() {
    > +    return (sys_prot_t)MICROPY_BEGIN_ATOMIC_SECTION();
    > +}
    > +
    > +void sys_arch_unprotect(sys_prot_t state) {
    > +    MICROPY_END_ATOMIC_SECTION((mp_uint_t)state);
    > +}
    > +*/
    > +
    > 
+/******************************************************************************/
    > +// Polling callbacks for the interfaces connected to lwIP. Right now it 
calls
    > +// itself a "list" but isn't; we only support a single interface.
    > +
    > +typedef struct nic_poll {
    > +    void (* poll)(void *arg);
    > +    void *poll_arg;
    > +} nic_poll_t;
    > +
    > +STATIC nic_poll_t lwip_poll_list;
    > +
    > +void mod_lwip_register_poll(void (* poll)(void *arg), void *poll_arg) {
    > +    DEBUG_printf("Register poll: %p (argp %p)\n", poll, poll_arg);
    > +    lwip_poll_list.poll = poll;
    > +    lwip_poll_list.poll_arg = poll_arg;
    > +}
    > +
    > +void mod_lwip_deregister_poll(void (* poll)(void *arg), void *poll_arg) {
    > +    lwip_poll_list.poll = NULL;
    > +}
    > +
    > 
+/******************************************************************************/
    > +// The lwip global functions.
    > +#include <netif/uknetdev.h>
    > +
    > +STATIC mp_obj_t mod_lwip_reset() {
    > +    DEBUG_printf("lwip reset\n");
    > +    //lwip_init();
    > +    lwip_poll_list.poll = NULL;
    > +
    > +    /* Register uknetdev polling to molwip */
    > +    mod_lwip_register_poll(uknetdev_poll_all, NULL);
    > +    return mp_const_none;
    > +}
    > +MP_DEFINE_CONST_FUN_OBJ_0(mod_lwip_reset_obj, mod_lwip_reset);
    > +
    > +STATIC mp_obj_t mod_lwip_callback() {
    > +    DEBUG_printf("Exec callback\n");
    > +    if (lwip_poll_list.poll != NULL) {
    > +        lwip_poll_list.poll(lwip_poll_list.poll_arg);
    > +    }
    > +    sys_check_timeouts();
    > +    return mp_const_none;
    > +}
    > +MP_DEFINE_CONST_FUN_OBJ_0(mod_lwip_callback_obj, mod_lwip_callback);
    > +
    > +typedef struct _getaddrinfo_state_t {
    > +    volatile int status;
    > +    volatile ip_addr_t ipaddr;
    > +} getaddrinfo_state_t;
    > +
    > +// Callback for incoming DNS requests.
    > +STATIC void lwip_getaddrinfo_cb(const char *name, ip_addr_t *ipaddr, 
void *arg) {
    > +    getaddrinfo_state_t *state = arg;
    > +    if (ipaddr != NULL) {
    > +        state->status = 1;
    > +        state->ipaddr = *ipaddr;
    > +    } else {
    > +        // error
    > +        state->status = -2;
    > +    }
    > +}
    > +
    > +// lwip.getaddrinfo
    > +STATIC mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) {
    > +    if (n_args > 2) {
    > +        mp_warning("getaddrinfo constraints not supported");
    > +    }
    > +    DEBUG_printf("getaddrinfo\n");
    > +
    > +    mp_obj_t host_in = args[0], port_in = args[1];
    > +    const char *host = mp_obj_str_get_str(host_in);
    > +    mp_int_t port = mp_obj_get_int(port_in);
    > +
    > +    getaddrinfo_state_t state;
    > +    state.status = 0;
    > +
    > +    err_t ret = dns_gethostbyname(host, (ip_addr_t*)&state.ipaddr, 
lwip_getaddrinfo_cb, &state);
    > +    switch (ret) {
    > +        case ERR_OK:
    > +            // cached
    > +            state.status = 1;
    > +            break;
    > +        case ERR_INPROGRESS:
    > +            while (state.status == 0) {
    > +                poll_sockets();
    > +            }
    > +            break;
    > +        default:
    > +            state.status = ret;
    > +    }
    > +
    > +    if (state.status < 0) {
    > +        // TODO: CPython raises gaierror, we raise with native lwIP 
negative error
    > +        // values, to differentiate from normal errno's at least in such 
way.
    > +        mp_raise_OSError(state.status);
    > +    }
    > +
    > +    mp_obj_tuple_t *tuple = mp_obj_new_tuple(5, NULL);
    > +    tuple->items[0] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_AF_INET);
    > +    tuple->items[1] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_STREAM);
    > +    tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0);
    > +    tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_);
    > +    tuple->items[4] = netutils_format_inet_addr((uint8_t*)&state.ipaddr, 
port, NETUTILS_BIG);
    > +    return mp_obj_new_list(1, (mp_obj_t*)&tuple);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_getaddrinfo_obj, 2, 6, 
lwip_getaddrinfo);
    > +
    > +// Debug functions
    > +/*
    > +STATIC mp_obj_t lwip_print_pcbs() {
    > +    tcp_debug_print_pcbs();
    > +    return mp_const_none;
    > +}
    > +MP_DEFINE_CONST_FUN_OBJ_0(lwip_print_pcbs_obj, lwip_print_pcbs);
    > +*/
    > +#ifdef MICROPY_PY_LWIP
    > +
    > +STATIC const mp_rom_map_elem_t mp_module_lwip_globals_table[] = {
    > +    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_lwip) },
    > +    { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&mod_lwip_reset_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_callback), MP_ROM_PTR(&mod_lwip_callback_obj) 
},
    > +    { MP_ROM_QSTR(MP_QSTR_getaddrinfo), 
MP_ROM_PTR(&lwip_getaddrinfo_obj) },
    > +    //{ MP_ROM_QSTR(MP_QSTR_print_pcbs), 
MP_ROM_PTR(&lwip_print_pcbs_obj) },
    > +    // objects
    > +    { MP_ROM_QSTR(MP_QSTR_socket), MP_ROM_PTR(&lwip_socket_type) },
    > +#ifdef MICROPY_PY_LWIP_SLIP
    > +    { MP_ROM_QSTR(MP_QSTR_slip), MP_ROM_PTR(&lwip_slip_type) },
    > +#endif
    > +    // class constants
    > +    { MP_ROM_QSTR(MP_QSTR_AF_INET), MP_ROM_INT(MOD_NETWORK_AF_INET) },
    > +    { MP_ROM_QSTR(MP_QSTR_AF_INET6), MP_ROM_INT(MOD_NETWORK_AF_INET6) },
    > +
    > +    { MP_ROM_QSTR(MP_QSTR_SOCK_STREAM), 
MP_ROM_INT(MOD_NETWORK_SOCK_STREAM) },
    > +    { MP_ROM_QSTR(MP_QSTR_SOCK_DGRAM), 
MP_ROM_INT(MOD_NETWORK_SOCK_DGRAM) },
    > +    { MP_ROM_QSTR(MP_QSTR_SOCK_RAW), MP_ROM_INT(MOD_NETWORK_SOCK_RAW) },
    > +
    > +    { MP_ROM_QSTR(MP_QSTR_SOL_SOCKET), MP_ROM_INT(1) },
    > +    { MP_ROM_QSTR(MP_QSTR_SO_REUSEADDR), MP_ROM_INT(SOF_REUSEADDR) },
    > +
    > +    { MP_ROM_QSTR(MP_QSTR_IPPROTO_IP), MP_ROM_INT(0) },
    > +    { MP_ROM_QSTR(MP_QSTR_IP_ADD_MEMBERSHIP), 
MP_ROM_INT(IP_ADD_MEMBERSHIP) },
    > +};
    > +
    > +STATIC MP_DEFINE_CONST_DICT(mp_module_lwip_globals, 
mp_module_lwip_globals_table);
    > +
    > +const mp_obj_module_t mp_module_lwip = {
    > +    .base = { &mp_type_module },
    > +    .globals = (mp_obj_dict_t*)&mp_module_lwip_globals,
    > +};
    > +
    > +#endif // MICROPY_PY_LWIP
    > diff --git a/extmod/modusocket.c b/extmod/modusocket.c
    > new file mode 100644
    > index 0000000..61402e0
    > --- /dev/null
    > +++ b/extmod/modusocket.c
    > @@ -0,0 +1,624 @@
    > +/*
    > + * This file is part of the MicroPython project, http://micropython.org/
    > + *
    > + * The MIT License (MIT)
    > + *
    > + * Copyright (c) 2013, 2014 Damien P. George
    > + * Copyright (c) 2014 Paul Sokolovsky
    > + *
    > + * Permission is hereby granted, free of charge, to any person obtaining 
a copy
    > + * of this software and associated documentation files (the "Software"), 
to deal
    > + * in the Software without restriction, including without limitation the 
rights
    > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or 
sell
    > + * copies of the Software, and to permit persons to whom the Software is
    > + * furnished to do so, subject to the following conditions:
    > + *
    > + * The above copyright notice and this permission notice shall be 
included in
    > + * all copies or substantial portions of the Software.
    > + *
    > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR
    > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY,
    > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
SHALL THE
    > + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
ARISING FROM,
    > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
DEALINGS IN
    > + * THE SOFTWARE.
    > + */
    > +
    > +#include <stdio.h>
    > +#include <assert.h>
    > +#include <string.h>
    > +#include <unistd.h>
    > +#include <fcntl.h>
    > +#include <sys/stat.h>
    > +#include <sys/types.h>
    > +#include <sys/socket.h>
    > +#include <netinet/in.h>
    > +#include <arpa/inet.h>
    > +#include <netdb.h>
    > +#include <errno.h>
    > +#include <math.h>
    > +
    > +#include "py/objtuple.h"
    > +#include "py/objstr.h"
    > +#include "py/runtime.h"
    > +#include "py/stream.h"
    > +#include "py/builtin.h"
    > +#include "py/mphal.h"
    > +
    > +/*
    > +  The idea of this module is to implement reasonable minimum of
    > +  socket-related functions to write typical clients and servers.
    > +  The module named "usocket" on purpose, to allow to make
    > +  Python-level module more (or fully) compatible with CPython
    > +  "socket", e.g.:
    > +  ---- socket.py ----
    > +  from usocket import *
    > +  from socket_more_funcs import *
    > +  from socket_more_funcs2 import *
    > +  -------------------
    > +  I.e. this module should stay lean, and more functions (if needed)
    > +  should be add to separate modules (C or Python level).
    > + */
    > +
    > +// This type must "inherit" from mp_obj_fdfile_t, i.e. matching subset of
    > +// fields should have the same layout.
    > +typedef struct _mp_obj_socket_t {
    > +    mp_obj_base_t base;
    > +    int fd;
    > +    bool blocking;
    > +} mp_obj_socket_t;
    > +
    > +const mp_obj_type_t mp_type_socket;
    > +
    > +// Helper functions
    > +static inline mp_obj_t mp_obj_from_sockaddr(const struct sockaddr *addr, 
socklen_t len) {
    > +    return mp_obj_new_bytes((const byte *)addr, len);
    > +}
    > +
    > +STATIC mp_obj_socket_t *socket_new(int fd) {
    > +    mp_obj_socket_t *o = m_new_obj(mp_obj_socket_t);
    > +    o->base.type = &mp_type_socket;
    > +    o->fd = fd;
    > +    o->blocking = true;
    > +    return o;
    > +}
    > +
    > +
    > +STATIC void socket_print(const mp_print_t *print, mp_obj_t self_in, 
mp_print_kind_t kind) {
    > +    (void)kind;
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
    > +    mp_printf(print, "<_socket %d>", self->fd);
    > +}
    > +
    > +STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, 
int *errcode) {
    > +    mp_obj_socket_t *o = MP_OBJ_TO_PTR(o_in);
    > +    mp_int_t r = read(o->fd, buf, size);
    > +    if (r == -1) {
    > +        int err = errno;
    > +        // On blocking socket, we get EAGAIN in case 
SO_RCVTIMEO/SO_SNDTIMEO
    > +        // timed out, and need to convert that to ETIMEDOUT.
    > +        if (err == EAGAIN && o->blocking) {
    > +            err = MP_ETIMEDOUT;
    > +        }
    > +
    > +        *errcode = err;
    > +        return MP_STREAM_ERROR;
    > +    }
    > +    return r;
    > +}
    > +
    > +STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t 
size, int *errcode) {
    > +    mp_obj_socket_t *o = MP_OBJ_TO_PTR(o_in);
    > +    mp_int_t r = write(o->fd, buf, size);
    > +    if (r == -1) {
    > +        int err = errno;
    > +        // On blocking socket, we get EAGAIN in case 
SO_RCVTIMEO/SO_SNDTIMEO
    > +        // timed out, and need to convert that to ETIMEDOUT.
    > +        if (err == EAGAIN && o->blocking) {
    > +            err = MP_ETIMEDOUT;
    > +        }
    > +
    > +        *errcode = err;
    > +        return MP_STREAM_ERROR;
    > +    }
    > +    return r;
    > +}
    > +
    > +STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, 
uintptr_t arg, int *errcode) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(o_in);
    > +    (void)arg;
    > +    switch (request) {
    > +        case MP_STREAM_CLOSE:
    > +            // There's a POSIX drama regarding return value of close in 
general,
    > +            // and EINTR error in particular. See e.g.
    > +            // http://lwn.net/Articles/576478/
    > +            // http://austingroupbugs.net/view.php?id=529
    > +            // The rationale MicroPython follows is that close() just 
releases
    > +            // file descriptor. If you're interested to catch I/O errors 
before
    > +            // closing fd, fsync() it.
    > +            close(self->fd);
    > +            return 0;
    > +
    > +        case MP_STREAM_GET_FILENO:
    > +            return self->fd;
    > +
    > +        default:
    > +            *errcode = MP_EINVAL;
    > +            return MP_STREAM_ERROR;
    > +    }
    > +}
    > +
    > +STATIC mp_obj_t socket_fileno(mp_obj_t self_in) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
    > +    return MP_OBJ_NEW_SMALL_INT(self->fd);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_fileno_obj, socket_fileno);
    > +
    > +STATIC mp_obj_t socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
    > +    mp_buffer_info_t bufinfo;
    > +    mp_get_buffer_raise(addr_in, &bufinfo, MP_BUFFER_READ);
    > +    int r = connect(self->fd, (const struct sockaddr *)bufinfo.buf, 
bufinfo.len);
    > +    RAISE_ERRNO(r, errno);
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect);
    > +
    > +STATIC mp_obj_t socket_bind(mp_obj_t self_in, mp_obj_t addr_in) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
    > +    mp_buffer_info_t bufinfo;
    > +    mp_get_buffer_raise(addr_in, &bufinfo, MP_BUFFER_READ);
    > +    int r = bind(self->fd, (const struct sockaddr *)bufinfo.buf, 
bufinfo.len);
    > +    RAISE_ERRNO(r, errno);
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind);
    > +
    > +STATIC mp_obj_t socket_listen(mp_obj_t self_in, mp_obj_t backlog_in) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
    > +    int r = listen(self->fd, MP_OBJ_SMALL_INT_VALUE(backlog_in));
    > +    RAISE_ERRNO(r, errno);
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_listen_obj, socket_listen);
    > +
    > +STATIC mp_obj_t socket_accept(mp_obj_t self_in) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
    > +    // sockaddr_storage isn't stack-friendly (129 bytes or so)
    > +    //struct sockaddr_storage addr;
    > +    byte addr[32];
    > +    socklen_t addr_len = sizeof(addr);
    > +    int fd = accept(self->fd, (struct sockaddr*)&addr, &addr_len);
    > +    RAISE_ERRNO(fd, errno);
    > +
    > +    mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
    > +    t->items[0] = MP_OBJ_FROM_PTR(socket_new(fd));
    > +    t->items[1] = mp_obj_new_bytearray(addr_len, &addr);
    > +
    > +    return MP_OBJ_FROM_PTR(t);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept);
    > +
    > +// Note: besides flag param, this differs from read() in that
    > +// this does not swallow blocking errors (EAGAIN, EWOULDBLOCK) -
    > +// these would be thrown as exceptions.
    > +STATIC mp_obj_t socket_recv(size_t n_args, const mp_obj_t *args) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(args[0]);
    > +    int sz = MP_OBJ_SMALL_INT_VALUE(args[1]);
    > +    int flags = 0;
    > +
    > +    if (n_args > 2) {
    > +        flags = MP_OBJ_SMALL_INT_VALUE(args[2]);
    > +    }
    > +
    > +    byte *buf = m_new(byte, sz);
    > +    int out_sz = recv(self->fd, buf, sz, flags);
    > +    RAISE_ERRNO(out_sz, errno);
    > +
    > +    mp_obj_t ret = mp_obj_new_str_of_type(&mp_type_bytes, buf, out_sz);
    > +    m_del(char, buf, sz);
    > +    return ret;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recv_obj, 2, 3, 
socket_recv);
    > +
    > +STATIC mp_obj_t socket_recvfrom(size_t n_args, const mp_obj_t *args) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(args[0]);
    > +    int sz = MP_OBJ_SMALL_INT_VALUE(args[1]);
    > +    int flags = 0;
    > +
    > +    if (n_args > 2) {
    > +        flags = MP_OBJ_SMALL_INT_VALUE(args[2]);
    > +    }
    > +
    > +    struct sockaddr_storage addr;
    > +    socklen_t addr_len = sizeof(addr);
    > +
    > +    byte *buf = m_new(byte, sz);
    > +    int out_sz = recvfrom(self->fd, buf, sz, flags, (struct 
sockaddr*)&addr, &addr_len);
    > +    RAISE_ERRNO(out_sz, errno);
    > +
    > +    mp_obj_t buf_o = mp_obj_new_str_of_type(&mp_type_bytes, buf, out_sz);
    > +    m_del(char, buf, sz);
    > +
    > +    mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
    > +    t->items[0] = buf_o;
    > +    t->items[1] = mp_obj_from_sockaddr((struct sockaddr*)&addr, 
addr_len);
    > +
    > +    return MP_OBJ_FROM_PTR(t);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recvfrom_obj, 2, 3, 
socket_recvfrom);
    > +
    > +// Note: besides flag param, this differs from write() in that
    > +// this does not swallow blocking errors (EAGAIN, EWOULDBLOCK) -
    > +// these would be thrown as exceptions.
    > +STATIC mp_obj_t socket_send(size_t n_args, const mp_obj_t *args) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(args[0]);
    > +    int flags = 0;
    > +
    > +    if (n_args > 2) {
    > +        flags = MP_OBJ_SMALL_INT_VALUE(args[2]);
    > +    }
    > +
    > +    mp_buffer_info_t bufinfo;
    > +    mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
    > +    int out_sz = send(self->fd, bufinfo.buf, bufinfo.len, flags);
    > +    RAISE_ERRNO(out_sz, errno);
    > +
    > +    return MP_OBJ_NEW_SMALL_INT(out_sz);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_send_obj, 2, 3, 
socket_send);
    > +
    > +STATIC mp_obj_t socket_sendto(size_t n_args, const mp_obj_t *args) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(args[0]);
    > +    int flags = 0;
    > +
    > +    mp_obj_t dst_addr = args[2];
    > +    if (n_args > 3) {
    > +        flags = MP_OBJ_SMALL_INT_VALUE(args[2]);
    > +        dst_addr = args[3];
    > +    }
    > +
    > +    mp_buffer_info_t bufinfo, addr_bi;
    > +    mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
    > +    mp_get_buffer_raise(dst_addr, &addr_bi, MP_BUFFER_READ);
    > +    int out_sz = sendto(self->fd, bufinfo.buf, bufinfo.len, flags,
    > +                        (struct sockaddr *)addr_bi.buf, addr_bi.len);
    > +    RAISE_ERRNO(out_sz, errno);
    > +
    > +    return MP_OBJ_NEW_SMALL_INT(out_sz);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_sendto_obj, 3, 4, 
socket_sendto);
    > +
    > +STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) {
    > +    (void)n_args; // always 4
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(args[0]);
    > +    int level = MP_OBJ_SMALL_INT_VALUE(args[1]);
    > +    int option = mp_obj_get_int(args[2]);
    > +
    > +    const void *optval;
    > +    socklen_t optlen;
    > +    int val;
    > +    if (MP_OBJ_IS_INT(args[3])) {
    > +        val = mp_obj_int_get_truncated(args[3]);
    > +        optval = &val;
    > +        optlen = sizeof(val);
    > +    } else {
    > +        mp_buffer_info_t bufinfo;
    > +        mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ);
    > +        optval = bufinfo.buf;
    > +        optlen = bufinfo.len;
    > +    }
    > +    int r = setsockopt(self->fd, level, option, optval, optlen);
    > +    RAISE_ERRNO(r, errno);
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, 
socket_setsockopt);
    > +
    > +STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
    > +    int val = mp_obj_is_true(flag_in);
    > +    int flags = fcntl(self->fd, F_GETFL, 0);
    > +    RAISE_ERRNO(flags, errno);
    > +    if (val) {
    > +        flags &= ~O_NONBLOCK;
    > +    } else {
    > +        flags |= O_NONBLOCK;
    > +    }
    > +    flags = fcntl(self->fd, F_SETFL, flags);
    > +    RAISE_ERRNO(flags, errno);
    > +    self->blocking = val;
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, 
socket_setblocking);
    > +
    > +STATIC mp_obj_t socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) 
{
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
    > +    struct timeval tv = {0,};
    > +    bool new_blocking = true;
    > +
    > +    if (timeout_in == mp_const_none) {
    > +        setsockopt(self->fd, SOL_SOCKET, SO_RCVTIMEO, NULL, 0);
    > +        setsockopt(self->fd, SOL_SOCKET, SO_SNDTIMEO, NULL, 0);
    > +    } else {
    > +        #if MICROPY_PY_BUILTINS_FLOAT
    > +        mp_float_t val = mp_obj_get_float(timeout_in);
    > +        double ipart;
    > +        tv.tv_usec = round(modf(val, &ipart) * 1000000);
    > +        tv.tv_sec = ipart;
    > +        #else
    > +        tv.tv_sec = mp_obj_get_int(timeout_in);
    > +        #endif
    > +
    > +        // For SO_RCVTIMEO/SO_SNDTIMEO, zero timeout means infinity, but
    > +        // for Python API it means non-blocking.
    > +        if (tv.tv_sec == 0 && tv.tv_usec == 0) {
    > +            new_blocking = false;
    > +        } else {
    > +            setsockopt(self->fd, SOL_SOCKET, SO_RCVTIMEO,
    > +                       &tv, sizeof(struct timeval));
    > +            setsockopt(self->fd, SOL_SOCKET, SO_SNDTIMEO,
    > +                       &tv, sizeof(struct timeval));
    > +        }
    > +    }
    > +
    > +    if (self->blocking != new_blocking) {
    > +        socket_setblocking(self_in, mp_obj_new_bool(new_blocking));
    > +    }
    > +
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_settimeout_obj, 
socket_settimeout);
    > +
    > +STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) {
    > +    // TODO: CPython explicitly says that closing returned object 
doesn't close
    > +    // the original socket (Python2 at all says that fd is dup()ed). But 
we
    > +    // save on the bloat.
    > +    mp_obj_socket_t *self = MP_OBJ_TO_PTR(args[0]);
    > +    mp_obj_t *new_args = alloca(n_args * sizeof(mp_obj_t));
    > +    memcpy(new_args + 1, args + 1, (n_args - 1) * sizeof(mp_obj_t));
    > +    new_args[0] = MP_OBJ_NEW_SMALL_INT(self->fd);
    > +    return mp_builtin_open(n_args, new_args, 
(mp_map_t*)&mp_const_empty_map);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, 
socket_makefile);
    > +
    > +STATIC mp_obj_t socket_make_new(const mp_obj_type_t *type_in, size_t 
n_args, size_t n_kw, const mp_obj_t *args) {
    > +    (void)type_in;
    > +    (void)n_kw;
    > +
    > +    int family = AF_INET;
    > +    int type = SOCK_STREAM;
    > +    int proto = 0;
    > +
    > +    if (n_args > 0) {
    > +        assert(MP_OBJ_IS_SMALL_INT(args[0]));
    > +        family = MP_OBJ_SMALL_INT_VALUE(args[0]);
    > +        if (n_args > 1) {
    > +            assert(MP_OBJ_IS_SMALL_INT(args[1]));
    > +            type = MP_OBJ_SMALL_INT_VALUE(args[1]);
    > +            if (n_args > 2) {
    > +                assert(MP_OBJ_IS_SMALL_INT(args[2]));
    > +                proto = MP_OBJ_SMALL_INT_VALUE(args[2]);
    > +            }
    > +        }
    > +    }
    > +
    > +    int fd = socket(family, type, proto);
    > +    RAISE_ERRNO(fd, errno);
    > +    return MP_OBJ_FROM_PTR(socket_new(fd));
    > +}
    > +
    > +STATIC const mp_rom_map_elem_t usocket_locals_dict_table[] = {
    > +    { MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&socket_fileno_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_makefile), MP_ROM_PTR(&socket_makefile_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) 
},
    > +    { MP_ROM_QSTR(MP_QSTR_readline), 
MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&socket_connect_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_bind), MP_ROM_PTR(&socket_bind_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&socket_listen_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_accept), MP_ROM_PTR(&socket_accept_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&socket_recv_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_recvfrom), MP_ROM_PTR(&socket_recvfrom_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&socket_send_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_sendto), MP_ROM_PTR(&socket_sendto_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_setsockopt), 
MP_ROM_PTR(&socket_setsockopt_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_setblocking), 
MP_ROM_PTR(&socket_setblocking_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_settimeout), 
MP_ROM_PTR(&socket_settimeout_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
    > +};
    > +
    > +STATIC MP_DEFINE_CONST_DICT(usocket_locals_dict, 
usocket_locals_dict_table);
    > +
    > +STATIC const mp_stream_p_t usocket_stream_p = {
    > +    .read = socket_read,
    > +    .write = socket_write,
    > +    .ioctl = socket_ioctl,
    > +};
    > +
    > +const mp_obj_type_t mp_type_socket = {
    > +    { &mp_type_type },
    > +    .name = MP_QSTR_socket,
    > +    .print = socket_print,
    > +    .make_new = socket_make_new,
    > +    .getiter = NULL,
    > +    .iternext = NULL,
    > +    .protocol = &usocket_stream_p,
    > +    .locals_dict = (mp_obj_dict_t*)&usocket_locals_dict,
    > +};
    > +
    > +#define BINADDR_MAX_LEN sizeof(struct in6_addr)
    > +STATIC mp_obj_t mod_socket_inet_pton(mp_obj_t family_in, mp_obj_t 
addr_in) {
    > +    int family = mp_obj_get_int(family_in);
    > +    byte binaddr[BINADDR_MAX_LEN];
    > +    int r = inet_pton(family, mp_obj_str_get_str(addr_in), binaddr);
    > +    RAISE_ERRNO(r, errno);
    > +    if (r == 0) {
    > +        mp_raise_OSError(MP_EINVAL);
    > +    }
    > +    int binaddr_len = 0;
    > +    switch (family) {
    > +        case AF_INET:
    > +            binaddr_len = sizeof(struct in_addr);
    > +            break;
    > +        case AF_INET6:
    > +            binaddr_len = sizeof(struct in6_addr);
    > +            break;
    > +    }
    > +    return mp_obj_new_bytes(binaddr, binaddr_len);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_socket_inet_pton_obj, 
mod_socket_inet_pton);
    > +
    > +STATIC mp_obj_t mod_socket_inet_ntop(mp_obj_t family_in, mp_obj_t 
binaddr_in) {
    > +    int family = mp_obj_get_int(family_in);
    > +    mp_buffer_info_t bufinfo;
    > +    mp_get_buffer_raise(binaddr_in, &bufinfo, MP_BUFFER_READ);
    > +    vstr_t vstr;
    > +    vstr_init_len(&vstr, family == AF_INET ? INET_ADDRSTRLEN : 
INET6_ADDRSTRLEN);
    > +    if (inet_ntop(family, bufinfo.buf, vstr.buf, vstr.len) == NULL) {
    > +        mp_raise_OSError(errno);
    > +    }
    > +    vstr.len = strlen(vstr.buf);
    > +    return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_socket_inet_ntop_obj, 
mod_socket_inet_ntop);
    > +
    > +STATIC mp_obj_t mod_socket_getaddrinfo(size_t n_args, const mp_obj_t 
*args) {
    > +    // TODO: Implement 5th and 6th args
    > +
    > +    const char *host = mp_obj_str_get_str(args[0]);
    > +    const char *serv = NULL;
    > +    struct addrinfo hints;
    > +    char buf[6];
    > +    memset(&hints, 0, sizeof(hints));
    > +    // getaddrinfo accepts port in string notation, so however
    > +    // it may seem stupid, we need to convert int to str
    > +    if (MP_OBJ_IS_SMALL_INT(args[1])) {
    > +        unsigned port = (unsigned short)MP_OBJ_SMALL_INT_VALUE(args[1]);
    > +        snprintf(buf, sizeof(buf), "%u", port);
    > +        serv = buf;
    > +        hints.ai_flags = AI_NUMERICSERV;
    > +#ifdef __UCLIBC_MAJOR__
    > +#if __UCLIBC_MAJOR__ == 0 && (__UCLIBC_MINOR__ < 9 || (__UCLIBC_MINOR__ 
== 9 && __UCLIBC_SUBLEVEL__ <= 32))
    > +// "warning" requires -Wno-cpp which is a relatively new gcc option, so 
we choose not to use it.
    > +//#warning Working around uClibc bug with numeric service name
    > +        // Older versions og uClibc have bugs when numeric ports in 
service
    > +        // arg require also hints.ai_socktype (or hints.ai_protocol) != 0
    > +        // This actually was fixed in 0.9.32.1, but uClibc doesn't allow 
to
    > +        // test for that.
    > +        // 
http://git.uclibc.org/uClibc/commit/libc/inet/getaddrinfo.c?id=bc3be18145e4d5
    > +        // Note that this is crude workaround, precluding UDP socket 
addresses
    > +        // to be returned. TODO: set only if not set by Python args.
    > +        hints.ai_socktype = SOCK_STREAM;
    > +#endif
    > +#endif
    > +    } else {
    > +        serv = mp_obj_str_get_str(args[1]);
    > +    }
    > +
    > +    if (n_args > 2) {
    > +        hints.ai_family = MP_OBJ_SMALL_INT_VALUE(args[2]);
    > +        if (n_args > 3) {
    > +            hints.ai_socktype = MP_OBJ_SMALL_INT_VALUE(args[3]);
    > +        }
    > +    }
    > +
    > +    struct addrinfo *addr_list;
    > +    int res = getaddrinfo(host, serv, &hints, &addr_list);
    > +
    > +    if (res != 0) {
    > +        // CPython: socket.gaierror
    > +        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, 
"[addrinfo error %d]", res));
    > +    }
    > +    assert(addr_list);
    > +
    > +    mp_obj_t list = mp_obj_new_list(0, NULL);
    > +    for (struct addrinfo *addr = addr_list; addr; addr = addr->ai_next) {
    > +        mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL));
    > +        t->items[0] = MP_OBJ_NEW_SMALL_INT(addr->ai_family);
    > +        t->items[1] = MP_OBJ_NEW_SMALL_INT(addr->ai_socktype);
    > +        t->items[2] = MP_OBJ_NEW_SMALL_INT(addr->ai_protocol);
    > +        // "canonname will be a string representing the canonical name 
of the host
    > +        // if AI_CANONNAME is part of the flags argument; else canonname 
will be empty." ??
    > +        if (addr->ai_canonname) {
    > +            t->items[3] = 
MP_OBJ_NEW_QSTR(qstr_from_str(addr->ai_canonname));
    > +        } else {
    > +            t->items[3] = mp_const_none;
    > +        }
    > +        t->items[4] = mp_obj_new_bytearray(addr->ai_addrlen, 
addr->ai_addr);
    > +        mp_obj_list_append(list, MP_OBJ_FROM_PTR(t));
    > +    }
    > +    freeaddrinfo(addr_list);
    > +    return list;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_socket_getaddrinfo_obj, 
2, 4, mod_socket_getaddrinfo);
    > +
    > +STATIC mp_obj_t mod_socket_sockaddr(mp_obj_t sockaddr_in) {
    > +    mp_buffer_info_t bufinfo;
    > +    mp_get_buffer_raise(sockaddr_in, &bufinfo, MP_BUFFER_READ);
    > +    switch (((struct sockaddr*)bufinfo.buf)->sa_family) {
    > +        case AF_INET: {
    > +            struct sockaddr_in *sa = (struct sockaddr_in*)bufinfo.buf;
    > +            mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
    > +            t->items[0] = MP_OBJ_NEW_SMALL_INT(AF_INET);
    > +            t->items[1] = mp_obj_new_bytes((byte*)&sa->sin_addr, 
sizeof(sa->sin_addr));
    > +            t->items[2] = MP_OBJ_NEW_SMALL_INT(ntohs(sa->sin_port));
    > +            return MP_OBJ_FROM_PTR(t);
    > +        }
    > +        case AF_INET6: {
    > +            struct sockaddr_in6 *sa = (struct sockaddr_in6*)bufinfo.buf;
    > +            mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL));
    > +            t->items[0] = MP_OBJ_NEW_SMALL_INT(AF_INET6);
    > +            t->items[1] = mp_obj_new_bytes((byte*)&sa->sin6_addr, 
sizeof(sa->sin6_addr));
    > +            t->items[2] = MP_OBJ_NEW_SMALL_INT(ntohs(sa->sin6_port));
    > +            t->items[3] = MP_OBJ_NEW_SMALL_INT(ntohl(sa->sin6_flowinfo));
    > +            t->items[4] = MP_OBJ_NEW_SMALL_INT(ntohl(sa->sin6_scope_id));
    > +            return MP_OBJ_FROM_PTR(t);
    > +        }
    > +        default: {
    > +            struct sockaddr *sa = (struct sockaddr*)bufinfo.buf;
    > +            mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
    > +            t->items[0] = MP_OBJ_NEW_SMALL_INT(sa->sa_family);
    > +            t->items[1] = mp_obj_new_bytes((byte*)sa->sa_data, 
bufinfo.len - offsetof(struct sockaddr, sa_data));
    > +            return MP_OBJ_FROM_PTR(t);
    > +        }
    > +    }
    > +    return mp_const_none;
    > +}
    > +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_sockaddr_obj, 
mod_socket_sockaddr);
    > +
    > +STATIC const mp_rom_map_elem_t mp_module_socket_globals_table[] = {
    > +    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usocket) },
    > +    { MP_ROM_QSTR(MP_QSTR_socket), MP_ROM_PTR(&mp_type_socket) },
    > +    { MP_ROM_QSTR(MP_QSTR_getaddrinfo), 
MP_ROM_PTR(&mod_socket_getaddrinfo_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_inet_pton), 
MP_ROM_PTR(&mod_socket_inet_pton_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_inet_ntop), 
MP_ROM_PTR(&mod_socket_inet_ntop_obj) },
    > +    { MP_ROM_QSTR(MP_QSTR_sockaddr), 
MP_ROM_PTR(&mod_socket_sockaddr_obj) },
    > +
    > +#define C(name) { MP_ROM_QSTR(MP_QSTR_ ## name), MP_ROM_INT(name) }
    > +    C(AF_UNIX),
    > +    C(AF_INET),
    > +    C(AF_INET6),
    > +    C(SOCK_STREAM),
    > +    C(SOCK_DGRAM),
    > +    C(SOCK_RAW),
    > +
    > +    C(MSG_DONTROUTE),
    > +    C(MSG_DONTWAIT),
    > +
    > +    C(SOL_SOCKET),
    > +    C(SO_BROADCAST),
    > +    C(SO_ERROR),
    > +    C(SO_KEEPALIVE),
    > +    C(SO_LINGER),
    > +    C(SO_REUSEADDR),
    > +#undef C
    > +};
    > +
    > +STATIC MP_DEFINE_CONST_DICT(mp_module_socket_globals, 
mp_module_socket_globals_table);
    > +
    > +const mp_obj_module_t mp_module_socket = {
    > +    .base = { &mp_type_module },
    > +    .globals = (mp_obj_dict_t*)&mp_module_socket_globals,
    > +};
    > diff --git a/include/modlwip.h b/include/modlwip.h
    > new file mode 100644
    > index 0000000..f754686
    > --- /dev/null
    > +++ b/include/modlwip.h
    > @@ -0,0 +1,111 @@
    > +/*
    > + * This file is part of the Micro Python project, http://micropython.org/
    > + *
    > + * The MIT License (MIT)
    > + *
    > + * Copyright (c) 2013, 2014 Damien P. George
    > + * Copyright (c) 2015 Galen Hazelwood
    > + *
    > + * Permission is hereby granted, free of charge, to any person obtaining 
a copy
    > + * of this software and associated documentation files (the "Software"), 
to deal
    > + * in the Software without restriction, including without limitation the 
rights
    > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or 
sell
    > + * copies of the Software, and to permit persons to whom the Software is
    > + * furnished to do so, subject to the following conditions:
    > + *
    > + * The above copyright notice and this permission notice shall be 
included in
    > + * all copies or substantial portions of the Software.
    > + *
    > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR
    > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY,
    > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
SHALL THE
    > + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
ARISING FROM,
    > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
DEALINGS IN
    > + * THE SOFTWARE.
    > + */
    > +
    > +#include <string.h>
    > +#include <errno.h>
    > +#include <stdio.h>
    > +
    > +#include "py/nlr.h"
    > +#include "py/objlist.h"
    > +#include "py/runtime.h"
    > +#include "py/stream.h"
    > +#include "py/mphal.h"
    > +
    > +#include "netutils.h"
    > +
    > +#include "lwip/init.h"
    > +#include "lwip/timers.h"
    > +#include "lwip/tcp.h"
    > +#include "lwip/udp.h"
    > +#include "lwip/dns.h"
    > +#include "lwip/tcp_impl.h"
    > +#include "lwip/netif.h"
    > +#include "lwip/inet.h"
    > +#include "lwip/prot/ethernet.h"
    > +//#include <mini-os/lwip-net.h>
    > +
    > +typedef struct _lwip_socket_obj_t {
    > +    mp_obj_base_t base;
    > +
    > +    volatile union {
    > +        struct tcp_pcb *tcp;
    > +        struct udp_pcb *udp;
    > +    } pcb;
    > +    volatile union {
    > +        struct pbuf *pbuf;
    > +        struct tcp_pcb *connection;
    > +    } incoming;
    > +    mp_obj_t callback;
    > +    byte peer[4];
    > +    mp_uint_t peer_port;
    > +    mp_uint_t timeout;
    > +    uint16_t leftover_count;
    > +
    > +    uint8_t domain;
    > +    uint8_t type;
    > +
    > +    #define STATE_NEW 0
    > +    #define STATE_CONNECTING 1
    > +    #define STATE_CONNECTED 2
    > +    #define STATE_PEER_CLOSED 3
    > +    // Negative value is lwIP error
    > +    int8_t state;
    > +} lwip_socket_obj_t;
    > +
    > +struct mcargs {
    > +  struct eth_addr mac;
    > +  struct netif    netif;
    > +  ip4_addr_t      ip;
    > +  ip4_addr_t      mask;
    > +  ip4_addr_t      gw;
    > +  #if LWIP_DNS
    > +  ip4_addr_t      dns0;
    > +  ip4_addr_t      dns1;
    > +  #endif
    > +} args;
    > +
    > +
    > +void lwip_socket_print(const mp_print_t *print, mp_obj_t self_in, 
mp_print_kind_t kind);
    > +mp_obj_t lwip_socket_close(mp_obj_t self_in);
    > +mp_obj_t lwip_socket_bind(mp_obj_t self_in, mp_obj_t addr_in);
    > +mp_obj_t lwip_socket_listen(mp_obj_t self_in, mp_obj_t backlog_in);
    > +mp_obj_t lwip_socket_accept(mp_obj_t self_in);
    > +mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in);
    > +void lwip_socket_check_connected(lwip_socket_obj_t *socket);
    > +mp_obj_t lwip_socket_send(mp_obj_t self_in, mp_obj_t buf_in);
    > +mp_obj_t lwip_socket_recv(mp_obj_t self_in, mp_obj_t len_in);
    > +mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t 
addr_in);
    > +mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in);
    > +mp_obj_t lwip_socket_sendall(mp_obj_t self_in, mp_obj_t buf_in);
    > +mp_obj_t lwip_socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in);
    > +mp_obj_t lwip_socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in);
    > +mp_obj_t lwip_socket_setsockopt(mp_uint_t n_args, const mp_obj_t *args);
    > +mp_obj_t lwip_socket_makefile(mp_uint_t n_args, const mp_obj_t *args);
    > +mp_uint_t lwip_socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, 
int *errcode);
    > +mp_uint_t lwip_socket_write(mp_obj_t self_in, const void *buf, mp_uint_t 
size, int *errcode);
    > +mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, mp_uint_t 
n_args, mp_uint_t n_kw, const mp_obj_t *args);
    > +mp_obj_t lwip_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in);
    > diff --git a/include/mpconfigport.h b/include/mpconfigport.h
    > new file mode 100644
    > index 0000000..5de97bf
    > --- /dev/null
    > +++ b/include/mpconfigport.h
    > @@ -0,0 +1,320 @@
    > +/*
    > + * This file is part of the MicroPython project, http://micropython.org/
    > + *
    > + * The MIT License (MIT)
    > + *
    > + * Copyright (c) 2013, 2014 Damien P. George
    > + *
    > + * Permission is hereby granted, free of charge, to any person obtaining 
a copy
    > + * of this software and associated documentation files (the "Software"), 
to deal
    > + * in the Software without restriction, including without limitation the 
rights
    > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or 
sell
    > + * copies of the Software, and to permit persons to whom the Software is
    > + * furnished to do so, subject to the following conditions:
    > + *
    > + * The above copyright notice and this permission notice shall be 
included in
    > + * all copies or substantial portions of the Software.
    > + *
    > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR
    > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY,
    > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
SHALL THE
    > + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
ARISING FROM,
    > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
DEALINGS IN
    > + * THE SOFTWARE.
    > + */
    > +
    > +// options to control how MicroPython is built
    > +#define MICROPY_HW_BOARD_NAME "unix"
    > +#define MICROPY_HW_MCU_NAME "unknown-cpu"
    > +
    > +#define MICROPY_ALLOC_PATH_MAX      (PATH_MAX)
    > +#define MICROPY_PERSISTENT_CODE_LOAD (1)
    > +#if !defined(MICROPY_EMIT_X64) && defined(__x86_64__)
    > +    #define MICROPY_EMIT_X64        (0)
    > +#endif
    > +#if !defined(MICROPY_EMIT_X86) && defined(__i386__)
    > +    #define MICROPY_EMIT_X86        (1)
    > +#endif
    > +#if !defined(MICROPY_EMIT_THUMB) && defined(__thumb2__)
    > +    #define MICROPY_EMIT_THUMB      (1)
    > +    #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p) | 
1))
    > +#endif
    > +// Some compilers define __thumb2__ and __arm__ at the same time, let
    > +// autodetected thumb2 emitter have priority.
    > +#if !defined(MICROPY_EMIT_ARM) && defined(__arm__) && 
!defined(__thumb2__)
    > +    #define MICROPY_EMIT_ARM        (1)
    > +#endif
    > +#define MICROPY_COMP_MODULE_CONST   (1)
    > +#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1)
    > +#define MICROPY_COMP_RETURN_IF_EXPR (1)
    > +#define MICROPY_ENABLE_GC           (0)
    > +#define MICROPY_ENABLE_FINALISER    (0)
    > +#define MICROPY_STACK_CHECK         (1)
    > +#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1)
    > +#define MICROPY_MEM_STATS           (1)
    > +#define MICROPY_DEBUG_PRINTERS      (1)
    > +// Printing debug to stderr may give tests which
    > +// check stdout a chance to pass, etc.
    > +#define MICROPY_DEBUG_PRINTER_DEST  mp_stderr_print
    > +#define MICROPY_READER_POSIX        (1)
    > +#define MICROPY_USE_READLINE_HISTORY (1)
    > +#define MICROPY_HELPER_REPL         (1)
    > +#define MICROPY_REPL_EMACS_KEYS     (1)
    > +#define MICROPY_REPL_AUTO_INDENT    (1)
    > +#define MICROPY_HELPER_LEXER_UNIX   (1)
    > +#define MICROPY_ENABLE_SOURCE_LINE  (1)
    > +#define MICROPY_FLOAT_IMPL          (MICROPY_FLOAT_IMPL_DOUBLE)
    > +#define MICROPY_LONGINT_IMPL        (MICROPY_LONGINT_IMPL_MPZ)
    > +#define MICROPY_STREAMS_NON_BLOCK   (1)
    > +#define MICROPY_STREAMS_POSIX_API   (1)
    > +#define MICROPY_OPT_COMPUTED_GOTO   (1)
    > +#ifndef MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE
    > +#define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE (1)
    > +#endif
    > +#define MICROPY_CAN_OVERRIDE_BUILTINS (1)
    > +#define MICROPY_PY_FUNCTION_ATTRS   (1)
    > +#define MICROPY_PY_DESCRIPTORS      (1)
    > +#define MICROPY_PY_BUILTINS_STR_UNICODE (1)
    > +#define MICROPY_PY_BUILTINS_STR_CENTER (1)
    > +#define MICROPY_PY_BUILTINS_STR_PARTITION (1)
    > +#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1)
    > +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1)
    > +#define MICROPY_PY_BUILTINS_FROZENSET (1)
    > +#define MICROPY_PY_BUILTINS_COMPILE (1)
    > +#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1)
    > +#define MICROPY_PY_BUILTINS_INPUT   (1)
    > +#define MICROPY_PY_BUILTINS_POW3    (1)
    > +#define MICROPY_PY_MICROPYTHON_MEM_INFO (1)
    > +#define MICROPY_PY_ALL_SPECIAL_METHODS (1)
    > +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1)
    > +#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1)
    > +#define MICROPY_PY_SYS_EXIT         (1)
    > +#if defined(__APPLE__) && defined(__MACH__)
    > +    #define MICROPY_PY_SYS_PLATFORM  "darwin"
    > +#else
    > +    #define MICROPY_PY_SYS_PLATFORM  "linux"
    > +#endif
    > +#define MICROPY_PY_SYS_MAXSIZE      (1)
    > +#define MICROPY_PY_SYS_STDFILES     (1)
    > +#define MICROPY_PY_SYS_EXC_INFO     (1)
    > +#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
    > +#ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS
    > +#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
    > +#endif
    > +#define MICROPY_PY_CMATH            (1)
    > +#define MICROPY_PY_IO_FILEIO        (1)
    > +#define MICROPY_PY_IO_RESOURCE_STREAM (1)
    > +#define MICROPY_PY_GC_COLLECT_RETVAL (1)
    > +#define MICROPY_MODULE_FROZEN_STR   (1)
    > +
    > +#define MICROPY_STACKLESS           (0)
    > +#define MICROPY_STACKLESS_STRICT    (0)
    > +
    > +#define MICROPY_PY_OS_STATVFS       (1)
    > +#define MICROPY_PY_UTIME            (1)
    > +#define MICROPY_PY_UTIME_MP_HAL     (1)
    > +#define MICROPY_PY_UERRNO           (1)
    > +#define MICROPY_PY_UCTYPES          (1)
    > +#define MICROPY_PY_UZLIB            (1)
    > +#define MICROPY_PY_UJSON            (1)
    > +#define MICROPY_PY_URE              (1)
    > +#define MICROPY_PY_UHEAPQ           (1)
    > +#define MICROPY_PY_UTIMEQ           (1)
    > +#define MICROPY_PY_UHASHLIB         (1)
    > +#if MICROPY_PY_USSL && MICROPY_SSL_AXTLS
    > +#define MICROPY_PY_UHASHLIB_SHA1    (1)
    > +#endif
    > +#define MICROPY_PY_UBINASCII        (1)
    > +#define MICROPY_PY_UBINASCII_CRC32  (1)
    > +#define MICROPY_PY_URANDOM          (1)
    > +#ifndef MICROPY_PY_USELECT_POSIX
    > +#define MICROPY_PY_USELECT_POSIX    (1)
    > +#endif
    > +#define MICROPY_PY_WEBSOCKET        (1)
    > +#define MICROPY_PY_MACHINE          (1)
    > +#define MICROPY_PY_MACHINE_PULSE    (1)
    > +#define MICROPY_MACHINE_MEM_GET_READ_ADDR   mod_machine_mem_get_addr
    > +#define MICROPY_MACHINE_MEM_GET_WRITE_ADDR  mod_machine_mem_get_addr
    > +
    > +#define MICROPY_FATFS_ENABLE_LFN       (1)
    > +#define MICROPY_FATFS_RPATH            (2)
    > +#define MICROPY_FATFS_MAX_SS           (4096)
    > +#define MICROPY_FATFS_LFN_CODE_PAGE    (437) /* 1=SFN/ANSI 
437=LFN/U.S.(OEM) */
    > +#define MICROPY_VFS_FAT                (0)
    > +
    > +// Define to MICROPY_ERROR_REPORTING_DETAILED to get function, etc.
    > +// names in exception messages (may require more RAM).
    > +#define MICROPY_ERROR_REPORTING     (MICROPY_ERROR_REPORTING_DETAILED)
    > +#define MICROPY_WARNINGS            (1)
    > +#define MICROPY_PY_STR_BYTES_CMP_WARN (1)
    > +
    > +// Define to 1 to use undertested inefficient GC helper implementation
    > +// (if more efficient arch-specific one is not available).
    > +#ifndef MICROPY_GCREGS_SETJMP
    > +    #ifdef __mips__
    > +        #define MICROPY_GCREGS_SETJMP (1)
    > +    #else
    > +        #define MICROPY_GCREGS_SETJMP (0)
    > +    #endif
    > +#endif
    > +
    > +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF   (1)
    > +#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE  (256)
    > +#define MICROPY_KBD_EXCEPTION       (1)
    > +#define MICROPY_ASYNC_KBD_INTR      (1)
    > +
    > +extern const struct _mp_obj_module_t mp_module_machine;
    > +extern const struct _mp_obj_module_t mp_module_os;
    > +extern const struct _mp_obj_module_t mp_module_uos_vfs;
    > +extern const struct _mp_obj_module_t mp_module_uselect;
    > +extern const struct _mp_obj_module_t mp_module_time;
    > +extern const struct _mp_obj_module_t mp_module_termios;
    > +extern const struct _mp_obj_module_t mp_module_socket;
    > +extern const struct _mp_obj_module_t mp_module_ffi;
    > +extern const struct _mp_obj_module_t mp_module_jni;
    > +
    > +#if MICROPY_PY_UOS_VFS
    > +#define MICROPY_PY_UOS_VFS_DEF { MP_ROM_QSTR(MP_QSTR_uos_vfs), 
MP_ROM_PTR(&mp_module_uos_vfs) },
    > +#else
    > +#define MICROPY_PY_UOS_VFS_DEF
    > +#endif
    > +#if MICROPY_PY_FFI
    > +#define MICROPY_PY_FFI_DEF { MP_ROM_QSTR(MP_QSTR_ffi), 
MP_ROM_PTR(&mp_module_ffi) },
    > +#else
    > +#define MICROPY_PY_FFI_DEF
    > +#endif
    > +#if MICROPY_PY_JNI
    > +#define MICROPY_PY_JNI_DEF { MP_ROM_QSTR(MP_QSTR_jni), 
MP_ROM_PTR(&mp_module_jni) },
    > +#else
    > +#define MICROPY_PY_JNI_DEF
    > +#endif
    > +#if MICROPY_PY_UTIME
    > +#define MICROPY_PY_UTIME_DEF { MP_ROM_QSTR(MP_QSTR_utime), 
MP_ROM_PTR(&mp_module_time) },
    > +#else
    > +#define MICROPY_PY_UTIME_DEF
    > +#endif
    > +#if MICROPY_PY_TERMIOS
    > +#define MICROPY_PY_TERMIOS_DEF { MP_ROM_QSTR(MP_QSTR_termios), 
MP_ROM_PTR(&mp_module_termios) },
    > +#else
    > +#define MICROPY_PY_TERMIOS_DEF
    > +#endif
    > +#if MICROPY_PY_SOCKET
    > +#define MICROPY_PY_SOCKET_DEF { MP_ROM_QSTR(MP_QSTR_usocket), 
MP_ROM_PTR(&mp_module_socket) },
    > +#else
    > +#define MICROPY_PY_SOCKET_DEF
    > +#endif
    > +#if MICROPY_PY_USELECT_POSIX
    > +#define MICROPY_PY_USELECT_DEF { MP_ROM_QSTR(MP_QSTR_uselect), 
MP_ROM_PTR(&mp_module_uselect) },
    > +#else
    > +#define MICROPY_PY_USELECT_DEF
    > +#endif
    > +
    > +#define MICROPY_PORT_BUILTIN_MODULES \
    > +    MICROPY_PY_FFI_DEF \
    > +    MICROPY_PY_JNI_DEF \
    > +    MICROPY_PY_UTIME_DEF \
    > +    MICROPY_PY_SOCKET_DEF \
    > +    { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&mp_module_machine) }, \
    > +    { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_os) }, \
    > +    MICROPY_PY_UOS_VFS_DEF \
    > +    MICROPY_PY_USELECT_DEF \
    > +    MICROPY_PY_TERMIOS_DEF \
    > +
    > +// type definitions for the specific machine
    > +
    > +// For size_t and ssize_t
    > +#include <unistd.h>
    > +
    > +// assume that if we already defined the obj repr then we also defined 
types
    > +#ifndef MICROPY_OBJ_REPR
    > +#ifdef __LP64__
    > +typedef long mp_int_t; // must be pointer size
    > +typedef unsigned long mp_uint_t; // must be pointer size
    > +#else
    > +// These are definitions for machines where sizeof(int) == sizeof(void*),
    > +// regardless of actual size.
    > +typedef int mp_int_t; // must be pointer size
    > +typedef unsigned int mp_uint_t; // must be pointer size
    > +#endif
    > +#endif
    > +
    > +// Cannot include <sys/types.h>, as it may lead to symbol name clashes
    > +#if _FILE_OFFSET_BITS == 64 && !defined(__LP64__)
    > +typedef long long mp_off_t;
    > +#else
    > +typedef long mp_off_t;
    > +#endif
    > +
    > +void mp_unix_alloc_exec(size_t min_size, void** ptr, size_t *size);
    > +void mp_unix_free_exec(void *ptr, size_t size);
    > +void mp_unix_mark_exec(void);
    > +#define MP_PLAT_ALLOC_EXEC(min_size, ptr, size) 
mp_unix_alloc_exec(min_size, ptr, size)
    > +#define MP_PLAT_FREE_EXEC(ptr, size) mp_unix_free_exec(ptr, size)
    > +#ifndef MICROPY_FORCE_PLAT_ALLOC_EXEC
    > +// Use MP_PLAT_ALLOC_EXEC for any executable memory allocation, 
including for FFI
    > +// (overriding libffi own implementation)
    > +#define MICROPY_FORCE_PLAT_ALLOC_EXEC (1)
    > +#endif
    > +
    > +#if MICROPY_PY_OS_DUPTERM
    > +#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, 
len)
    > +#else
    > +#define MP_PLAT_PRINT_STRN(str, len) do { ssize_t ret = write(1, str, 
len); (void)ret; } while (0)
    > +#endif
    > +
    > +#ifdef __linux__
    > +// Can access physical memory using /dev/mem
    > +#define MICROPY_PLAT_DEV_MEM  (1)
    > +#endif
    > +
    > +// Assume that select() call, interrupted with a signal, and erroring
    > +// with EINTR, updates remaining timeout value.
    > +#define MICROPY_SELECT_REMAINING_TIME (1)
    > +
    > +#ifdef __ANDROID__
    > +#include <android/api-level.h>
    > +#if __ANDROID_API__ < 4
    > +// Bionic libc in Android 1.5 misses these 2 functions
    > +#define MP_NEED_LOG2 (1)
    > +#define nan(x) NAN
    > +#endif
    > +#endif
    > +
    > +#define MICROPY_PORT_BUILTINS \
    > +    { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
    > +
    > +#define MP_STATE_PORT MP_STATE_VM
    > +
    > +#define MICROPY_PORT_ROOT_POINTERS \
    > +    const char *readline_hist[50]; \
    > +    void *mmap_region_head; \
    > +
    > +// We need to provide a declaration/definition of alloca()
    > +// unless support for it is disabled.
    > +#if !defined(MICROPY_NO_ALLOCA) || MICROPY_NO_ALLOCA == 0
    > +#ifdef __FreeBSD__
    > +#include <stdlib.h>
    > +#else
    > +#include <alloca.h>
    > +#endif
    > +#endif
    > +
    > +// From "man readdir": "Under glibc, programs can check for the 
availability
    > +// of the fields [in struct dirent] not defined in POSIX.1 by testing 
whether
    > +// the macros [...], _DIRENT_HAVE_D_TYPE are defined."
    > +// Other libc's don't define it, but proactively assume that 
dirent->d_type
    > +// is available on a modern *nix system.
    > +#ifndef _DIRENT_HAVE_D_TYPE
    > +#define _DIRENT_HAVE_D_TYPE (1)
    > +#endif
    > +// This macro is not provided by glibc but we need it so ports that 
don't have
    > +// dirent->d_ino can disable the use of this field.
    > +#ifndef _DIRENT_HAVE_D_INO
    > +#define _DIRENT_HAVE_D_INO (1)
    > +#endif
    > +
    > +#ifndef __APPLE__
    > +// For debugging purposes, make printf() available to any source file.
    > +#include <stdio.h>
    > +#endif
    > diff --git a/main.c b/main.c
    > new file mode 100644
    > index 0000000..c2ddcfd
    > --- /dev/null
    > +++ b/main.c
    > @@ -0,0 +1,543 @@
    > +/*
    > + * This file is part of the MicroPython project, http://micropython.org/
    > + *
    > + * The MIT License (MIT)
    > + *
    > + * Copyright (c) 2013, 2014 Damien P. George
    > + *
    > + * Permission is hereby granted, free of charge, to any person obtaining 
a copy
    > + * of this software and associated documentation files (the "Software"), 
to deal
    > + * in the Software without restriction, including without limitation the 
rights
    > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or 
sell
    > + * copies of the Software, and to permit persons to whom the Software is
    > + * furnished to do so, subject to the following conditions:
    > + *
    > + * The above copyright notice and this permission notice shall be 
included in
    > + * all copies or substantial portions of the Software.
    > + *
    > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR
    > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY,
    > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
SHALL THE
    > + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
ARISING FROM,
    > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
DEALINGS IN
    > + * THE SOFTWARE.
    > + */
    > +
    > +#include <stdint.h>
    > +#include <stdbool.h>
    > +#include <stdio.h>
    > +#include <string.h>
    > +#include <stdlib.h>
    > +#include <stdarg.h>
    > +#include <unistd.h>
    > +#include <ctype.h>
    > +#include <sys/stat.h>
    > +#include <sys/types.h>
    > +#include <errno.h>
    > +#include <signal.h>
    > +
    > +#include <uk/plat/memory.h>
    > +
    > +#include "py/mpstate.h"
    > +#include "py/nlr.h"
    > +#include "py/compile.h"
    > +#include "py/runtime.h"
    > +#include "py/builtin.h"
    > +#include "py/repl.h"
    > +#include "py/gc.h"
    > +#include "py/stackctrl.h"
    > +#include "py/mphal.h"
    > +#include "py/mpthread.h"
    > +#include "extmod/misc.h"
    > +#include "genhdr/mpversion.h"
    > +#include "input.h"
    > +#include "pyexec.h"
    > +
    > +// Command line options, with their defaults
    > +STATIC bool compile_only = false;
    > +STATIC uint emit_opt = MP_EMIT_OPT_NONE;
    > +
    > +#if MICROPY_ENABLE_GC
    > +// Heap size of GC heap (if enabled)
    > +// Make it larger on a 64 bit machine, because pointers are larger.
    > +long heap_size = 1024*1024 * (sizeof(mp_uint_t) / 4);
    > +#endif
    > +
    > +STATIC void stderr_print_strn(void *env, const char *str, size_t len) {
    > +    (void)env;
    > +    ssize_t dummy = write(STDERR_FILENO, str, len);
    > +    mp_uos_dupterm_tx_strn(str, len);
    > +    (void)dummy;
    > +}
    > +
    > +const mp_print_t mp_stderr_print = {NULL, stderr_print_strn};
    > +
    > +#define FORCED_EXIT (0x100)
    > +// If exc is SystemExit, return value where FORCED_EXIT bit set,
    > +// and lower 8 bits are SystemExit value. For all other exceptions,
    > +// return 1.
    > +STATIC int handle_uncaught_exception(mp_obj_base_t *exc) {
    > +    // check for SystemExit
    > +    if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), 
MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
    > +        // None is an exit value of 0; an int is its value; anything 
else is 1
    > +        mp_obj_t exit_val = 
mp_obj_exception_get_value(MP_OBJ_FROM_PTR(exc));
    > +        mp_int_t val = 0;
    > +        if (exit_val != mp_const_none && !mp_obj_get_int_maybe(exit_val, 
&val)) {
    > +            val = 1;
    > +        }
    > +        return FORCED_EXIT | (val & 255);
    > +    }
    > +
    > +    // Report all other exceptions
    > +    mp_obj_print_exception(&mp_stderr_print, MP_OBJ_FROM_PTR(exc));
    > +    return 1;
    > +}
    > +
    > +#define LEX_SRC_STR (1)
    > +#define LEX_SRC_VSTR (2)
    > +#define LEX_SRC_FILENAME (3)
    > +#define LEX_SRC_STDIN (4)
    > +
    > +// Returns standard error codes: 0 for success, 1 for all other errors,
    > +// except if FORCED_EXIT bit is set then script raised SystemExit and the
    > +// value of the exit is in the lower 8 bits of the return value
    > +STATIC int execute_from_lexer(int source_kind, const void *source, 
mp_parse_input_kind_t input_kind, bool is_repl) {
    > +    mp_hal_set_interrupt_char(CHAR_CTRL_C);
    > +
    > +    nlr_buf_t nlr;
    > +    if (nlr_push(&nlr) == 0) {
    > +        // create lexer based on source kind
    > +        mp_lexer_t *lex;
    > +        if (source_kind == LEX_SRC_STR) {
    > +            const char *line = source;
    > +            lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, 
strlen(line), false);
    > +        } else if (source_kind == LEX_SRC_VSTR) {
    > +            const vstr_t *vstr = source;
    > +            lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, 
vstr->buf, vstr->len, false);
    > +        } else if (source_kind == LEX_SRC_FILENAME) {
    > +            lex = mp_lexer_new_from_file((const char*)source);
    > +        } else { // LEX_SRC_STDIN
    > +            lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, 0, false);
    > +        }
    > +
    > +        qstr source_name = lex->source_name;
    > +
    > +        #if MICROPY_PY___FILE__
    > +        if (input_kind == MP_PARSE_FILE_INPUT) {
    > +            mp_store_global(MP_QSTR___file__, 
MP_OBJ_NEW_QSTR(source_name));
    > +        }
    > +        #endif
    > +
    > +        mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
    > +
    > +        #if defined(MICROPY_UNIX_COVERAGE)
    > +        // allow to print the parse tree in the coverage build
    > +        if (mp_verbose_flag >= 3) {
    > +            printf("----------------\n");
    > +            mp_parse_node_print(parse_tree.root, 0);
    > +            printf("----------------\n");
    > +        }
    > +        #endif
    > +
    > +        mp_obj_t module_fun = mp_compile(&parse_tree, source_name, 
emit_opt, is_repl);
    > +
    > +        if (!compile_only) {
    > +            // execute it
    > +            mp_call_function_0(module_fun);
    > +            // check for pending exception
    > +            if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
    > +                mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
    > +                MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
    > +                nlr_raise(obj);
    > +            }
    > +        }
    > +
    > +        mp_hal_set_interrupt_char(-1);
    > +        nlr_pop();
    > +        return 0;
    > +
    > +    } else {
    > +        // uncaught exception
    > +        mp_hal_set_interrupt_char(-1);
    > +        return handle_uncaught_exception(nlr.ret_val);
    > +    }
    > +}
    > +
    > +STATIC int do_file(const char *file) {
    > +    return execute_from_lexer(LEX_SRC_FILENAME, file, 
MP_PARSE_FILE_INPUT, false);
    > +}
    > +
    > +STATIC int do_str(const char *str) {
    > +    return execute_from_lexer(LEX_SRC_STR, str, MP_PARSE_FILE_INPUT, 
false);
    > +}
    > +
    > +STATIC int usage(char **argv) {
    > +    printf(
    > +"usage: %s [<opts>] [-X <implopt>] [-c <command>] [<filename>]\n"
    > +"Options:\n"
    > +"-v : verbose (trace various operations); can be multiple\n"
    > +"-O[N] : apply bytecode optimizations of level N\n"
    > +"\n"
    > +"Implementation specific options (-X):\n", argv[0]
    > +);
    > +    int impl_opts_cnt = 0;
    > +    printf(
    > +"  compile-only                 -- parse and compile only\n"
    > +"  emit={bytecode,native,viper} -- set the default code emitter\n"
    > +);
    > +    impl_opts_cnt++;
    > +#if MICROPY_ENABLE_GC
    > +    printf(
    > +"  heapsize=<n>[w][K|M] -- set the heap size for the GC (default %ld)\n"
    > +, heap_size);
    > +    impl_opts_cnt++;
    > +#endif
    > +
    > +    if (impl_opts_cnt == 0) {
    > +        printf("  (none)\n");
    > +    }
    > +
    > +    return 1;
    > +}
    > +
    > +// Process options which set interpreter init options
    > +STATIC void pre_process_options(int argc, char **argv) {
    > +    for (int a = 1; a < argc; a++) {
    > +        if (argv[a][0] == '-') {
    > +            if (strcmp(argv[a], "-X") == 0) {
    > +                if (a + 1 >= argc) {
    > +                    exit(usage(argv));
    > +                }
    > +                if (0) {
    > +                } else if (strcmp(argv[a + 1], "compile-only") == 0) {
    > +                    compile_only = true;
    > +                } else if (strcmp(argv[a + 1], "emit=bytecode") == 0) {
    > +                    emit_opt = MP_EMIT_OPT_BYTECODE;
    > +                } else if (strcmp(argv[a + 1], "emit=native") == 0) {
    > +                    emit_opt = MP_EMIT_OPT_NATIVE_PYTHON;
    > +                } else if (strcmp(argv[a + 1], "emit=viper") == 0) {
    > +                    emit_opt = MP_EMIT_OPT_VIPER;
    > +#if MICROPY_ENABLE_GC
    > +                } else if (strncmp(argv[a + 1], "heapsize=", 
sizeof("heapsize=") - 1) == 0) {
    > +                    char *end;
    > +                    heap_size = strtol(argv[a + 1] + sizeof("heapsize=") 
- 1, &end, 0);
    > +                    // Don't bring unneeded libc dependencies like 
tolower()
    > +                    // If there's 'w' immediately after number, adjust 
it for
    > +                    // target word size. Note that it should be *before* 
size
    > +                    // suffix like K or M, to avoid confusion with 
kilowords,
    > +                    // etc. the size is still in bytes, just can be 
adjusted
    > +                    // for word size (taking 32bit as baseline).
    > +                    bool word_adjust = false;
    > +                    if ((*end | 0x20) == 'w') {
    > +                        word_adjust = true;
    > +                        end++;
    > +                    }
    > +                    if ((*end | 0x20) == 'k') {
    > +                        heap_size *= 1024;
    > +                    } else if ((*end | 0x20) == 'm') {
    > +                        heap_size *= 1024 * 1024;
    > +                    } else {
    > +                        // Compensate for ++ below
    > +                        --end;
    > +                    }
    > +                    if (*++end != 0) {
    > +                        goto invalid_arg;
    > +                    }
    > +                    if (word_adjust) {
    > +                        heap_size = heap_size * BYTES_PER_WORD / 4;
    > +                    }
    > +                    // If requested size too small, we'll crash anyway
    > +                    if (heap_size < 700) {
    > +                        goto invalid_arg;
    > +                    }
    > +#endif
    > +                } else {
    > +#if MICROPY_ENABLE_GC
    > +invalid_arg:
    > +#endif
    > +                    printf("Invalid option\n");
    > +                    exit(usage(argv));
    > +                }
    > +                a++;
    > +            }
    > +        }
    > +    }
    > +}
    > +
    > +STATIC void set_sys_argv(char *argv[], int argc, int start_arg) {
    > +    for (int i = start_arg; i < argc; i++) {
    > +        mp_obj_list_append(mp_sys_argv, 
MP_OBJ_NEW_QSTR(qstr_from_str(argv[i])));
    > +    }
    > +}
    > +
    > +#ifdef _WIN32
    > +#define PATHLIST_SEP_CHAR ';'
    > +#else
    > +#define PATHLIST_SEP_CHAR ':'
    > +#endif
    > +
    > +MP_NOINLINE int micropython_main_(int argc, char **argv);
    > +
    > +int micropython_main(int argc, char **argv) {
    > +    #if MICROPY_PY_THREAD
    > +    mp_thread_init();
    > +    #endif
    > +    // We should capture stack top ASAP after start, and it should be
    > +    // captured guaranteedly before any other stack variables are 
allocated.
    > +    // For this, actual main (renamed main_) should not be inlined into
    > +    // this function. main_() itself may have other functions inlined 
(with
    > +    // their own stack variables), that's why we need this main/main_ 
split.
    > +    mp_stack_ctrl_init();
    > +    return micropython_main_(argc, argv);
    > +}
    > +
    > +MP_NOINLINE int micropython_main_(int argc, char **argv) {
    > +    #ifdef SIGPIPE
    > +    // Do not raise SIGPIPE, instead return EPIPE. Otherwise, e.g. 
writing
    > +    // to peer-closed socket will lead to sudden termination of 
MicroPython
    > +    // process. SIGPIPE is particularly nasty, because unix shell doesn't
    > +    // print anything for it, so the above looks like completely sudden 
and
    > +    // silent termination for unknown reason. Ignoring SIGPIPE is also 
what
    > +    // CPython does. Note that this may lead to problems using 
MicroPython
    > +    // scripts as pipe filters, but again, that's what CPython does. So,
    > +    // scripts which want to follow unix shell pipe semantics (where 
SIGPIPE
    > +    // means "pipe was requested to terminate, it's not an error"), 
should
    > +    // catch EPIPE themselves.
    > +    signal(SIGPIPE, SIG_IGN);
    > +    #endif
    > +
    > +    mp_stack_set_limit(40000 * (BYTES_PER_WORD / 4));
    > +
    > +    pre_process_options(argc, argv);
    > +
    > +#if MICROPY_ENABLE_GC
    > +    char *heap = malloc(heap_size);
    > +    gc_init(heap, heap + heap_size);
    > +#endif
    > +
    > +    mp_init();
    > +
    > +    char *home = getenv("HOME");
    > +    char *path = getenv("MICROPYPATH");
    > +    if (path == NULL) {
    > +        #ifdef MICROPY_PY_SYS_PATH_DEFAULT
    > +        path = MICROPY_PY_SYS_PATH_DEFAULT;
    > +        #else
    > +        path = "~/.micropython/lib:/usr/lib/micropython";
    > +        #endif
    > +    }
    > +    size_t path_num = 1; // [0] is for current dir (or base dir of the 
script)
    > +    if (*path == ':') {
    > +        path_num++;
    > +    }
    > +    for (char *p = path; p != NULL; p = strchr(p, PATHLIST_SEP_CHAR)) {
    > +        path_num++;
    > +        if (p != NULL) {
    > +            p++;
    > +        }
    > +    }
    > +    mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_path), path_num);
    > +    mp_obj_t *path_items;
    > +    mp_obj_list_get(mp_sys_path, &path_num, &path_items);
    > +    path_items[0] = MP_OBJ_NEW_QSTR(MP_QSTR_);
    > +    {
    > +    char *p = path;
    > +    for (mp_uint_t i = 1; i < path_num; i++) {
    > +        char *p1 = strchr(p, PATHLIST_SEP_CHAR);
    > +        if (p1 == NULL) {
    > +            p1 = p + strlen(p);
    > +        }
    > +        if (p[0] == '~' && p[1] == '/' && home != NULL) {
    > +            // Expand standalone ~ to $HOME
    > +            int home_l = strlen(home);
    > +            vstr_t vstr;
    > +            vstr_init(&vstr, home_l + (p1 - p - 1) + 1);
    > +            vstr_add_strn(&vstr, home, home_l);
    > +            vstr_add_strn(&vstr, p + 1, p1 - p - 1);
    > +            path_items[i] = mp_obj_new_str_from_vstr(&mp_type_str, 
&vstr);
    > +        } else {
    > +            path_items[i] = MP_OBJ_NEW_QSTR(qstr_from_strn(p, p1 - p));
    > +        }
    > +        p = p1 + 1;
    > +    }
    > +    }
    > +
    > +    mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), 0);
    > +
    > +    #if defined(MICROPY_UNIX_COVERAGE)
    > +    {
    > +        MP_DECLARE_CONST_FUN_OBJ_0(extra_coverage_obj);
    > +        mp_store_global(QSTR_FROM_STR_STATIC("extra_coverage"), 
MP_OBJ_FROM_PTR(&extra_coverage_obj));
    > +    }
    > +    #endif
    > +
    > +    // Here is some example code to create a class and instance of that 
class.
    > +    // First is the Python, then the C code.
    > +    //
    > +    // class TestClass:
    > +    //     pass
    > +    // test_obj = TestClass()
    > +    // test_obj.attr = 42
    > +    //
    > +    // mp_obj_t test_class_type, test_class_instance;
    > +    // test_class_type = 
mp_obj_new_type(QSTR_FROM_STR_STATIC("TestClass"), mp_const_empty_tuple, 
mp_obj_new_dict(0));
    > +    // mp_store_name(QSTR_FROM_STR_STATIC("test_obj"), 
test_class_instance = mp_call_function_0(test_class_type));
    > +    // mp_store_attr(test_class_instance, QSTR_FROM_STR_STATIC("attr"), 
mp_obj_new_int(42));
    > +
    > +    /*
    > +    printf("bytes:\n");
    > +    printf("    total %d\n", m_get_total_bytes_allocated());
    > +    printf("    cur   %d\n", m_get_current_bytes_allocated());
    > +    printf("    peak  %d\n", m_get_peak_bytes_allocated());
    > +    */
    > +
    > +    const int NOTHING_EXECUTED = -2;
    > +    int ret = NOTHING_EXECUTED;
    > +    for (int a = 1; a < argc; a++) {
    > +        if (argv[a][0] == '-') {
    > +            if (strcmp(argv[a], "-c") == 0) {
    > +                if (a + 1 >= argc) {
    > +                    return usage(argv);
    > +                }
    > +                ret = do_str(argv[a + 1]);
    > +                if (ret & FORCED_EXIT) {
    > +                    break;
    > +                }
    > +                a += 1;
    > +            } else if (strcmp(argv[a], "-m") == 0) {
    > +                if (a + 1 >= argc) {
    > +                    return usage(argv);
    > +                }
    > +                mp_obj_t import_args[4];
    > +                import_args[0] = mp_obj_new_str(argv[a + 1], 
strlen(argv[a + 1]), false);
    > +                import_args[1] = import_args[2] = mp_const_none;
    > +                // Ask __import__ to handle imported module specially - 
set its __name__
    > +                // to __main__, and also return this leaf module, not 
top-level package
    > +                // containing it.
    > +                import_args[3] = mp_const_false;
    > +                // TODO: 
https://docs.python.org/3/using/cmdline.html#cmdoption-m :
    > +                // "the first element of sys.argv will be the full path 
to
    > +                // the module file (while the module file is being 
located,
    > +                // the first element will be set to "-m")."
    > +                set_sys_argv(argv, argc, a + 1);
    > +
    > +                mp_obj_t mod;
    > +                nlr_buf_t nlr;
    > +                bool subpkg_tried = false;
    > +
    > +            reimport:
    > +                if (nlr_push(&nlr) == 0) {
    > +                    mod = 
mp_builtin___import__(MP_ARRAY_SIZE(import_args), import_args);
    > +                    nlr_pop();
    > +                } else {
    > +                    // uncaught exception
    > +                    return handle_uncaught_exception(nlr.ret_val) & 0xff;
    > +                }
    > +
    > +                if (mp_obj_is_package(mod) && !subpkg_tried) {
    > +                    subpkg_tried = true;
    > +                    vstr_t vstr;
    > +                    int len = strlen(argv[a + 1]);
    > +                    vstr_init(&vstr, len + sizeof(".__main__"));
    > +                    vstr_add_strn(&vstr, argv[a + 1], len);
    > +                    vstr_add_strn(&vstr, ".__main__", 
sizeof(".__main__") - 1);
    > +                    import_args[0] = 
mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
    > +                    goto reimport;
    > +                }
    > +
    > +                ret = 0;
    > +                break;
    > +            } else if (strcmp(argv[a], "-X") == 0) {
    > +                a += 1;
    > +            #if MICROPY_DEBUG_PRINTERS
    > +            } else if (strcmp(argv[a], "-v") == 0) {
    > +                mp_verbose_flag++;
    > +            #endif
    > +            } else if (strncmp(argv[a], "-O", 2) == 0) {
    > +                if (unichar_isdigit(argv[a][2])) {
    > +                    MP_STATE_VM(mp_optimise_value) = argv[a][2] & 0xf;
    > +                } else {
    > +                    MP_STATE_VM(mp_optimise_value) = 0;
    > +                    for (char *p = argv[a] + 1; *p && *p == 'O'; p++, 
MP_STATE_VM(mp_optimise_value)++);
    > +                }
    > +            } else {
    > +                return usage(argv);
    > +            }
    > +        } else {
    > +            char *pathbuf = malloc(PATH_MAX);
    > +            char *basedir = realpath(argv[a], pathbuf);
    > +            if (basedir == NULL) {
    > +                mp_printf(&mp_stderr_print, "%s: can't open file '%s': 
[Errno %d] %s\n", argv[0], argv[a], errno, strerror(errno));
    > +                // CPython exits with 2 in such case
    > +                ret = 2;
    > +                break;
    > +            }
    > +
    > +            // Set base dir of the script as first entry in sys.path
    > +            char *p = strrchr(basedir, '/');
    > +            path_items[0] = MP_OBJ_NEW_QSTR(qstr_from_strn(basedir, p - 
basedir));
    > +            free(pathbuf);
    > +
    > +            set_sys_argv(argv, argc, a);
    > +            ret = do_file(argv[a]);
    > +            break;
    > +        }
    > +    }
    > +
    > +    /* see if script is available from initrd */
    > +    struct ukplat_memregion_desc img;
    > +    char *cstr;
    > +    if (ukplat_memregion_find_initrd0(&img) >= 0) {
    > +      cstr = (char *)img.base;
    > +      ret = do_str(cstr);
    > +    }
    > +    /* repl mode */
    > +    else {
    > +#if MICROPY_REPL_EVENT_DRIVEN
    > +    pyexec_event_repl_init();
    > +    for (;;) {
    > +      int c = mp_hal_stdin_rx_chr();
    > +      if (pyexec_event_repl_process_char(c)) {
    > + break;
    > +      }
    > +    }
    > +#else
    > +    pyexec_friendly_repl();
    > +#endif
    > +    }
    > +
    > +    #if MICROPY_PY_MICROPYTHON_MEM_INFO
    > +    if (mp_verbose_flag) {
    > +        mp_micropython_mem_info(0, NULL);
    > +    }
    > +    #endif
    > +
    > +    mp_deinit();
    > +
    > +#if MICROPY_ENABLE_GC && !defined(NDEBUG)
    > +    // We don't really need to free memory since we are about to exit the
    > +    // process, but doing so helps to find memory leaks.
    > +    free(heap);
    > +#endif
    > +
    > +    //printf("total bytes = %d\n", m_get_total_bytes_allocated());
    > +    return ret & 0xff;
    > +}
    > +
    > +uint mp_import_stat(const char *path) {
    > +    struct stat st;
    > +    if (stat(path, &st) == 0) {
    > +        if (S_ISDIR(st.st_mode)) {
    > +            return MP_IMPORT_STAT_DIR;
    > +        } else if (S_ISREG(st.st_mode)) {
    > +            return MP_IMPORT_STAT_FILE;
    > +        }
    > +    }
    > +    return MP_IMPORT_STAT_NO_EXIST;
    > +}
    > +
    > +void nlr_jump_fail(void *val) {
    > +    printf("FATAL: uncaught NLR %p\n", val);
    > +    exit(1);
    > +}
    _______________________________________________
    Minios-devel mailing list
    Minios-devel@xxxxxxxxxxxxxxxxxxxx
    https://lists.xenproject.org/mailman/listinfo/minios-devel

_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.