From ff93fd4f50321c6190e1659b19e64fef3045a484 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 5 Oct 2017 10:48:23 +1100 Subject: py/persistentcode: Bump .mpy version number to version 3. The binary and unary ops have changed bytecode encoding. --- ports/minimal/frozentest.mpy | Bin 255 -> 255 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'ports') diff --git a/ports/minimal/frozentest.mpy b/ports/minimal/frozentest.mpy index 87f9581bf..7c6809bf6 100644 Binary files a/ports/minimal/frozentest.mpy and b/ports/minimal/frozentest.mpy differ -- cgit v1.2.3 From 8c7db42ee3d6e47e4a58e4cb573dc1a30c96fa32 Mon Sep 17 00:00:00 2001 From: Li Weiwei Date: Wed, 27 Sep 2017 22:04:42 +0800 Subject: stm32/modnwwiznet5k: Get the IP address of an established socket. When wiznet5k_socket_accept is called, if a socket is established, get the IP address of the socket. --- ports/stm32/modnwwiznet5k.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'ports') diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index 21e4dbbbf..78249816d 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -173,11 +173,7 @@ STATIC int wiznet5k_socket_accept(mod_network_socket_obj_t *socket, mod_network_ 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; + getSn_DIPR((uint8_t)socket2->u_param.fileno, ip); *port = getSn_PORT(socket2->u_param.fileno); // WIZnet turns the listening socket into the client socket, so we -- cgit v1.2.3 From 6f1a61542746e3ef3704f6e563e2526454df0112 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 6 Oct 2017 14:32:42 +1100 Subject: stm32/boards: Fix typos in stm32f767_af.csv table. --- ports/stm32/boards/stm32f767_af.csv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'ports') diff --git a/ports/stm32/boards/stm32f767_af.csv b/ports/stm32/boards/stm32f767_af.csv index db27818c6..1708dfcca 100644 --- a/ports/stm32/boards/stm32f767_af.csv +++ b/ports/stm32/boards/stm32f767_af.csv @@ -15,9 +15,9 @@ PortA,PA11,,TIM1_CH4,,,,,,USART1_CTS,,CAN1_RX,OTG_FS_DM,,,,LCD_R4,EVENTOUT PortA,PA12,,TIM1_ETR,,,,,,USART1_RTS,SAI2_FS_B,CAN1_TX,OTG_FS_DP,,,,LCD_R5,EVENTOUT PortA,PA13,JTMS,SWDIO,,,,,,,,,,,,,,EVENTOUT PortA,PA14,JTCK,SWCLK,,,,,,,,,,,,,,EVENTOUT -PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,HDMICE,CSPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,,UART4_RTS,,,,,,,EVENTOUT -PortB,PB0,,TIM1_CH2N,TIM3_CH3T,IM8_CH2N,,,,,UART4_CTS,LCD_R3,OTG_HS_ULPI_D1,ETH_MII_RXD2,,,,EVENTOUT -PortB,PB1,,TIM1_CH3N,TIM3_CH4T,IM8_CH3N,,,,,,LCD_R6,OTG_HS_ULPI_D2,ETH_MII_RXD3,,,,EVENTOUT +PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,HDMICEC,SPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,,UART4_RTS,,,,,,,EVENTOUT +PortB,PB0,,TIM1_CH2N,TIM3_CH3T,TIM8_CH2N,,,,,UART4_CTS,LCD_R3,OTG_HS_ULPI_D1,ETH_MII_RXD2,,,,EVENTOUT +PortB,PB1,,TIM1_CH3N,TIM3_CH4T,TIM8_CH3N,,,,,,LCD_R6,OTG_HS_ULPI_D2,ETH_MII_RXD3,,,,EVENTOUT PortB,PB2,,,,,,,SAI1_SD_A,SPI3_MOSI/I2S3_SD,,QUADSPI_CLK,,,,,,EVENTOUT PortB,PB3,JTDO/TRACESWO,TIM2_CH2,,,,SPI1_SCK/I2S1_CK,SPI3_SCK/I2S3_CK,,,,SDMMC2_D2,,,,,EVENTOUT PortB,PB4,NJTRST,,TIM3_CH1,,,SPI1_MISO,SPI3_MISO,SPI2_NSS/I2S2_WS,,,SDMMC2_D3,,,,,EVENTOUT @@ -41,7 +41,7 @@ PortC,PC5,,,,,,,,,SPDIFRX_IN3,,,ETH_MII_RXD1/ETH_RMII_RXD1,FMC_SDCKE0,,,EVENTOUT PortC,PC6,,,TIM3_CH1,TIM8_CH1,,I2S2_MCK,,,USART6_TX,,SDMMC2_D6,,SDMMC1_D6,DCMI_D0,LCD_HSYNC,EVENTOUT PortC,PC7,,,TIM3_CH2,TIM8_CH2,,,I2S3_MCK,,USART6_RX,,SDMMC2_D7,,SDMMC1_D7,DCMI_D1,LCD_G6,EVENTOUT PortC,PC8,TRACED1,,TIM3_CH3,TIM8_CH3,,,,UART5_RTS,USART6_CK,,,,SDMMC1_D0,DCMI_D2,,EVENTOUT -PortC,PC9,MCO2,,TIM3_CH4,TIM8_CH4,I2C3_SDA,I2S_CKIN,,UART5_CTS,,QUADSPI_BK1_IO0,,,SDMMC1_D1,DCMI_D3,,EVENTOUT +PortC,PC9,MCO2,,TIM3_CH4,TIM8_CH4,I2C3_SDA,I2S2_CKIN,,UART5_CTS,,QUADSPI_BK1_IO0,,,SDMMC1_D1,DCMI_D3,,EVENTOUT PortC,PC10,,,,,,,SPI3_SCK/I2S3_CK,USART3_TX,UART4_TX,QUADSPI_BK1_IO1,,,SDMMC1_D2,DCMI_D8,LCD_R2,EVENTOUT PortC,PC11,,,,,,,SPI3_MISO,USART3_RX,UART4_RX,QUADSPI_BK2_NCS,,,SDMMC1_D3,DCMI_D4,,EVENTOUT PortC,PC12,TRACED3,,,,,,SPI3_MOSI/I2S3_SD,USART3_CK,UART5_TX,,,,SDMMC1_CK,DCMI_D9,,EVENTOUT -- cgit v1.2.3 From 58ea239510530b5ce80aa24cd084b43698309bb4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 7 Oct 2017 14:08:50 +0300 Subject: zephyr: Use CONFIG_NET_APP_SETTINGS to setup initial network addresses. Ideally, these should be configurable from Python (using network module), but as that doesn't exist, we better off using Zephyr's native bootstrap configuration facility. --- ports/zephyr/main.c | 5 ++++- ports/zephyr/prj_base.conf | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'ports') diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 7255d981f..7eb9da3e1 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -60,7 +60,9 @@ static char *stack_top; static char heap[MICROPY_HEAP_SIZE]; void init_zephyr(void) { - // TODO: Make addresses configurable + // We now rely on CONFIG_NET_APP_SETTINGS to set up bootstrap + // network addresses. +#if 0 #ifdef CONFIG_NETWORKING if (net_if_get_default() == NULL) { // If there's no default networking interface, @@ -81,6 +83,7 @@ void init_zephyr(void) { static struct in6_addr in6addr_my = {{{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}}; net_if_ipv6_addr_add(net_if_get_default(), &in6addr_my, NET_ADDR_MANUAL, 0); #endif +#endif } int real_main(void) { diff --git a/ports/zephyr/prj_base.conf b/ports/zephyr/prj_base.conf index c3ba2812f..a4cc14513 100644 --- a/ports/zephyr/prj_base.conf +++ b/ports/zephyr/prj_base.conf @@ -18,6 +18,14 @@ CONFIG_NET_SOCKETS=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_NET_NBUF_RX_COUNT=5 +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_INIT_TIMEOUT=3 +CONFIG_NET_APP_NEED_IPV6=y +CONFIG_NET_APP_NEED_IPV4=y +CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_APP_MY_IPV4_GW="192.0.2.2" + # DNS CONFIG_DNS_RESOLVER=y CONFIG_DNS_RESOLVER_ADDITIONAL_QUERIES=2 -- cgit v1.2.3 From 4514f073c18ab94b3f0f790ae813de61f38d367b Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 7 Oct 2017 17:09:53 +0300 Subject: zephyr: Switch to interrupt-driven pull-style console. While this console API improves handling on real hardware boards (e.g. clipboard paste is much more reliable, as well as programmatic communication), it vice-versa poses problems under QEMU, apparently because it doesn't emulate UART interrupt handling faithfully. That leads to inability to run the testsuite on QEMU at all. To work that around, we have to suuport both old and new console routines, and use the old ones under QEMU. --- ports/zephyr/prj_base.conf | 6 ++++++ ports/zephyr/prj_qemu_cortex_m3.conf | 4 ++++ ports/zephyr/prj_qemu_x86.conf | 4 ++++ ports/zephyr/src/zephyr_start.c | 5 +++++ ports/zephyr/uart_core.c | 14 ++++++++++++++ 5 files changed, 33 insertions(+) (limited to 'ports') diff --git a/ports/zephyr/prj_base.conf b/ports/zephyr/prj_base.conf index a4cc14513..fbcedf260 100644 --- a/ports/zephyr/prj_base.conf +++ b/ports/zephyr/prj_base.conf @@ -4,6 +4,12 @@ CONFIG_REBOOT=y CONFIG_STDOUT_CONSOLE=y CONFIG_CONSOLE_HANDLER=y CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS=y + +CONFIG_CONSOLE_PULL=y +CONFIG_CONSOLE_GETCHAR=y +CONFIG_CONSOLE_GETCHAR_BUFSIZE=128 +CONFIG_CONSOLE_PUTCHAR_BUFSIZE=128 + CONFIG_NEWLIB_LIBC=y CONFIG_FLOAT=y CONFIG_MAIN_STACK_SIZE=4096 diff --git a/ports/zephyr/prj_qemu_cortex_m3.conf b/ports/zephyr/prj_qemu_cortex_m3.conf index 09614c362..1ade981e2 100644 --- a/ports/zephyr/prj_qemu_cortex_m3.conf +++ b/ports/zephyr/prj_qemu_cortex_m3.conf @@ -1,3 +1,7 @@ +# Interrupt-driven UART console has emulation artifacts under QEMU, +# disable it +CONFIG_CONSOLE_PULL=n + # Networking drivers # SLIP driver for QEMU CONFIG_NET_SLIP_TAP=y diff --git a/ports/zephyr/prj_qemu_x86.conf b/ports/zephyr/prj_qemu_x86.conf index ef60cfec9..9bc81259a 100644 --- a/ports/zephyr/prj_qemu_x86.conf +++ b/ports/zephyr/prj_qemu_x86.conf @@ -1,3 +1,7 @@ +# Interrupt-driven UART console has emulation artifacts under QEMU, +# disable it +CONFIG_CONSOLE_PULL=n + # Networking drivers # SLIP driver for QEMU CONFIG_NET_SLIP_TAP=y diff --git a/ports/zephyr/src/zephyr_start.c b/ports/zephyr/src/zephyr_start.c index 9e8a90beb..452e304ca 100644 --- a/ports/zephyr/src/zephyr_start.c +++ b/ports/zephyr/src/zephyr_start.c @@ -24,11 +24,16 @@ * THE SOFTWARE. */ #include +#include #include "zephyr_getchar.h" int real_main(void); void main(void) { +#ifdef CONFIG_CONSOLE_PULL + console_init(); +#else zephyr_getchar_init(); +#endif real_main(); } diff --git a/ports/zephyr/uart_core.c b/ports/zephyr/uart_core.c index 1e85053cd..e41fb9acc 100644 --- a/ports/zephyr/uart_core.c +++ b/ports/zephyr/uart_core.c @@ -28,6 +28,7 @@ #include "src/zephyr_getchar.h" // Zephyr headers #include +#include /* * Core UART functions to implement for a port @@ -35,11 +36,23 @@ // Receive single character int mp_hal_stdin_rx_chr(void) { +#ifdef CONFIG_CONSOLE_PULL + return console_getchar(); +#else return zephyr_getchar(); +#endif } // Send string of given length void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { +#ifdef CONFIG_CONSOLE_PULL + while (len--) { + char c = *str++; + while (console_putchar(c) == -1) { + k_sleep(1); + } + } +#else static struct device *uart_console_dev; if (uart_console_dev == NULL) { uart_console_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME); @@ -48,4 +61,5 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { while (len--) { uart_poll_out(uart_console_dev, *str++); } +#endif } -- cgit v1.2.3 From 6db132e1302dcca4671e0d67b872ca226c54682f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 5 Oct 2017 15:27:21 +1100 Subject: esp8266/modnetwork: Add "bssid" keyword arg to WLAN.connect() method. --- ports/esp8266/modnetwork.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) (limited to 'ports') diff --git a/ports/esp8266/modnetwork.c b/ports/esp8266/modnetwork.c index 2c61e5dcd..b41a11f59 100644 --- a/ports/esp8266/modnetwork.c +++ b/ports/esp8266/modnetwork.c @@ -93,29 +93,55 @@ STATIC mp_obj_t esp_active(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_active_obj, 1, 2, esp_active); -STATIC mp_obj_t esp_connect(size_t n_args, const mp_obj_t *args) { - require_if(args[0], STATION_IF); +STATIC mp_obj_t esp_connect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_ssid, ARG_password, ARG_bssid }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { 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); + + require_if(pos_args[0], STATION_IF); struct station_config config = {{0}}; size_t len; const char *p; + bool set_config = false; - if (n_args > 1) { - p = mp_obj_str_get_data(args[1], &len); + // set parameters based on given args + if (args[ARG_ssid].u_obj != mp_const_none) { + p = mp_obj_str_get_data(args[ARG_ssid].u_obj, &len); len = MIN(len, sizeof(config.ssid)); memcpy(config.ssid, p, len); - if (n_args > 2) { - p = mp_obj_str_get_data(args[2], &len); - len = MIN(len, sizeof(config.password)); - memcpy(config.password, p, len); + set_config = true; + } + if (args[ARG_password].u_obj != mp_const_none) { + p = mp_obj_str_get_data(args[ARG_password].u_obj, &len); + len = MIN(len, sizeof(config.password)); + memcpy(config.password, p, len); + set_config = true; + } + if (args[ARG_bssid].u_obj != mp_const_none) { + p = mp_obj_str_get_data(args[ARG_bssid].u_obj, &len); + if (len != sizeof(config.bssid)) { + mp_raise_ValueError(NULL); } + config.bssid_set = 1; + memcpy(config.bssid, p, sizeof(config.bssid)); + set_config = true; + } + if (set_config) { error_check(wifi_station_set_config(&config), "Cannot set STA config"); } error_check(wifi_station_connect(), "Cannot connect to AP"); return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_connect_obj, 1, 7, esp_connect); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp_connect_obj, 1, esp_connect); STATIC mp_obj_t esp_disconnect(mp_obj_t self_in) { require_if(self_in, STATION_IF); -- cgit v1.2.3 From 81a06d2c9c3ce081043e1eb948b65014f1b1786a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 4 Oct 2017 21:48:14 +1100 Subject: lib/libm: Remove implementation of log2f, use MP_NEED_LOG2 instead. --- lib/libm/math.c | 10 ---------- ports/stm32/mpconfigport.h | 3 +++ 2 files changed, 3 insertions(+), 10 deletions(-) (limited to 'ports') diff --git a/lib/libm/math.c b/lib/libm/math.c index 5e00740d1..6b65202cf 100644 --- a/lib/libm/math.c +++ b/lib/libm/math.c @@ -48,16 +48,6 @@ float copysignf(float x, float y) { } #endif -// some compilers define log2f in terms of logf -#ifdef log2f -#undef log2f -#endif -// some compilers have _M_LN2 defined in math.h, some don't -#ifndef _M_LN2 -#define _M_LN2 (0.69314718055994530942) -#endif -float log2f(float x) { return logf(x) / (float)_M_LN2; } - static const float _M_LN10 = 2.30258509299404; // 0x40135d8e float log10f(float x) { return logf(x) / (float)_M_LN10; } diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 7888b5355..06606e62a 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -335,6 +335,9 @@ static inline mp_uint_t disable_irq(void) { } while (0); #endif +// We need an implementation of the log2 function which is not a macro +#define MP_NEED_LOG2 (1) + // There is no classical C heap in bare-metal ports, only Python // garbage-collected heap. For completeness, emulate C heap via // GC heap. Note that MicroPython core never uses malloc() and friends, -- cgit v1.2.3 From 1b7d6a795149588eb44c1b33dd7c34fe6669460a Mon Sep 17 00:00:00 2001 From: Vitor Massaru Iha Date: Sun, 8 Oct 2017 15:28:32 -0300 Subject: esp8266/modules/webrepl_setup: Add info about allowed password length. This patch also makes the code more concise by combining the checks for the password length. --- ports/esp8266/modules/webrepl_setup.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'ports') diff --git a/ports/esp8266/modules/webrepl_setup.py b/ports/esp8266/modules/webrepl_setup.py index d91600e6e..5288c49c0 100644 --- a/ports/esp8266/modules/webrepl_setup.py +++ b/ports/esp8266/modules/webrepl_setup.py @@ -17,12 +17,9 @@ def getpass(prompt): def input_pass(): while 1: - passwd1 = getpass("New password: ") - if len(passwd1) < 4: - print("Password too short") - continue - elif len(passwd1) > 9: - print("Password too long") + passwd1 = getpass("New password (4-9 chars): ") + if len(passwd1) < 4 or len(passwd1) > 9: + print("Invalid password length") continue passwd2 = getpass("Confirm password: ") if passwd1 == passwd2: -- cgit v1.2.3 From 7c7c7b161df5be99aee9dea444b6e4beacf78438 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 13 Oct 2017 12:00:47 +1100 Subject: stm32/usbd_cdc_interface: Don't reset CDC output buf on initialisation. So that characters can be buffered before the USB device is connected (restoring behviour of the driver before recent state refactoring). --- ports/stm32/usbd_cdc_interface.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'ports') diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c index 4c49c9321..2e9fba917 100644 --- a/ports/stm32/usbd_cdc_interface.c +++ b/ports/stm32/usbd_cdc_interface.c @@ -57,10 +57,14 @@ #define CDC_SEND_BREAK 0x23 uint8_t *usbd_cdc_init(usbd_cdc_itf_t *cdc, usbd_cdc_msc_hid_state_t *usbd) { + // Link the parent state cdc->usbd = usbd; + + // Reset all the CDC state + // Note: we don't reset tx_buf_ptr_in in order to allow the output buffer to + // be filled (by usbd_cdc_tx_always) before the USB device is connected. cdc->rx_buf_put = 0; cdc->rx_buf_get = 0; - cdc->tx_buf_ptr_in = 0; cdc->tx_buf_ptr_out = 0; cdc->tx_buf_ptr_out_shadow = 0; cdc->tx_buf_ptr_wait_count = 0; -- cgit v1.2.3 From e39fcda8eb1bfc1ccc1660079189f9ae39392abe Mon Sep 17 00:00:00 2001 From: Li Weiwei Date: Fri, 13 Oct 2017 09:14:07 +0800 Subject: stm32/usbd_cdc_interface.h: Fix code comments after recent refactor. --- ports/stm32/usbd_cdc_interface.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'ports') diff --git a/ports/stm32/usbd_cdc_interface.h b/ports/stm32/usbd_cdc_interface.h index 6dcab7d47..98b8fc077 100644 --- a/ports/stm32/usbd_cdc_interface.h +++ b/ports/stm32/usbd_cdc_interface.h @@ -43,8 +43,8 @@ typedef struct _usbd_cdc_itf_t { uint16_t rx_buf_get; // circular buffer index uint8_t tx_buf[USBD_CDC_TX_DATA_SIZE]; // data for USB IN endpoind is stored in this buffer - uint16_t tx_buf_ptr_in; // increment this pointer modulo APP_TX_DATA_SIZE when new data is available - volatile uint16_t tx_buf_ptr_out; // increment this pointer modulo APP_TX_DATA_SIZE when data is drained + uint16_t tx_buf_ptr_in; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when new data is available + volatile uint16_t tx_buf_ptr_out; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when data is drained uint16_t tx_buf_ptr_out_shadow; // shadow of above uint8_t tx_buf_ptr_wait_count; // used to implement a timeout waiting for low-level USB driver uint8_t tx_need_empty_packet; // used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size -- cgit v1.2.3 From 37282f8fc135a16ff36e2afe0de907cbde531ed0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 13 Oct 2017 20:01:57 +1100 Subject: extmod/uos_dupterm: Update uos.dupterm() and helper funcs to have index. The uos.dupterm() signature and behaviour is updated to reflect the latest enhancements in the docs. It has minor backwards incompatibility in that it no longer accepts zero arguments. The dupterm_rx helper function is moved from esp8266 to extmod and generalised to support multiple dupterm slots. A port can specify multiple slots by defining the MICROPY_PY_OS_DUPTERM config macro to an integer, being the number of slots it wants to have; 0 means to disable the dupterm feature altogether. The unix and esp8266 ports are updated to work with the new interface and are otherwise unchanged with respect to functionality. --- extmod/misc.h | 3 +- extmod/uos_dupterm.c | 96 ++++++++++++++++++++++++++++++---------- ports/esp8266/esp_mphal.c | 37 +--------------- ports/esp8266/main.c | 2 - ports/esp8266/modules/webrepl.py | 4 +- ports/unix/unix_mphal.c | 17 +++---- py/mpstate.h | 2 +- py/runtime.c | 7 +++ 8 files changed, 95 insertions(+), 73 deletions(-) (limited to 'ports') diff --git a/extmod/misc.h b/extmod/misc.h index 6c13592c7..d6f6d859c 100644 --- a/extmod/misc.h +++ b/extmod/misc.h @@ -35,8 +35,9 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj); #if MICROPY_PY_OS_DUPTERM +int mp_uos_dupterm_rx_chr(void); void mp_uos_dupterm_tx_strn(const char *str, size_t len); -void mp_uos_deactivate(const char *msg, mp_obj_t exc); +void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc); #else #define mp_uos_dupterm_tx_strn(s, l) #endif diff --git a/extmod/uos_dupterm.c b/extmod/uos_dupterm.c index 1d6f02dce..d4326d326 100644 --- a/extmod/uos_dupterm.c +++ b/extmod/uos_dupterm.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2016 Paul Sokolovsky + * Copyright (c) 2017 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 @@ -31,12 +32,13 @@ #include "py/objtuple.h" #include "py/objarray.h" #include "py/stream.h" +#include "lib/utils/interrupt_char.h" #if MICROPY_PY_OS_DUPTERM -void mp_uos_deactivate(const char *msg, mp_obj_t exc) { - mp_obj_t term = MP_STATE_PORT(term_obj); - MP_STATE_PORT(term_obj) = NULL; +void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc) { + mp_obj_t term = MP_STATE_VM(dupterm_objs[dupterm_idx]); + MP_STATE_VM(dupterm_objs[dupterm_idx]) = MP_OBJ_NULL; mp_printf(&mp_plat_print, msg); if (exc != MP_OBJ_NULL) { mp_obj_print_exception(&mp_plat_print, exc); @@ -44,48 +46,94 @@ void mp_uos_deactivate(const char *msg, mp_obj_t exc) { mp_stream_close(term); } +int mp_uos_dupterm_rx_chr(void) { + for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) { + if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) { + continue; + } + + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t readinto_m[3]; + mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_readinto, readinto_m); + readinto_m[2] = MP_STATE_VM(dupterm_arr_obj); + mp_obj_t res = mp_call_method_n_kw(1, 0, readinto_m); + if (res == mp_const_none) { + nlr_pop(); + } else if (res == MP_OBJ_NEW_SMALL_INT(0)) { + mp_uos_deactivate(idx, "dupterm: EOF received, deactivating\n", MP_OBJ_NULL); + nlr_pop(); + } else { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(MP_STATE_VM(dupterm_arr_obj), &bufinfo, MP_BUFFER_READ); + nlr_pop(); + if (*(byte*)bufinfo.buf == mp_interrupt_char) { + // Signal keyboard interrupt to be raised as soon as the VM resumes + mp_keyboard_interrupt(); + return -2; + } + return *(byte*)bufinfo.buf; + } + } else { + mp_uos_deactivate(idx, "dupterm: Exception in read() method, deactivating: ", nlr.ret_val); + } + } + + // No chars available + return -1; +} + void mp_uos_dupterm_tx_strn(const char *str, size_t len) { - if (MP_STATE_PORT(term_obj) != MP_OBJ_NULL) { + for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) { + if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) { + continue; + } nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_obj_t write_m[3]; - mp_load_method(MP_STATE_PORT(term_obj), MP_QSTR_write, write_m); + mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_write, write_m); - mp_obj_array_t *arr = MP_OBJ_TO_PTR(MP_STATE_PORT(dupterm_arr_obj)); + mp_obj_array_t *arr = MP_OBJ_TO_PTR(MP_STATE_VM(dupterm_arr_obj)); void *org_items = arr->items; arr->items = (void*)str; arr->len = len; - write_m[2] = MP_STATE_PORT(dupterm_arr_obj); + write_m[2] = MP_STATE_VM(dupterm_arr_obj); mp_call_method_n_kw(1, 0, write_m); - arr = MP_OBJ_TO_PTR(MP_STATE_PORT(dupterm_arr_obj)); + arr = MP_OBJ_TO_PTR(MP_STATE_VM(dupterm_arr_obj)); arr->items = org_items; arr->len = 1; nlr_pop(); } else { - mp_uos_deactivate("dupterm: Exception in write() method, deactivating: ", nlr.ret_val); + mp_uos_deactivate(idx, "dupterm: Exception in write() method, deactivating: ", nlr.ret_val); } } } STATIC mp_obj_t mp_uos_dupterm(size_t n_args, const mp_obj_t *args) { - if (n_args == 0) { - if (MP_STATE_PORT(term_obj) == MP_OBJ_NULL) { - return mp_const_none; - } else { - return MP_STATE_PORT(term_obj); - } + mp_int_t idx = 0; + if (n_args == 2) { + idx = mp_obj_get_int(args[1]); + } + + if (idx < 0 || idx >= MICROPY_PY_OS_DUPTERM) { + mp_raise_ValueError("invalid dupterm index"); + } + + mp_obj_t previous_obj = MP_STATE_VM(dupterm_objs[idx]); + if (previous_obj == MP_OBJ_NULL) { + previous_obj = mp_const_none; + } + if (args[0] == mp_const_none) { + MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL; } else { - if (args[0] == mp_const_none) { - MP_STATE_PORT(term_obj) = MP_OBJ_NULL; - } else { - MP_STATE_PORT(term_obj) = args[0]; - if (MP_STATE_PORT(dupterm_arr_obj) == MP_OBJ_NULL) { - MP_STATE_PORT(dupterm_arr_obj) = mp_obj_new_bytearray(1, ""); - } + MP_STATE_VM(dupterm_objs[idx]) = args[0]; + if (MP_STATE_VM(dupterm_arr_obj) == MP_OBJ_NULL) { + MP_STATE_VM(dupterm_arr_obj) = mp_obj_new_bytearray(1, ""); } - return mp_const_none; } + + return previous_obj; } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj, 0, 1, mp_uos_dupterm); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj, 1, 2, mp_uos_dupterm); #endif diff --git a/ports/esp8266/esp_mphal.c b/ports/esp8266/esp_mphal.c index 71d4c5062..9f4f051fd 100644 --- a/ports/esp8266/esp_mphal.c +++ b/ports/esp8266/esp_mphal.c @@ -155,41 +155,6 @@ void mp_hal_signal_input(void) { #endif } -static int call_dupterm_read(void) { - if (MP_STATE_PORT(term_obj) == NULL) { - return -1; - } - - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_obj_t readinto_m[3]; - mp_load_method(MP_STATE_PORT(term_obj), MP_QSTR_readinto, readinto_m); - readinto_m[2] = MP_STATE_PORT(dupterm_arr_obj); - mp_obj_t res = mp_call_method_n_kw(1, 0, readinto_m); - if (res == mp_const_none) { - nlr_pop(); - return -2; - } - if (res == MP_OBJ_NEW_SMALL_INT(0)) { - mp_uos_deactivate("dupterm: EOF received, deactivating\n", MP_OBJ_NULL); - nlr_pop(); - return -1; - } - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(MP_STATE_PORT(dupterm_arr_obj), &bufinfo, MP_BUFFER_READ); - nlr_pop(); - if (*(byte*)bufinfo.buf == mp_interrupt_char) { - mp_keyboard_interrupt(); - return -2; - } - return *(byte*)bufinfo.buf; - } else { - mp_uos_deactivate("dupterm: Exception in read() method, deactivating: ", nlr.ret_val); - } - - return -1; -} - STATIC void dupterm_task_handler(os_event_t *evt) { static byte lock; if (lock) { @@ -197,7 +162,7 @@ STATIC void dupterm_task_handler(os_event_t *evt) { } lock = 1; while (1) { - int c = call_dupterm_read(); + int c = mp_uos_dupterm_rx_chr(); if (c < 0) { break; } diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c index 7f5dca84e..d1b88a8ce 100644 --- a/ports/esp8266/main.c +++ b/ports/esp8266/main.c @@ -51,8 +51,6 @@ STATIC void mp_reset(void) { mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib)); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_)); mp_obj_list_init(mp_sys_argv, 0); - MP_STATE_PORT(term_obj) = MP_OBJ_NULL; - MP_STATE_PORT(dupterm_arr_obj) = MP_OBJ_NULL; #if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA extern void esp_native_code_init(void); esp_native_code_init(); diff --git a/ports/esp8266/modules/webrepl.py b/ports/esp8266/modules/webrepl.py index 5a76e9b26..aa156d148 100644 --- a/ports/esp8266/modules/webrepl.py +++ b/ports/esp8266/modules/webrepl.py @@ -31,7 +31,9 @@ def setup_conn(port, accept_handler): def accept_conn(listen_sock): global client_s cl, remote_addr = listen_sock.accept() - if uos.dupterm(): + prev = uos.dupterm(None) + uos.dupterm(prev) + if prev: print("\nConcurrent WebREPL connection from", remote_addr, "rejected") cl.close() return diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index 2b273d834..f27c62fd1 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -108,11 +108,11 @@ void mp_hal_stdio_mode_orig(void) { #endif #if MICROPY_PY_OS_DUPTERM -static int call_dupterm_read(void) { +static int call_dupterm_read(size_t idx) { nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_obj_t read_m[3]; - mp_load_method(MP_STATE_PORT(term_obj), MP_QSTR_read, read_m); + mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_read, read_m); read_m[2] = MP_OBJ_NEW_SMALL_INT(1); mp_obj_t res = mp_call_method_n_kw(1, 0, read_m); if (res == mp_const_none) { @@ -122,18 +122,18 @@ static int call_dupterm_read(void) { mp_get_buffer_raise(res, &bufinfo, MP_BUFFER_READ); if (bufinfo.len == 0) { mp_printf(&mp_plat_print, "dupterm: EOF received, deactivating\n"); - MP_STATE_PORT(term_obj) = NULL; + MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL; return -1; } nlr_pop(); return *(byte*)bufinfo.buf; } else { // Temporarily disable dupterm to avoid infinite recursion - mp_obj_t save_term = MP_STATE_PORT(term_obj); - MP_STATE_PORT(term_obj) = NULL; + mp_obj_t save_term = MP_STATE_VM(dupterm_objs[idx]); + MP_STATE_VM(dupterm_objs[idx]) = NULL; mp_printf(&mp_plat_print, "dupterm: "); mp_obj_print_exception(&mp_plat_print, nlr.ret_val); - MP_STATE_PORT(term_obj) = save_term; + MP_STATE_VM(dupterm_objs[idx]) = save_term; } return -1; @@ -143,10 +143,11 @@ static int call_dupterm_read(void) { int mp_hal_stdin_rx_chr(void) { unsigned char c; #if MICROPY_PY_OS_DUPTERM - if (MP_STATE_PORT(term_obj) != MP_OBJ_NULL) { + // TODO only support dupterm one slot at the moment + if (MP_STATE_VM(dupterm_objs[0]) != MP_OBJ_NULL) { int c; do { - c = call_dupterm_read(); + c = call_dupterm_read(0); } while (c == -2); if (c == -1) { goto main_term; diff --git a/py/mpstate.h b/py/mpstate.h index eca14a9e4..6a39ebdea 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -168,7 +168,7 @@ typedef struct _mp_state_vm_t { // root pointers for extmod #if MICROPY_PY_OS_DUPTERM - mp_obj_t term_obj; + mp_obj_t dupterm_objs[MICROPY_PY_OS_DUPTERM]; mp_obj_t dupterm_arr_obj; #endif diff --git a/py/runtime.c b/py/runtime.c index 069548deb..17e5d235c 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -102,6 +102,13 @@ void mp_init(void) { MP_STATE_VM(mp_module_builtins_override_dict) = NULL; #endif + #if MICROPY_PY_OS_DUPTERM + for (size_t i = 0; i < MICROPY_PY_OS_DUPTERM; ++i) { + MP_STATE_VM(dupterm_objs[i]) = MP_OBJ_NULL; + } + MP_STATE_VM(dupterm_arr_obj) = MP_OBJ_NULL; + #endif + #if MICROPY_FSUSERMOUNT // zero out the pointers to the user-mounted devices memset(MP_STATE_VM(fs_user_mount), 0, sizeof(MP_STATE_VM(fs_user_mount))); -- cgit v1.2.3 From 65ba481cb0e067a97f8a999e2522b0656ca3b7fe Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Oct 2017 13:16:00 +1100 Subject: stm32/modnwwiznet5k: Implement WIZNET5K.isconnected() method. --- docs/library/network.rst | 5 +++++ ports/stm32/modnwwiznet5k.c | 7 +++++++ 2 files changed, 12 insertions(+) (limited to 'ports') diff --git a/docs/library/network.rst b/docs/library/network.rst index 258b2a20c..acb578a7c 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -268,6 +268,11 @@ parameter should be `id`. Methods ------- + .. method:: wiznet5k.isconnected() + + Returns ``True`` if the physical Ethernet link is connected and up. + Returns ``False`` otherwise. + .. method:: wiznet5k.ifconfig([(ip, subnet, gateway, dns)]) Get/set IP address, subnet mask, gateway and DNS. diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index 78249816d..a9e5f5aa9 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -414,6 +414,12 @@ STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_regs_obj, wiznet5k_regs); +STATIC mp_obj_t wiznet5k_isconnected(mp_obj_t self_in) { + (void)self_in; + return mp_obj_new_bool(wizphy_getphylink() == PHY_LINK_ON); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_isconnected_obj, wiznet5k_isconnected); + /// \method ifconfig([(ip, subnet, gateway, dns)]) /// Get/set IP address, subnet mask, gateway and DNS. STATIC mp_obj_t wiznet5k_ifconfig(size_t n_args, const mp_obj_t *args) { @@ -445,6 +451,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wiznet5k_ifconfig_obj, 1, 2, wiznet5k STATIC const mp_rom_map_elem_t wiznet5k_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_regs), MP_ROM_PTR(&wiznet5k_regs_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&wiznet5k_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&wiznet5k_isconnected_obj) }, }; STATIC MP_DEFINE_CONST_DICT(wiznet5k_locals_dict, wiznet5k_locals_dict_table); -- cgit v1.2.3 From 5d7b0b237b502b7511624bad5fd8fc3092d91972 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Oct 2017 13:17:15 +1100 Subject: stm32/modusocket: Make getaddrinfo() work when passed an IP address. --- ports/stm32/modusocket.c | 56 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 18 deletions(-) (limited to 'ports') diff --git a/ports/stm32/modusocket.c b/ports/stm32/modusocket.c index c5ab3b6d7..4d14c355f 100644 --- a/ports/stm32/modusocket.c +++ b/ports/stm32/modusocket.c @@ -390,29 +390,49 @@ STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { size_t hlen; const char *host = mp_obj_str_get_data(host_in, &hlen); mp_int_t port = mp_obj_get_int(port_in); + uint8_t out_ip[MOD_NETWORK_IPADDR_BUF_SIZE]; + bool have_ip = false; + + if (hlen > 0) { + // check if host is already in IP form + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + netutils_parse_ipv4_addr(host_in, out_ip, NETUTILS_BIG); + have_ip = true; + nlr_pop(); + } else { + // swallow exception: host was not in IP form so need to do DNS lookup + } + } - // find a NIC that can do a name lookup - for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { - mp_obj_t nic = MP_STATE_PORT(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->gethostbyname != NULL) { - uint8_t out_ip[MOD_NETWORK_IPADDR_BUF_SIZE]; - int ret = nic_type->gethostbyname(nic, host, hlen, out_ip); - if (ret != 0) { - // TODO CPython raises: socket.gaierror: [Errno -2] Name or service not known - mp_raise_OSError(ret); + if (!have_ip) { + // find a NIC that can do a name lookup + for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { + mp_obj_t nic = MP_STATE_PORT(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->gethostbyname != NULL) { + int ret = nic_type->gethostbyname(nic, host, hlen, out_ip); + if (ret != 0) { + // TODO CPython raises: socket.gaierror: [Errno -2] Name or service not known + mp_raise_OSError(ret); + } + have_ip = true; + break; } - 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(out_ip, port, NETUTILS_BIG); - return mp_obj_new_list(1, (mp_obj_t*)&tuple); } } - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "no available NIC")); + if (!have_ip) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "no available NIC")); + } + + 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(out_ip, port, NETUTILS_BIG); + return mp_obj_new_list(1, (mp_obj_t*)&tuple); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_usocket_getaddrinfo_obj, mod_usocket_getaddrinfo); -- cgit v1.2.3 From 0a30ad96c886aacf897b3e87cbdb24fb46c5c43d Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Oct 2017 13:19:11 +1100 Subject: stm32/modusocket: Return OSError(-2) if getaddrinfo fails. This matches the behaviour of getaddrinfo in extmod/modlwip.c. --- ports/stm32/modnwcc3k.c | 2 +- ports/stm32/modnwwiznet5k.c | 2 +- ports/stm32/modusocket.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'ports') diff --git a/ports/stm32/modnwcc3k.c b/ports/stm32/modnwcc3k.c index 551206da8..8cc0a613d 100644 --- a/ports/stm32/modnwcc3k.c +++ b/ports/stm32/modnwcc3k.c @@ -125,7 +125,7 @@ STATIC int cc3k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uin if (ip == 0) { // unknown host - return MP_ENOENT; + return -2; } out_ip[0] = ip >> 24; diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index a9e5f5aa9..9e9fb9aea 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -92,7 +92,7 @@ STATIC int wiznet5k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, return 0; } else { // failure - return MP_ENOENT; + return -2; } } diff --git a/ports/stm32/modusocket.c b/ports/stm32/modusocket.c index 4d14c355f..71a237b0d 100644 --- a/ports/stm32/modusocket.c +++ b/ports/stm32/modusocket.c @@ -413,7 +413,6 @@ STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { if (nic_type->gethostbyname != NULL) { int ret = nic_type->gethostbyname(nic, host, hlen, out_ip); if (ret != 0) { - // TODO CPython raises: socket.gaierror: [Errno -2] Name or service not known mp_raise_OSError(ret); } have_ip = true; -- cgit v1.2.3 From 5c437963d742627ba172d48d601c118c4577e7b7 Mon Sep 17 00:00:00 2001 From: Li Weiwei Date: Tue, 10 Oct 2017 14:22:22 +0800 Subject: stm32/mpconfigport.h: Add MICROPY_THREAD_YIELD() macro. --- ports/stm32/mpconfigport.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'ports') diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 06606e62a..2d59a1df8 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -326,6 +326,8 @@ static inline mp_uint_t disable_irq(void) { __WFI(); \ } \ } while (0); + +#define MICROPY_THREAD_YIELD() pyb_thread_yield() #else #define MICROPY_EVENT_POLL_HOOK \ do { \ @@ -333,6 +335,8 @@ static inline mp_uint_t disable_irq(void) { mp_handle_pending(); \ __WFI(); \ } while (0); + +#define MICROPY_THREAD_YIELD() #endif // We need an implementation of the log2 function which is not a macro -- cgit v1.2.3 From e36821a7662e9c65ee2bfb8c23925a10d2167faf Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Oct 2017 15:36:57 +1100 Subject: stm32/modnwwiznet5k: Add support for W5500 Ethernet chip. Which Wiznet chip to use is a compile-time option: MICROPY_PY_WIZNET5K should be set to either 5200 or 5500 to support either one of these Ethernet chips. The driver is called network.WIZNET5K in both cases. Note that this commit introduces a breaking-change at the build level because previously the valid values for MICROPY_PY_WIZNET5K were 0 and 1 but now they are 0, 5200 and 5500. --- ports/stm32/Makefile | 6 +++--- ports/stm32/modnwwiznet5k.c | 14 ++++++++++++-- ports/stm32/mpconfigport.mk | 5 ++++- 3 files changed, 19 insertions(+), 6 deletions(-) (limited to 'ports') diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 913f0913e..68b007471 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -286,13 +286,13 @@ SRC_USBDEV = $(addprefix $(USBDEV_DIR)/,\ class/src/usbd_msc_data.c \ ) -ifeq ($(MICROPY_PY_WIZNET5K),1) +ifneq ($(MICROPY_PY_WIZNET5K),0) WIZNET5K_DIR=drivers/wiznet5k INC += -I$(TOP)/$(WIZNET5K_DIR) -CFLAGS_MOD += -DMICROPY_PY_WIZNET5K=1 +CFLAGS_MOD += -DMICROPY_PY_WIZNET5K=$(MICROPY_PY_WIZNET5K) -D_WIZCHIP_=$(MICROPY_PY_WIZNET5K) SRC_MOD += modnwwiznet5k.c SRC_MOD += $(addprefix $(WIZNET5K_DIR)/,\ - ethernet/w5200/w5200.c \ + ethernet/w$(MICROPY_PY_WIZNET5K)/w$(MICROPY_PY_WIZNET5K).c \ ethernet/wizchip_conf.c \ ethernet/socket.c \ internet/dns/dns.c \ diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index 9e9fb9aea..d59125054 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -398,7 +398,12 @@ STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { if (i % 16 == 0) { printf("\n %04x:", i); } - printf(" %02x", WIZCHIP_READ(i)); + #if MICROPY_PY_WIZNET5K == 5200 + uint32_t reg = i; + #else + uint32_t reg = _W5500_IO_BASE_ | i << 8; + #endif + printf(" %02x", WIZCHIP_READ(reg)); } for (int sn = 0; sn < 4; ++sn) { printf("\nWiz SREG[%d]:", sn); @@ -406,7 +411,12 @@ STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { if (i % 16 == 0) { printf("\n %04x:", i); } - printf(" %02x", WIZCHIP_READ(WIZCHIP_SREG_ADDR(sn, i))); + #if MICROPY_PY_WIZNET5K == 5200 + uint32_t reg = WIZCHIP_SREG_ADDR(sn, i); + #else + uint32_t reg = _W5500_IO_BASE_ | i << 8 | WIZCHIP_SREG_BLOCK(sn) << 3; + #endif + printf(" %02x", WIZCHIP_READ(reg)); } } printf("\n"); diff --git a/ports/stm32/mpconfigport.mk b/ports/stm32/mpconfigport.mk index 64145383e..e708de6c1 100644 --- a/ports/stm32/mpconfigport.mk +++ b/ports/stm32/mpconfigport.mk @@ -1,6 +1,9 @@ # Enable/disable extra modules -# wiznet5k module for ethernet support +# wiznet5k module for ethernet support; valid values are: +# 0 : no Wiznet support +# 5200 : support for W5200 module +# 5500 : support for W5500 module MICROPY_PY_WIZNET5K ?= 0 # cc3k module for wifi support -- cgit v1.2.3 From 285ac585322f0ffced76d25a6f19cd1fa30b3514 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 17 Oct 2017 16:31:12 +1100 Subject: stm32/modnwwiznet5k: Increase SPI bus speed to 42MHz. The W5200 and W5500 can support up to 80MHz so 42MHz (the maximum the pyboard can do in its standard configuration) should be safe. Tested to give around 1050000 kbytes/sec TCP download speed on a W5500, which is about 10% more than with the previous SPI speed of 21MHz. --- ports/stm32/modnwwiznet5k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ports') diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index d59125054..717d88b39 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -346,7 +346,7 @@ STATIC mp_obj_t wiznet5k_make_new(const mp_obj_type_t *type, size_t n_args, size wiznet5k_obj.spi->Init.CLKPolarity = SPI_POLARITY_LOW; // clock is low when idle wiznet5k_obj.spi->Init.CLKPhase = SPI_PHASE_1EDGE; // data latched on first edge, which is rising edge for low-idle wiznet5k_obj.spi->Init.NSS = SPI_NSS_SOFT; - wiznet5k_obj.spi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; // clock freq = f_PCLK / this_prescale_value; Wiz820i can do up to 80MHz + wiznet5k_obj.spi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; // clock freq = f_PCLK / this_prescale_value; Wiz820i can do up to 80MHz wiznet5k_obj.spi->Init.FirstBit = SPI_FIRSTBIT_MSB; wiznet5k_obj.spi->Init.TIMode = SPI_TIMODE_DISABLED; wiznet5k_obj.spi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED; -- cgit v1.2.3 From 8fa3d2996c9d69b1a6ba20f6beb354f4ce0001c8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 17 Oct 2017 16:34:10 +1100 Subject: stm32/modnwwiznet5k: Implement stream ioctl for the Wiznet driver. Now supports polling for read and write ability. --- ports/stm32/modnwwiznet5k.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'ports') diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index 717d88b39..763137c70 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -30,6 +30,7 @@ #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" @@ -305,9 +306,19 @@ STATIC int wiznet5k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_ } STATIC int wiznet5k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) { - // TODO - *_errno = MP_EINVAL; - return -1; + if (request == MP_STREAM_POLL) { + int ret = 0; + if (arg & MP_STREAM_POLL_RD && getSn_RX_RSR(socket->u_param.fileno) != 0) { + ret |= MP_STREAM_POLL_RD; + } + if (arg & MP_STREAM_POLL_WR && getSn_TX_FSR(socket->u_param.fileno) != 0) { + ret |= MP_STREAM_POLL_WR; + } + return ret; + } else { + *_errno = MP_EINVAL; + return MP_STREAM_ERROR; + } } #if 0 -- cgit v1.2.3 From 0eb333e3cf2319cfb1bf558b8f1c6e3e513fb9d8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 19 Oct 2017 14:15:32 +1100 Subject: stm32/mphalport: Improve efficiency of mp_hal_stdout_tx_strn_cooked. Also simplifies the code by removing the specialised (and inefficient) cooked functions from UART and USB_VCP. --- ports/stm32/mphalport.c | 20 +++++++++++++++----- ports/stm32/uart.c | 15 --------------- ports/stm32/uart.h | 1 - ports/stm32/usb.c | 14 -------------- ports/stm32/usb.h | 1 - 5 files changed, 15 insertions(+), 36 deletions(-) (limited to 'ports') diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index ab3dc227a..3bea6e2d9 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -58,13 +58,23 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { } } +// Efficiently convert "\n" to "\r\n" void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { - // send stdout to UART and USB CDC VCP - if (MP_STATE_PORT(pyb_stdio_uart) != NULL) { - uart_tx_strn_cooked(MP_STATE_PORT(pyb_stdio_uart), str, len); + const char *last = str; + while (len--) { + if (*str == '\n') { + if (str > last) { + mp_hal_stdout_tx_strn(last, str - last); + } + mp_hal_stdout_tx_strn("\r\n", 2); + ++str; + last = str; + } else { + ++str; + } } - if (usb_vcp_is_enabled()) { - usb_vcp_send_strn_cooked(str, len); + if (str > last) { + mp_hal_stdout_tx_strn(last, str - last); } } diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 2b2f782f9..0b46d4f04 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -452,26 +452,11 @@ STATIC size_t uart_tx_data(pyb_uart_obj_t *self, const void *src_in, size_t num_ return num_tx; } -STATIC void uart_tx_char(pyb_uart_obj_t *uart_obj, int c) { - uint16_t ch = c; - int errcode; - uart_tx_data(uart_obj, &ch, 1, &errcode); -} - void uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len) { int errcode; uart_tx_data(uart_obj, str, len, &errcode); } -void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len) { - for (const char *top = str + len; str < top; str++) { - if (*str == '\n') { - uart_tx_char(uart_obj, '\r'); - } - uart_tx_char(uart_obj, *str); - } -} - // this IRQ handler is set up to handle RXNE interrupts only void uart_irq_handler(mp_uint_t uart_id) { // get the uart object diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h index e96b25b5f..d176520a1 100644 --- a/ports/stm32/uart.h +++ b/ports/stm32/uart.h @@ -48,6 +48,5 @@ void uart_irq_handler(mp_uint_t uart_id); mp_uint_t uart_rx_any(pyb_uart_obj_t *uart_obj); int uart_rx_char(pyb_uart_obj_t *uart_obj); void uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len); -void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len); #endif // MICROPY_INCLUDED_STMHAL_UART_H diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index 8f89107ba..69f381d9b 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -177,20 +177,6 @@ void usb_vcp_send_strn(const char *str, int len) { #endif } -void usb_vcp_send_strn_cooked(const char *str, int len) { -#ifdef USE_DEVICE_MODE - if (usb_device.enabled) { - for (const char *top = str + len; str < top; str++) { - if (*str == '\n') { - usbd_cdc_tx_always(&usb_device.usbd_cdc_itf, (const uint8_t*)"\r\n", 2); - } else { - usbd_cdc_tx_always(&usb_device.usbd_cdc_itf, (const uint8_t*)str, 1); - } - } - } -#endif -} - /******************************************************************************/ // MicroPython bindings for USB diff --git a/ports/stm32/usb.h b/ports/stm32/usb.h index f60ea8033..41c461fb2 100644 --- a/ports/stm32/usb.h +++ b/ports/stm32/usb.h @@ -63,7 +63,6 @@ void pyb_usb_dev_deinit(void); bool usb_vcp_is_enabled(void); int usb_vcp_recv_byte(uint8_t *c); // if a byte is available, return 1 and put the byte in *c, else return 0 void usb_vcp_send_strn(const char* str, int len); -void usb_vcp_send_strn_cooked(const char *str, int len); void pyb_usb_host_init(void); void pyb_usb_host_process(void); -- cgit v1.2.3 From d6bf3658f44e22f06e0d293b8fe0542b57538b4f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 19 Oct 2017 14:16:42 +1100 Subject: stm32: Make uos.dupterm() conform to specs by using extmod version. The legacy function pyb.repl_uart() is still provided and retains its original behaviour (it only accepts a UART object). uos.dupterm() will now accept any object with write/readinto methods. At the moment there is just 1 dupterm slot. --- ports/stm32/modpyb.c | 26 ++++++++++++++++++++++++-- ports/stm32/moduos.c | 25 ++----------------------- ports/stm32/mpconfigport.h | 1 + ports/stm32/mphalport.c | 6 ++++++ 4 files changed, 33 insertions(+), 25 deletions(-) (limited to 'ports') diff --git a/ports/stm32/modpyb.c b/ports/stm32/modpyb.c index 176fc8466..81cbdcc19 100644 --- a/ports/stm32/modpyb.c +++ b/ports/stm32/modpyb.c @@ -27,7 +27,7 @@ #include #include -#include "py/obj.h" +#include "py/runtime.h" #include "py/gc.h" #include "py/builtin.h" #include "py/mphal.h" @@ -104,6 +104,28 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_elapsed_micros_obj, pyb_elapsed_micros); MP_DECLARE_CONST_FUN_OBJ_KW(pyb_main_obj); // defined in main.c +// Get or set the UART object that the REPL is repeated on. +// This is a legacy function, use of uos.dupterm is preferred. +STATIC mp_obj_t pyb_repl_uart(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + if (MP_STATE_PORT(pyb_stdio_uart) == NULL) { + return mp_const_none; + } else { + return MP_STATE_PORT(pyb_stdio_uart); + } + } else { + if (args[0] == mp_const_none) { + MP_STATE_PORT(pyb_stdio_uart) = NULL; + } else if (mp_obj_get_type(args[0]) == &pyb_uart_type) { + MP_STATE_PORT(pyb_stdio_uart) = args[0]; + } else { + mp_raise_ValueError("need a UART object"); + } + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_repl_uart_obj, 0, 1, pyb_repl_uart); + STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_pyb) }, @@ -126,7 +148,7 @@ STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_sleep_obj) }, { MP_ROM_QSTR(MP_QSTR_standby), MP_ROM_PTR(&machine_deepsleep_obj) }, { MP_ROM_QSTR(MP_QSTR_main), MP_ROM_PTR(&pyb_main_obj) }, - { MP_ROM_QSTR(MP_QSTR_repl_uart), MP_ROM_PTR(&mod_os_dupterm_obj) }, + { MP_ROM_QSTR(MP_QSTR_repl_uart), MP_ROM_PTR(&pyb_repl_uart_obj) }, { MP_ROM_QSTR(MP_QSTR_usb_mode), MP_ROM_PTR(&pyb_usb_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_hid_mouse), MP_ROM_PTR(&pyb_usb_hid_mouse_obj) }, diff --git a/ports/stm32/moduos.c b/ports/stm32/moduos.c index f661b3b5e..f6e1483d3 100644 --- a/ports/stm32/moduos.c +++ b/ports/stm32/moduos.c @@ -33,6 +33,7 @@ #include "lib/timeutils/timeutils.h" #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" +#include "extmod/misc.h" #include "extmod/vfs.h" #include "extmod/vfs_fat.h" #include "genhdr/mpversion.h" @@ -105,28 +106,6 @@ STATIC mp_obj_t os_urandom(mp_obj_t num) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); #endif -// Get or set the UART object that the REPL is repeated on. -// TODO should accept any object with read/write methods. -STATIC mp_obj_t os_dupterm(size_t n_args, const mp_obj_t *args) { - if (n_args == 0) { - if (MP_STATE_PORT(pyb_stdio_uart) == NULL) { - return mp_const_none; - } else { - return MP_STATE_PORT(pyb_stdio_uart); - } - } else { - if (args[0] == mp_const_none) { - MP_STATE_PORT(pyb_stdio_uart) = NULL; - } else if (mp_obj_get_type(args[0]) == &pyb_uart_type) { - MP_STATE_PORT(pyb_stdio_uart) = args[0]; - } else { - mp_raise_ValueError("need a UART object"); - } - return mp_const_none; - } -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_dupterm_obj, 0, 1, os_dupterm); - STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, @@ -154,7 +133,7 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { #endif // these are MicroPython extensions - { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mod_os_dupterm_obj) }, + { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 2d59a1df8..51d442561 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -126,6 +126,7 @@ #define MICROPY_PY_USELECT (1) #define MICROPY_PY_UTIMEQ (1) #define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index 3bea6e2d9..e9c4f28f7 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -3,6 +3,7 @@ #include "py/runtime.h" #include "py/mperrno.h" #include "py/mphal.h" +#include "extmod/misc.h" #include "usb.h" #include "uart.h" @@ -38,6 +39,10 @@ int mp_hal_stdin_rx_chr(void) { } else if (MP_STATE_PORT(pyb_stdio_uart) != NULL && uart_rx_any(MP_STATE_PORT(pyb_stdio_uart))) { return uart_rx_char(MP_STATE_PORT(pyb_stdio_uart)); } + int dupterm_c = mp_uos_dupterm_rx_chr(); + if (dupterm_c >= 0) { + return dupterm_c; + } MICROPY_EVENT_POLL_HOOK } } @@ -56,6 +61,7 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { if (usb_vcp_is_enabled()) { usb_vcp_send_strn(str, len); } + mp_uos_dupterm_tx_strn(str, len); } // Efficiently convert "\n" to "\r\n" -- cgit v1.2.3 From cfff12612f92d6ae460e9748394a0cedf3b3d31d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 23 Oct 2017 12:09:37 +0300 Subject: unix: Rename modsocket.c to modusocket.c. Unix naming is historical, before current conventions were established. All other ports however have it as "modusocket.c", so rename for consistency and to avoid confusion. --- ports/unix/Makefile | 2 +- ports/unix/modsocket.c | 582 ------------------------------------------------ ports/unix/modusocket.c | 582 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 583 insertions(+), 583 deletions(-) delete mode 100644 ports/unix/modsocket.c create mode 100644 ports/unix/modusocket.c (limited to 'ports') diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 1a1ea01f7..b96391f69 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -97,7 +97,7 @@ SRC_MOD += modtermios.c endif ifeq ($(MICROPY_PY_SOCKET),1) CFLAGS_MOD += -DMICROPY_PY_SOCKET=1 -SRC_MOD += modsocket.c +SRC_MOD += modusocket.c endif ifeq ($(MICROPY_PY_THREAD),1) CFLAGS_MOD += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=0 diff --git a/ports/unix/modsocket.c b/ports/unix/modsocket.c deleted file mode 100644 index 7e82554c7..000000000 --- a/ports/unix/modsocket.c +++ /dev/null @@ -1,582 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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). - */ - -#define MICROPY_SOCKET_EXTRA (0) - -// 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; -} 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; - 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) { - *errcode = errno; - 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) { - *errcode = errno; - return MP_STREAM_ERROR; - } - return r; -} - -STATIC mp_obj_t socket_close(mp_obj_t self_in) { - mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in); - // 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 mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); - -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); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); - -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_close), MP_ROM_PTR(&socket_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, -}; - -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, -}; - -#if MICROPY_SOCKET_EXTRA -STATIC mp_obj_t mod_socket_htons(mp_obj_t arg) { - return MP_OBJ_NEW_SMALL_INT(htons(MP_OBJ_SMALL_INT_VALUE(arg))); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_htons_obj, mod_socket_htons); - - -STATIC mp_obj_t mod_socket_gethostbyname(mp_obj_t arg) { - const char *s = mp_obj_str_get_str(arg); - struct hostent *h = gethostbyname(s); - if (h == NULL) { - // CPython: socket.herror - mp_raise_OSError(h_errno); - } - assert(h->h_length == 4); - return mp_obj_new_int(*(int*)*h->h_addr_list); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_gethostbyname_obj, mod_socket_gethostbyname); -#endif // MICROPY_SOCKET_EXTRA - -#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) }, -#if MICROPY_SOCKET_EXTRA - { MP_ROM_QSTR(MP_QSTR_htons), MP_ROM_PTR(&mod_socket_htons_obj) }, - { MP_ROM_QSTR(MP_QSTR_gethostbyname), MP_ROM_PTR(&mod_socket_gethostbyname_obj) }, -#endif - -#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/ports/unix/modusocket.c b/ports/unix/modusocket.c new file mode 100644 index 000000000..7e82554c7 --- /dev/null +++ b/ports/unix/modusocket.c @@ -0,0 +1,582 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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). + */ + +#define MICROPY_SOCKET_EXTRA (0) + +// 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; +} 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; + 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) { + *errcode = errno; + 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) { + *errcode = errno; + return MP_STREAM_ERROR; + } + return r; +} + +STATIC mp_obj_t socket_close(mp_obj_t self_in) { + mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in); + // 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 mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); + +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); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); + +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_close), MP_ROM_PTR(&socket_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, +}; + +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, +}; + +#if MICROPY_SOCKET_EXTRA +STATIC mp_obj_t mod_socket_htons(mp_obj_t arg) { + return MP_OBJ_NEW_SMALL_INT(htons(MP_OBJ_SMALL_INT_VALUE(arg))); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_htons_obj, mod_socket_htons); + + +STATIC mp_obj_t mod_socket_gethostbyname(mp_obj_t arg) { + const char *s = mp_obj_str_get_str(arg); + struct hostent *h = gethostbyname(s); + if (h == NULL) { + // CPython: socket.herror + mp_raise_OSError(h_errno); + } + assert(h->h_length == 4); + return mp_obj_new_int(*(int*)*h->h_addr_list); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_gethostbyname_obj, mod_socket_gethostbyname); +#endif // MICROPY_SOCKET_EXTRA + +#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) }, +#if MICROPY_SOCKET_EXTRA + { MP_ROM_QSTR(MP_QSTR_htons), MP_ROM_PTR(&mod_socket_htons_obj) }, + { MP_ROM_QSTR(MP_QSTR_gethostbyname), MP_ROM_PTR(&mod_socket_gethostbyname_obj) }, +#endif + +#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, +}; -- cgit v1.2.3 From f4059dcc0c5251256a53fcb49f56fdda6e879341 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 24 Oct 2017 22:39:36 +1100 Subject: all: Use NULL instead of "" when calling mp_raise exception helpers. This is the established way of doing it and reduces code size by a little bit. --- extmod/modlwip.c | 2 +- extmod/modussl_axtls.c | 2 +- extmod/modutimeq.c | 2 +- extmod/modwebrepl.c | 2 +- ports/esp8266/machine_hspi.c | 2 +- ports/esp8266/machine_wdt.c | 2 +- ports/unix/modjni.c | 2 +- py/objarray.c | 2 +- py/objlist.c | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) (limited to 'ports') diff --git a/extmod/modlwip.c b/extmod/modlwip.c index f7e776af9..bbb01b5d7 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -1032,7 +1032,7 @@ STATIC mp_obj_t lwip_socket_sendall(mp_obj_t self_in, mp_obj_t buf_in) { break; } case MOD_NETWORK_SOCK_DGRAM: - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); break; } diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 88a89a23d..719a65cd1 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -152,7 +152,7 @@ STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) { // Currently supports only blocking mode (void)self_in; if (!mp_obj_is_true(flag_in)) { - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); } return mp_const_none; } diff --git a/extmod/modutimeq.c b/extmod/modutimeq.c index 94cbd20d2..620e7484b 100644 --- a/extmod/modutimeq.c +++ b/extmod/modutimeq.c @@ -146,7 +146,7 @@ STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) { } mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref); if (!MP_OBJ_IS_TYPE(list_ref, &mp_type_list) || ret->len < 3) { - mp_raise_TypeError(""); + mp_raise_TypeError(NULL); } struct qentry *item = &heap->items[0]; diff --git a/extmod/modwebrepl.c b/extmod/modwebrepl.c index 4ff282aac..3aba5c0f1 100644 --- a/extmod/modwebrepl.c +++ b/extmod/modwebrepl.c @@ -308,7 +308,7 @@ STATIC mp_obj_t webrepl_set_password(mp_obj_t passwd_in) { size_t len; const char *passwd = mp_obj_str_get_data(passwd_in, &len); if (len > sizeof(webrepl_passwd) - 1) { - mp_raise_ValueError(""); + mp_raise_ValueError(NULL); } strcpy(webrepl_passwd, passwd); return mp_const_none; diff --git a/ports/esp8266/machine_hspi.c b/ports/esp8266/machine_hspi.c index eaabbab7e..9fd0f4868 100644 --- a/ports/esp8266/machine_hspi.c +++ b/ports/esp8266/machine_hspi.c @@ -149,7 +149,7 @@ mp_obj_t machine_hspi_make_new(const mp_obj_type_t *type, size_t n_args, size_t // args[0] holds the id of the peripheral if (args[0] != MP_OBJ_NEW_SMALL_INT(1)) { // FlashROM is on SPI0, so far we don't support its usage - mp_raise_ValueError(""); + mp_raise_ValueError(NULL); } machine_hspi_obj_t *self = m_new_obj(machine_hspi_obj_t); diff --git a/ports/esp8266/machine_wdt.c b/ports/esp8266/machine_wdt.c index 04b42782e..4432297fa 100644 --- a/ports/esp8266/machine_wdt.c +++ b/ports/esp8266/machine_wdt.c @@ -51,7 +51,7 @@ STATIC mp_obj_t machine_wdt_make_new(const mp_obj_type_t *type_in, size_t n_args case 0: return &wdt_default; default: - mp_raise_ValueError(""); + mp_raise_ValueError(NULL); } } diff --git a/ports/unix/modjni.c b/ports/unix/modjni.c index 15b6d9cd7..f29c095cf 100644 --- a/ports/unix/modjni.c +++ b/ports/unix/modjni.c @@ -266,7 +266,7 @@ STATIC mp_obj_t jobject_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) return mp_const_none; } } - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); } if (!JJ(IsInstanceOf, self->obj, List_class)) { diff --git a/py/objarray.c b/py/objarray.c index 8a3e7faad..7003ec9e7 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -286,7 +286,7 @@ STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs // Otherwise, can only look for a scalar numeric value in an array if (MP_OBJ_IS_INT(rhs_in) || mp_obj_is_float(rhs_in)) { - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); } return mp_const_false; diff --git a/py/objlist.c b/py/objlist.c index bc22d9fc3..1a18f937d 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -158,7 +158,7 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); } mp_int_t len_adj = slice.start - slice.stop; @@ -198,7 +198,7 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_obj_get_array(value, &value_len, &value_items); mp_bound_slice_t slice_out; if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice_out)) { - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); } mp_int_t len_adj = value_len - (slice_out.stop - slice_out.start); //printf("Len adj: %d\n", len_adj); -- cgit v1.2.3 From 9a7e3469b28b02b05b85789acc6c0a78b9804659 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 24 Oct 2017 23:11:42 +0300 Subject: unix/modusocket: Remove #if MICROPY_SOCKET_EXTRA code blocks. These defined couple of functions added during initial experimentation, which aren't part of MicroPython API and no longer used or needed. --- ports/unix/modusocket.c | 26 -------------------------- 1 file changed, 26 deletions(-) (limited to 'ports') diff --git a/ports/unix/modusocket.c b/ports/unix/modusocket.c index 7e82554c7..cfb6a9f5e 100644 --- a/ports/unix/modusocket.c +++ b/ports/unix/modusocket.c @@ -60,8 +60,6 @@ should be add to separate modules (C or Python level). */ -#define MICROPY_SOCKET_EXTRA (0) - // 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 { @@ -382,26 +380,6 @@ const mp_obj_type_t mp_type_socket = { .locals_dict = (mp_obj_dict_t*)&usocket_locals_dict, }; -#if MICROPY_SOCKET_EXTRA -STATIC mp_obj_t mod_socket_htons(mp_obj_t arg) { - return MP_OBJ_NEW_SMALL_INT(htons(MP_OBJ_SMALL_INT_VALUE(arg))); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_htons_obj, mod_socket_htons); - - -STATIC mp_obj_t mod_socket_gethostbyname(mp_obj_t arg) { - const char *s = mp_obj_str_get_str(arg); - struct hostent *h = gethostbyname(s); - if (h == NULL) { - // CPython: socket.herror - mp_raise_OSError(h_errno); - } - assert(h->h_length == 4); - return mp_obj_new_int(*(int*)*h->h_addr_list); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_gethostbyname_obj, mod_socket_gethostbyname); -#endif // MICROPY_SOCKET_EXTRA - #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); @@ -549,10 +527,6 @@ STATIC const mp_rom_map_elem_t mp_module_socket_globals_table[] = { { 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) }, -#if MICROPY_SOCKET_EXTRA - { MP_ROM_QSTR(MP_QSTR_htons), MP_ROM_PTR(&mod_socket_htons_obj) }, - { MP_ROM_QSTR(MP_QSTR_gethostbyname), MP_ROM_PTR(&mod_socket_gethostbyname_obj) }, -#endif #define C(name) { MP_ROM_QSTR(MP_QSTR_ ## name), MP_ROM_INT(name) } C(AF_UNIX), -- cgit v1.2.3 From 24c8eda744e6c13866d70caec1b5c316caee64ac Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 28 Oct 2017 13:05:57 +0300 Subject: unix: Enable MICROPY_PY_REVERSE_SPECIAL_METHODS. With inplace methods now disabled by default, it makes sense to enable reverse methods, as they allow for more useful features, e.g. allow for datetime module to implement both 2 * HOUR and HOUR * 2 (where HOUR is e.g. timedelta object). --- ports/unix/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) (limited to 'ports') diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 50b979279..db382e0a7 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -85,6 +85,7 @@ #define MICROPY_PY_BUILTINS_POW3 (1) #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) #define MICROPY_PY_ALL_SPECIAL_METHODS (1) +#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) #define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) #define MICROPY_PY_SYS_EXIT (1) -- cgit v1.2.3 From 5ae9586541f93065d8dd46862f1f226274c4c5c4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 1 Nov 2017 11:00:30 +1100 Subject: teensy: Get port compiling without any warnings. --- ports/teensy/teensy_hal.c | 1 + ports/teensy/uart.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'ports') diff --git a/ports/teensy/teensy_hal.c b/ports/teensy/teensy_hal.c index 84d68cff8..7ce82f1d2 100644 --- a/ports/teensy/teensy_hal.c +++ b/ports/teensy/teensy_hal.c @@ -49,6 +49,7 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { // send stdout to UART and USB CDC VCP if (MP_STATE_PORT(pyb_stdio_uart) != NULL) { + void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len); uart_tx_strn_cooked(MP_STATE_PORT(pyb_stdio_uart), str, len); } if (usb_vcp_is_enabled()) { diff --git a/ports/teensy/uart.c b/ports/teensy/uart.c index 3d5111217..a8cfd63ea 100644 --- a/ports/teensy/uart.c +++ b/ports/teensy/uart.c @@ -426,11 +426,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_send_obj, 1, pyb_uart_send); /// /// Return value: if `recv` is an integer then a new buffer of the bytes received, /// otherwise the same buffer that was passed in to `recv`. +#if 0 STATIC const mp_arg_t pyb_uart_recv_args[] = { { MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, }; #define PYB_UART_RECV_NUM_ARGS MP_ARRAY_SIZE(pyb_uart_recv_args) +#endif STATIC mp_obj_t pyb_uart_recv(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { // TODO assumes transmission size is 8-bits wide -- cgit v1.2.3