diff options
Diffstat (limited to 'ports/esp32s2')
| -rw-r--r-- | ports/esp32s2/Makefile | 4 | ||||
| -rw-r--r-- | ports/esp32s2/boards/espressif_kaluga_1/mpconfigboard.mk | 6 | ||||
| -rw-r--r-- | ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/board.c | 106 | ||||
| -rw-r--r-- | ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/pins.c | 3 | ||||
| -rw-r--r-- | ports/esp32s2/common-hal/alarm/__init__.c | 8 | ||||
| -rw-r--r-- | ports/esp32s2/common-hal/audiobusio/I2SOut.c | 3 | ||||
| -rw-r--r-- | ports/esp32s2/common-hal/busio/I2C.c | 2 | ||||
| -rw-r--r-- | ports/esp32s2/common-hal/socketpool/Socket.c | 372 | ||||
| -rw-r--r-- | ports/esp32s2/common-hal/socketpool/Socket.h | 2 | ||||
| -rw-r--r-- | ports/esp32s2/common-hal/socketpool/SocketPool.c | 21 | ||||
| -rw-r--r-- | ports/esp32s2/common-hal/ssl/SSLContext.c | 25 | ||||
| -rw-r--r-- | ports/esp32s2/common-hal/ssl/SSLSocket.c | 176 | ||||
| -rw-r--r-- | ports/esp32s2/common-hal/ssl/SSLSocket.h | 44 | ||||
| -rw-r--r-- | ports/esp32s2/common-hal/supervisor/Runtime.c | 6 | ||||
| -rw-r--r-- | ports/esp32s2/mpconfigport.mk | 18 |
15 files changed, 538 insertions, 258 deletions
diff --git a/ports/esp32s2/Makefile b/ports/esp32s2/Makefile index aac934782..b4ff0612b 100644 --- a/ports/esp32s2/Makefile +++ b/ports/esp32s2/Makefile @@ -164,7 +164,9 @@ LIBS += -lm endif # TinyUSB defines -CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32S2 -DCFG_TUSB_OS=OPT_OS_FREERTOS -DCFG_TUD_CDC_RX_BUFSIZE=1024 -DCFG_TUD_CDC_TX_BUFSIZE=1024 -DCFG_TUD_MSC_BUFSIZE=4096 -DCFG_TUD_MIDI_RX_BUFSIZE=128 -DCFG_TUD_MIDI_TX_BUFSIZE=128 +CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32S2 -DCFG_TUSB_OS=OPT_OS_FREERTOS -DCFG_TUD_CDC_RX_BUFSIZE=1024 -DCFG_TUD_CDC_TX_BUFSIZE=1024 +CFLAGS += -DCFG_TUD_MSC_BUFSIZE=4096 -DCFG_TUD_MIDI_RX_BUFSIZE=128 -DCFG_TUD_MIDI_TX_BUFSIZE=128 +CFLAGS += -DCFG_TUD_VENDOR_RX_BUFSIZE=128 -DCFG_TUD_VENDOR_TX_BUFSIZE=128 ###################################### diff --git a/ports/esp32s2/boards/espressif_kaluga_1/mpconfigboard.mk b/ports/esp32s2/boards/espressif_kaluga_1/mpconfigboard.mk index 2dce03881..52c63cb9c 100644 --- a/ports/esp32s2/boards/espressif_kaluga_1/mpconfigboard.mk +++ b/ports/esp32s2/boards/espressif_kaluga_1/mpconfigboard.mk @@ -14,4 +14,10 @@ CIRCUITPY_ESP_FLASH_MODE=dio CIRCUITPY_ESP_FLASH_FREQ=80m CIRCUITPY_ESP_FLASH_SIZE=4MB +# We only have enough endpoints available in hardware to +# enable ONE of these at a time. +CIRCUITPY_USB_MIDI = 1 +CIRCUITPY_USB_HID = 0 +CIRCUITPY_USB_VENDOR = 0 + CIRCUITPY_MODULE=wrover diff --git a/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/board.c b/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/board.c index aaef97c7d..4aa7b3b24 100644 --- a/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/board.c +++ b/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/board.c @@ -27,6 +27,109 @@ #include "supervisor/board.h" #include "mpconfigboard.h" #include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" + +#define DELAY 0x80 + +// display init sequence according to LilyGO example app +uint8_t display_init_sequence[] = { + // sw reset + 0x01, 0 | DELAY, 150, + // sleep out + 0x11, 0 | DELAY, 255, + // normal display mode on + 0x13, 0, + // display and color format settings + 0x36, 1, 0x08, + 0xB6, 2, 0x0A, 0x82, + 0x3A, 1 | DELAY, 0x55, 10, + // ST7789V frame rate setting + 0xB2, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33, + // voltages: VGH / VGL + 0xB7, 1, 0x35, + // ST7789V power setting + 0xBB, 1, 0x28, + 0xC0, 1, 0x0C, + 0xC2, 2, 0x01, 0xFF, + 0xC3, 1, 0x10, + 0xC4, 1, 0x20, + 0xC6, 1, 0x0F, + 0xD0, 2, 0xA4, 0xA1, + // ST7789V gamma setting + 0xE0, 14, 0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x32, 0x44, 0x42, 0x06, 0x0E, 0x12, 0x14, 0x17, + 0xE1, 14, 0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x31, 0x54, 0x47, 0x0E, 0x1C, 0x17, 0x1B, 0x1E, + 0x21, 0, + // display on + 0x29, 0 | DELAY, 255, + }; + +static void display_init(void) { + busio_spi_obj_t* spi = &displays[0].fourwire_bus.inline_bus; + + common_hal_busio_spi_construct( + spi, + &pin_GPIO36, // CLK + &pin_GPIO35, // MOSI + NULL // MISO not connected + ); + + common_hal_busio_spi_never_reset(spi); + + displayio_fourwire_obj_t* bus = &displays[0].fourwire_bus; + bus->base.type = &displayio_fourwire_type; + + common_hal_displayio_fourwire_construct( + bus, + spi, + &pin_GPIO37, // DC + &pin_GPIO34, // CS + &pin_GPIO38, // RST + 40000000, // baudrate + 0, // polarity + 0 // phase + ); + + displayio_display_obj_t* display = &displays[0].display; + display->base.type = &displayio_display_type; + + // workaround as board_init() is called before reset_port() in main.c + pwmout_reset(); + + common_hal_displayio_display_construct( + display, + bus, + 240, // width (after rotation) + 135, // height (after rotation) + 52, // column start + 40, // row start + 90, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + 0x37, // set vertical scroll command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO33, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness (ignored) + false, // auto_brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false // SH1107_addressing + ); + + common_hal_never_reset_pin(&pin_GPIO33); // backlight pin +} void board_init(void) { // USB @@ -38,6 +141,9 @@ void board_init(void) { common_hal_never_reset_pin(&pin_GPIO43); common_hal_never_reset_pin(&pin_GPIO44); #endif /* DEBUG */ + + // Display + display_init(); } bool board_requests_safe_mode(void) { diff --git a/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/pins.c b/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/pins.c index bda0fd227..a30c6797a 100644 --- a/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/pins.c +++ b/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/pins.c @@ -1,4 +1,5 @@ #include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, @@ -41,13 +42,13 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO10) }, // 1.14 inch LCD ST7789 - { MP_ROM_QSTR(MP_QSTR_LCD_MISO), MP_ROM_PTR(&pin_GPIO4) }, { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_GPIO35) }, { MP_ROM_QSTR(MP_QSTR_LCD_CLK), MP_ROM_PTR(&pin_GPIO36) }, { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_GPIO34) }, { MP_ROM_QSTR(MP_QSTR_LCD_RST), MP_ROM_PTR(&pin_GPIO38) }, { MP_ROM_QSTR(MP_QSTR_LCD_BCKL), MP_ROM_PTR(&pin_GPIO33) }, { MP_ROM_QSTR(MP_QSTR_LCD_D_C), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, // Peripheral Power control { MP_ROM_QSTR(MP_QSTR_PE_POWER), MP_ROM_PTR(&pin_GPIO14) }, diff --git a/ports/esp32s2/common-hal/alarm/__init__.c b/ports/esp32s2/common-hal/alarm/__init__.c index 1cec5d4ae..be435e210 100644 --- a/ports/esp32s2/common-hal/alarm/__init__.c +++ b/ports/esp32s2/common-hal/alarm/__init__.c @@ -150,6 +150,14 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala void NORETURN alarm_enter_deep_sleep(void) { alarm_pin_pinalarm_prepare_for_deep_sleep(); alarm_touch_touchalarm_prepare_for_deep_sleep(); + + // Disable brownout detection, which appears to be triggered sometimes when + // waking from deep sleep. + // See https://www.esp32.com/viewtopic.php?f=13&t=19208#p71084 + // and https://github.com/adafruit/circuitpython/issues/4025#issuecomment-771027606 + // TODO: We can remove this workaround when ESP-IDF handles this. + CLEAR_PERI_REG_MASK(RTC_CNTL_BROWN_OUT_REG, RTC_CNTL_BROWN_OUT_RST_ENA); + // The ESP-IDF caches the deep sleep settings and applies them before sleep. // We don't need to worry about resetting them in the interim. esp_deep_sleep_start(); diff --git a/ports/esp32s2/common-hal/audiobusio/I2SOut.c b/ports/esp32s2/common-hal/audiobusio/I2SOut.c index 86322be86..eb9b6f8e1 100644 --- a/ports/esp32s2/common-hal/audiobusio/I2SOut.c +++ b/ports/esp32s2/common-hal/audiobusio/I2SOut.c @@ -63,6 +63,9 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t* self, self->bit_clock = bit_clock; self->word_select = word_select; self->data = data; + claim_pin(bit_clock); + claim_pin(word_select); + claim_pin(data); } bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t* self) { diff --git a/ports/esp32s2/common-hal/busio/I2C.c b/ports/esp32s2/common-hal/busio/I2C.c index 772262d0a..8372eb8c3 100644 --- a/ports/esp32s2/common-hal/busio/I2C.c +++ b/ports/esp32s2/common-hal/busio/I2C.c @@ -85,7 +85,7 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, if (gpio_get_level(sda->number) == 0 || gpio_get_level(scl->number) == 0) { reset_pin_number(sda->number); reset_pin_number(scl->number); - mp_raise_RuntimeError(translate("SDA or SCL needs a pull up")); + mp_raise_RuntimeError(translate("No pull up found on SDA or SCL; check your wiring")); } #endif diff --git a/ports/esp32s2/common-hal/socketpool/Socket.c b/ports/esp32s2/common-hal/socketpool/Socket.c index fc50ca3ab..5f93807bb 100644 --- a/ports/esp32s2/common-hal/socketpool/Socket.c +++ b/ports/esp32s2/common-hal/socketpool/Socket.c @@ -3,7 +3,6 @@ * * The MIT License (MIT) * - * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries * Copyright (c) 2020 Lucian Copeland for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -38,16 +37,15 @@ #include "components/lwip/lwip/src/include/lwip/sys.h" #include "components/lwip/lwip/src/include/lwip/netdb.h" -STATIC socketpool_socket_obj_t * open_socket_handles[CONFIG_LWIP_MAX_SOCKETS]; // 4 on the wrover/wroom +STATIC socketpool_socket_obj_t * open_socket_handles[CONFIG_LWIP_MAX_SOCKETS]; void socket_reset(void) { for (size_t i = 0; i < MP_ARRAY_SIZE(open_socket_handles); i++) { if (open_socket_handles[i]) { if (open_socket_handles[i]->num > 0) { + // Close automatically clears socket handle common_hal_socketpool_socket_close(open_socket_handles[i]); - open_socket_handles[i] = NULL; } else { - // accidentally got a TCP socket in here, or something. open_socket_handles[i] = NULL; } } @@ -64,73 +62,14 @@ bool register_open_socket(socketpool_socket_obj_t* self) { return false; } -STATIC void _lazy_init_LWIP(socketpool_socket_obj_t* self) { - if (self->num != -1) { - return; //safe to call on existing socket - } - if (self->tls != NULL) { - mp_raise_RuntimeError(translate("Invalid use of TLS Socket")); - } - int socknum = -1; - socknum = lwip_socket(self->family, self->type, self->ipproto); - if (socknum < 0 || !register_open_socket(self)) { - mp_raise_RuntimeError(translate("Out of sockets")); - } - self->num = socknum; - lwip_fcntl(socknum, F_SETFL, O_NONBLOCK); -} - -STATIC void _lazy_init_TLS(socketpool_socket_obj_t* self) { - if (self->type != SOCK_STREAM || self->num != -1) { - mp_raise_RuntimeError(translate("Invalid socket for TLS")); - } - esp_tls_t* tls_handle = esp_tls_init(); - if (tls_handle == NULL) { - mp_raise_espidf_MemoryError(); - } - self->tls = tls_handle; -} - -void common_hal_socketpool_socket_settimeout(socketpool_socket_obj_t* self, mp_uint_t timeout_ms) { - self->timeout_ms = timeout_ms; -} - -bool common_hal_socketpool_socket_bind(socketpool_socket_obj_t* self, - const char* host, size_t hostlen, uint8_t port) { - _lazy_init_LWIP(self); - - struct sockaddr_in bind_addr; - bind_addr.sin_addr.s_addr = inet_addr(host); - bind_addr.sin_family = AF_INET; - bind_addr.sin_port = htons(port); - - int opt = 1; - int err = lwip_setsockopt(self->num, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); - if (err != 0) { - mp_raise_RuntimeError(translate("Issue setting SO_REUSEADDR")); - } - int result = lwip_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) == 0; - return result; -} - -bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t* self, int backlog) { - return lwip_listen(self->num, backlog) == 0; -} - socketpool_socket_obj_t* common_hal_socketpool_socket_accept(socketpool_socket_obj_t* self, - uint8_t* ip, uint *port) { + uint8_t* ip, uint32_t *port) { struct sockaddr_in accept_addr; socklen_t socklen = sizeof(accept_addr); int newsoc = -1; bool timed_out = false; uint64_t start_ticks = supervisor_ticks_ms64(); - if (self->timeout_ms != (uint)-1) { - mp_printf(&mp_plat_print, "will timeout"); - } else { - mp_printf(&mp_plat_print, "won't timeout"); - } - // Allow timeouts and interrupts while (newsoc == -1 && !timed_out && @@ -140,7 +79,7 @@ socketpool_socket_obj_t* common_hal_socketpool_socket_accept(socketpool_socket_o } RUN_BACKGROUND_TASKS; newsoc = lwip_accept(self->num, (struct sockaddr *)&accept_addr, &socklen); - // In non-blocking mode, fail instead of looping + // In non-blocking mode, fail instead of timing out if (newsoc == -1 && self->timeout_ms == 0) { mp_raise_OSError(MP_EAGAIN); } @@ -159,9 +98,8 @@ socketpool_socket_obj_t* common_hal_socketpool_socket_accept(socketpool_socket_o socketpool_socket_obj_t *sock = m_new_obj_with_finaliser(socketpool_socket_obj_t); sock->base.type = &socketpool_socket_type; sock->num = newsoc; - sock->tls = NULL; - sock->ssl_context = NULL; sock->pool = self->pool; + sock->connected = true; if (!register_open_socket(sock)) { mp_raise_OSError(MP_EBADF); @@ -175,72 +113,136 @@ socketpool_socket_obj_t* common_hal_socketpool_socket_accept(socketpool_socket_o } } -bool common_hal_socketpool_socket_connect(socketpool_socket_obj_t* self, - const char* host, mp_uint_t hostlen, mp_int_t port) { - // For simplicity we use esp_tls for all TCP connections. If it's not SSL, ssl_context will be - // NULL and should still work. This makes regular TCP connections more memory expensive but TLS - // should become more and more common. Therefore, we optimize for the TLS case. +bool common_hal_socketpool_socket_bind(socketpool_socket_obj_t* self, + const char* host, size_t hostlen, uint32_t port) { + struct sockaddr_in bind_addr; + bind_addr.sin_addr.s_addr = inet_addr(host); + bind_addr.sin_family = AF_INET; + bind_addr.sin_port = htons(port); - // Todo: move to SSL Wrapper and add lwip_connect() - _lazy_init_TLS(self); + int opt = 1; + int err = lwip_setsockopt(self->num, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + if (err != 0) { + mp_raise_RuntimeError(translate("Cannot set socket options")); + } + int result = lwip_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) == 0; + return result; +} - esp_tls_cfg_t* tls_config = NULL; - if (self->ssl_context != NULL) { - tls_config = &self->ssl_context->ssl_config; +void common_hal_socketpool_socket_close(socketpool_socket_obj_t* self) { + self->connected = false; + if (self->num >= 0) { + lwip_shutdown(self->num, 0); + lwip_close(self->num); + self->num = -1; } - int result = esp_tls_conn_new_sync(host, hostlen, port, tls_config, self->tls); - self->connected = result >= 0; - if (result < 0) { - int esp_tls_code; - int flags; - esp_err_t err = esp_tls_get_and_clear_last_error(self->tls->error_handle, &esp_tls_code, &flags); - - if (err == ESP_ERR_MBEDTLS_SSL_SETUP_FAILED) { - mp_raise_espidf_MemoryError(); - } else if (ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED) { - mp_raise_OSError_msg_varg(translate("Failed SSL handshake")); - } else { - mp_raise_OSError_msg_varg(translate("Unhandled ESP TLS error %d %d %x %d"), esp_tls_code, flags, err, result); + // Remove socket record + for (size_t i = 0; i < MP_ARRAY_SIZE(open_socket_handles); i++) { + if (open_socket_handles[i] == self) { + open_socket_handles[i] = NULL; } + } +} + +bool common_hal_socketpool_socket_connect(socketpool_socket_obj_t* self, + const char* host, size_t hostlen, uint32_t port) { + const struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + }; + struct addrinfo *result_i; + int error = lwip_getaddrinfo(host, NULL, &hints, &result_i); + if (error != 0 || result_i == NULL) { + mp_raise_OSError(EHOSTUNREACH); + } + + // Set parameters + struct sockaddr_in dest_addr; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wcast-align" + dest_addr.sin_addr.s_addr = ((struct sockaddr_in *)result_i->ai_addr)->sin_addr.s_addr; + #pragma GCC diagnostic pop + freeaddrinfo(result_i); + + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(port); + + // Replace above with function call ----- + + // Switch to blocking mode for this one call + int opts; + opts = lwip_fcntl(self->num,F_GETFL,0); + opts = opts & (~O_NONBLOCK); + lwip_fcntl(self->num, F_SETFL, opts); + + int result = -1; + result = lwip_connect(self->num, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in)); + + // Switch back once complete + opts = opts | O_NONBLOCK; + lwip_fcntl(self->num, F_SETFL, opts); + + if (result >= 0) { + self->connected = true; + return true; } else { - // Connection successful, set the timeout on the underlying socket. We can't rely on the IDF - // to do it because the config structure is only used for TLS connections. Generally, we - // shouldn't hit this timeout because we try to only read available data. However, there is - // always a chance that we try to read something that is used internally. - int fd; - esp_tls_get_conn_sockfd(self->tls, &fd); - struct timeval tv; - tv.tv_sec = 2 * 60; // Two minutes - tv.tv_usec = 0; - setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + mp_raise_OSError(errno); } +} - return self->connected; +bool common_hal_socketpool_socket_get_closed(socketpool_socket_obj_t* self) { + return self->num < 0; } bool common_hal_socketpool_socket_get_connected(socketpool_socket_obj_t* self) { return self->connected; } -mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t* self, const uint8_t* buf, mp_uint_t len) { - int sent = -1; - if (self->num != -1) { - // LWIP Socket - // TODO: deal with potential failure/add timeout? - sent = lwip_send(self->num, buf, len, 0); - } else if (self->tls != NULL) { - // TLS Socket - sent = esp_tls_conn_write(self->tls, buf, len); +bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t* self, int backlog) { + return lwip_listen(self->num, backlog) == 0; +} + +mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t* self, + uint8_t* buf, uint32_t len, uint8_t* ip, uint *port) { + + struct sockaddr_in source_addr; + socklen_t socklen = sizeof(source_addr); + + // LWIP Socket + uint64_t start_ticks = supervisor_ticks_ms64(); + int received = -1; + bool timed_out = false; + while (received == -1 && + !timed_out && + !mp_hal_is_interrupted()) { + if (self->timeout_ms != (uint)-1 && self->timeout_ms != 0) { + timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; + } + RUN_BACKGROUND_TASKS; + received = lwip_recvfrom(self->num, buf, len, 0, (struct sockaddr *)&source_addr, &socklen); + + // In non-blocking mode, fail instead of looping + if (received == -1 && self->timeout_ms == 0) { + mp_raise_OSError(MP_EAGAIN); + } } - if (sent < 0) { - mp_raise_OSError(MP_ENOTCONN); + if (!timed_out) { + memcpy((void *)ip, (void*)&source_addr.sin_addr.s_addr, sizeof(source_addr.sin_addr.s_addr)); + *port = source_addr.sin_port; + } else { + mp_raise_OSError(ETIMEDOUT); } - return sent; + + if (received < 0) { + mp_raise_BrokenPipeError(); + return 0; + } + + return received; } -mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t* self, const uint8_t* buf, mp_uint_t len) { +mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t* self, const uint8_t* buf, uint32_t len) { int received = 0; bool timed_out = false; @@ -255,54 +257,14 @@ mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t* self, timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; } RUN_BACKGROUND_TASKS; - received = lwip_recv(self->num, (void*) buf, len - 1, 0); + received = lwip_recv(self->num, (void*) buf, len, 0); // In non-blocking mode, fail instead of looping if (received == -1 && self->timeout_ms == 0) { mp_raise_OSError(MP_EAGAIN); } } - } else if (self->tls != NULL) { - // TLS Socket - int status = 0; - uint64_t start_ticks = supervisor_ticks_ms64(); - int sockfd; - esp_err_t err = esp_tls_get_conn_sockfd(self->tls, &sockfd); - if (err != ESP_OK) { - mp_raise_OSError(MP_EBADF); - } - while (received == 0 && - status >= 0 && - !timed_out && - !mp_hal_is_interrupted()) { - if (self->timeout_ms != (uint)-1) { - timed_out = self->timeout_ms == 0 || supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; - } - RUN_BACKGROUND_TASKS; - size_t available = esp_tls_get_bytes_avail(self->tls); - if (available == 0) { - // This reads the raw socket buffer and is used for non-TLS connections - // and between encrypted TLS blocks. - status = lwip_ioctl(sockfd, FIONREAD, &available); - } - size_t remaining = len - received; - if (available > remaining) { - available = remaining; - } - if (available > 0) { - status = esp_tls_conn_read(self->tls, (void*) buf + received, available); - if (status == 0) { - // Reading zero when something is available indicates a closed - // connection. (The available bytes could have been TLS internal.) - break; - } - if (status > 0) { - received += status; - } - } - } } else { - // Socket does not have a valid descriptor of either type mp_raise_OSError(MP_EBADF); } @@ -312,29 +274,43 @@ mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t* self, return received; } -mp_uint_t common_hal_socketpool_socket_sendto(socketpool_socket_obj_t* self, - const char* host, size_t hostlen, uint8_t port, const uint8_t* buf, mp_uint_t len) { +mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t* self, const uint8_t* buf, uint32_t len) { + int sent = -1; + if (self->num != -1) { + // LWIP Socket + // TODO: deal with potential failure/add timeout? + sent = lwip_send(self->num, buf, len, 0); + } else { + mp_raise_OSError(MP_EBADF); + } - _lazy_init_LWIP(self); + if (sent < 0) { + mp_raise_OSError(errno); + } + return sent; +} - // Get the IP address string +mp_uint_t common_hal_socketpool_socket_sendto(socketpool_socket_obj_t* self, + const char* host, size_t hostlen, uint32_t port, const uint8_t* buf, uint32_t len) { + + // Set parameters const struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, }; - struct addrinfo *result; - int error = lwip_getaddrinfo(host, NULL, &hints, &result); - if (error != 0 || result == NULL) { - return 0; + struct addrinfo *result_i; + int error = lwip_getaddrinfo(host, NULL, &hints, &result_i); + if (error != 0 || result_i == NULL) { + mp_raise_OSError(EHOSTUNREACH); } // Set parameters struct sockaddr_in dest_addr; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" - dest_addr.sin_addr.s_addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr; + dest_addr.sin_addr.s_addr = ((struct sockaddr_in *)result_i->ai_addr)->sin_addr.s_addr; #pragma GCC diagnostic pop - freeaddrinfo(result); + freeaddrinfo(result_i); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(port); @@ -347,66 +323,6 @@ mp_uint_t common_hal_socketpool_socket_sendto(socketpool_socket_obj_t* self, return bytes_sent; } -mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t* self, - uint8_t* buf, mp_uint_t len, uint8_t* ip, uint *port) { - - _lazy_init_LWIP(self); - - struct sockaddr_in source_addr; - socklen_t socklen = sizeof(source_addr); - - // LWIP Socket - uint64_t start_ticks = supervisor_ticks_ms64(); - int received = -1; - bool timed_out = false; - while (received == -1 && - !timed_out && - !mp_hal_is_interrupted()) { - if (self->timeout_ms != (uint)-1 && self->timeout_ms != 0) { - timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; - } - RUN_BACKGROUND_TASKS; - received = lwip_recvfrom(self->num, buf, len, 0, (struct sockaddr *)&source_addr, &socklen); - - // In non-blocking mode, fail instead of looping - if (received == -1 && self->timeout_ms == 0) { - mp_raise_OSError(MP_EAGAIN); - } - } - - if (!timed_out) { - memcpy((void *)ip, (void*)&source_addr.sin_addr.s_addr, sizeof(source_addr.sin_addr.s_addr)); - *port = source_addr.sin_port; - } else { - mp_raise_OSError(ETIMEDOUT); - } - - if (received < 0) { - mp_raise_BrokenPipeError(); - return 0; - } - - return received; -} - -void common_hal_socketpool_socket_close(socketpool_socket_obj_t* self) { - self->connected = false; - if (self->tls != NULL) { - esp_tls_conn_destroy(self->tls); - self->tls = NULL; - } - if (self->num >= 0) { - lwip_shutdown(self->num, 0); - lwip_close(self->num); - self->num = -1; - } -} - -bool common_hal_socketpool_socket_get_closed(socketpool_socket_obj_t* self) { - return self->tls == NULL && self->num < 0; -} - - -mp_uint_t common_hal_socketpool_socket_get_hash(socketpool_socket_obj_t* self) { - return self->num; +void common_hal_socketpool_socket_settimeout(socketpool_socket_obj_t* self, uint32_t timeout_ms) { + self->timeout_ms = timeout_ms; } diff --git a/ports/esp32s2/common-hal/socketpool/Socket.h b/ports/esp32s2/common-hal/socketpool/Socket.h index 4e6cfa5ef..b86f5597c 100644 --- a/ports/esp32s2/common-hal/socketpool/Socket.h +++ b/ports/esp32s2/common-hal/socketpool/Socket.h @@ -41,8 +41,6 @@ typedef struct { int family; int ipproto; bool connected; - esp_tls_t* tls; - ssl_sslcontext_obj_t* ssl_context; socketpool_socketpool_obj_t* pool; mp_uint_t timeout_ms; } socketpool_socket_obj_t; diff --git a/ports/esp32s2/common-hal/socketpool/SocketPool.c b/ports/esp32s2/common-hal/socketpool/SocketPool.c index 70a8b4eca..fbd6dca7a 100644 --- a/ports/esp32s2/common-hal/socketpool/SocketPool.c +++ b/ports/esp32s2/common-hal/socketpool/SocketPool.c @@ -25,6 +25,7 @@ */ #include "shared-bindings/socketpool/SocketPool.h" +#include "common-hal/socketpool/Socket.h" #include "py/runtime.h" #include "shared-bindings/wifi/__init__.h" @@ -65,23 +66,23 @@ socketpool_socket_obj_t* common_hal_socketpool_socket(socketpool_socketpool_obj_ mp_raise_NotImplementedError(translate("Only IPv4 sockets supported")); } - // Consider LWIP and MbedTLS "variant" sockets to be incompatible (for now) - // The variant of the socket is determined by whether the socket is wrapped - // by SSL. If no TLS handle is set in sslcontext_wrap_socket, the first call - // of bind() or connect() will create a LWIP socket with a corresponding - // socketnum. - // TODO: move MbedTLS to its own duplicate Socket or Server API, maybe? socketpool_socket_obj_t *sock = m_new_obj_with_finaliser(socketpool_socket_obj_t); sock->base.type = &socketpool_socket_type; - sock->num = -1; sock->type = socket_type; sock->family = addr_family; sock->ipproto = ipproto; + sock->pool = self; sock->timeout_ms = (uint)-1; - sock->tls = NULL; - sock->ssl_context = NULL; - sock->pool = self; + // Create LWIP socket + int socknum = -1; + socknum = lwip_socket(sock->family, sock->type, sock->ipproto); + if (socknum < 0 || !register_open_socket(sock)) { + mp_raise_RuntimeError(translate("Out of sockets")); + } + sock->num = socknum; + // Sockets should be nonblocking in most cases + lwip_fcntl(socknum, F_SETFL, O_NONBLOCK); return sock; } diff --git a/ports/esp32s2/common-hal/ssl/SSLContext.c b/ports/esp32s2/common-hal/ssl/SSLContext.c index e24fd338b..afc3ecce2 100644 --- a/ports/esp32s2/common-hal/ssl/SSLContext.c +++ b/ports/esp32s2/common-hal/ssl/SSLContext.c @@ -25,6 +25,9 @@ */ #include "shared-bindings/ssl/SSLContext.h" +#include "shared-bindings/ssl/SSLSocket.h" + +#include "bindings/espidf/__init__.h" #include "py/runtime.h" @@ -32,10 +35,26 @@ void common_hal_ssl_sslcontext_construct(ssl_sslcontext_obj_t* self) { } -socketpool_socket_obj_t* common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t* self, +ssl_sslsocket_obj_t* common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t* self, socketpool_socket_obj_t* socket, bool server_side, const char* server_hostname) { - socket->ssl_context = self; + if (socket->type != SOCK_STREAM) { + mp_raise_RuntimeError(translate("Invalid socket for TLS")); + } + + ssl_sslsocket_obj_t *sock = m_new_obj_with_finaliser(ssl_sslsocket_obj_t); + sock->base.type = &ssl_sslsocket_type; + sock->ssl_context = self; + sock->sock = socket; + + esp_tls_t* tls_handle = esp_tls_init(); + if (tls_handle == NULL) { + mp_raise_espidf_MemoryError(); + } + sock->tls = tls_handle; + + // TODO: do something with the original socket? Don't call a close on the internal LWIP. + // Should we store server hostname on the socket in case connect is called with an ip? - return socket; + return sock; } diff --git a/ports/esp32s2/common-hal/ssl/SSLSocket.c b/ports/esp32s2/common-hal/ssl/SSLSocket.c new file mode 100644 index 000000000..10c29108c --- /dev/null +++ b/ports/esp32s2/common-hal/ssl/SSLSocket.c @@ -0,0 +1,176 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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 "shared-bindings/ssl/SSLSocket.h" +#include "shared-bindings/socketpool/Socket.h" +#include "shared-bindings/ssl/SSLContext.h" + +#include "bindings/espidf/__init__.h" +#include "lib/utils/interrupt_char.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "supervisor/shared/tick.h" + +ssl_sslsocket_obj_t* common_hal_ssl_sslsocket_accept(ssl_sslsocket_obj_t* self, + uint8_t* ip, uint32_t *port) { + socketpool_socket_obj_t * sock = common_hal_socketpool_socket_accept(self->sock, ip, port); + ssl_sslsocket_obj_t * sslsock = common_hal_ssl_sslcontext_wrap_socket(self->ssl_context, sock, false, NULL); + return sslsock; +} + +bool common_hal_ssl_sslsocket_bind(ssl_sslsocket_obj_t* self, + const char* host, size_t hostlen, uint32_t port) { + return common_hal_socketpool_socket_bind(self->sock, host, hostlen, port); +} + +void common_hal_ssl_sslsocket_close(ssl_sslsocket_obj_t* self) { + common_hal_socketpool_socket_close(self->sock); + esp_tls_conn_destroy(self->tls); + self->tls = NULL; +} + +bool common_hal_ssl_sslsocket_connect(ssl_sslsocket_obj_t* self, + const char* host, size_t hostlen, uint32_t port) { + esp_tls_cfg_t* tls_config = NULL; + tls_config = &self->ssl_context->ssl_config; + int result = esp_tls_conn_new_sync(host, hostlen, port, tls_config, self->tls); + self->sock->connected = result >= 0; + if (result < 0) { + int esp_tls_code; + int flags; + esp_err_t err = esp_tls_get_and_clear_last_error(self->tls->error_handle, &esp_tls_code, &flags); + + if (err == ESP_ERR_MBEDTLS_SSL_SETUP_FAILED) { + mp_raise_espidf_MemoryError(); + } else if (ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED) { + mp_raise_OSError_msg_varg(translate("Failed SSL handshake")); + } else { + mp_raise_OSError_msg_varg(translate("Unhandled ESP TLS error %d %d %x %d"), esp_tls_code, flags, err, result); + } + } else { + // Connection successful, set the timeout on the underlying socket. We can't rely on the IDF + // to do it because the config structure is only used for TLS connections. Generally, we + // shouldn't hit this timeout because we try to only read available data. However, there is + // always a chance that we try to read something that is used internally. + int fd; + esp_tls_get_conn_sockfd(self->tls, &fd); + struct timeval tv; + tv.tv_sec = 2 * 60; // Two minutes + tv.tv_usec = 0; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + } + + return self->sock->connected; +} + +bool common_hal_ssl_sslsocket_get_closed(ssl_sslsocket_obj_t* self) { + return self->tls == NULL && self->sock->num < 0; +} + +bool common_hal_ssl_sslsocket_get_connected(ssl_sslsocket_obj_t* self) { + return self->sock->connected; +} + +bool common_hal_ssl_sslsocket_listen(ssl_sslsocket_obj_t* self, int backlog) { + return common_hal_socketpool_socket_listen(self->sock, backlog); +} + +mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t* self, const uint8_t* buf, uint32_t len) { + int received = 0; + bool timed_out = false; + int status = 0; + uint64_t start_ticks = supervisor_ticks_ms64(); + int sockfd; + esp_err_t err = esp_tls_get_conn_sockfd(self->tls, &sockfd); + if (err != ESP_OK) { + mp_raise_OSError(MP_EBADF); + } + while (received == 0 && + status >= 0 && + !timed_out && + !mp_hal_is_interrupted()) { + if (self->sock->timeout_ms != (uint)-1 && self->sock->timeout_ms != 0) { + timed_out = self->sock->timeout_ms == 0 || supervisor_ticks_ms64() - start_ticks >= self->sock->timeout_ms; + } + RUN_BACKGROUND_TASKS; + size_t available = esp_tls_get_bytes_avail(self->tls); + if (available == 0) { + // This reads the raw socket buffer and is used for non-TLS connections + // and between encrypted TLS blocks. + status = lwip_ioctl(sockfd, FIONREAD, &available); + } + size_t remaining = len - received; + if (available > remaining) { + available = remaining; + } + if (available > 0) { + status = esp_tls_conn_read(self->tls, (void*) buf + received, available); + if (status == 0) { + // Reading zero when something is available indicates a closed + // connection. (The available bytes could have been TLS internal.) + break; + } + if (status > 0) { + received += status; + } + } + // In non-blocking mode, fail instead of timing out + if (received==0 && self->sock->timeout_ms == 0) { + mp_raise_OSError(MP_EAGAIN); + } + } + + if (timed_out) { + mp_raise_OSError(ETIMEDOUT); + } + return received; +} + +mp_uint_t common_hal_ssl_sslsocket_send(ssl_sslsocket_obj_t* self, const uint8_t* buf, uint32_t len) { + int sent = -1; + sent = esp_tls_conn_write(self->tls, buf, len); + + if (sent < 0) { + int esp_tls_code; + int flags; + esp_err_t err = esp_tls_get_and_clear_last_error(self->tls->error_handle, &esp_tls_code, &flags); + + if (err == ESP_ERR_MBEDTLS_SSL_SETUP_FAILED) { + mp_raise_espidf_MemoryError(); + } else if (ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED) { + mp_raise_OSError_msg_varg(translate("Failed SSL handshake")); + } else { + mp_raise_OSError_msg_varg(translate("Unhandled ESP TLS error %d %d %x %d"), esp_tls_code, flags, err, sent); + } + } + return sent; +} + +void common_hal_ssl_sslsocket_settimeout(ssl_sslsocket_obj_t* self, uint32_t timeout_ms) { + self->sock->timeout_ms = timeout_ms; +} diff --git a/ports/esp32s2/common-hal/ssl/SSLSocket.h b/ports/esp32s2/common-hal/ssl/SSLSocket.h new file mode 100644 index 000000000..e9e5bff06 --- /dev/null +++ b/ports/esp32s2/common-hal/ssl/SSLSocket.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_ESP32S2_COMMON_HAL_SSL_SSLSOCKET_H +#define MICROPY_INCLUDED_ESP32S2_COMMON_HAL_SSL_SSLSOCKET_H + +#include "py/obj.h" + +#include "common-hal/ssl/SSLContext.h" +#include "common-hal/socketpool/Socket.h" + +#include "components/esp-tls/esp_tls.h" + +typedef struct { + mp_obj_base_t base; + socketpool_socket_obj_t * sock; + esp_tls_t* tls; + ssl_sslcontext_obj_t* ssl_context; +} ssl_sslsocket_obj_t; + +#endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_SSL_SSLSOCKET_H diff --git a/ports/esp32s2/common-hal/supervisor/Runtime.c b/ports/esp32s2/common-hal/supervisor/Runtime.c index ea663f897..974f26cec 100644 --- a/ports/esp32s2/common-hal/supervisor/Runtime.c +++ b/ports/esp32s2/common-hal/supervisor/Runtime.c @@ -28,10 +28,10 @@ #include "shared-bindings/supervisor/Runtime.h" #include "supervisor/serial.h" -bool common_hal_get_serial_connected(void) { +bool common_hal_supervisor_runtime_get_serial_connected(void) { return (bool) serial_connected(); } -bool common_hal_get_serial_bytes_available(void) { - return (bool) serial_bytes_available(); +bool common_hal_supervisor_runtime_get_serial_bytes_available(void) { + return (bool) serial_bytes_available(); } diff --git a/ports/esp32s2/mpconfigport.mk b/ports/esp32s2/mpconfigport.mk index 562c60998..5073d8812 100644 --- a/ports/esp32s2/mpconfigport.mk +++ b/ports/esp32s2/mpconfigport.mk @@ -28,18 +28,18 @@ CIRCUITPY_FREQUENCYIO = 1 CIRCUITPY_I2CPERIPHERAL = 0 CIRCUITPY_ROTARYIO = 1 CIRCUITPY_NVM = 1 +CIRCUITPY_PS2IO ?= 1 +CIRCUITPY_TOUCHIO_USE_NATIVE ?= 1 # We don't have enough endpoints to include MIDI. -CIRCUITPY_USB_MIDI = 0 +CIRCUITPY_USB_MIDI ?= 0 +CIRCUITPY_USB_HID ?= 1 +# We have borrowed the VENDOR nomenclature from tinyusb. VENDOR AKA WEBUSB +CIRCUITPY_USB_VENDOR ?= 0 CIRCUITPY_WIFI = 1 CIRCUITPY_WATCHDOG ?= 1 -CIRCUITPY_ESPIDF = 1 - -ifndef CIRCUITPY_PS2IO -CIRCUITPY_PS2IO = 1 -endif -ifndef CIRCUITPY_TOUCHIO_USE_NATIVE -CIRCUITPY_TOUCHIO_USE_NATIVE = 1 -endif +CIRCUITPY_ESPIDF = 1 CIRCUITPY_MODULE ?= none + +USB_NUM_EP = 5 |
