diff --git a/stmhal/Makefile b/stmhal/Makefile index abf78ee8da..367cebd152 100644 --- a/stmhal/Makefile +++ b/stmhal/Makefile @@ -196,7 +196,7 @@ ifeq ($(MICROPY_PY_WIZNET5K),1) WIZNET5K_DIR=drivers/wiznet5k INC += -I$(TOP)/$(WIZNET5K_DIR) CFLAGS_MOD += -DMICROPY_PY_WIZNET5K=1 -SRC_MOD += modwiznet5k.c +SRC_MOD += modnwwiznet5k.c SRC_MOD += $(addprefix $(WIZNET5K_DIR)/,\ ethernet/w5200/w5200.c \ ethernet/wizchip_conf.c \ @@ -210,7 +210,7 @@ ifeq ($(MICROPY_PY_CC3K),1) CC3000_DIR=drivers/cc3000 INC += -I$(TOP)/$(CC3000_DIR)/inc CFLAGS_MOD += -DMICROPY_PY_CC3K=1 -SRC_MOD += modcc3k.c +SRC_MOD += modnwcc3k.c SRC_MOD += $(addprefix $(CC3000_DIR)/src/,\ cc3000_common.c \ evnt_handler.c \ diff --git a/stmhal/modcc3k.c b/stmhal/modcc3k.c deleted file mode 100644 index 5eab86101b..0000000000 --- a/stmhal/modcc3k.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 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. - */ - -// We can't include stdio.h because it defines _types_fd_set, but we -// need to use the CC3000 version of this type. - -#include -#include -#include -#include - -// CC3000 defines its own ENOBUFS (different to standard one!) -#undef ENOBUFS - -#include "stm32f4xx_hal.h" -#include "mpconfig.h" -#include "nlr.h" -#include "misc.h" -#include "qstr.h" -#include "obj.h" -#include "objtuple.h" -#include "objlist.h" -#include "stream.h" -#include "runtime.h" -#include "modnetwork.h" -#include "pin.h" -#include "genhdr/pins.h" -#include "spi.h" -#include "pybioctl.h" - -#include "hci.h" -#include "socket.h" -#include "inet_ntop.h" -#include "inet_pton.h" -#include "ccspi.h" -#include "wlan.h" -#include "nvmem.h" -#include "netapp.h" -#include "patch_prog.h" - -/// \moduleref network - -int CC3000_EXPORT(errno); // for cc3000 driver - -STATIC mp_obj_t cc3k_socket_new(mp_uint_t family, mp_uint_t type, mp_uint_t protocol, int *_errno); - -STATIC volatile uint32_t fd_closed_state = 0; -STATIC volatile bool wlan_connected = false; -STATIC volatile bool ip_obtained = false; - -STATIC int cc3k_get_fd_closed_state(int fd) { - return fd_closed_state & (1 << fd); -} - -STATIC void cc3k_set_fd_closed_state(int fd) { - fd_closed_state |= 1 << fd; -} - -STATIC void cc3k_reset_fd_closed_state(int fd) { - fd_closed_state &= ~(1 << fd); -} - -STATIC void cc3k_callback(long event_type, char *data, unsigned char length) { - switch (event_type) { - case HCI_EVNT_WLAN_UNSOL_CONNECT: - wlan_connected = true; - break; - case HCI_EVNT_WLAN_UNSOL_DISCONNECT: - // link down - wlan_connected = false; - ip_obtained = false; - break; - case HCI_EVNT_WLAN_UNSOL_DHCP: - ip_obtained = true; - break; - case HCI_EVNT_BSD_TCP_CLOSE_WAIT: - // mark socket for closure - cc3k_set_fd_closed_state(data[0]); - break; - } -} - -STATIC mp_obj_t cc3k_socket(mp_obj_t nic, int domain, int type, int fileno, int *_errno) { - switch (domain) { - case MOD_NETWORK_AF_INET: domain = AF_INET; break; - case MOD_NETWORK_AF_INET6: domain = AF_INET6; break; - default: *_errno = EAFNOSUPPORT; return MP_OBJ_NULL; - } - - switch (type) { - case MOD_NETWORK_SOCK_STREAM: type = SOCK_STREAM; break; - case MOD_NETWORK_SOCK_DGRAM: type = SOCK_DGRAM; break; - case MOD_NETWORK_SOCK_RAW: type = SOCK_RAW; break; - default: *_errno = EINVAL; return MP_OBJ_NULL; - } - - return cc3k_socket_new(domain, type, 0, _errno); -} - -STATIC int cc3k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip) { - uint32_t ip; - if (CC3000_EXPORT(gethostbyname)((char*)name, len, &ip) < 0) { - return CC3000_EXPORT(errno); - } - - if (ip == 0) { - // unknown host - return ENOENT; - } - - out_ip[0] = ip >> 24; - out_ip[1] = ip >> 16; - out_ip[2] = ip >> 8; - out_ip[3] = ip; - - return 0; -} - -/******************************************************************************/ -// Micro Python bindings; CC3k class - -/// \class CC3k - driver for CC3000 Wifi modules - -typedef struct _cc3k_obj_t { - mp_obj_base_t base; -} cc3k_obj_t; - -/// \classmethod \constructor(spi, pin_cs, pin_en, pin_irq) -/// Initialise the CC3000 using the given SPI bus and pins and return a CC3k object. -// -// Note: pins were originally hard-coded to: -// PYBv1.0: init(pyb.SPI(2), pyb.Pin.board.Y5, pyb.Pin.board.Y4, pyb.Pin.board.Y3) -// [SPI on Y position; Y6=B13=SCK, Y7=B14=MISO, Y8=B15=MOSI] -// -// STM32F4DISC: init(pyb.SPI(2), pyb.Pin.cpu.A15, pyb.Pin.cpu.B10, pyb.Pin.cpu.B11) -STATIC mp_obj_t cc3k_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { - // check arguments - mp_arg_check_num(n_args, n_kw, 4, 4, false); - - // set the pins to use - SpiInit( - spi_get_handle(args[0]), - pin_find(args[1]), - pin_find(args[2]), - pin_find(args[3]) - ); - - // initialize and start the module - wlan_init(cc3k_callback, NULL, NULL, NULL, - ReadWlanInterruptPin, SpiResumeSpi, SpiPauseSpi, WriteWlanPin); - - if (wlan_start(0) != 0) { - nlr_raise(mp_obj_new_exception_msg( - &mp_type_OSError, "Failed to init wlan module")); - } - - // set connection policy. this should be called explicitly by the user - // wlan_ioctl_set_connection_policy(0, 0, 0); - - // Mask out all non-required events from the CC3000 - wlan_set_event_mask(HCI_EVNT_WLAN_KEEPALIVE| - HCI_EVNT_WLAN_UNSOL_INIT| - HCI_EVNT_WLAN_ASYNC_PING_REPORT| - HCI_EVNT_WLAN_ASYNC_SIMPLE_CONFIG_DONE); - - cc3k_obj_t *cc3k = m_new_obj(cc3k_obj_t); - cc3k->base.type = (mp_obj_type_t*)&mod_network_nic_type_cc3k; - - // register with network module - mod_network_register_nic(cc3k); - - return cc3k; -} - -/// \method connect(ssid, key=None, *, security=WPA2, bssid=None) -STATIC mp_obj_t cc3k_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - static const mp_arg_t allowed_args[] = { - { MP_QSTR_ssid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_key, MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = WLAN_SEC_WPA2} }, - { MP_QSTR_bssid, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - }; - - // parse args - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - // get ssid - mp_uint_t ssid_len; - const char *ssid = mp_obj_str_get_data(args[0].u_obj, &ssid_len); - - // get key and sec - mp_uint_t key_len = 0; - const char *key = NULL; - mp_uint_t sec = WLAN_SEC_UNSEC; - if (args[1].u_obj != mp_const_none) { - key = mp_obj_str_get_data(args[1].u_obj, &key_len); - sec = args[2].u_int; - } - - // get bssid - const char *bssid = NULL; - if (args[3].u_obj != mp_const_none) { - bssid = mp_obj_str_get_str(args[3].u_obj); - } - - // connect to AP - if (wlan_connect(sec, (char*)ssid, ssid_len, (uint8_t*)bssid, (uint8_t*)key, key_len) != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "could not connect to ssid=%s, sec=%d, key=%s\n", ssid, sec, key)); - } - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(cc3k_connect_obj, 1, cc3k_connect); - -STATIC mp_obj_t cc3k_disconnect(mp_obj_t self_in) { - int ret = wlan_disconnect(); - return mp_obj_new_int(ret); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(cc3k_disconnect_obj, cc3k_disconnect); - -STATIC mp_obj_t cc3k_is_connected(mp_obj_t self_in) { - if (wlan_connected && ip_obtained) { - return mp_const_true; - } - return mp_const_false; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(cc3k_is_connected_obj, cc3k_is_connected); - -STATIC mp_obj_t cc3k_ifconfig(mp_obj_t self_in) { - tNetappIpconfigRetArgs ipconfig={{0}}; - - uint8_t *ip = &ipconfig.aucIP[0]; - uint8_t *mask= &ipconfig.aucSubnetMask[0]; - uint8_t *gw= &ipconfig.aucDefaultGateway[0]; - uint8_t *dhcp= &ipconfig.aucDHCPServer[0]; - uint8_t *dns= &ipconfig.aucDNSServer[0]; - uint8_t *mac= &ipconfig.uaMacAddr[0]; - uint8_t *ssid= &ipconfig.uaSSID[0]; - - netapp_ipconfig(&ipconfig); - - printf ("IP:%d.%d.%d.%d\n" \ - "Mask:%d.%d.%d.%d\n"\ - "GW:%d.%d.%d.%d\n" \ - "DHCP:%d.%d.%d.%d\n"\ - "DNS:%d.%d.%d.%d\n" \ - "MAC:%02X:%02X:%02X:%02X:%02X:%02X\n"\ - "SSID: %s\n", - ip[3], ip[2], ip[1], ip[0], - mask[3], mask[2], mask[1], mask[0], - gw[3], gw[2], gw[1], gw[0], - dhcp[3], dhcp[2], dhcp[1], dhcp[0], - dns[3], dns[2], dns[1], dns[0], - mac[5], mac[4], mac[3], mac[2], mac[1], mac[0], ssid); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(cc3k_ifconfig_obj, cc3k_ifconfig); - -STATIC mp_obj_t cc3k_patch_version(mp_obj_t self_in) { - uint8_t pver[2]; - mp_obj_tuple_t *t_pver; - - nvmem_read_sp_version(pver); - t_pver = mp_obj_new_tuple(2, NULL); - t_pver->items[0] = mp_obj_new_int(pver[0]); - t_pver->items[1] = mp_obj_new_int(pver[1]); - return t_pver; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(cc3k_patch_version_obj, cc3k_patch_version); - -STATIC mp_obj_t cc3k_patch_program(mp_obj_t self_in, mp_obj_t key_in) { - const char *key = mp_obj_str_get_str(key_in); - if (key[0] == 'p' && key[1] == 'g' && key[2] == 'm' && key[3] == '\0') { - patch_prog_start(); - } else { - printf("please pass 'pgm' as argument in order to program\n"); - } - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(cc3k_patch_program_obj, cc3k_patch_program); - -STATIC const mp_map_elem_t cc3k_locals_dict_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&cc3k_connect_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), (mp_obj_t)&cc3k_disconnect_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_is_connected), (mp_obj_t)&cc3k_is_connected_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_ifconfig), (mp_obj_t)&cc3k_ifconfig_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_patch_version), (mp_obj_t)&cc3k_patch_version_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_patch_program), (mp_obj_t)&cc3k_patch_program_obj }, - - // class constants - { MP_OBJ_NEW_QSTR(MP_QSTR_WEP), MP_OBJ_NEW_SMALL_INT(WLAN_SEC_WEP) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_WPA), MP_OBJ_NEW_SMALL_INT(WLAN_SEC_WPA) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_WPA2), MP_OBJ_NEW_SMALL_INT(WLAN_SEC_WPA2) }, -}; - -STATIC MP_DEFINE_CONST_DICT(cc3k_locals_dict, cc3k_locals_dict_table); - -const mod_network_nic_type_t mod_network_nic_type_cc3k = { - .base = { - { &mp_type_type }, - .name = MP_QSTR_CC3k, - //.print = cc3k_print, - .make_new = cc3k_make_new, - .locals_dict = (mp_obj_t)&cc3k_locals_dict, - }, - .socket = cc3k_socket, - .gethostbyname = cc3k_gethostbyname, -}; - -/******************************************************************************/ -// Micro Python bindings; CC3k socket class - -#define MAX_ADDRSTRLEN (128) -#define MAX_RX_PACKET (CC3000_RX_BUFFER_SIZE-CC3000_MINIMAL_RX_SIZE-1) -#define MAX_TX_PACKET (CC3000_TX_BUFFER_SIZE-CC3000_MINIMAL_TX_SIZE-1) - -typedef struct _cc3k_socket_obj_t { - mp_obj_base_t base; - int fd; -} cc3k_socket_obj_t; - -STATIC const mp_obj_type_t cc3k_socket_type; - -STATIC mp_obj_t cc3k_socket_new(mp_uint_t family, mp_uint_t type, mp_uint_t protocol, int *_errno) { - // create socket object - cc3k_socket_obj_t *s = m_new_obj_with_finaliser(cc3k_socket_obj_t); - s->base.type = (mp_obj_t)&cc3k_socket_type; - - // open socket - s->fd = CC3000_EXPORT(socket)(family, type, protocol); - if (s->fd < 0) { - m_del_obj(cc3k_socket_obj_t, s); - *_errno = CC3000_EXPORT(errno); - return MP_OBJ_NULL; - } - - // clear socket state - cc3k_reset_fd_closed_state(s->fd); - - return s; -} - -STATIC void cc3k_socket_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { - cc3k_socket_obj_t *self = self_in; - printf("", self->fd); -} - -STATIC mp_obj_t cc3k_socket_send(mp_obj_t self_in, mp_obj_t buf_in) { - cc3k_socket_obj_t *self = self_in; - - if (cc3k_get_fd_closed_state(self->fd)) { - CC3000_EXPORT(closesocket)(self->fd); - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(EPIPE))); - } - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); - - // CC3K does not handle fragmentation, and will overflow, - // split the packet into smaller ones and send them out. - mp_int_t bytes = 0; - while (bytes < bufinfo.len) { - int n = MIN((bufinfo.len - bytes), MAX_TX_PACKET); - n = CC3000_EXPORT(send)(self->fd, (uint8_t*)bufinfo.buf + bytes, n, 0); - if (n <= 0) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(CC3000_EXPORT(errno)))); - } - bytes += n; - } - - return MP_OBJ_NEW_SMALL_INT(bytes); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(cc3k_socket_send_obj, cc3k_socket_send); - -STATIC mp_obj_t cc3k_socket_recv(mp_obj_t self_in, mp_obj_t len_in) { - cc3k_socket_obj_t *self = self_in; - - if (cc3k_get_fd_closed_state(self->fd)) { - CC3000_EXPORT(closesocket)(self->fd); - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(EPIPE))); - } - - // recv upto MAX_RX_PACKET - mp_int_t len = mp_obj_get_int(len_in); - len = MIN(len, MAX_RX_PACKET); - - byte *buf; - mp_obj_t ret_obj = mp_obj_str_builder_start(&mp_type_bytes, len, &buf); - len = CC3000_EXPORT(recv)(self->fd, buf, len, 0); - if (len == 0) { - return mp_const_empty_bytes; - } else if (len < 0) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(CC3000_EXPORT(errno)))); - } else { - return mp_obj_str_builder_end_with_len(ret_obj, len); - } -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(cc3k_socket_recv_obj, cc3k_socket_recv); - -STATIC mp_obj_t cc3k_socket_bind(mp_obj_t self_in, mp_obj_t addr_obj) { - cc3k_socket_obj_t *self = self_in; - - mp_obj_t *addr; - mp_obj_get_array_fixed_n(addr_obj, 2, &addr); - - // fill sockaddr struct - int port = mp_obj_get_int(addr[1]); - sockaddr_in addr_in = { - .sin_family = AF_INET, - .sin_port = htons(port), - .sin_addr.s_addr = 0,// INADDR_ANY - .sin_zero = {0} - }; - - const char *host = mp_obj_str_get_str(addr[0]); - if (strlen(host) && !inet_pton(AF_INET, host, &addr_in.sin_addr.s_addr)) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "invalid IP address")); - } - - // bind socket - if (CC3000_EXPORT(bind)(self->fd, (sockaddr*) &addr_in, sizeof(sockaddr_in)) < 0) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "bind failed")); - } - return mp_const_true; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(cc3k_socket_bind_obj, cc3k_socket_bind); - -STATIC mp_obj_t cc3k_socket_listen(mp_obj_t self_in, mp_obj_t backlog) { - cc3k_socket_obj_t *self = self_in; - if (CC3000_EXPORT(listen)(self->fd, mp_obj_get_int(backlog)) < 0) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "listen failed")); - } - - return mp_const_true; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(cc3k_socket_listen_obj, cc3k_socket_listen); - -STATIC mp_obj_t cc3k_socket_accept(mp_obj_t self_in) { - cc3k_socket_obj_t *self = self_in; - int fd; - - sockaddr addr; - socklen_t addr_len = sizeof(sockaddr); - - // accept incoming connection - if ((fd = CC3000_EXPORT(accept)(self->fd, &addr, &addr_len)) < 0) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "accept failed")); - } - - // clear socket state - cc3k_reset_fd_closed_state(fd); - - // create new socket object - cc3k_socket_obj_t *socket_obj = m_new_obj_with_finaliser(cc3k_socket_obj_t); - socket_obj->base.type = (mp_obj_t)&cc3k_socket_type; - socket_obj->fd = fd; - - char buf[MAX_ADDRSTRLEN]={0}; - if (inet_ntop(addr.sa_family, - &(((sockaddr_in*)&addr)->sin_addr), buf, MAX_ADDRSTRLEN) == NULL) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "invalid IP address")); - } - - mp_obj_tuple_t *cli = mp_obj_new_tuple(2, NULL); - mp_obj_tuple_t *cli_addr = mp_obj_new_tuple(2, NULL); - - cli->items[0] = socket_obj; - cli->items[1] = cli_addr; - cli_addr->items[0] = mp_obj_new_str(buf, strlen(buf), false); - cli_addr->items[1] = mp_obj_new_int(((sockaddr_in*)&addr)->sin_port); - - return cli; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(cc3k_socket_accept_obj, cc3k_socket_accept); - -STATIC mp_obj_t cc3k_socket_connect(mp_obj_t self_in, mp_obj_t addr_obj) { - cc3k_socket_obj_t *self = self_in; - - mp_obj_t *addr; - mp_obj_get_array_fixed_n(addr_obj, 2, &addr); - - // fill sockaddr struct - int port = mp_obj_get_int(addr[1]); - sockaddr_in addr_in = { - .sin_family = AF_INET, - .sin_port = htons(port), - .sin_addr.s_addr = 0, // to be filled below using inet_pton - .sin_zero = {0} - }; - - const char *host = mp_obj_str_get_str(addr[0]); - if (!inet_pton(AF_INET, host, &addr_in.sin_addr.s_addr)) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "invalid IP address")); - } - - //printf("doing connect: fd=%d, sockaddr=(%d, %d, %lu)\n", self->fd, addr_in.sin_family, addr_in.sin_port, addr_in.sin_addr.s_addr); - - int ret = CC3000_EXPORT(connect)(self->fd, (sockaddr*)&addr_in, sizeof(sockaddr_in)); - if (ret != 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "[Errno %d] connect failed", ret)); - } - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(cc3k_socket_connect_obj, cc3k_socket_connect); - -STATIC mp_obj_t cc3k_socket_settimeout(mp_obj_t self_in, mp_obj_t timeout) { - cc3k_socket_obj_t *self = self_in; - int optval = mp_obj_get_int(timeout); - socklen_t optlen = sizeof(optval); - - if (CC3000_EXPORT(setsockopt)(self->fd, SOL_SOCKET, SOCKOPT_RECV_TIMEOUT, &optval, optlen) != 0) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "setsockopt failed")); - } - - return mp_const_true; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(cc3k_socket_settimeout_obj, cc3k_socket_settimeout); - -STATIC mp_obj_t cc3k_socket_setblocking(mp_obj_t self_in, mp_obj_t blocking) { - cc3k_socket_obj_t *self = self_in; - int optval; - socklen_t optlen = sizeof(optval); - - if (mp_obj_get_int(blocking)) { - optval = SOCK_OFF; // Enable non-blocking - } else { - optval = SOCK_ON; - } - - if (CC3000_EXPORT(setsockopt)(self->fd, SOL_SOCKET, SOCKOPT_RECV_NONBLOCK, &optval, optlen) != 0 || - CC3000_EXPORT(setsockopt)(self->fd, SOL_SOCKET, SOCKOPT_ACCEPT_NONBLOCK, &optval, optlen) != 0 ) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "setsockopt failed")); - } - - return mp_const_true; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(cc3k_socket_setblocking_obj, cc3k_socket_setblocking); - -STATIC mp_obj_t cc3k_socket_close(mp_obj_t self_in) { - cc3k_socket_obj_t *self = self_in; - CC3000_EXPORT(closesocket)(self->fd); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(cc3k_socket_close_obj, cc3k_socket_close); - -STATIC const mp_map_elem_t cc3k_socket_locals_dict_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&cc3k_socket_send_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&cc3k_socket_recv_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_bind), (mp_obj_t)&cc3k_socket_bind_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_listen), (mp_obj_t)&cc3k_socket_listen_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_accept), (mp_obj_t)&cc3k_socket_accept_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&cc3k_socket_connect_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_settimeout), (mp_obj_t)&cc3k_socket_settimeout_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_setblocking), (mp_obj_t)&cc3k_socket_setblocking_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&cc3k_socket_close_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&cc3k_socket_close_obj }, -}; - -STATIC MP_DEFINE_CONST_DICT(cc3k_socket_locals_dict, cc3k_socket_locals_dict_table); - -mp_uint_t cc3k_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { - cc3k_socket_obj_t *self = self_in; - mp_uint_t ret; - if (request == MP_IOCTL_POLL) { - mp_uint_t flags = arg; - ret = 0; - int fd = self->fd; - - // init fds - fd_set rfds, wfds, xfds; - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&xfds); - - // set fds if needed - if (flags & MP_IOCTL_POLL_RD) { - FD_SET(fd, &rfds); - - // A socked that just closed is available for reading. A call to - // recv() returns 0 which is consistent with BSD. - if (cc3k_get_fd_closed_state(fd)) { - ret |= MP_IOCTL_POLL_RD; - } - } - if (flags & MP_IOCTL_POLL_WR) { - FD_SET(fd, &wfds); - } - if (flags & MP_IOCTL_POLL_HUP) { - FD_SET(fd, &xfds); - } - - // call cc3000 select with minimum timeout - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 1; - int nfds = CC3000_EXPORT(select)(fd + 1, &rfds, &wfds, &xfds, &tv); - - // check for error - if (nfds == -1) { - *errcode = CC3000_EXPORT(errno); - return -1; - } - - // check return of select - if (FD_ISSET(fd, &rfds)) { - ret |= MP_IOCTL_POLL_RD; - } - if (FD_ISSET(fd, &wfds)) { - ret |= MP_IOCTL_POLL_WR; - } - if (FD_ISSET(fd, &xfds)) { - ret |= MP_IOCTL_POLL_HUP; - } - } else { - *errcode = EINVAL; - ret = -1; - } - return ret; -} - -STATIC const mp_stream_p_t cc3k_socket_stream_p = { - .ioctl = cc3k_ioctl, - .is_text = false, -}; - -STATIC const mp_obj_type_t cc3k_socket_type = { - { &mp_type_type }, - .name = MP_QSTR_socket, - .print = cc3k_socket_print, - .stream_p = &cc3k_socket_stream_p, - .locals_dict = (mp_obj_t)&cc3k_socket_locals_dict, -}; diff --git a/stmhal/modnetwork.c b/stmhal/modnetwork.c index fd3fee9284..e4d0fcdf9e 100644 --- a/stmhal/modnetwork.c +++ b/stmhal/modnetwork.c @@ -61,6 +61,18 @@ void mod_network_register_nic(mp_obj_t nic) { mp_obj_list_append(&mod_network_nic_list, nic); } +mp_obj_t mod_network_find_nic(const uint8_t *ip) { + // find a NIC that is suited to given IP address + for (mp_uint_t i = 0; i < mod_network_nic_list.len; i++) { + mp_obj_t nic = mod_network_nic_list.items[i]; + // TODO check IP suitability here + //mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); + return nic; + } + + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "no available NIC")); +} + STATIC mp_obj_t network_route(void) { return &mod_network_nic_list; } @@ -70,10 +82,10 @@ STATIC const mp_map_elem_t mp_module_network_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_network) }, #if MICROPY_PY_WIZNET5K - { MP_OBJ_NEW_QSTR(MP_QSTR_WIZnet5k), (mp_obj_t)&mod_network_nic_type_wiznet5k }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WIZNET5K), (mp_obj_t)&mod_network_nic_type_wiznet5k }, #endif #if MICROPY_PY_CC3K - { MP_OBJ_NEW_QSTR(MP_QSTR_CC3k), (mp_obj_t)&mod_network_nic_type_cc3k }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CC3K), (mp_obj_t)&mod_network_nic_type_cc3k }, #endif { MP_OBJ_NEW_QSTR(MP_QSTR_route), (mp_obj_t)&network_route_obj }, @@ -90,18 +102,32 @@ const mp_obj_module_t mp_module_network = { /******************************************************************************/ // Miscellaneous helpers +void mod_network_convert_ipv4_endianness(uint8_t *ip) { + uint8_t ip0 = ip[0]; ip[0] = ip[3]; ip[3] = ip0; + uint8_t ip1 = ip[1]; ip[1] = ip[2]; ip[2] = ip1; +} + +// Takes an address of the form '192.168.0.1' and converts it to network format +// in out_ip (big endian, so the 192 is the first byte). void mod_network_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip) { - const char *addr_str = mp_obj_str_get_str(addr_in); + mp_uint_t addr_len; + const char *addr_str = mp_obj_str_get_data(addr_in, &addr_len); + if (addr_len == 0) { + // special case of no address given + memset(out_ip, 0, MOD_NETWORK_IPADDR_BUF_SIZE); + return; + } const char *s = addr_str; + const char *s_top = addr_str + addr_len; for (mp_uint_t i = 0;; i++) { mp_uint_t val = 0; - for (; *s && *s != '.'; s++) { + for (; s < s_top && *s != '.'; s++) { val = val * 10 + *s - '0'; } out_ip[i] = val; - if (i == 3 && *s == '\0') { + if (i == 3 && s == s_top) { return; - } else if (i < 3 && *s == '.') { + } else if (i < 3 && s < s_top && *s == '.') { s++; } else { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "invalid IP address")); diff --git a/stmhal/modnetwork.h b/stmhal/modnetwork.h index ed31a602f2..8e23f877f2 100644 --- a/stmhal/modnetwork.h +++ b/stmhal/modnetwork.h @@ -33,22 +33,55 @@ #define MOD_NETWORK_SOCK_DGRAM (2) #define MOD_NETWORK_SOCK_RAW (3) +struct _mod_network_socket_obj_t; + typedef struct _mod_network_nic_type_t { mp_obj_type_t base; - // API for a generic NIC - mp_obj_t (*socket)(mp_obj_t nic, int domain, int type, int fileno, int *_errno); + // API for non-socket operations int (*gethostbyname)(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *ip_out); + + // API for socket operations; return -1 on error + int (*socket)(struct _mod_network_socket_obj_t *socket, int *_errno); + void (*close)(struct _mod_network_socket_obj_t *socket); + int (*bind)(struct _mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno); + int (*listen)(struct _mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno); + int (*accept)(struct _mod_network_socket_obj_t *socket, struct _mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno); + int (*connect)(struct _mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno); + mp_uint_t (*send)(struct _mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno); + mp_uint_t (*recv)(struct _mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno); + mp_uint_t (*sendto)(struct _mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno); + mp_uint_t (*recvfrom)(struct _mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno); + int (*setsockopt)(struct _mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno); + int (*settimeout)(struct _mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno); + int (*ioctl)(struct _mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno); } mod_network_nic_type_t; +typedef struct _mod_network_socket_obj_t { + mp_obj_base_t base; + mp_obj_t nic; + mod_network_nic_type_t *nic_type; + union { + struct { + uint8_t domain; + uint8_t type; + int8_t fileno; + } u_param; + mp_uint_t u_state; + }; +} mod_network_socket_obj_t; + extern struct _mp_obj_list_t mod_network_nic_list; extern const mod_network_nic_type_t mod_network_nic_type_wiznet5k; extern const mod_network_nic_type_t mod_network_nic_type_cc3k; void mod_network_init(void); void mod_network_register_nic(mp_obj_t nic); +mp_obj_t mod_network_find_nic(const uint8_t *ip); +void mod_network_convert_ipv4_endianness(uint8_t *ip); void mod_network_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip); mp_uint_t mod_network_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip); mp_obj_t mod_network_format_ipv4_addr(uint8_t *ip); +mp_obj_t mod_network_format_ipv4_addr(uint8_t *ip); mp_obj_t mod_network_format_inet_addr(uint8_t *ip, mp_uint_t port); diff --git a/stmhal/modnwcc3k.c b/stmhal/modnwcc3k.c new file mode 100644 index 0000000000..41358a06cb --- /dev/null +++ b/stmhal/modnwcc3k.c @@ -0,0 +1,618 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 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. + */ + +// We can't include stdio.h because it defines _types_fd_set, but we +// need to use the CC3000 version of this type. + +#include +#include +#include +#include + +// CC3000 defines its own ENOBUFS (different to standard one!) +#undef ENOBUFS + +#include "stm32f4xx_hal.h" +#include "mpconfig.h" +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "objtuple.h" +#include "objlist.h" +#include "stream.h" +#include "runtime.h" +#include "modnetwork.h" +#include "pin.h" +#include "genhdr/pins.h" +#include "spi.h" +#include "pybioctl.h" + +#include "hci.h" +#include "socket.h" +#include "inet_ntop.h" +#include "inet_pton.h" +#include "ccspi.h" +#include "wlan.h" +#include "nvmem.h" +#include "netapp.h" +#include "patch_prog.h" + +#define MAX_ADDRSTRLEN (128) +#define MAX_RX_PACKET (CC3000_RX_BUFFER_SIZE-CC3000_MINIMAL_RX_SIZE-1) +#define MAX_TX_PACKET (CC3000_TX_BUFFER_SIZE-CC3000_MINIMAL_TX_SIZE-1) + +#define MAKE_SOCKADDR(addr, ip, port) \ + sockaddr addr; \ + addr.sa_family = AF_INET; \ + addr.sa_data[0] = port >> 8; \ + addr.sa_data[1] = port; \ + addr.sa_data[2] = ip[0]; \ + addr.sa_data[3] = ip[1]; \ + addr.sa_data[4] = ip[2]; \ + addr.sa_data[5] = ip[3]; + +#define UNPACK_SOCKADDR(addr, ip, port) \ + port = (addr.sa_data[0] << 8) | addr.sa_data[1]; \ + ip[0] = addr.sa_data[2]; \ + ip[1] = addr.sa_data[3]; \ + ip[2] = addr.sa_data[4]; \ + ip[3] = addr.sa_data[5]; + +STATIC int cc3k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno); + +int CC3000_EXPORT(errno); // for cc3000 driver + +STATIC volatile uint32_t fd_closed_state = 0; +STATIC volatile bool wlan_connected = false; +STATIC volatile bool ip_obtained = false; + +STATIC int cc3k_get_fd_closed_state(int fd) { + return fd_closed_state & (1 << fd); +} + +STATIC void cc3k_set_fd_closed_state(int fd) { + fd_closed_state |= 1 << fd; +} + +STATIC void cc3k_reset_fd_closed_state(int fd) { + fd_closed_state &= ~(1 << fd); +} + +STATIC void cc3k_callback(long event_type, char *data, unsigned char length) { + switch (event_type) { + case HCI_EVNT_WLAN_UNSOL_CONNECT: + wlan_connected = true; + break; + case HCI_EVNT_WLAN_UNSOL_DISCONNECT: + // link down + wlan_connected = false; + ip_obtained = false; + break; + case HCI_EVNT_WLAN_UNSOL_DHCP: + ip_obtained = true; + break; + case HCI_EVNT_BSD_TCP_CLOSE_WAIT: + // mark socket for closure + cc3k_set_fd_closed_state(data[0]); + break; + } +} + +STATIC int cc3k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip) { + uint32_t ip; + // CC3000 gethostbyname is unreliable and usually returns -95 on first call + for (int retry = 5; CC3000_EXPORT(gethostbyname)((char*)name, len, &ip) < 0; retry--) { + if (retry == 0 || CC3000_EXPORT(errno) != -95) { + return CC3000_EXPORT(errno); + } + HAL_Delay(50); + } + + if (ip == 0) { + // unknown host + return ENOENT; + } + + out_ip[0] = ip >> 24; + out_ip[1] = ip >> 16; + out_ip[2] = ip >> 8; + out_ip[3] = ip; + + return 0; +} + +STATIC int cc3k_socket_socket(mod_network_socket_obj_t *socket, int *_errno) { + if (socket->u_param.domain != MOD_NETWORK_AF_INET) { + *_errno = EAFNOSUPPORT; + return -1; + } + + mp_uint_t type; + switch (socket->u_param.type) { + case MOD_NETWORK_SOCK_STREAM: type = SOCK_STREAM; break; + case MOD_NETWORK_SOCK_DGRAM: type = SOCK_DGRAM; break; + case MOD_NETWORK_SOCK_RAW: type = SOCK_RAW; break; + default: *_errno = EINVAL; return -1; + } + + // open socket + int fd = CC3000_EXPORT(socket)(AF_INET, type, 0); + if (fd < 0) { + *_errno = CC3000_EXPORT(errno); + return -1; + } + + // clear socket state + cc3k_reset_fd_closed_state(fd); + + // store state of this socket + socket->u_state = fd; + + // make accept blocking by default + int optval = SOCK_OFF; + socklen_t optlen = sizeof(optval); + CC3000_EXPORT(setsockopt)(socket->u_state, SOL_SOCKET, SOCKOPT_ACCEPT_NONBLOCK, &optval, optlen); + + return 0; +} + +STATIC void cc3k_socket_close(mod_network_socket_obj_t *socket) { + CC3000_EXPORT(closesocket)(socket->u_state); +} + +STATIC int cc3k_socket_bind(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { + MAKE_SOCKADDR(addr, ip, port) + int ret = CC3000_EXPORT(bind)(socket->u_state, &addr, sizeof(addr)); + if (ret != 0) { + *_errno = ret; + return -1; + } + return 0; +} + +STATIC int cc3k_socket_listen(mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno) { + int ret = CC3000_EXPORT(listen)(socket->u_state, backlog); + if (ret != 0) { + *_errno = ret; + return -1; + } + return 0; +} + +STATIC int cc3k_socket_accept(mod_network_socket_obj_t *socket, mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno) { + // accept incoming connection + int fd; + sockaddr addr; + socklen_t addr_len = sizeof(addr); + if ((fd = CC3000_EXPORT(accept)(socket->u_state, &addr, &addr_len)) < 0) { + if (fd == SOC_IN_PROGRESS) { + *_errno = EAGAIN; + } else { + *_errno = -fd; + } + return -1; + } + + // clear socket state + cc3k_reset_fd_closed_state(fd); + + // store state in new socket object + socket2->u_state = fd; + + // return ip and port + // it seems CC3000 returns little endian for accept?? + //UNPACK_SOCKADDR(addr, ip, *port); + *port = (addr.sa_data[1] << 8) | addr.sa_data[0]; + ip[3] = addr.sa_data[2]; + ip[2] = addr.sa_data[3]; + ip[1] = addr.sa_data[4]; + ip[0] = addr.sa_data[5]; + + return 0; +} + +STATIC int cc3k_socket_connect(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { + MAKE_SOCKADDR(addr, ip, port) + int ret = CC3000_EXPORT(connect)(socket->u_state, &addr, sizeof(addr)); + if (ret != 0) { + *_errno = CC3000_EXPORT(errno); + return -1; + } + return 0; +} + +STATIC mp_uint_t cc3k_socket_send(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) { + if (cc3k_get_fd_closed_state(socket->u_state)) { + CC3000_EXPORT(closesocket)(socket->u_state); + *_errno = EPIPE; + return -1; + } + + // CC3K does not handle fragmentation, and will overflow, + // split the packet into smaller ones and send them out. + mp_int_t bytes = 0; + while (bytes < len) { + int n = MIN((len - bytes), MAX_TX_PACKET); + n = CC3000_EXPORT(send)(socket->u_state, (uint8_t*)buf + bytes, n, 0); + if (n <= 0) { + *_errno = CC3000_EXPORT(errno); + return -1; + } + bytes += n; + } + + return bytes; +} + +STATIC mp_uint_t cc3k_socket_recv(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) { + // check the socket is open + if (cc3k_get_fd_closed_state(socket->u_state)) { + // socket is closed, but CC3000 may have some data remaining in buffer, so check + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(socket->u_state, &rfds); + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 1; + int nfds = CC3000_EXPORT(select)(socket->u_state + 1, &rfds, NULL, NULL, &tv); + if (nfds == -1 || !FD_ISSET(socket->u_state, &rfds)) { + // no data waiting, so close socket and return 0 data + CC3000_EXPORT(closesocket)(socket->u_state); + return 0; + } + } + + // cap length at MAX_RX_PACKET + len = MIN(len, MAX_RX_PACKET); + + // do the recv + int ret = CC3000_EXPORT(recv)(socket->u_state, buf, len, 0); + if (ret < 0) { + *_errno = CC3000_EXPORT(errno); + return -1; + } + + return ret; +} + +STATIC mp_uint_t cc3k_socket_sendto(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) { + MAKE_SOCKADDR(addr, ip, port) + int ret = CC3000_EXPORT(sendto)(socket->u_state, (byte*)buf, len, 0, (sockaddr*)&addr, sizeof(addr)); + if (ret < 0) { + *_errno = CC3000_EXPORT(errno); + return -1; + } + return ret; +} + +STATIC mp_uint_t cc3k_socket_recvfrom(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) { + sockaddr addr; + socklen_t addr_len = sizeof(addr); + mp_int_t ret = CC3000_EXPORT(recvfrom)(socket->u_state, buf, len, 0, &addr, &addr_len); + if (ret < 0) { + *_errno = CC3000_EXPORT(errno); + return -1; + } + UNPACK_SOCKADDR(addr, ip, *port); + return ret; +} + +STATIC int cc3k_socket_setsockopt(mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno) { + int ret = CC3000_EXPORT(setsockopt)(socket->u_state, level, opt, optval, optlen); + if (ret < 0) { + *_errno = CC3000_EXPORT(errno); + return -1; + } + return 0; +} + +STATIC int cc3k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno) { + int ret; + if (timeout_ms == 0 || timeout_ms == -1) { + int optval; + socklen_t optlen = sizeof(optval); + if (timeout_ms == 0) { + // set non-blocking mode + optval = SOCK_ON; + } else { + // set blocking mode + optval = SOCK_OFF; + } + ret = CC3000_EXPORT(setsockopt)(socket->u_state, SOL_SOCKET, SOCKOPT_RECV_NONBLOCK, &optval, optlen); + if (ret == 0) { + ret = CC3000_EXPORT(setsockopt)(socket->u_state, SOL_SOCKET, SOCKOPT_ACCEPT_NONBLOCK, &optval, optlen); + } + } else { + // set timeout + socklen_t optlen = sizeof(timeout_ms); + ret = CC3000_EXPORT(setsockopt)(socket->u_state, SOL_SOCKET, SOCKOPT_RECV_TIMEOUT, &timeout_ms, optlen); + } + + if (ret != 0) { + *_errno = CC3000_EXPORT(errno); + return -1; + } + + return 0; +} + +STATIC int cc3k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) { + mp_uint_t ret; + if (request == MP_IOCTL_POLL) { + mp_uint_t flags = arg; + ret = 0; + int fd = socket->u_state; + + // init fds + fd_set rfds, wfds, xfds; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + + // set fds if needed + if (flags & MP_IOCTL_POLL_RD) { + FD_SET(fd, &rfds); + + // A socked that just closed is available for reading. A call to + // recv() returns 0 which is consistent with BSD. + if (cc3k_get_fd_closed_state(fd)) { + ret |= MP_IOCTL_POLL_RD; + } + } + if (flags & MP_IOCTL_POLL_WR) { + FD_SET(fd, &wfds); + } + if (flags & MP_IOCTL_POLL_HUP) { + FD_SET(fd, &xfds); + } + + // call cc3000 select with minimum timeout + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 1; + int nfds = CC3000_EXPORT(select)(fd + 1, &rfds, &wfds, &xfds, &tv); + + // check for error + if (nfds == -1) { + *_errno = CC3000_EXPORT(errno); + return -1; + } + + // check return of select + if (FD_ISSET(fd, &rfds)) { + ret |= MP_IOCTL_POLL_RD; + } + if (FD_ISSET(fd, &wfds)) { + ret |= MP_IOCTL_POLL_WR; + } + if (FD_ISSET(fd, &xfds)) { + ret |= MP_IOCTL_POLL_HUP; + } + } else { + *_errno = EINVAL; + ret = -1; + } + return ret; +} + +/******************************************************************************/ +// Micro Python bindings; CC3K class + +typedef struct _cc3k_obj_t { + mp_obj_base_t base; +} cc3k_obj_t; + +// \classmethod \constructor(spi, pin_cs, pin_en, pin_irq) +// Initialise the CC3000 using the given SPI bus and pins and return a CC3K object. +// +// Note: pins were originally hard-coded to: +// PYBv1.0: init(pyb.SPI(2), pyb.Pin.board.Y5, pyb.Pin.board.Y4, pyb.Pin.board.Y3) +// [SPI on Y position; Y6=B13=SCK, Y7=B14=MISO, Y8=B15=MOSI] +// +// STM32F4DISC: init(pyb.SPI(2), pyb.Pin.cpu.A15, pyb.Pin.cpu.B10, pyb.Pin.cpu.B11) +STATIC mp_obj_t cc3k_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 4, 4, false); + + // set the pins to use + SpiInit( + spi_get_handle(args[0]), + pin_find(args[1]), + pin_find(args[2]), + pin_find(args[3]) + ); + + // initialize and start the module + wlan_init(cc3k_callback, NULL, NULL, NULL, + ReadWlanInterruptPin, SpiResumeSpi, SpiPauseSpi, WriteWlanPin); + + if (wlan_start(0) != 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "failed to init CC3000 module")); + } + + // set connection policy. this should be called explicitly by the user + // wlan_ioctl_set_connection_policy(0, 0, 0); + + // Mask out all non-required events from the CC3000 + wlan_set_event_mask(HCI_EVNT_WLAN_KEEPALIVE| + HCI_EVNT_WLAN_UNSOL_INIT| + HCI_EVNT_WLAN_ASYNC_PING_REPORT| + HCI_EVNT_WLAN_ASYNC_SIMPLE_CONFIG_DONE); + + cc3k_obj_t *cc3k = m_new_obj(cc3k_obj_t); + cc3k->base.type = (mp_obj_type_t*)&mod_network_nic_type_cc3k; + + // register with network module + mod_network_register_nic(cc3k); + + return cc3k; +} + +// method connect(ssid, key=None, *, security=WPA2, bssid=None) +STATIC mp_obj_t cc3k_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_ssid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_key, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = WLAN_SEC_WPA2} }, + { MP_QSTR_bssid, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get ssid + mp_uint_t ssid_len; + const char *ssid = mp_obj_str_get_data(args[0].u_obj, &ssid_len); + + // get key and sec + mp_uint_t key_len = 0; + const char *key = NULL; + mp_uint_t sec = WLAN_SEC_UNSEC; + if (args[1].u_obj != mp_const_none) { + key = mp_obj_str_get_data(args[1].u_obj, &key_len); + sec = args[2].u_int; + } + + // get bssid + const char *bssid = NULL; + if (args[3].u_obj != mp_const_none) { + bssid = mp_obj_str_get_str(args[3].u_obj); + } + + // connect to AP + if (wlan_connect(sec, (char*)ssid, ssid_len, (uint8_t*)bssid, (uint8_t*)key, key_len) != 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "could not connect to ssid=%s, sec=%d, key=%s\n", ssid, sec, key)); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(cc3k_connect_obj, 1, cc3k_connect); + +STATIC mp_obj_t cc3k_disconnect(mp_obj_t self_in) { + // should we check return value? + wlan_disconnect(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(cc3k_disconnect_obj, cc3k_disconnect); + +STATIC mp_obj_t cc3k_isconnected(mp_obj_t self_in) { + return MP_BOOL(wlan_connected && ip_obtained); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(cc3k_isconnected_obj, cc3k_isconnected); + +STATIC mp_obj_t cc3k_ifconfig(mp_obj_t self_in) { + tNetappIpconfigRetArgs ipconfig; + netapp_ipconfig(&ipconfig); + + // CC3000 returns little endian, but we want big endian + mod_network_convert_ipv4_endianness(ipconfig.aucIP); + mod_network_convert_ipv4_endianness(ipconfig.aucSubnetMask); + mod_network_convert_ipv4_endianness(ipconfig.aucDefaultGateway); + mod_network_convert_ipv4_endianness(ipconfig.aucDNSServer); + mod_network_convert_ipv4_endianness(ipconfig.aucDHCPServer); + + // render MAC address + char mac_str[18]; + const uint8_t *mac = ipconfig.uaMacAddr; + mp_uint_t mac_len = snprintf(mac_str, 18, "%02X:%02x:%02x:%02x:%02x:%02x", mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]); + + // create and return tuple with ifconfig info + mp_obj_t tuple[7] = { + mod_network_format_ipv4_addr(ipconfig.aucIP), + mod_network_format_ipv4_addr(ipconfig.aucSubnetMask), + mod_network_format_ipv4_addr(ipconfig.aucDefaultGateway), + mod_network_format_ipv4_addr(ipconfig.aucDNSServer), + mod_network_format_ipv4_addr(ipconfig.aucDHCPServer), + mp_obj_new_str(mac_str, mac_len, false), + mp_obj_new_str((const char*)ipconfig.uaSSID, strlen((const char*)ipconfig.uaSSID), false), + }; + return mp_obj_new_tuple(MP_ARRAY_SIZE(tuple), tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(cc3k_ifconfig_obj, cc3k_ifconfig); + +STATIC mp_obj_t cc3k_patch_version(mp_obj_t self_in) { + uint8_t pver[2]; + mp_obj_tuple_t *t_pver; + + nvmem_read_sp_version(pver); + t_pver = mp_obj_new_tuple(2, NULL); + t_pver->items[0] = mp_obj_new_int(pver[0]); + t_pver->items[1] = mp_obj_new_int(pver[1]); + return t_pver; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(cc3k_patch_version_obj, cc3k_patch_version); + +STATIC mp_obj_t cc3k_patch_program(mp_obj_t self_in, mp_obj_t key_in) { + const char *key = mp_obj_str_get_str(key_in); + if (key[0] == 'p' && key[1] == 'g' && key[2] == 'm' && key[3] == '\0') { + patch_prog_start(); + } else { + printf("pass 'pgm' as argument in order to program\n"); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(cc3k_patch_program_obj, cc3k_patch_program); + +STATIC const mp_map_elem_t cc3k_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&cc3k_connect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), (mp_obj_t)&cc3k_disconnect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_isconnected), (mp_obj_t)&cc3k_isconnected_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ifconfig), (mp_obj_t)&cc3k_ifconfig_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_patch_version), (mp_obj_t)&cc3k_patch_version_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_patch_program), (mp_obj_t)&cc3k_patch_program_obj }, + + // class constants + { MP_OBJ_NEW_QSTR(MP_QSTR_WEP), MP_OBJ_NEW_SMALL_INT(WLAN_SEC_WEP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WPA), MP_OBJ_NEW_SMALL_INT(WLAN_SEC_WPA) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WPA2), MP_OBJ_NEW_SMALL_INT(WLAN_SEC_WPA2) }, +}; + +STATIC MP_DEFINE_CONST_DICT(cc3k_locals_dict, cc3k_locals_dict_table); + +const mod_network_nic_type_t mod_network_nic_type_cc3k = { + .base = { + { &mp_type_type }, + .name = MP_QSTR_CC3K, + .make_new = cc3k_make_new, + .locals_dict = (mp_obj_t)&cc3k_locals_dict, + }, + .gethostbyname = cc3k_gethostbyname, + .socket = cc3k_socket_socket, + .close = cc3k_socket_close, + .bind = cc3k_socket_bind, + .listen = cc3k_socket_listen, + .accept = cc3k_socket_accept, + .connect = cc3k_socket_connect, + .send = cc3k_socket_send, + .recv = cc3k_socket_recv, + .sendto = cc3k_socket_sendto, + .recvfrom = cc3k_socket_recvfrom, + .setsockopt = cc3k_socket_setsockopt, + .settimeout = cc3k_socket_settimeout, + .ioctl = cc3k_socket_ioctl, +}; diff --git a/stmhal/modwiznet5k.c b/stmhal/modnwwiznet5k.c similarity index 52% rename from stmhal/modwiznet5k.c rename to stmhal/modnwwiznet5k.c index e91bd502a7..4818ff9eb1 100644 --- a/stmhal/modwiznet5k.c +++ b/stmhal/modnwwiznet5k.c @@ -50,10 +50,6 @@ /// \moduleref network -#define IPADDR_BUF_SIZE (4) - -STATIC mp_obj_t wiznet5k_socket_new(uint8_t sn, mp_uint_t type); - typedef struct _wiznet5k_obj_t { mp_obj_base_t base; mp_uint_t cris_state; @@ -91,49 +87,8 @@ STATIC void wiz_spi_write(const uint8_t *buf, uint32_t len) { (void)status; } -// Check the return value from Wiz socket calls: -// - on error (<0) an exception is raised -// - SOCK_OK or SOCK_BUSY does nothing -// - anything positive does nothing -STATIC void check_sock_return_value(int8_t ret) { - // TODO convert Wiz errno's to POSIX ones - if (ret < 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "socket error %d", ret)); - } -} - -STATIC mp_obj_t wiznet5k_socket(mp_obj_t self_in, int domain, int type, int fileno, int *_errno) { - if (domain != MOD_NETWORK_AF_INET) { - *_errno = EAFNOSUPPORT; - return MP_OBJ_NULL; - } - - switch (type) { - case MOD_NETWORK_SOCK_STREAM: type = Sn_MR_TCP; break; - case MOD_NETWORK_SOCK_DGRAM: type = Sn_MR_UDP; break; - default: *_errno = EINVAL; return MP_OBJ_NULL; - } - - if (fileno < 0) { - // get first unused socket number - for (mp_uint_t sn = 0; sn < _WIZCHIP_SOCK_NUM_; sn++) { - if ((wiznet5k_obj.socket_used & (1 << sn)) == 0) { - fileno = sn; - break; - } - } - if (fileno < 0) { - // too many open sockets - *_errno = EMFILE; - return MP_OBJ_NULL; - } - } - - return wiznet5k_socket_new(fileno, type); -} - STATIC int wiznet5k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip) { - uint8_t dns_ip[IPADDR_BUF_SIZE] = {8, 8, 8, 8}; + uint8_t dns_ip[MOD_NETWORK_IPADDR_BUF_SIZE] = {8, 8, 8, 8}; uint8_t *buf = m_new(uint8_t, MAX_DNS_BUF_SIZE); DNS_init(0, buf); mp_int_t ret = DNS_run(dns_ip, (uint8_t*)name, out_ip); @@ -146,31 +101,228 @@ STATIC int wiznet5k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, return ENOENT; } } + +STATIC int wiznet5k_socket_socket(mod_network_socket_obj_t *socket, int *_errno) { + if (socket->u_param.domain != MOD_NETWORK_AF_INET) { + *_errno = EAFNOSUPPORT; + return -1; + } + + switch (socket->u_param.type) { + case MOD_NETWORK_SOCK_STREAM: socket->u_param.type = Sn_MR_TCP; break; + case MOD_NETWORK_SOCK_DGRAM: socket->u_param.type = Sn_MR_UDP; break; + default: *_errno = EINVAL; return -1; + } + + if (socket->u_param.fileno == -1) { + // get first unused socket number + for (mp_uint_t sn = 0; sn < _WIZCHIP_SOCK_NUM_; sn++) { + if ((wiznet5k_obj.socket_used & (1 << sn)) == 0) { + wiznet5k_obj.socket_used |= (1 << sn); + socket->u_param.fileno = sn; + break; + } + } + if (socket->u_param.fileno == -1) { + // too many open sockets + *_errno = EMFILE; + return -1; + } + } + + // WIZNET does not have a concept of pure "open socket". You need to know + // if it's a server or client at the time of creation of the socket. + // So, we defer the open until we know what kind of socket we want. + + // use "domain" to indicate that this socket has not yet been opened + socket->u_param.domain = 0; + + return 0; +} + +STATIC void wiznet5k_socket_close(mod_network_socket_obj_t *socket) { + uint8_t sn = (uint8_t)socket->u_param.fileno; + if (sn < _WIZCHIP_SOCK_NUM_) { + wiznet5k_obj.socket_used &= ~(1 << sn); + WIZCHIP_EXPORT(close)(sn); + } +} + +STATIC int wiznet5k_socket_bind(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { + // open the socket in server mode (if port != 0) + mp_int_t ret = WIZCHIP_EXPORT(socket)(socket->u_param.fileno, socket->u_param.type, port, 0); + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + + // indicate that this socket has been opened + socket->u_param.domain = 1; + + // success + return 0; +} + +STATIC int wiznet5k_socket_listen(mod_network_socket_obj_t *socket, mp_int_t backlog, int *_errno) { + mp_int_t ret = WIZCHIP_EXPORT(listen)(socket->u_param.fileno); + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return 0; +} + +STATIC int wiznet5k_socket_accept(mod_network_socket_obj_t *socket, mod_network_socket_obj_t *socket2, byte *ip, mp_uint_t *port, int *_errno) { + for (;;) { + int sr = getSn_SR((uint8_t)socket->u_param.fileno); + if (sr == SOCK_ESTABLISHED) { + socket2->u_param = socket->u_param; + // TODO need to populate this with the correct values + ip[0] = 0; + ip[1] = 0; + ip[2] = 0; + ip[3] = 0; + *port = getSn_PORT(socket2->u_param.fileno); + + // WIZnet turns the listening socket into the client socket, so we + // need to re-bind and re-listen on another socket for the server. + // TODO handle errors, especially no-more-sockets error + socket->u_param.domain = MOD_NETWORK_AF_INET; + socket->u_param.fileno = -1; + int _errno2; + if (wiznet5k_socket_socket(socket, &_errno2) != 0) { + //printf("(bad resocket %d)\n", _errno2); + } else if (wiznet5k_socket_bind(socket, NULL, *port, &_errno2) != 0) { + //printf("(bad rebind %d)\n", _errno2); + } else if (wiznet5k_socket_listen(socket, 0, &_errno2) != 0) { + //printf("(bad relisten %d)\n", _errno2); + } + + return 0; + } + if (sr == SOCK_CLOSED || sr == SOCK_CLOSE_WAIT) { + wiznet5k_socket_close(socket); + *_errno = ENOTCONN; // ?? + return -1; + } + HAL_Delay(1); + } +} + +STATIC int wiznet5k_socket_connect(mod_network_socket_obj_t *socket, byte *ip, mp_uint_t port, int *_errno) { + // use "bind" function to open the socket in client mode + if (wiznet5k_socket_bind(socket, ip, 0, _errno) != 0) { + return -1; + } + + // now connect + mp_int_t ret = WIZCHIP_EXPORT(connect)(socket->u_param.fileno, ip, port); + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + + // success + return 0; +} + +STATIC mp_uint_t wiznet5k_socket_send(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) { + mp_int_t ret = WIZCHIP_EXPORT(send)(socket->u_param.fileno, (byte*)buf, len); + // TODO convert Wiz errno's to POSIX ones + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +STATIC mp_uint_t wiznet5k_socket_recv(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) { + mp_int_t ret = WIZCHIP_EXPORT(recv)(socket->u_param.fileno, buf, len); + // TODO convert Wiz errno's to POSIX ones + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +STATIC mp_uint_t wiznet5k_socket_sendto(mod_network_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) { + if (socket->u_param.domain == 0) { + // socket not opened; use "bind" function to open the socket in client mode + if (wiznet5k_socket_bind(socket, ip, 0, _errno) != 0) { + return -1; + } + } + + mp_int_t ret = WIZCHIP_EXPORT(sendto)(socket->u_param.fileno, (byte*)buf, len, ip, port); + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +STATIC mp_uint_t wiznet5k_socket_recvfrom(mod_network_socket_obj_t *socket, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) { + uint16_t port2; + mp_int_t ret = WIZCHIP_EXPORT(recvfrom)(socket->u_param.fileno, buf, len, ip, &port2); + *port = port2; + if (ret < 0) { + wiznet5k_socket_close(socket); + *_errno = -ret; + return -1; + } + return ret; +} + +STATIC int wiznet5k_socket_setsockopt(mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno) { + // TODO + *_errno = EINVAL; + return -1; +} + +STATIC int wiznet5k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno) { + // TODO + *_errno = EINVAL; + return -1; + + /* + if (timeout_ms == 0) { + // set non-blocking mode + uint8_t arg = SOCK_IO_NONBLOCK; + WIZCHIP_EXPORT(ctlsocket)(socket->u_param.fileno, CS_SET_IOMODE, &arg); + } + */ +} + +STATIC int wiznet5k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) { + // TODO + *_errno = EINVAL; + return -1; +} + +#if 0 +STATIC void wiznet5k_socket_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { + wiznet5k_socket_obj_t *self = self_in; + print(env, "", self->sn, getSn_MR(self->sn)); +} + +STATIC mp_obj_t wiznet5k_socket_disconnect(mp_obj_t self_in) { + mp_int_t ret = WIZCHIP_EXPORT(disconnect)(self->sn); + return 0; +} +#endif + /******************************************************************************/ // Micro Python bindings -/// \class WIZnet5k - driver for WIZnet5x00 Ethernet modules -/// -/// This class allows you to control WIZnet5x00 Ethernet adaptors based on -/// the W5200 and W5500 chipsets (only W5200 tested). -/// -/// Example usage: -/// -/// import wiznet5k -/// w = wiznet5k.WIZnet5k() -/// print(w.ipaddr()) -/// w.gethostbyname('micropython.org') -/// s = w.socket() -/// s.connect(('192.168.0.2', 8080)) -/// s.send('hello') -/// print(s.recv(10)) - -STATIC void wiznet5k_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { - print(env, "WIZnet5k()"); -} - /// \classmethod \constructor(spi, pin_cs, pin_rst) -/// Create and return a WIZnet5k object. +/// Create and return a WIZNET5K object. STATIC mp_obj_t wiznet5k_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { // check arguments mp_arg_check_num(n_args, n_kw, 3, 3, false); @@ -222,7 +374,7 @@ STATIC mp_obj_t wiznet5k_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t uint8_t sn_size[16] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; // 2k buffer for each socket ctlwizchip(CW_INIT_WIZCHIP, sn_size); - // TODO make this configurable! + // set some sensible default values; they are configurable using ifconfig method wiz_NetInfo netinfo = { .mac = {0x00, 0x08, 0xdc, 0xab, 0xcd, 0xef}, .ip = {192, 168, 0, 18}, @@ -233,6 +385,9 @@ STATIC mp_obj_t wiznet5k_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t }; ctlnetwork(CN_SET_NETINFO, (void*)&netinfo); + // seems we need a small delay after init + HAL_Delay(250); + // register with network module mod_network_register_nic(&wiznet5k_obj); @@ -241,7 +396,7 @@ STATIC mp_obj_t wiznet5k_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t } /// \method regs() -/// Dump WIZnet5k registers. +/// Dump WIZNET5K registers. STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { //wiznet5k_obj_t *self = self_in; printf("Wiz CREG:"); @@ -265,9 +420,9 @@ STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_regs_obj, wiznet5k_regs); -/// \method ipaddr([(ip, subnet, gateway, dns)]) +/// \method ifconfig([(ip, subnet, gateway, dns)]) /// Get/set IP address, subnet mask, gateway and DNS. -STATIC mp_obj_t wiznet5k_ipaddr(mp_uint_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t wiznet5k_ifconfig(mp_uint_t n_args, const mp_obj_t *args) { wiz_NetInfo netinfo; ctlnetwork(CN_GET_NETINFO, &netinfo); if (n_args == 1) { @@ -291,11 +446,11 @@ STATIC mp_obj_t wiznet5k_ipaddr(mp_uint_t n_args, const mp_obj_t *args) { return mp_const_none; } } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wiznet5k_ipaddr_obj, 1, 2, wiznet5k_ipaddr); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wiznet5k_ifconfig_obj, 1, 2, wiznet5k_ifconfig); STATIC const mp_map_elem_t wiznet5k_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_regs), (mp_obj_t)&wiznet5k_regs_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_ipaddr), (mp_obj_t)&wiznet5k_ipaddr_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ifconfig), (mp_obj_t)&wiznet5k_ifconfig_obj }, }; STATIC MP_DEFINE_CONST_DICT(wiznet5k_locals_dict, wiznet5k_locals_dict_table); @@ -303,172 +458,22 @@ STATIC MP_DEFINE_CONST_DICT(wiznet5k_locals_dict, wiznet5k_locals_dict_table); const mod_network_nic_type_t mod_network_nic_type_wiznet5k = { .base = { { &mp_type_type }, - .name = MP_QSTR_WIZnet5k, - .print = wiznet5k_print, + .name = MP_QSTR_WIZNET5K, .make_new = wiznet5k_make_new, .locals_dict = (mp_obj_t)&wiznet5k_locals_dict, }, - .socket = wiznet5k_socket, .gethostbyname = wiznet5k_gethostbyname, -}; - -/******************************************************************************/ -// Micro Python bindings; WIZnet5x00 socket class - -typedef struct _wiznet5k_socket_obj_t { - mp_obj_base_t base; - uint8_t sn; - uint8_t type; -} wiznet5k_socket_obj_t; - -STATIC const mp_obj_type_t wiznet5k_socket_type; - -STATIC mp_obj_t wiznet5k_socket_new(uint8_t sn, mp_uint_t type) { - wiznet5k_socket_obj_t *s = m_new_obj(wiznet5k_socket_obj_t); - s->base.type = &wiznet5k_socket_type; - s->sn = sn; - s->type = type; - return s; -} - -STATIC void wiznet5k_socket_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { - wiznet5k_socket_obj_t *self = self_in; - print(env, "", self->sn, getSn_MR(self->sn)); -} - -STATIC mp_obj_t wiznet5k_socket_close(mp_obj_t self_in) { - wiznet5k_socket_obj_t *self = self_in; - wiznet5k_obj.socket_used &= ~(1 << self->sn); - mp_int_t ret = WIZCHIP_EXPORT(close)(self->sn); - check_sock_return_value(ret); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_socket_close_obj, wiznet5k_socket_close); - -STATIC mp_obj_t wiznet5k_socket_bind(mp_obj_t self_in, mp_obj_t addr_in) { - wiznet5k_socket_obj_t *self = self_in; - - uint8_t ip[IPADDR_BUF_SIZE]; - mp_uint_t port = mod_network_parse_inet_addr(addr_in, ip); - - // open the socket in server mode - mp_int_t ret = WIZCHIP_EXPORT(socket)(self->sn, self->type, port, 0); - check_sock_return_value(ret); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(wiznet5k_socket_bind_obj, wiznet5k_socket_bind); - -STATIC mp_obj_t wiznet5k_socket_listen(mp_obj_t self_in, mp_obj_t backlog) { - wiznet5k_socket_obj_t *self = self_in; - mp_int_t ret = WIZCHIP_EXPORT(listen)(self->sn); - check_sock_return_value(ret); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(wiznet5k_socket_listen_obj, wiznet5k_socket_listen); - -STATIC mp_obj_t wiznet5k_socket_accept(mp_obj_t self_in) { - //wiznet5k_socket_obj_t *self = self_in; - - // TODO what to do here exactly? - - mp_obj_t tuple[2] = {self_in, mp_const_none}; - return mp_obj_new_tuple(2, tuple); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_socket_accept_obj, wiznet5k_socket_accept); - -STATIC mp_obj_t wiznet5k_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { - wiznet5k_socket_obj_t *self = self_in; - - // first open the socket in client mode - mp_int_t ret = WIZCHIP_EXPORT(socket)(self->sn, self->type, 0, 0); - check_sock_return_value(ret); - - // now connect - uint8_t ip[IPADDR_BUF_SIZE]; - mp_uint_t port = mod_network_parse_inet_addr(addr_in, ip); - ret = WIZCHIP_EXPORT(connect)(self->sn, ip, port); - check_sock_return_value(ret); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(wiznet5k_socket_connect_obj, wiznet5k_socket_connect); - -STATIC mp_obj_t wiznet5k_socket_disconnect(mp_obj_t self_in) { - wiznet5k_socket_obj_t *self = self_in; - mp_int_t ret = WIZCHIP_EXPORT(disconnect)(self->sn); - check_sock_return_value(ret); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_socket_disconnect_obj, wiznet5k_socket_disconnect); - -STATIC mp_obj_t wiznet5k_socket_send(mp_obj_t self_in, mp_obj_t data_in) { - wiznet5k_socket_obj_t *self = self_in; - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); - mp_int_t ret = WIZCHIP_EXPORT(send)(self->sn, bufinfo.buf, bufinfo.len); - check_sock_return_value(ret); - return mp_obj_new_int(ret); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(wiznet5k_socket_send_obj, wiznet5k_socket_send); - -STATIC mp_obj_t wiznet5k_socket_recv(mp_obj_t self_in, mp_obj_t len_in) { - wiznet5k_socket_obj_t *self = self_in; - mp_int_t len = mp_obj_get_int(len_in); - byte *buf; - mp_obj_t ret_obj = mp_obj_str_builder_start(&mp_type_bytes, len, &buf); - mp_int_t ret = WIZCHIP_EXPORT(recv)(self->sn, buf, len); - check_sock_return_value(ret); - return mp_obj_str_builder_end_with_len(ret_obj, len); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(wiznet5k_socket_recv_obj, wiznet5k_socket_recv); - -STATIC mp_obj_t wiznet5k_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { - wiznet5k_socket_obj_t *self = self_in; - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); - uint8_t ip[IPADDR_BUF_SIZE]; - mp_uint_t port = mod_network_parse_inet_addr(addr_in, ip); - mp_int_t ret = WIZCHIP_EXPORT(sendto)(self->sn, bufinfo.buf, bufinfo.len, ip, port); - check_sock_return_value(ret); - return mp_obj_new_int(ret); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_3(wiznet5k_socket_sendto_obj, wiznet5k_socket_sendto); - -STATIC mp_obj_t wiznet5k_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { - wiznet5k_socket_obj_t *self = self_in; - mp_int_t len = mp_obj_get_int(len_in); - uint8_t *buf = m_new(uint8_t, len); - uint8_t ip[4]; - uint16_t port; - mp_int_t ret = WIZCHIP_EXPORT(recvfrom)(self->sn, buf, len, ip, &port); - check_sock_return_value(ret); - mp_obj_t tuple[2] = { - mp_obj_new_bytes(buf, ret), - mod_network_format_inet_addr(ip, port), - }; - return mp_obj_new_tuple(2, tuple); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(wiznet5k_socket_recvfrom_obj, wiznet5k_socket_recvfrom); - -STATIC const mp_map_elem_t wiznet5k_socket_locals_dict_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&wiznet5k_socket_close_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_bind), (mp_obj_t)&wiznet5k_socket_bind_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_listen), (mp_obj_t)&wiznet5k_socket_listen_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_accept), (mp_obj_t)&wiznet5k_socket_accept_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&wiznet5k_socket_connect_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), (mp_obj_t)&wiznet5k_socket_disconnect_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&wiznet5k_socket_send_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&wiznet5k_socket_recv_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_sendto), (mp_obj_t)&wiznet5k_socket_sendto_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_recvfrom), (mp_obj_t)&wiznet5k_socket_recvfrom_obj }, -}; - -STATIC MP_DEFINE_CONST_DICT(wiznet5k_socket_locals_dict, wiznet5k_socket_locals_dict_table); - -STATIC const mp_obj_type_t wiznet5k_socket_type = { - { &mp_type_type }, - .name = MP_QSTR_socket, - .print = wiznet5k_socket_print, - .locals_dict = (mp_obj_t)&wiznet5k_socket_locals_dict, + .socket = wiznet5k_socket_socket, + .close = wiznet5k_socket_close, + .bind = wiznet5k_socket_bind, + .listen = wiznet5k_socket_listen, + .accept = wiznet5k_socket_accept, + .connect = wiznet5k_socket_connect, + .send = wiznet5k_socket_send, + .recv = wiznet5k_socket_recv, + .sendto = wiznet5k_socket_sendto, + .recvfrom = wiznet5k_socket_recvfrom, + .setsockopt = wiznet5k_socket_setsockopt, + .settimeout = wiznet5k_socket_settimeout, + .ioctl = wiznet5k_socket_ioctl, }; diff --git a/stmhal/modusocket.c b/stmhal/modusocket.c index 048f052982..1c0b036d34 100644 --- a/stmhal/modusocket.c +++ b/stmhal/modusocket.c @@ -41,43 +41,346 @@ #include "runtime.h" #include "modnetwork.h" -/// \module usocket - socket module -/// -/// Socket functionality. +/******************************************************************************/ +// socket class -/// \function socket(family=AF_INET, type=SOCK_STREAM, fileno=-1) -/// Create a socket. -STATIC mp_obj_t mod_usocket_socket(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - static const mp_arg_t allowed_args[] = { - { MP_QSTR_family, MP_ARG_INT, {.u_int = MOD_NETWORK_AF_INET} }, - { MP_QSTR_type, MP_ARG_INT, {.u_int = MOD_NETWORK_SOCK_STREAM} }, - { MP_QSTR_fileno, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - }; +STATIC const mp_obj_type_t socket_type; - // parse args - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); +// constructor socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) +STATIC mp_obj_t socket_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 4, false); - // find a NIC that can create a socket and call it - for (mp_uint_t i = 0; i < mod_network_nic_list.len; i++) { - mp_obj_t nic = mod_network_nic_list.items[i]; - mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); - if (nic_type->socket != NULL) { - int _errno; - mp_obj_t obj = nic_type->socket(nic, args[0].u_int, args[1].u_int, args[2].u_int, &_errno); - if (obj == MP_OBJ_NULL) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); - } else { - return obj; + // create socket object (not bound to any NIC yet) + mod_network_socket_obj_t *s = m_new_obj_with_finaliser(mod_network_socket_obj_t); + s->base.type = (mp_obj_t)&socket_type; + s->nic = MP_OBJ_NULL; + s->nic_type = NULL; + s->u_param.domain = MOD_NETWORK_AF_INET; + s->u_param.type = MOD_NETWORK_SOCK_STREAM; + s->u_param.fileno = -1; + if (n_args >= 1) { + s->u_param.domain = mp_obj_get_int(args[0]); + if (n_args >= 2) { + s->u_param.type = mp_obj_get_int(args[1]); + if (n_args >= 4) { + s->u_param.fileno = mp_obj_get_int(args[3]); } } } - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "no available NIC")); + return s; } -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_usocket_socket_obj, 0, mod_usocket_socket); -/// \function getaddrinfo(host, port) +STATIC void socket_select_nic(mod_network_socket_obj_t *self, const byte *ip) { + if (self->nic == MP_OBJ_NULL) { + // select NIC based on IP + self->nic = mod_network_find_nic(ip); + self->nic_type = (mod_network_nic_type_t*)mp_obj_get_type(self->nic); + + // call the NIC to open the socket + int _errno; + if (self->nic_type->socket(self, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + } +} +// method socket.close() +STATIC mp_obj_t socket_close(mp_obj_t self_in) { + mod_network_socket_obj_t *self = self_in; + if (self->nic != MP_OBJ_NULL) { + self->nic_type->close(self); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); + +// method socket.bind(address) +STATIC mp_obj_t socket_bind(mp_obj_t self_in, mp_obj_t addr_in) { + mod_network_socket_obj_t *self = self_in; + + // get address + uint8_t ip[MOD_NETWORK_IPADDR_BUF_SIZE]; + mp_uint_t port = mod_network_parse_inet_addr(addr_in, ip); + + // check if we need to select a NIC + socket_select_nic(self, ip); + + // call the NIC to bind the socket + int _errno; + if (self->nic_type->bind(self, ip, port, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind); + +// method socket.listen(backlog) +STATIC mp_obj_t socket_listen(mp_obj_t self_in, mp_obj_t backlog) { + mod_network_socket_obj_t *self = self_in; + + if (self->nic == MP_OBJ_NULL) { + // not connected + // TODO I think we can listen even if not bound... + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOTCONN))); + } + + int _errno; + if (self->nic_type->listen(self, mp_obj_get_int(backlog), &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_listen_obj, socket_listen); + +// method socket.accept() +STATIC mp_obj_t socket_accept(mp_obj_t self_in) { + mod_network_socket_obj_t *self = self_in; + + // create new socket object + mod_network_socket_obj_t *socket2 = m_new_obj_with_finaliser(mod_network_socket_obj_t); + socket2->base.type = (mp_obj_t)&socket_type; + socket2->nic = self->nic; + socket2->nic_type = self->nic_type; + + // accept incoming connection + uint8_t ip[MOD_NETWORK_IPADDR_BUF_SIZE]; + mp_uint_t port; + int _errno; + if (self->nic_type->accept(self, socket2, ip, &port, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + // make the return value + mp_obj_tuple_t *client = mp_obj_new_tuple(2, NULL); + client->items[0] = socket2; + client->items[1] = mod_network_format_inet_addr(ip, port); + + return client; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept); + +// method socket.connect(address) +STATIC mp_obj_t socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { + mod_network_socket_obj_t *self = self_in; + + // get address + uint8_t ip[MOD_NETWORK_IPADDR_BUF_SIZE]; + mp_uint_t port = mod_network_parse_inet_addr(addr_in, ip); + + // check if we need to select a NIC + socket_select_nic(self, ip); + + // call the NIC to connect the socket + int _errno; + if (self->nic_type->connect(self, ip, port, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect); + +// method socket.send(bytes) +STATIC mp_obj_t socket_send(mp_obj_t self_in, mp_obj_t buf_in) { + mod_network_socket_obj_t *self = self_in; + if (self->nic == MP_OBJ_NULL) { + // not connected + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(EPIPE))); + } + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + int _errno; + mp_uint_t ret = self->nic_type->send(self, bufinfo.buf, bufinfo.len, &_errno); + if (ret == -1) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + return mp_obj_new_int_from_uint(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_send_obj, socket_send); + +// method socket.recv(bufsize) +STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { + mod_network_socket_obj_t *self = self_in; + if (self->nic == MP_OBJ_NULL) { + // not connected + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOTCONN))); + } + mp_int_t len = mp_obj_get_int(len_in); + byte *buf; + mp_obj_t ret_obj = mp_obj_str_builder_start(&mp_type_bytes, len, &buf); + int _errno; + mp_uint_t ret = self->nic_type->recv(self, buf, len, &_errno); + if (ret == -1) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + if (ret == 0) { + return mp_const_empty_bytes; + } + return mp_obj_str_builder_end_with_len(ret_obj, ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); + +// method socket.sendto(bytes, address) +STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { + mod_network_socket_obj_t *self = self_in; + + // get the data + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); + + // get address + uint8_t ip[MOD_NETWORK_IPADDR_BUF_SIZE]; + mp_uint_t port = mod_network_parse_inet_addr(addr_in, ip); + + // check if we need to select a NIC + socket_select_nic(self, ip); + + // call the NIC to sendto + int _errno; + mp_int_t ret = self->nic_type->sendto(self, bufinfo.buf, bufinfo.len, ip, port, &_errno); + if (ret == -1) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(socket_sendto_obj, socket_sendto); + +// method socket.recvfrom(bufsize) +STATIC mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { + mod_network_socket_obj_t *self = self_in; + if (self->nic == MP_OBJ_NULL) { + // not connected + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOTCONN))); + } + mp_int_t len = mp_obj_get_int(len_in); + byte *buf; + mp_obj_t ret_obj = mp_obj_str_builder_start(&mp_type_bytes, len, &buf); + byte ip[4]; + mp_uint_t port; + int _errno; + mp_int_t ret = self->nic_type->recvfrom(self, buf, len, ip, &port, &_errno); + if (ret == -1) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + if (ret == 0) { + ret_obj = mp_const_empty_bytes; + } else { + ret_obj = mp_obj_str_builder_end_with_len(ret_obj, ret); + } + mp_obj_t tuple[2] = { + ret_obj, + mod_network_format_inet_addr(ip, port), + }; + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); + +// method socket.setsockopt(level, optname, value) +STATIC mp_obj_t socket_setsockopt(mp_uint_t n_args, const mp_obj_t *args) { + mod_network_socket_obj_t *self = args[0]; + + mp_int_t level = mp_obj_get_int(args[1]); + mp_int_t opt = mp_obj_get_int(args[2]); + + const void *optval; + mp_uint_t optlen; + if (mp_obj_is_integer(args[3])) { + int val = mp_obj_int_get(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 _errno; + if (self->nic_type->setsockopt(self, level, opt, optval, optlen, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt); + +// method socket.settimeout(value) +// timeout=0 means non-blocking +// timeout=None means blocking +// otherwise, timeout is in seconds +STATIC mp_obj_t socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) { + mod_network_socket_obj_t *self = self_in; + if (self->nic == MP_OBJ_NULL) { + // not connected + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOTCONN))); + } + mp_uint_t timeout; + if (timeout_in == mp_const_none) { + timeout = -1; + } else { + timeout = 1000 * mp_obj_get_float(timeout_in); + } + int _errno; + if (self->nic_type->settimeout(self, timeout, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_settimeout_obj, socket_settimeout); + +// method socket.setblocking(flag) +STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t blocking) { + if (mp_obj_is_true(blocking)) { + return socket_settimeout(self_in, mp_const_none); + } else { + return socket_settimeout(self_in, MP_OBJ_NEW_SMALL_INT(0)); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); + +STATIC const mp_map_elem_t socket_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&socket_close_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&socket_close_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_bind), (mp_obj_t)&socket_bind_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_listen), (mp_obj_t)&socket_listen_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_accept), (mp_obj_t)&socket_accept_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&socket_connect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&socket_send_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&socket_recv_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sendto), (mp_obj_t)&socket_sendto_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_recvfrom), (mp_obj_t)&socket_recvfrom_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_setsockopt), (mp_obj_t)&socket_setsockopt_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_settimeout), (mp_obj_t)&socket_settimeout_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_setblocking), (mp_obj_t)&socket_setblocking_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(socket_locals_dict, socket_locals_dict_table); + +mp_uint_t socket_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + mod_network_socket_obj_t *self = self_in; + return self->nic_type->ioctl(self, request, arg, errcode); +} + +STATIC const mp_stream_p_t socket_stream_p = { + .ioctl = socket_ioctl, + .is_text = false, +}; + +STATIC const mp_obj_type_t socket_type = { + { &mp_type_type }, + .name = MP_QSTR_socket, + .make_new = socket_make_new, + .stream_p = &socket_stream_p, + .locals_dict = (mp_obj_t)&socket_locals_dict, +}; + +/******************************************************************************/ +// usocket module + +// function usocket.getaddrinfo(host, port) STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { mp_uint_t hlen; const char *host = mp_obj_str_get_data(host_in, &hlen); @@ -111,7 +414,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_usocket_getaddrinfo_obj, mod_usocket_getadd STATIC const mp_map_elem_t mp_module_usocket_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usocket) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&mod_usocket_socket_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&socket_type }, { MP_OBJ_NEW_QSTR(MP_QSTR_getaddrinfo), (mp_obj_t)&mod_usocket_getaddrinfo_obj }, // class constants diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h index 3b89dc9f1a..e274c4f178 100644 --- a/stmhal/qstrdefsport.h +++ b/stmhal/qstrdefsport.h @@ -378,6 +378,18 @@ Q(show) Q(usocket) Q(socket) Q(getaddrinfo) +Q(bind) +Q(listen) +Q(accept) +Q(connect) +Q(send) +Q(recv) +Q(sendto) +Q(recvfrom) +Q(setblocking) +Q(setsockopt) +Q(settimeout) +Q(close) Q(AF_INET) Q(AF_INET6) Q(SOCK_STREAM) @@ -388,39 +400,19 @@ Q(SOCK_RAW) Q(network) Q(route) -// for WIZnet5k class +// for WIZNET5K class #if MICROPY_PY_WIZNET5K -Q(wiznet5k) -Q(WIZnet5k) +Q(WIZNET5K) Q(regs) Q(ipaddr) -Q(socket) -Q(family) -Q(type) -Q(fileno) -Q(close) -Q(bind) -Q(listen) -Q(accept) -Q(connect) -Q(disconnect) -Q(send) -Q(recv) -Q(sendto) -Q(recvfrom) -Q(gethostbyname) -Q(AF_INET) -Q(SOCK_STREAM) -Q(SOCK_DGRAM) #endif -// for CC3k class +// for CC3K class #if MICROPY_PY_CC3K -Q(cc3k) -Q(CC3k) +Q(CC3K) Q(connect) Q(disconnect) -Q(is_connected) +Q(isconnected) Q(ifconfig) Q(patch_version) Q(patch_program) @@ -431,16 +423,6 @@ Q(ssid) Q(key) Q(security) Q(bssid) -Q(send) -Q(recv) -Q(bind) -Q(listen) -Q(accept) -Q(connect) -Q(settimeout) -Q(setblocking) -Q(close) -Q(__del__) #endif // for stm module