[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |