From b90b69ca2e02055c5ea2d2f58b0a9fd97e8b491a Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 25 Nov 2025 19:48:01 -0500 Subject: [PATCH 1/4] wip; compiles but doesn't link --- .../airlift/common-hal/socketpool/Socket.c | 710 ++++++++++++++ .../airlift/common-hal/socketpool/Socket.h | 29 + .../common-hal/socketpool/SocketPool.c | 124 +++ .../common-hal/socketpool/SocketPool.h | 13 + .../airlift/common-hal/socketpool/__init__.c | 13 + .../airlift/common-hal/socketpool/__init__.h | 12 + devices/airlift/common-hal/ssl/SSLContext.c | 44 + devices/airlift/common-hal/ssl/SSLContext.h | 18 + devices/airlift/common-hal/ssl/SSLSocket.c | 471 +++++++++ devices/airlift/common-hal/ssl/SSLSocket.h | 34 + devices/airlift/common-hal/ssl/__init__.c | 18 + devices/airlift/common-hal/ssl/__init__.h | 9 + devices/airlift/common-hal/wifi/Monitor.c | 151 +++ devices/airlift/common-hal/wifi/Monitor.h | 17 + devices/airlift/common-hal/wifi/Network.c | 82 ++ devices/airlift/common-hal/wifi/Network.h | 14 + devices/airlift/common-hal/wifi/Radio.c | 910 ++++++++++++++++++ devices/airlift/common-hal/wifi/Radio.h | 129 +++ .../airlift/common-hal/wifi/ScannedNetworks.c | 159 +++ .../airlift/common-hal/wifi/ScannedNetworks.h | 35 + devices/airlift/common-hal/wifi/__init__.c | 362 +++++++ devices/airlift/common-hal/wifi/__init__.h | 24 + docs/shared_bindings_matrix.py | 4 +- frozen/Adafruit_CircuitPython_AHTx0 | 2 +- frozen/Adafruit_CircuitPython_APDS9960 | 2 +- frozen/Adafruit_CircuitPython_BLE | 2 +- ...ircuitPython_BLE_Apple_Notification_Center | 2 +- frozen/Adafruit_CircuitPython_Bitmap_Font | 2 +- frozen/Adafruit_CircuitPython_BusDevice | 2 +- .../Adafruit_CircuitPython_CircuitPlayground | 2 +- .../Adafruit_CircuitPython_ConnectionManager | 2 +- frozen/Adafruit_CircuitPython_Crickit | 2 +- frozen/Adafruit_CircuitPython_DRV2605 | 2 +- frozen/Adafruit_CircuitPython_DS3231 | 2 +- .../Adafruit_CircuitPython_DisplayIO_SSD1306 | 2 +- frozen/Adafruit_CircuitPython_Display_Text | 2 +- frozen/Adafruit_CircuitPython_DotStar | 2 +- frozen/Adafruit_CircuitPython_ESP32SPI | 2 +- frozen/Adafruit_CircuitPython_FakeRequests | 2 +- frozen/Adafruit_CircuitPython_FocalTouch | 2 +- frozen/Adafruit_CircuitPython_HID | 2 +- frozen/Adafruit_CircuitPython_HTTPServer | 2 +- frozen/Adafruit_CircuitPython_IRRemote | 2 +- frozen/Adafruit_CircuitPython_IS31FL3731 | 2 +- frozen/Adafruit_CircuitPython_ImageLoad | 2 +- frozen/Adafruit_CircuitPython_LC709203F | 2 +- frozen/Adafruit_CircuitPython_LED_Animation | 2 +- frozen/Adafruit_CircuitPython_LIS3DH | 2 +- frozen/Adafruit_CircuitPython_LSM6DS | 2 +- frozen/Adafruit_CircuitPython_MIDI | 2 +- frozen/Adafruit_CircuitPython_MPU6050 | 2 +- frozen/Adafruit_CircuitPython_Motor | 2 +- frozen/Adafruit_CircuitPython_NeoPixel | 2 +- frozen/Adafruit_CircuitPython_PCF8563 | 2 +- frozen/Adafruit_CircuitPython_Pixel_Framebuf | 2 +- frozen/Adafruit_CircuitPython_PortalBase | 2 +- frozen/Adafruit_CircuitPython_ProgressBar | 2 +- frozen/Adafruit_CircuitPython_RFM69 | 2 +- frozen/Adafruit_CircuitPython_RFM9x | 2 +- frozen/Adafruit_CircuitPython_Register | 2 +- frozen/Adafruit_CircuitPython_Requests | 2 +- frozen/Adafruit_CircuitPython_SD | 2 +- frozen/Adafruit_CircuitPython_SHT4x | 2 +- frozen/Adafruit_CircuitPython_SSD1306 | 2 +- frozen/Adafruit_CircuitPython_SSD1680 | 2 +- frozen/Adafruit_CircuitPython_ST7789 | 2 +- frozen/Adafruit_CircuitPython_SimpleIO | 2 +- frozen/Adafruit_CircuitPython_SimpleMath | 2 +- frozen/Adafruit_CircuitPython_Thermistor | 2 +- frozen/Adafruit_CircuitPython_Ticks | 2 +- frozen/Adafruit_CircuitPython_UC8151D | 2 +- frozen/Adafruit_CircuitPython_Wave | 2 +- frozen/Adafruit_CircuitPython_Wiznet5k | 2 +- frozen/Adafruit_CircuitPython_asyncio | 2 +- frozen/Adafruit_CircuitPython_framebuf | 2 +- frozen/Adafruit_CircuitPython_seesaw | 2 +- ports/atmel-samd/mpconfigport.mk | 1 + ports/espressif/Makefile | 2 +- .../boards/makerfabs_tft7/mpconfigboard.mk | 2 +- .../espressif/common-hal/socketpool/Socket.c | 2 +- ports/espressif/common-hal/wifi/Radio.c | 4 + ports/espressif/mpconfigport.mk | 12 +- ports/espressif/supervisor/port.c | 6 +- .../boards/cytron_edu_pico_w/mpconfigboard.mk | 6 +- .../boards/pajenicko_picopad/mpconfigboard.mk | 6 +- .../pimoroni_badger2040w/mpconfigboard.mk | 6 +- .../pimoroni_inky_frame_5_7/mpconfigboard.mk | 9 +- .../pimoroni_inky_frame_7_3/mpconfigboard.mk | 9 +- .../pimoroni_pico_dv_base_w/mpconfigboard.mk | 6 +- .../pimoroni_pico_plus2w/mpconfigboard.mk | 8 +- .../pimoroni_plasma2040w/mpconfigboard.mk | 9 +- .../pimoroni_plasma2350w/mpconfigboard.mk | 9 +- .../raspberry_pi_pico2_w/mpconfigboard.mk | 6 +- .../raspberry_pi_pico_w/mpconfigboard.mk | 8 +- .../mpconfigboard.mk | 8 +- .../wiznet_w5100s_evb_pico/mpconfigboard.mk | 2 +- .../wiznet_w5100s_evb_pico2/mpconfigboard.mk | 2 +- .../wiznet_w5500_evb_pico/mpconfigboard.mk | 2 +- .../wiznet_w5500_evb_pico2/mpconfigboard.mk | 2 +- ports/raspberrypi/common-hal/wifi/Radio.c | 7 + ports/raspberrypi/supervisor/port.c | 4 +- .../zephyr-cp/common-hal/socketpool/Socket.c | 2 +- py/circuitpy_defns.mk | 64 +- py/circuitpy_mpconfig.mk | 49 +- shared-bindings/wifi/Radio.c | 62 ++ shared-bindings/wifi/Radio.h | 8 +- 106 files changed, 3669 insertions(+), 142 deletions(-) create mode 100644 devices/airlift/common-hal/socketpool/Socket.c create mode 100644 devices/airlift/common-hal/socketpool/Socket.h create mode 100644 devices/airlift/common-hal/socketpool/SocketPool.c create mode 100644 devices/airlift/common-hal/socketpool/SocketPool.h create mode 100644 devices/airlift/common-hal/socketpool/__init__.c create mode 100644 devices/airlift/common-hal/socketpool/__init__.h create mode 100644 devices/airlift/common-hal/ssl/SSLContext.c create mode 100644 devices/airlift/common-hal/ssl/SSLContext.h create mode 100644 devices/airlift/common-hal/ssl/SSLSocket.c create mode 100644 devices/airlift/common-hal/ssl/SSLSocket.h create mode 100644 devices/airlift/common-hal/ssl/__init__.c create mode 100644 devices/airlift/common-hal/ssl/__init__.h create mode 100644 devices/airlift/common-hal/wifi/Monitor.c create mode 100644 devices/airlift/common-hal/wifi/Monitor.h create mode 100644 devices/airlift/common-hal/wifi/Network.c create mode 100644 devices/airlift/common-hal/wifi/Network.h create mode 100644 devices/airlift/common-hal/wifi/Radio.c create mode 100644 devices/airlift/common-hal/wifi/Radio.h create mode 100644 devices/airlift/common-hal/wifi/ScannedNetworks.c create mode 100644 devices/airlift/common-hal/wifi/ScannedNetworks.h create mode 100644 devices/airlift/common-hal/wifi/__init__.c create mode 100644 devices/airlift/common-hal/wifi/__init__.h diff --git a/devices/airlift/common-hal/socketpool/Socket.c b/devices/airlift/common-hal/socketpool/Socket.c new file mode 100644 index 0000000000000..1765d8c0e0c4b --- /dev/null +++ b/devices/airlift/common-hal/socketpool/Socket.c @@ -0,0 +1,710 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Lucian Copeland for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/socketpool/Socket.h" + +#include "shared/runtime/interrupt_char.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "shared-bindings/socketpool/SocketPool.h" +#include "common-hal/socketpool/__init__.h" +#include "common-hal/wifi/__init__.h" +#if CIRCUITPY_SSL_NATIVE +#include "shared-bindings/ssl/SSLSocket.h" +#include "shared-module/ssl/SSLSocket.h" +#endif +#include "supervisor/port.h" +#include "supervisor/shared/tick.h" +#include "supervisor/workflow.h" + +// void socketpool_resolve_host_or_throw(int family, int type, const char *hostname, struct sockaddr_storage *addr, int port) { +// // struct addrinfo *result_i; +// // const struct addrinfo hints = { +// // .ai_family = family, +// // .ai_socktype = type, +// // }; +// // int error = socketpool_getaddrinfo_common(hostname, port, &hints, &result_i); +// // if (error != 0 || result_i == NULL) { +// // common_hal_socketpool_socketpool_raise_gaierror_noname(); +// // } +// // memcpy(addr, result_i->ai_addr, sizeof(struct sockaddr_storage)); +// // lwip_freeaddrinfo(result_i); +// } + +// static void resolve_host_or_throw(socketpool_socket_obj_t *self, const char *hostname, struct sockaddr_storage *addr, int port) { +// // socketpool_resolve_host_or_throw(self->family, self->type, hostname, addr, port); +// } + +// StackType_t socket_select_stack[2 * configMINIMAL_STACK_SIZE]; + +/* Socket state table: + * 0 := Closed (unused) + * 1 := Open + * 2 := Closing (remove from rfds) + * Index into socket_fd_state is calculated from actual lwip fd. idx := fd - LWIP_SOCKET_OFFSET +*/ +#define FDSTATE_CLOSED 0 +#define FDSTATE_OPEN 1 +#define FDSTATE_CLOSING 2 +// static uint8_t socket_fd_state[CONFIG_LWIP_MAX_SOCKETS]; + +// How long to wait between checks for a socket to connect. +#define SOCKET_CONNECT_POLL_INTERVAL_MS 100 + +// static socketpool_socket_obj_t *user_socket[CONFIG_LWIP_MAX_SOCKETS]; +// StaticTask_t socket_select_task_buffer; +// TaskHandle_t socket_select_task_handle; +// static int socket_change_fd = -1; + +// static void socket_select_task(void *arg) { +// uint64_t signal; +// fd_set readfds; +// fd_set excptfds; + +// while (true) { +// FD_ZERO(&readfds); +// FD_ZERO(&excptfds); +// FD_SET(socket_change_fd, &readfds); +// int max_fd = socket_change_fd; +// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { +// if ((socket_fd_state[i] == FDSTATE_OPEN) && (user_socket[i] == NULL)) { +// int sockfd = i + LWIP_SOCKET_OFFSET; +// max_fd = MAX(max_fd, sockfd); +// FD_SET(sockfd, &readfds); +// FD_SET(sockfd, &excptfds); +// } +// } + +// int num_triggered = select(max_fd + 1, &readfds, NULL, &excptfds, NULL); +// // Hard error (or someone closed a socket on another thread) +// if (num_triggered == -1) { +// assert(errno == EBADF); +// continue; +// } + +// assert(num_triggered > 0); + +// // Notice event trigger +// if (FD_ISSET(socket_change_fd, &readfds)) { +// read(socket_change_fd, &signal, sizeof(signal)); +// num_triggered--; +// } + +// // Handle active FDs, close the dead ones +// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { +// int sockfd = i + LWIP_SOCKET_OFFSET; +// if (socket_fd_state[i] != FDSTATE_CLOSED) { +// if (FD_ISSET(sockfd, &readfds) || FD_ISSET(sockfd, &excptfds)) { +// if (socket_fd_state[i] == FDSTATE_CLOSING) { +// socket_fd_state[i] = FDSTATE_CLOSED; +// num_triggered--; +// } +// } +// } +// } + +// if (num_triggered > 0) { +// // Wake up CircuitPython by queuing request +// supervisor_workflow_request_background(); +// ulTaskNotifyTake(pdTRUE, portMAX_DELAY); +// } +// } + +// close(socket_change_fd); +// socket_change_fd = -1; +// vTaskDelete(NULL); +// } + +void socket_user_reset(void) { + // if (socket_change_fd < 0) { + // esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT(); + // ESP_ERROR_CHECK(esp_vfs_eventfd_register(&config)); + // + // // Clear initial socket states + // for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { + // socket_fd_state[i] = FDSTATE_CLOSED; + // user_socket[i] = NULL; + // } + // socket_change_fd = eventfd(0, 0); + // // Run this at the same priority as CP so that the web workflow background task can be + // // queued while CP is running. Both tasks can still sleep and, therefore, sleep overall. + // socket_select_task_handle = xTaskCreateStaticPinnedToCore(socket_select_task, + // "socket_select", + // 2 * configMINIMAL_STACK_SIZE, + // NULL, + // uxTaskPriorityGet(NULL), + // socket_select_stack, + // &socket_select_task_buffer, + // xPortGetCoreID()); + // } else { + // // Not init - close open user sockets + // for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { + // if ((socket_fd_state[i] == FDSTATE_OPEN) && user_socket[i]) { + // common_hal_socketpool_socket_close(user_socket[i]); + // } + // } + // } +} + +// Unblock select task (ok if not blocked yet) +void socketpool_socket_poll_resume(void) { + // if (socket_select_task_handle) { + // xTaskNotifyGive(socket_select_task_handle); + // } +} + +// The writes below send an event to the socket select task so that it redoes the +// select with the new open socket set. + +// static bool register_open_socket(int fd) { +// // if (fd < FD_SETSIZE) { +// // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_OPEN; +// // user_socket[fd - LWIP_SOCKET_OFFSET] = NULL; +// // +// // uint64_t signal = 1; +// // write(socket_change_fd, &signal, sizeof(signal)); +// // socketpool_socket_poll_resume(); +// // return true; +// // } +// // return false; +// return 0; +// } + +// static void mark_user_socket(int fd, socketpool_socket_obj_t *obj) { +// // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_OPEN; +// // user_socket[fd - LWIP_SOCKET_OFFSET] = obj; +// // // No need to wakeup select task +// } + +// static bool _socketpool_socket(socketpool_socketpool_obj_t *self, +// socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, +// int proto, +// socketpool_socket_obj_t *sock) { +// int addr_family; +// int ipproto; + +// if (family == SOCKETPOOL_AF_INET) { +// addr_family = AF_INET; +// ipproto = IPPROTO_IP; +// #if CIRCUITPY_SOCKETPOOL_IPV6 +// } else { // INET6 +// addr_family = AF_INET6; +// ipproto = IPPROTO_IPV6; +// #endif +// } + +// int socket_type; +// if (type == SOCKETPOOL_SOCK_STREAM) { +// socket_type = SOCK_STREAM; +// } else if (type == SOCKETPOOL_SOCK_DGRAM) { +// socket_type = SOCK_DGRAM; +// } else { // SOCKETPOOL_SOCK_RAW +// socket_type = SOCK_RAW; +// ipproto = proto; +// } +// sock->type = socket_type; +// sock->family = addr_family; +// sock->ipproto = ipproto; +// sock->pool = self; +// sock->timeout_ms = (uint)-1; + +// // Create LWIP socket +// int socknum = -1; +// socknum = lwip_socket(sock->family, sock->type, sock->ipproto); +// if (socknum < 0) { +// return false; +// } + +// sock->num = socknum; +// // Sockets should be nonblocking in most cases +// lwip_fcntl(socknum, F_SETFL, O_NONBLOCK); + +// return true; +// } + +// special entry for workflow listener (register system socket) +bool socketpool_socket(socketpool_socketpool_obj_t *self, + socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, + int proto, socketpool_socket_obj_t *sock) { + // + // if (!_socketpool_socket(self, family, type, proto, sock)) { + // return false; + // } + // + // // This shouldn't happen since we have room for the same number of sockets as LWIP. + // if (!register_open_socket(sock->num)) { + // lwip_close(sock->num); + // return false; + // } + // return true; + return 0; +} + +socketpool_socket_obj_t *common_hal_socketpool_socket(socketpool_socketpool_obj_t *self, + socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, int proto) { + // switch (family) { + // #if CIRCUITPY_SOCKETPOOL_IPV6 + // case SOCKETPOOL_AF_INET6: + // #endif + // case SOCKETPOOL_AF_INET: + // break; + // default: + // mp_raise_NotImplementedError(MP_ERROR_TEXT("Unsupported socket type")); + // } + // + // socketpool_socket_obj_t *sock = mp_obj_malloc_with_finaliser(socketpool_socket_obj_t, &socketpool_socket_type); + // + // if (!_socketpool_socket(self, family, type, proto, sock)) { + // mp_raise_RuntimeError(MP_ERROR_TEXT("Out of sockets")); + // } + // mark_user_socket(sock->num, sock); + // return sock; + return NULL; +} + +int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, socketpool_socket_obj_t *accepted) { + // struct sockaddr_storage peer_addr; + // socklen_t socklen = sizeof(peer_addr); + // int newsoc = -1; + // bool timed_out = false; + // uint64_t start_ticks = supervisor_ticks_ms64(); + // + // // Allow timeouts and interrupts + // while (newsoc == -1 && !timed_out) { + // if (self->timeout_ms != (uint)-1 && self->timeout_ms != 0) { + // timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; + // } + // RUN_BACKGROUND_TASKS; + // newsoc = lwip_accept(self->num, (struct sockaddr *)&peer_addr, &socklen); + // // In non-blocking mode, fail instead of timing out + // if (newsoc == -1 && (self->timeout_ms == 0 || mp_hal_is_interrupted())) { + // return -MP_EAGAIN; + // } + // } + // + // if (timed_out) { + // return -ETIMEDOUT; + // } + // + // if (newsoc < 0) { + // return -MP_EBADF; + // } + // + // // We got a socket. New client socket will not be non-blocking by default, so make it non-blocking. + // lwip_fcntl(newsoc, F_SETFL, O_NONBLOCK); + // + // if (accepted != NULL) { + // // Error if called with open socket object. + // assert(common_hal_socketpool_socket_get_closed(accepted)); + // + // // Register if system socket + // if (!register_open_socket(newsoc)) { + // lwip_close(newsoc); + // return -MP_EBADF; + // } + // + // // Replace the old accepted socket with the new one. + // accepted->num = newsoc; + // accepted->pool = self->pool; + // accepted->connected = true; + // accepted->type = self->type; + // } + // + // if (peer_out) { + // *peer_out = sockaddr_to_tuple(&peer_addr); + // } + // + // return newsoc; + return 0; +} + +socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out) { + // // Set the socket type only after the socketpool_socket_accept succeeds, so that the + // // finaliser is not called on a bad socket. + // socketpool_socket_obj_t *sock = mp_obj_malloc_with_finaliser(socketpool_socket_obj_t, NULL); + // int newsoc = socketpool_socket_accept(self, peer_out, NULL); + // + // if (newsoc > 0) { + // // Create the socket + // mark_user_socket(newsoc, sock); + // sock->base.type = &socketpool_socket_type; + // sock->num = newsoc; + // sock->pool = self->pool; + // sock->connected = true; + // sock->type = self->type; + // + // return sock; + // } else { + // mp_raise_OSError(-newsoc); + // return NULL; + // } + return NULL; +} + +size_t common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, + const char *host, size_t hostlen, uint32_t port) { + // struct sockaddr_storage bind_addr; + // const char *broadcast = ""; + // + // bind_addr.ss_family = self->family; + // + // #if CIRCUITPY_SOCKETPOOL_IPV6 + // if (self->family == AF_INET6) { + // struct sockaddr_in6 *addr6 = (void *)&bind_addr; + // addr6->sin6_port = htons(port); + // // no ipv6 broadcast + // if (hostlen == 0) { + // memset(&addr6->sin6_addr, 0, sizeof(addr6->sin6_addr)); + // } else { + // socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, port); + // } + // } else + // #endif + // { + // struct sockaddr_in *addr4 = (void *)&bind_addr; + // addr4->sin_port = htons(port); + // if (hostlen == 0) { + // addr4->sin_addr.s_addr = IPADDR_ANY; + // } else if (hostlen == strlen(broadcast) && + // memcmp(host, broadcast, strlen(broadcast)) == 0) { + // addr4->sin_addr.s_addr = IPADDR_BROADCAST; + // } else { + // socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, port); + // } + // } + // + // int result = lwip_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); + // if (result == 0) { + // return 0; + // } + // return errno; + return 0; +} + +void socketpool_socket_close(socketpool_socket_obj_t *self) { + // #if CIRCUITPY_SSL + // if (self->ssl_socket) { + // ssl_sslsocket_obj_t *ssl_socket = self->ssl_socket; + // self->ssl_socket = NULL; + // common_hal_ssl_sslsocket_close(ssl_socket); + // return; + // } + // #endif + // self->connected = false; + // int fd = self->num; + // // Ignore bogus/closed sockets + // if (fd >= LWIP_SOCKET_OFFSET) { + // if (user_socket[fd - LWIP_SOCKET_OFFSET] == NULL) { + // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_CLOSING; + // lwip_shutdown(fd, SHUT_RDWR); + // lwip_close(fd); + // } else { + // lwip_shutdown(fd, SHUT_RDWR); + // lwip_close(fd); + // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_CLOSED; + // user_socket[fd - LWIP_SOCKET_OFFSET] = NULL; + // } + // } + // self->num = -1; +} + +void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self) { + // socketpool_socket_close(self); +} + +void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, + const char *host, size_t hostlen, uint32_t port) { + // struct sockaddr_storage addr; + // resolve_host_or_throw(self, host, &addr, port); + // + // // Replace above with function call ----- + // + // // Emulate SO_CONTIMEO, which is not implemented by lwip. + // // All our sockets are non-blocking, so we check the timeout ourselves. + // + // int result = -1; + // result = lwip_connect(self->num, (struct sockaddr *)&addr, addr.s2_len); + // + // if (result == 0) { + // // Connected immediately. + // self->connected = true; + // return; + // } + // + // if (result < 0 && errno != EINPROGRESS) { + // // Some error happened; error is in errno. + // mp_raise_OSError(errno); + // return; + // } + // + // struct timeval timeout = { + // .tv_sec = 0, + // .tv_usec = SOCKET_CONNECT_POLL_INTERVAL_MS * 1000, + // }; + // + // // Keep checking, using select(), until timeout expires, at short intervals. + // // This allows ctrl-C interrupts to be detected and background tasks to run. + // mp_uint_t timeout_left = self->timeout_ms; + // + // while (timeout_left > 0) { + // RUN_BACKGROUND_TASKS; + // // Allow ctrl-C interrupt + // if (mp_hal_is_interrupted()) { + // return; + // } + // + // fd_set fds; + // FD_ZERO(&fds); + // FD_SET(self->num, &fds); + // + // result = select(self->num + 1, NULL, &fds, NULL, &timeout); + // if (result == 0) { + // // No change to fd's after waiting for timeout, so try again if some time is still left. + // // Don't wrap below 0, because we're using a uint. + // if (timeout_left < SOCKET_CONNECT_POLL_INTERVAL_MS) { + // timeout_left = 0; + // } else { + // timeout_left -= SOCKET_CONNECT_POLL_INTERVAL_MS; + // } + // continue; + // } + // + // if (result < 0) { + // // Some error happened when doing select(); error is in errno. + // mp_raise_OSError(errno); + // } + // + // // select() indicated the socket is writable. Check if any connection error occurred. + // int error_code = 0; + // socklen_t socklen = sizeof(error_code); + // result = getsockopt(self->num, SOL_SOCKET, SO_ERROR, &error_code, &socklen); + // if (result < 0 || error_code != 0) { + // mp_raise_OSError(error_code); + // } + // self->connected = true; + // return; + // } + // + // // No connection after timeout. The connection attempt is not stopped. + // // This imitates what happens in Python. + // mp_raise_OSError(ETIMEDOUT); +} + +bool common_hal_socketpool_socket_get_closed(socketpool_socket_obj_t *self) { + // return self->num < 0; + return 0; +} + +bool common_hal_socketpool_socket_get_connected(socketpool_socket_obj_t *self) { + // return self->connected; + return 0; +} + +bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *self, int backlog) { + // return lwip_listen(self->num, backlog) == 0; + return 0; +} + +mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *self, + uint8_t *buf, uint32_t len, mp_obj_t *source_out) { + // + // struct sockaddr_storage 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) { + // mp_raise_OSError(ETIMEDOUT); + // } + // + // if (received < 0) { + // mp_raise_BrokenPipeError(); + // return 0; + // } + // + // if (source_out) { + // *source_out = sockaddr_to_tuple(&source_addr); + // } + // + // return received; + return 0; +} + +int socketpool_socket_recv_into(socketpool_socket_obj_t *self, + const uint8_t *buf, uint32_t len) { + // int received = 0; + // bool timed_out = false; + // + // if (self->num != -1) { + // // LWIP Socket + // uint64_t start_ticks = supervisor_ticks_ms64(); + // received = -1; + // while (received == -1 && + // !timed_out) { + // 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_recv(self->num, (void *)buf, len, 0); + // // In non-blocking mode, fail instead of looping + // if (received < 1 && self->timeout_ms == 0) { + // if ((received == 0) || (errno == ENOTCONN)) { + // self->connected = false; + // return -MP_ENOTCONN; + // } + // return -MP_EAGAIN; + // } + // // Check this after going through the loop once so it can make + // // progress while interrupted. + // if (mp_hal_is_interrupted()) { + // if (received == -1) { + // return -MP_EAGAIN; + // } + // break; + // } + // } + // } else { + // return -MP_EBADF; + // } + // + // if (timed_out) { + // return -ETIMEDOUT; + // } + // return received; + return 0; +} + +mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) { + // int received = socketpool_socket_recv_into(self, buf, len); + // if (received < 0) { + // mp_raise_OSError(-received); + // } + // return received; + return 0; +} + +int 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 { + // sent = -MP_EBADF; + // } + // + // if (sent < 0) { + // if (errno == ECONNRESET || errno == ENOTCONN) { + // self->connected = false; + // } + // return -errno; + // } + // + // return sent; + return 0; +} + +mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) { + // int sent = socketpool_socket_send(self, buf, len); + // + // if (sent < 0) { + // mp_raise_OSError(-sent); + // } + // return sent; + return 0; +} + +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) { + // + // struct sockaddr_storage addr; + // resolve_host_or_throw(self, host, &addr, port); + // + // int bytes_sent = lwip_sendto(self->num, buf, len, 0, (struct sockaddr *)&addr, addr.s2_len); + // if (bytes_sent < 0) { + // mp_raise_BrokenPipeError(); + // return 0; + // } + // return bytes_sent; + return 0; +} + +void common_hal_socketpool_socket_settimeout(socketpool_socket_obj_t *self, uint32_t timeout_ms) { + // self->timeout_ms = timeout_ms; +} + +mp_int_t common_hal_socketpool_socket_get_type(socketpool_socket_obj_t *self) { + // return self->type; + return 0; +} + + +int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int level, int optname, const void *value, size_t optlen) { + // int err = lwip_setsockopt(self->num, level, optname, value, optlen); + // if (err != 0) { + // return -errno; + // } + // return 0; + return 0; +} + +bool common_hal_socketpool_readable(socketpool_socket_obj_t *self) { + // struct timeval immediate = {0, 0}; + // + // fd_set fds; + // FD_ZERO(&fds); + // FD_SET(self->num, &fds); + // int num_triggered = select(self->num + 1, &fds, NULL, &fds, &immediate); + // + // // including returning true in the error case + // return num_triggered != 0; + return 0; +} + +bool common_hal_socketpool_writable(socketpool_socket_obj_t *self) { + // struct timeval immediate = {0, 0}; + // + // fd_set fds; + // FD_ZERO(&fds); + // FD_SET(self->num, &fds); + // int num_triggered = select(self->num + 1, NULL, &fds, &fds, &immediate); + // + // // including returning true in the error case + // return num_triggered != 0; + return 0; +} + +void socketpool_socket_move(socketpool_socket_obj_t *self, socketpool_socket_obj_t *sock) { + // *sock = *self; + // self->connected = false; + // self->num = -1; +} + +void socketpool_socket_reset(socketpool_socket_obj_t *self) { + // if (self->base.type == &socketpool_socket_type) { + // return; + // } + // self->base.type = &socketpool_socket_type; + // self->connected = false; + // self->num = -1; +} diff --git a/devices/airlift/common-hal/socketpool/Socket.h b/devices/airlift/common-hal/socketpool/Socket.h new file mode 100644 index 0000000000000..92cb486b23a83 --- /dev/null +++ b/devices/airlift/common-hal/socketpool/Socket.h @@ -0,0 +1,29 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +#include "common-hal/socketpool/SocketPool.h" + +typedef struct ssl_sslsocket_obj ssl_sslsocket_obj_t; + +typedef struct { + mp_obj_base_t base; + int num; + int type; + int family; + int ipproto; + bool connected; + socketpool_socketpool_obj_t *pool; + ssl_sslsocket_obj_t *ssl_socket; + mp_uint_t timeout_ms; +} socketpool_socket_obj_t; + +void socket_user_reset(void); +// Unblock workflow socket select thread (platform specific) +void socketpool_socket_poll_resume(void); diff --git a/devices/airlift/common-hal/socketpool/SocketPool.c b/devices/airlift/common-hal/socketpool/SocketPool.c new file mode 100644 index 0000000000000..30f8fece5fc41 --- /dev/null +++ b/devices/airlift/common-hal/socketpool/SocketPool.c @@ -0,0 +1,124 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/socketpool/SocketPool.h" +#include "common-hal/socketpool/Socket.h" + +#include "py/runtime.h" +#include "shared-bindings/wifi/__init__.h" +#include "common-hal/socketpool/__init__.h" + +void common_hal_socketpool_socketpool_construct(socketpool_socketpool_obj_t *self, mp_obj_t radio) { + // if (radio != MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj)) { + // mp_raise_ValueError(MP_ERROR_TEXT("SocketPool can only be used with wifi.radio")); + // } +} + +// common_hal_socketpool_socket is in socketpool/Socket.c to centralize open socket tracking. + +// int socketpool_getaddrinfo_common(const char *host, int service, const struct addrinfo *hints, struct addrinfo **res) { +// // As of 2022, the version of lwip in esp-idf does not handle the +// // trailing-dot syntax of domain names, so emulate it. +// // Remove this once https://github.com/espressif/esp-idf/issues/10013 has +// // been implemented +// if (host) { +// size_t strlen_host = strlen(host); +// if (strlen_host && host[strlen_host - 1] == '.') { +// mp_obj_t nodot = mp_obj_new_str(host, strlen_host - 1); +// host = mp_obj_str_get_str(nodot); +// } +// } + +// char service_buf[6]; +// snprintf(service_buf, sizeof(service_buf), "%d", service); + +// return lwip_getaddrinfo(host, service_buf, hints, res); +// } + +// static mp_obj_t format_address(const struct sockaddr *addr, int family) { +// char ip_str[IPADDR_STRLEN_MAX]; // big enough for any supported address type +// const struct sockaddr_in *a = (void *)addr; + +// switch (family) { +// #if CIRCUITPY_SOCKETPOOL_IPV6 +// case AF_INET6: +// inet_ntop(family, &((const struct sockaddr_in6 *)a)->sin6_addr, ip_str, sizeof(ip_str)); +// break; +// #endif +// default: +// case AF_INET: +// inet_ntop(family, &((const struct sockaddr_in *)a)->sin_addr, ip_str, sizeof(ip_str)); +// break; +// } +// return mp_obj_new_str(ip_str, strlen(ip_str)); +// return mp_const_none; +// } + +// static mp_obj_t convert_sockaddr(const struct addrinfo *ai, int port) { +// // #if CIRCUITPY_SOCKETPOOL_IPV6 +// // mp_int_t n_tuple = ai->ai_family == AF_INET6 ? 4 : 2; +// // #else +// // mp_int_t n_tuple = 2; +// // #endif +// // mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(n_tuple, NULL)); +// // result->items[0] = format_address(ai->ai_addr, ai->ai_family); +// // result->items[1] = MP_OBJ_NEW_SMALL_INT(port); +// // #if CIRCUITPY_SOCKETPOOL_IPV6 +// // if (ai->ai_family == AF_INET6) { +// // const struct sockaddr_in6 *ai6 = (void *)ai->ai_addr; +// // result->items[2] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_flowinfo); +// // result->items[3] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_scope_id); +// // } +// // #endif +// // return result; +// return mp_const_none; +// } + +// static mp_obj_t convert_addrinfo(const struct addrinfo *ai, int port) { +// // MP_STATIC_ASSERT(AF_INET == SOCKETPOOL_AF_INET); +// // #if CIRCUITPY_SOCKETPOOL_IPV6 +// // MP_STATIC_ASSERT(AF_INET6 == SOCKETPOOL_AF_INET6); +// // #endif +// // // MP_STATIC_ASSERT(AF_UNSPEC == SOCKETPOOL_AF_UNSPEC); +// // mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL)); +// // result->items[0] = MP_OBJ_NEW_SMALL_INT(ai->ai_family); +// // result->items[1] = MP_OBJ_NEW_SMALL_INT(ai->ai_socktype); +// // result->items[2] = MP_OBJ_NEW_SMALL_INT(ai->ai_protocol); +// // result->items[3] = ai->ai_canonname ? mp_obj_new_str(ai->ai_canonname, strlen(ai->ai_canonname)) : MP_OBJ_NEW_QSTR(MP_QSTR_); +// // result->items[4] = convert_sockaddr(ai, port); +// // return result; +// return mp_const_none; +// } + +mp_obj_t common_hal_socketpool_getaddrinfo_raise(socketpool_socketpool_obj_t *self, const char *host, int port, int family, int type, int proto, int flags) { + // const struct addrinfo hints = { + // .ai_flags = flags, + // .ai_family = family, + // .ai_protocol = proto, + // .ai_socktype = type, + // }; + // + // struct addrinfo *res = NULL; + // int err = socketpool_getaddrinfo_common(host, port, &hints, &res); + // if (err != 0 || res == NULL) { + // common_hal_socketpool_socketpool_raise_gaierror_noname(); + // } + // + // nlr_buf_t nlr; + // if (nlr_push(&nlr) == 0) { + // mp_obj_t result = mp_obj_new_list(0, NULL); + // for (struct addrinfo *ai = res; ai; ai = ai->ai_next) { + // mp_obj_list_append(result, convert_addrinfo(ai, port)); + // } + // nlr_pop(); + // lwip_freeaddrinfo(res); + // return result; + // } else { + // lwip_freeaddrinfo(res); + // nlr_raise(MP_OBJ_FROM_PTR(nlr.ret_val)); + // } + return mp_const_none; +} diff --git a/devices/airlift/common-hal/socketpool/SocketPool.h b/devices/airlift/common-hal/socketpool/SocketPool.h new file mode 100644 index 0000000000000..66113c41bda53 --- /dev/null +++ b/devices/airlift/common-hal/socketpool/SocketPool.h @@ -0,0 +1,13 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; +} socketpool_socketpool_obj_t; diff --git a/devices/airlift/common-hal/socketpool/__init__.c b/devices/airlift/common-hal/socketpool/__init__.c new file mode 100644 index 0000000000000..3d004370ddb25 --- /dev/null +++ b/devices/airlift/common-hal/socketpool/__init__.c @@ -0,0 +1,13 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/socketpool/__init__.h" + +#include "common-hal/socketpool/Socket.h" + +void socketpool_user_reset(void) { + // socket_user_reset(); +} diff --git a/devices/airlift/common-hal/socketpool/__init__.h b/devices/airlift/common-hal/socketpool/__init__.h new file mode 100644 index 0000000000000..118a6a5e16d1b --- /dev/null +++ b/devices/airlift/common-hal/socketpool/__init__.h @@ -0,0 +1,12 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +struct addrinfo; + +// int socketpool_getaddrinfo_common(const char *host, int service, const struct addrinfo *hints, struct addrinfo **res); +// void socketpool_resolve_host_or_throw(int family, int type, const char *hostname, struct sockaddr_storage *addr, int port); diff --git a/devices/airlift/common-hal/ssl/SSLContext.c b/devices/airlift/common-hal/ssl/SSLContext.c new file mode 100644 index 0000000000000..aef51fab1489b --- /dev/null +++ b/devices/airlift/common-hal/ssl/SSLContext.c @@ -0,0 +1,44 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/ssl/SSLContext.h" +#include "shared-bindings/ssl/SSLSocket.h" + +#include "py/runtime.h" +#include "py/stream.h" + +void common_hal_ssl_sslcontext_construct(ssl_sslcontext_obj_t *self) { + // common_hal_ssl_sslcontext_set_default_verify_paths(self); +} + +void common_hal_ssl_sslcontext_load_verify_locations(ssl_sslcontext_obj_t *self, + const char *cadata) { + // self->crt_bundle_attach = NULL; + // self->use_global_ca_store = false; + // self->cacert_buf = (const unsigned char *)cadata; + // self->cacert_bytes = *cadata ? strlen(cadata) + 1 : 0; +} + +void common_hal_ssl_sslcontext_set_default_verify_paths(ssl_sslcontext_obj_t *self) { + // self->crt_bundle_attach = crt_bundle_attach; + // self->use_global_ca_store = true; + // self->cacert_buf = NULL; + // self->cacert_bytes = 0; +} + +bool common_hal_ssl_sslcontext_get_check_hostname(ssl_sslcontext_obj_t *self) { + // return self->check_name; + return false; +} + +void common_hal_ssl_sslcontext_set_check_hostname(ssl_sslcontext_obj_t *self, bool value) { + // self->check_name = value; +} + +void common_hal_ssl_sslcontext_load_cert_chain(ssl_sslcontext_obj_t *self, mp_buffer_info_t *cert_buf, mp_buffer_info_t *key_buf) { + // self->cert_buf = *cert_buf; + // self->key_buf = *key_buf; +} diff --git a/devices/airlift/common-hal/ssl/SSLContext.h b/devices/airlift/common-hal/ssl/SSLContext.h new file mode 100644 index 0000000000000..d420803d1611c --- /dev/null +++ b/devices/airlift/common-hal/ssl/SSLContext.h @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + bool check_name, use_global_ca_store; + const unsigned char *cacert_buf; + // size_t cacert_bytes; + // int (*crt_bundle_attach)(mbedtls_ssl_config *conf); + // mp_buffer_info_t cert_buf, key_buf; +} ssl_sslcontext_obj_t; diff --git a/devices/airlift/common-hal/ssl/SSLSocket.c b/devices/airlift/common-hal/ssl/SSLSocket.c new file mode 100644 index 0000000000000..ab329e445b60b --- /dev/null +++ b/devices/airlift/common-hal/ssl/SSLSocket.c @@ -0,0 +1,471 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2016 Linaro Ltd. +// SPDX-FileCopyrightText: Copyright (c) 2019 Paul Sokolovsky +// SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/ssl/SSLSocket.h" +#include "shared-bindings/ssl/SSLContext.h" + +#include "shared/runtime/interrupt_char.h" +#include "shared/netutils/netutils.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "supervisor/shared/tick.h" + +#include "shared-bindings/socketpool/enum.h" + +#define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR) + +// static NORETURN void mbedtls_raise_error(int err) { +// // _mbedtls_ssl_send and _mbedtls_ssl_recv (below) turn positive error codes from the +// // underlying socket into negative codes to pass them through mbedtls. Here we turn them +// // positive again so they get interpreted as the OSError they really are. The +// // cut-off of -256 is a bit hacky, sigh. +// if (err < 0 && err > -256) { +// mp_raise_OSError(-err); +// } + +// if (err == MBEDTLS_ERR_SSL_WANT_WRITE || err == MBEDTLS_ERR_SSL_WANT_READ) { +// mp_raise_OSError(MP_EWOULDBLOCK); +// } + +// #if defined(MBEDTLS_ERROR_C) +// // Including mbedtls_strerror takes about 1.5KB due to the error strings. +// // MBEDTLS_ERROR_C is the define used by mbedtls to conditionally include mbedtls_strerror. +// // It is set/unset in the MBEDTLS_CONFIG_FILE which is defined in the Makefile. + +// // Try to allocate memory for the message +// #define ERR_STR_MAX 80 // mbedtls_strerror truncates if it doesn't fit +// mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); +// byte *o_str_buf = m_malloc_without_collect(ERR_STR_MAX); +// if (o_str == NULL || o_str_buf == NULL) { +// mp_raise_OSError(err); +// } + +// // print the error message into the allocated buffer +// mbedtls_strerror(err, (char *)o_str_buf, ERR_STR_MAX); +// size_t len = strlen((char *)o_str_buf); + +// // Put the exception object together +// o_str->base.type = &mp_type_str; +// o_str->data = o_str_buf; +// o_str->len = len; +// o_str->hash = qstr_compute_hash(o_str->data, o_str->len); +// // raise +// mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), MP_OBJ_FROM_PTR(o_str)}; +// nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args)); +// #else +// // mbedtls is compiled without error strings so we simply return the err number +// mp_raise_OSError(err); // err is typically a large negative number +// #endif +// } + +// Because ssl_socket_send and ssl_socket_recv_into are callbacks from mbedtls code, +// it is not OK to exit them by raising an exception (nlr_jump'ing through +// foreign code is not permitted). Instead, preserve the error number of any OSError +// and turn anything else into -MP_EINVAL. +// static int call_method_errno(size_t n_args, const mp_obj_t *args) { +// nlr_buf_t nlr; +// mp_int_t result = -MP_EINVAL; +// if (nlr_push(&nlr) == 0) { +// mp_obj_t obj_result = mp_call_method_n_kw(n_args, 0, args); +// result = (obj_result == mp_const_none) ? 0 : mp_obj_get_int(obj_result); +// nlr_pop(); +// return result; +// } else { +// mp_obj_t exc = MP_OBJ_FROM_PTR(nlr.ret_val); +// if (nlr_push(&nlr) == 0) { +// result = -mp_obj_get_int(mp_load_attr(exc, MP_QSTR_errno)); +// nlr_pop(); +// } +// } +// return result; +// } + +// static int ssl_socket_send(ssl_sslsocket_obj_t *self, const byte *buf, size_t len) { +// mp_obj_array_t mv; +// mp_obj_memoryview_init(&mv, 'B', 0, len, (void *)buf); + +// self->send_args[2] = MP_OBJ_FROM_PTR(&mv); +// return call_method_errno(1, self->send_args); +// } + +// static int ssl_socket_recv_into(ssl_sslsocket_obj_t *self, byte *buf, size_t len) { +// mp_obj_array_t mv; +// mp_obj_memoryview_init(&mv, 'B' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW, 0, len, buf); + +// self->recv_into_args[2] = MP_OBJ_FROM_PTR(&mv); +// return call_method_errno(1, self->recv_into_args); +// } + +// static void ssl_socket_connect(ssl_sslsocket_obj_t *self, mp_obj_t addr_in) { +// self->connect_args[2] = addr_in; +// mp_call_method_n_kw(1, 0, self->connect_args); +// } + +// static void ssl_socket_bind(ssl_sslsocket_obj_t *self, mp_obj_t addr_in) { +// self->bind_args[2] = addr_in; +// mp_call_method_n_kw(1, 0, self->bind_args); +// } + +// static void ssl_socket_close(ssl_sslsocket_obj_t *self) { +// // swallow any exception raised by the underlying close method. +// // This is not ideal. However, it avoids printing "MemoryError:" +// // when attempting to close a userspace socket object during gc_sweep_all +// nlr_buf_t nlr; +// if (nlr_push(&nlr) == 0) { +// mp_call_method_n_kw(0, 0, self->close_args); +// nlr_pop(); +// } else { +// nlr_pop(); +// } +// } + +// static void ssl_socket_setsockopt(ssl_sslsocket_obj_t *self, mp_obj_t level_obj, mp_obj_t opt_obj, mp_obj_t optval_obj) { +// self->setsockopt_args[2] = level_obj; +// self->setsockopt_args[3] = opt_obj; +// self->setsockopt_args[4] = optval_obj; +// mp_call_method_n_kw(3, 0, self->setsockopt_args); +// } + +// static void ssl_socket_settimeout(ssl_sslsocket_obj_t *self, mp_obj_t timeout_obj) { +// self->settimeout_args[2] = timeout_obj; +// mp_call_method_n_kw(1, 0, self->settimeout_args); +// } + +// static void ssl_socket_listen(ssl_sslsocket_obj_t *self, mp_int_t backlog) { +// self->listen_args[2] = MP_OBJ_NEW_SMALL_INT(backlog); +// mp_call_method_n_kw(1, 0, self->listen_args); +// } + +// static mp_obj_t ssl_socket_accept(ssl_sslsocket_obj_t *self) { +// // return mp_call_method_n_kw(0, 0, self->accept_args); +// return mp_const_none; +// } + +// static int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) { +// ssl_sslsocket_obj_t *self = (ssl_sslsocket_obj_t *)ctx; + +// mp_int_t out_sz = ssl_socket_send(self, buf, len); +// DEBUG_PRINT("socket_send() -> %d", out_sz); +// if (out_sz < 0) { +// int err = -out_sz; +// DEBUG_PRINT("sock_stream->write() -> %d nonblocking? %d", out_sz, mp_is_nonblocking_error(err)); +// if (mp_is_nonblocking_error(err)) { +// return MBEDTLS_ERR_SSL_WANT_WRITE; +// } +// } +// return out_sz; +// } + +// static int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) { +// ssl_sslsocket_obj_t *self = (ssl_sslsocket_obj_t *)ctx; + +// mp_int_t out_sz = ssl_socket_recv_into(self, buf, len); +// DEBUG_PRINT("socket_recv() -> %d", out_sz); +// if (out_sz < 0) { +// int err = -out_sz; +// if (mp_is_nonblocking_error(err)) { +// return MBEDTLS_ERR_SSL_WANT_READ; +// } +// } +// return out_sz; +// } + + +ssl_sslsocket_obj_t *common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t *self, + mp_obj_t socket, bool server_side, const char *server_hostname) { + // + // mp_int_t socket_type = mp_obj_get_int(mp_load_attr(socket, MP_QSTR_type)); + // if (socket_type != SOCKETPOOL_SOCK_STREAM) { + // mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid socket for TLS")); + // } + // + // ssl_sslsocket_obj_t *o = mp_obj_malloc_with_finaliser(ssl_sslsocket_obj_t, &ssl_sslsocket_type); + // o->ssl_context = self; + // o->sock_obj = socket; + // o->poll_mask = 0; + // + // mp_load_method(socket, MP_QSTR_accept, o->accept_args); + // mp_load_method(socket, MP_QSTR_bind, o->bind_args); + // mp_load_method(socket, MP_QSTR_close, o->close_args); + // mp_load_method(socket, MP_QSTR_connect, o->connect_args); + // mp_load_method(socket, MP_QSTR_listen, o->listen_args); + // mp_load_method(socket, MP_QSTR_recv_into, o->recv_into_args); + // mp_load_method(socket, MP_QSTR_send, o->send_args); + // mp_load_method(socket, MP_QSTR_settimeout, o->settimeout_args); + // mp_load_method(socket, MP_QSTR_setsockopt, o->setsockopt_args); + // + // mbedtls_ssl_init(&o->ssl); + // mbedtls_ssl_config_init(&o->conf); + // mbedtls_x509_crt_init(&o->cacert); + // mbedtls_x509_crt_init(&o->cert); + // mbedtls_pk_init(&o->pkey); + // mbedtls_ctr_drbg_init(&o->ctr_drbg); + // #ifdef MBEDTLS_DEBUG_C + // // Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose + // mbedtls_debug_set_threshold(4); + // #endif + // + // mbedtls_entropy_init(&o->entropy); + // const byte seed[] = "upy"; + // int ret = mbedtls_ctr_drbg_seed(&o->ctr_drbg, mbedtls_entropy_func, &o->entropy, seed, sizeof(seed)); + // if (ret != 0) { + // goto cleanup; + // } + // + // ret = mbedtls_ssl_config_defaults(&o->conf, + // server_side ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, + // MBEDTLS_SSL_TRANSPORT_STREAM, + // MBEDTLS_SSL_PRESET_DEFAULT); + // if (ret != 0) { + // goto cleanup; + // } + // + // if (self->crt_bundle_attach != NULL) { + // mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + // self->crt_bundle_attach(&o->conf); + // } else if (self->cacert_buf && self->cacert_bytes) { + // ret = mbedtls_x509_crt_parse(&o->cacert, self->cacert_buf, self->cacert_bytes); + // if (ret != 0) { + // goto cleanup; + // } + // mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + // mbedtls_ssl_conf_ca_chain(&o->conf, &o->cacert, NULL); + // + // } else { + // mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_NONE); + // } + // mbedtls_ssl_conf_rng(&o->conf, mbedtls_ctr_drbg_random, &o->ctr_drbg); + // #ifdef MBEDTLS_DEBUG_C + // mbedtls_ssl_conf_dbg(&o->conf, mbedtls_debug, NULL); + // #endif + // + // ret = mbedtls_ssl_setup(&o->ssl, &o->conf); + // if (ret != 0) { + // goto cleanup; + // } + // + // if (server_hostname != NULL) { + // ret = mbedtls_ssl_set_hostname(&o->ssl, server_hostname); + // if (ret != 0) { + // goto cleanup; + // } + // } + // + // mbedtls_ssl_set_bio(&o->ssl, o, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL); + // + // if (self->cert_buf.buf != NULL) { + // #if MBEDTLS_VERSION_MAJOR >= 3 + // ret = mbedtls_pk_parse_key(&o->pkey, self->key_buf.buf, self->key_buf.len + 1, NULL, 0, urandom_adapter, NULL); + // #else + // ret = mbedtls_pk_parse_key(&o->pkey, self->key_buf.buf, self->key_buf.len + 1, NULL, 0); + // #endif + // if (ret != 0) { + // goto cleanup; + // } + // ret = mbedtls_x509_crt_parse(&o->cert, self->cert_buf.buf, self->cert_buf.len + 1); + // if (ret != 0) { + // goto cleanup; + // } + // + // ret = mbedtls_ssl_conf_own_cert(&o->conf, &o->cert, &o->pkey); + // if (ret != 0) { + // goto cleanup; + // } + // } + // return o; + // cleanup: + // mbedtls_pk_free(&o->pkey); + // mbedtls_x509_crt_free(&o->cert); + // mbedtls_x509_crt_free(&o->cacert); + // mbedtls_ssl_free(&o->ssl); + // mbedtls_ssl_config_free(&o->conf); + // mbedtls_ctr_drbg_free(&o->ctr_drbg); + // mbedtls_entropy_free(&o->entropy); + // + // if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) { + // mp_raise_type(&mp_type_MemoryError); + // } else if (ret == MBEDTLS_ERR_PK_BAD_INPUT_DATA) { + // mp_raise_ValueError(MP_ERROR_TEXT("invalid key")); + // } else if (ret == MBEDTLS_ERR_X509_BAD_INPUT_DATA) { + // mp_raise_ValueError(MP_ERROR_TEXT("invalid cert")); + // } else { + // mbedtls_raise_error(ret); + // } + return NULL; +} + +mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, uint8_t *buf, mp_uint_t len) { + // self->poll_mask = 0; + // int ret = mbedtls_ssl_read(&self->ssl, buf, len); + // DEBUG_PRINT("recv_into mbedtls_ssl_read() -> %d\n", ret); + // if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + // DEBUG_PRINT("returning %d\n", 0); + // // end of stream + // return 0; + // } + // if (ret >= 0) { + // DEBUG_PRINT("returning %d\n", ret); + // return ret; + // } + // if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + // self->poll_mask = MP_STREAM_POLL_WR; + // } + // DEBUG_PRINT("raising errno [error case] %d\n", ret); + // mbedtls_raise_error(ret); + return 0; +} + +mp_uint_t common_hal_ssl_sslsocket_send(ssl_sslsocket_obj_t *self, const uint8_t *buf, mp_uint_t len) { + // self->poll_mask = 0; + // int ret = mbedtls_ssl_write(&self->ssl, buf, len); + // DEBUG_PRINT("send mbedtls_ssl_write() -> %d\n", ret); + // if (ret >= 0) { + // DEBUG_PRINT("returning %d\n", ret); + // return ret; + // } + // if (ret == MBEDTLS_ERR_SSL_WANT_READ) { + // self->poll_mask = MP_STREAM_POLL_RD; + // } + // DEBUG_PRINT("raising errno [error case] %d\n", ret); + // mbedtls_raise_error(ret); + return 0; +} + +void common_hal_ssl_sslsocket_bind(ssl_sslsocket_obj_t *self, mp_obj_t addr_in) { + // ssl_socket_bind(self, addr_in); +} + +void common_hal_ssl_sslsocket_close(ssl_sslsocket_obj_t *self) { + // if (self->closed) { + // return; + // } + // self->closed = true; + // ssl_socket_close(self); + // mbedtls_pk_free(&self->pkey); + // mbedtls_x509_crt_free(&self->cert); + // mbedtls_x509_crt_free(&self->cacert); + // mbedtls_ssl_free(&self->ssl); + // mbedtls_ssl_config_free(&self->conf); + // mbedtls_ctr_drbg_free(&self->ctr_drbg); + // mbedtls_entropy_free(&self->entropy); +} + +// static void do_handshake(ssl_sslsocket_obj_t *self) { +// int ret; +// while ((ret = mbedtls_ssl_handshake(&self->ssl)) != 0) { +// if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { +// goto cleanup; +// } +// RUN_BACKGROUND_TASKS; +// if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { +// mp_handle_pending(true); +// } +// mp_hal_delay_ms(1); +// } + +// return; + +// cleanup: +// self->closed = true; +// mbedtls_pk_free(&self->pkey); +// mbedtls_x509_crt_free(&self->cert); +// mbedtls_x509_crt_free(&self->cacert); +// mbedtls_ssl_free(&self->ssl); +// mbedtls_ssl_config_free(&self->conf); +// mbedtls_ctr_drbg_free(&self->ctr_drbg); +// mbedtls_entropy_free(&self->entropy); + +// if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) { +// mp_raise_type(&mp_type_MemoryError); +// } else if (ret == MBEDTLS_ERR_PK_BAD_INPUT_DATA) { +// mp_raise_ValueError(MP_ERROR_TEXT("invalid key")); +// } else if (ret == MBEDTLS_ERR_X509_BAD_INPUT_DATA) { +// mp_raise_ValueError(MP_ERROR_TEXT("invalid cert")); +// } else { +// mbedtls_raise_error(ret); +// } +// } + +void common_hal_ssl_sslsocket_connect(ssl_sslsocket_obj_t *self, mp_obj_t addr_in) { + // ssl_socket_connect(self, addr_in); + // do_handshake(self); +} + +bool common_hal_ssl_sslsocket_get_closed(ssl_sslsocket_obj_t *self) { + // return self->closed; + return false; +} + +bool common_hal_ssl_sslsocket_get_connected(ssl_sslsocket_obj_t *self) { + // return !self->closed; + return false; +} + +void common_hal_ssl_sslsocket_listen(ssl_sslsocket_obj_t *self, int backlog) { + // return ssl_socket_listen(self, backlog); +} + +mp_obj_t common_hal_ssl_sslsocket_accept(ssl_sslsocket_obj_t *self) { + // mp_obj_t accepted = ssl_socket_accept(self); + // mp_obj_t sock = mp_obj_subscr(accepted, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_SENTINEL); + // ssl_sslsocket_obj_t *sslsock = common_hal_ssl_sslcontext_wrap_socket(self->ssl_context, sock, true, NULL); + // do_handshake(sslsock); + // mp_obj_t peer = mp_obj_subscr(accepted, MP_OBJ_NEW_SMALL_INT(1), MP_OBJ_SENTINEL); + // mp_obj_t tuple_contents[2]; + // tuple_contents[0] = MP_OBJ_FROM_PTR(sslsock); + // tuple_contents[1] = peer; + // return mp_obj_new_tuple(2, tuple_contents); + return mp_const_none; +} + +void common_hal_ssl_sslsocket_setsockopt(ssl_sslsocket_obj_t *self, mp_obj_t level_obj, mp_obj_t optname_obj, mp_obj_t optval_obj) { + // ssl_socket_setsockopt(self, level_obj, optname_obj, optval_obj); +} + +void common_hal_ssl_sslsocket_settimeout(ssl_sslsocket_obj_t *self, mp_obj_t timeout_obj) { + // ssl_socket_settimeout(self, timeout_obj); +} + +// static bool poll_common(ssl_sslsocket_obj_t *self, uintptr_t arg) { +// // Take into account that the library might have buffered data already +// int has_pending = 0; +// if (arg & MP_STREAM_POLL_RD) { +// has_pending = mbedtls_ssl_check_pending(&self->ssl); +// if (has_pending) { +// // Shortcut if we only need to read and we have buffered data, no need to go to the underlying socket +// return true; +// } +// } + +// // If the library signaled us that it needs reading or writing, only +// // check that direction +// if (self->poll_mask && (arg & MP_STREAM_POLL_RDWR)) { +// arg = (arg & ~MP_STREAM_POLL_RDWR) | self->poll_mask; +// } + +// // If direction the library needed is available, return a fake +// // result to the caller so that it reenters a read or a write to +// // allow the handshake to progress +// const mp_stream_p_t *stream_p = mp_get_stream_raise(self->sock_obj, MP_STREAM_OP_IOCTL); +// int errcode; +// mp_int_t ret = stream_p->ioctl(self->sock_obj, MP_STREAM_POLL, arg, &errcode); +// return ret != 0; +// } + +bool common_hal_ssl_sslsocket_readable(ssl_sslsocket_obj_t *self) { + // return poll_common(self, MP_STREAM_POLL_RD); + return false; +} + +bool common_hal_ssl_sslsocket_writable(ssl_sslsocket_obj_t *self) { + // return poll_common(self, MP_STREAM_POLL_WR); + return false; +} diff --git a/devices/airlift/common-hal/ssl/SSLSocket.h b/devices/airlift/common-hal/ssl/SSLSocket.h new file mode 100644 index 0000000000000..2af8039142452 --- /dev/null +++ b/devices/airlift/common-hal/ssl/SSLSocket.h @@ -0,0 +1,34 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Lucian Copeland for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef struct ssl_sslsocket_obj { + mp_obj_base_t base; + mp_obj_t sock_obj; + // ssl_sslcontext_obj_t *ssl_context; + // mbedtls_entropy_context entropy; + // mbedtls_ctr_drbg_context ctr_drbg; + // mbedtls_ssl_context ssl; + // mbedtls_ssl_config conf; + // mbedtls_x509_crt cacert; + // mbedtls_x509_crt cert; + // mbedtls_pk_context pkey; + uintptr_t poll_mask; + bool closed; + mp_obj_t accept_args[2]; + mp_obj_t bind_args[3]; + mp_obj_t close_args[2]; + mp_obj_t connect_args[3]; + mp_obj_t listen_args[3]; + mp_obj_t recv_into_args[3]; + mp_obj_t send_args[3]; + mp_obj_t setsockopt_args[5]; + mp_obj_t settimeout_args[3]; +} ssl_sslsocket_obj_t; diff --git a/devices/airlift/common-hal/ssl/__init__.c b/devices/airlift/common-hal/ssl/__init__.c new file mode 100644 index 0000000000000..3fe609f466741 --- /dev/null +++ b/devices/airlift/common-hal/ssl/__init__.c @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-module/ssl/__init__.h" +#include "shared-bindings/ssl/__init__.h" +#include "shared-bindings/ssl/SSLContext.h" + +void common_hal_ssl_create_default_context(ssl_sslcontext_obj_t *self) { + // common_hal_ssl_sslcontext_construct(self); +} + +void ssl_reset(void) { + // crt_bundle_detach(NULL); +} diff --git a/devices/airlift/common-hal/ssl/__init__.h b/devices/airlift/common-hal/ssl/__init__.h new file mode 100644 index 0000000000000..c5083fbe817fd --- /dev/null +++ b/devices/airlift/common-hal/ssl/__init__.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +void ssl_reset(void); diff --git a/devices/airlift/common-hal/wifi/Monitor.c b/devices/airlift/common-hal/wifi/Monitor.c new file mode 100644 index 0000000000000..6c9309964ffad --- /dev/null +++ b/devices/airlift/common-hal/wifi/Monitor.c @@ -0,0 +1,151 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/mpstate.h" +#include "py/runtime.h" + +#include "shared-bindings/wifi/Monitor.h" +#include "shared-bindings/wifi/Packet.h" + +#define MONITOR_PAYLOAD_FCS_LEN (4) +#define MONITOR_QUEUE_TIMEOUT_TICK (0) + +typedef struct { + void *payload; + unsigned channel; + uint32_t length; + signed rssi; +} monitor_packet_t; + +// static void wifi_monitor_cb(void *recv_buf, wifi_promiscuous_pkt_type_t type) { +// wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)recv_buf; + +// // prepare packet +// monitor_packet_t packet = { +// .channel = pkt->rx_ctrl.channel, +// .length = pkt->rx_ctrl.sig_len, +// .rssi = pkt->rx_ctrl.rssi, +// }; + +// // for now, the monitor only dumps the length of the MISC type frame +// if (type != WIFI_PKT_MISC && !pkt->rx_ctrl.rx_state) { +// packet.length -= MONITOR_PAYLOAD_FCS_LEN; +// packet.payload = malloc(packet.length); +// if (packet.payload) { +// memcpy(packet.payload, pkt->payload, packet.length); +// wifi_monitor_obj_t *self = MP_STATE_VM(wifi_monitor_singleton); +// if (self->queue) { +// // send packet +// if (xQueueSendFromISR(self->queue, &packet, NULL) != pdTRUE) { +// self->lost++; +// free(packet.payload); +// ESP_LOGE(TAG, "packet queue full"); +// } +// } +// } else { +// ESP_LOGE(TAG, "not enough memory for packet"); +// } +// } +// } + +void common_hal_wifi_monitor_construct(wifi_monitor_obj_t *self, uint8_t channel, size_t queue) { + // mp_rom_error_text_t monitor_mode_init_error = MP_ERROR_TEXT("monitor init failed"); + + // self->queue = xQueueCreate(queue, sizeof(monitor_packet_t)); + // if (!self->queue) { + // mp_raise_RuntimeError(monitor_mode_init_error); + // } + + // // start wifi promicuous mode + // wifi_promiscuous_filter_t wifi_filter = { + // .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT, + // }; + // esp_wifi_set_promiscuous_filter(&wifi_filter); + // esp_wifi_set_promiscuous_rx_cb(wifi_monitor_cb); + // if (esp_wifi_set_promiscuous(true) != ESP_OK) { + // mp_raise_RuntimeError(monitor_mode_init_error); + // } + // esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); + + // self->channel = channel; + // self->queue_length = queue; +} + +bool common_hal_wifi_monitor_deinited(void) { + // bool enabled; + // return (esp_wifi_get_promiscuous(&enabled) == ESP_ERR_WIFI_NOT_INIT) ? true : !enabled; + return false; +} + +void common_hal_wifi_monitor_deinit(wifi_monitor_obj_t *self) { + // if (common_hal_wifi_monitor_deinited()) { + // return; + // } + + // // disable wifi promiscuous mode + // esp_wifi_set_promiscuous(false); + + // // make sure to free all resources in the left items + // UBaseType_t left_items = uxQueueMessagesWaiting(self->queue); + // monitor_packet_t packet; + // while (left_items--) { + // xQueueReceive(self->queue, &packet, MONITOR_QUEUE_TIMEOUT_TICK); + // free(packet.payload); + // } + // vQueueDelete(self->queue); + // self->queue = NULL; +} + +void common_hal_wifi_monitor_set_channel(wifi_monitor_obj_t *self, uint8_t channel) { + // self->channel = channel; + // esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); +} + +mp_obj_t common_hal_wifi_monitor_get_channel(wifi_monitor_obj_t *self) { + // return MP_OBJ_NEW_SMALL_INT(self->channel); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_monitor_get_queue(wifi_monitor_obj_t *self) { + // return mp_obj_new_int_from_uint(self->queue_length); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_monitor_get_lost(wifi_monitor_obj_t *self) { + // size_t lost = self->lost; + // self->lost = 0; + // return mp_obj_new_int_from_uint(lost); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_monitor_get_queued(wifi_monitor_obj_t *self) { + // return mp_obj_new_int_from_uint(uxQueueMessagesWaiting(self->queue)); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_monitor_get_packet(wifi_monitor_obj_t *self) { + // monitor_packet_t packet; + + // if (xQueueReceive(self->queue, &packet, MONITOR_QUEUE_TIMEOUT_TICK) != pdTRUE) { + // return (mp_obj_t)&mp_const_empty_dict_obj; + // } + + // mp_obj_dict_t *dict = MP_OBJ_TO_PTR(mp_obj_new_dict(4)); + + // mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_CH), MP_OBJ_NEW_SMALL_INT(packet.channel)); + + // mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_LEN), MP_OBJ_NEW_SMALL_INT(packet.length)); + + // mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_RAW), mp_obj_new_bytes(packet.payload, packet.length)); + // free(packet.payload); + + // mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_RSSI), MP_OBJ_NEW_SMALL_INT(packet.rssi)); + + // return MP_OBJ_FROM_PTR(dict); + return mp_const_none; +} diff --git a/devices/airlift/common-hal/wifi/Monitor.h b/devices/airlift/common-hal/wifi/Monitor.h new file mode 100644 index 0000000000000..4e05cc36e80de --- /dev/null +++ b/devices/airlift/common-hal/wifi/Monitor.h @@ -0,0 +1,17 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + uint8_t channel; + size_t lost; + size_t queue_length; +// QueueHandle_t queue; +} wifi_monitor_obj_t; diff --git a/devices/airlift/common-hal/wifi/Network.c b/devices/airlift/common-hal/wifi/Network.c new file mode 100644 index 0000000000000..461c6e3a67fae --- /dev/null +++ b/devices/airlift/common-hal/wifi/Network.c @@ -0,0 +1,82 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared-bindings/wifi/Network.h" +#include "shared-bindings/wifi/AuthMode.h" + +mp_obj_t common_hal_wifi_network_get_ssid(wifi_network_obj_t *self) { + // const char *cstr = (const char *)self->record.ssid; + // return mp_obj_new_str(cstr, strlen(cstr)); + return mp_const_none; +} + +#define MAC_ADDRESS_LENGTH 6 + +mp_obj_t common_hal_wifi_network_get_bssid(wifi_network_obj_t *self) { + // return mp_obj_new_bytes(self->record.bssid, MAC_ADDRESS_LENGTH); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_network_get_rssi(wifi_network_obj_t *self) { + // return mp_obj_new_int(self->record.rssi); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_network_get_channel(wifi_network_obj_t *self) { + // return mp_obj_new_int(self->record.primary); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_network_get_country(wifi_network_obj_t *self) { + // const char *cstr = (const char *)self->record.country.cc; + // // 2 instead of strlen(cstr) as this gives us only the country-code + // return mp_obj_new_str(cstr, 2); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_network_get_authmode(wifi_network_obj_t *self) { + // uint32_t authmode_mask = 0; + // switch (self->record.authmode) { + // case WIFI_AUTH_OPEN: + // authmode_mask = AUTHMODE_OPEN; + // break; + // case WIFI_AUTH_WEP: + // authmode_mask = AUTHMODE_WEP; + // break; + // case WIFI_AUTH_WPA_PSK: + // authmode_mask = AUTHMODE_WPA | AUTHMODE_PSK; + // break; + // case WIFI_AUTH_WPA2_PSK: + // authmode_mask = AUTHMODE_WPA2 | AUTHMODE_PSK; + // break; + // case WIFI_AUTH_WPA_WPA2_PSK: + // authmode_mask = AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_PSK; + // break; + // case WIFI_AUTH_WPA2_ENTERPRISE: + // authmode_mask = AUTHMODE_WPA2 | AUTHMODE_ENTERPRISE; + // break; + // case WIFI_AUTH_WPA3_PSK: + // authmode_mask = AUTHMODE_WPA3 | AUTHMODE_PSK; + // break; + // case WIFI_AUTH_WPA2_WPA3_PSK: + // authmode_mask = AUTHMODE_WPA2 | AUTHMODE_WPA3 | AUTHMODE_PSK; + // break; + // default: + // break; + // } + // mp_obj_t authmode_list = mp_obj_new_list(0, NULL); + // if (authmode_mask != 0) { + // for (uint8_t i = 0; i < 32; i++) { + // if ((authmode_mask >> i) & 1) { + // mp_obj_list_append(authmode_list, cp_enum_find(&wifi_authmode_type, 1 << i)); + // } + // } + // } + // return authmode_list; + return mp_const_none; +} diff --git a/devices/airlift/common-hal/wifi/Network.h b/devices/airlift/common-hal/wifi/Network.h new file mode 100644 index 0000000000000..368f92b9126c4 --- /dev/null +++ b/devices/airlift/common-hal/wifi/Network.h @@ -0,0 +1,14 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; +// wifi_ap_record_t record; +} wifi_network_obj_t; diff --git a/devices/airlift/common-hal/wifi/Radio.c b/devices/airlift/common-hal/wifi/Radio.c new file mode 100644 index 0000000000000..b12177bd49d12 --- /dev/null +++ b/devices/airlift/common-hal/wifi/Radio.c @@ -0,0 +1,910 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/wifi/Radio.h" +#include "shared-bindings/wifi/Network.h" + +#include + +#include "common-hal/wifi/__init__.h" +#include "shared/runtime/interrupt_char.h" +#include "py/gc.h" +#include "py/mphal.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "shared-bindings/ipaddress/IPv4Address.h" +#include "shared-bindings/wifi/ScannedNetworks.h" +#include "shared-bindings/wifi/AuthMode.h" +#include "shared-bindings/time/__init__.h" +#include "shared-module/ipaddress/__init__.h" +#include "common-hal/socketpool/__init__.h" + +#if CIRCUITPY_MDNS +#include "common-hal/mdns/Server.h" +#endif + +#define MAC_ADDRESS_LENGTH 6 + +// Release CS and lock if held. +static void spi_end_transaction(wifi_radio_obj_t *self) { + if (common_hal_busio_spi_has_lock(self->spi)) { + common_hal_digitalio_digitalinout_set_value(self->cs, true); + common_hal_busio_spi_unlock(self->spi); + } +} + +// Wait for ready pin to become low or high. Raise an exception if a timeout is exceeded. +static void wait_for_ready(wifi_radio_obj_t *self, bool value, uint32_t timeout_ticks) { + uint64_t start = mp_hal_ticks_ms(); + while ((mp_hal_ticks_ms() - start) < timeout_ticks) { + if (common_hal_digitalio_digitalinout_get_value(self->ready) == value) { + return; + } + RUN_BACKGROUND_TASKS; + } + + // Timeout. Give up SPI control. + spi_end_transaction(self); + mp_raise_msg_varg(&mp_type_TimeoutError, MP_ERROR_TEXT("timeout waiting for ready %q"), + value ? MP_QSTR_True : MP_QSTR_False); +} + +// Wait for co-processor to be ready, then grab lock and CS. +static void spi_begin_transaction(wifi_radio_obj_t *self) { + // The ready line is set low when the NINA firmware is ready to start an SPI transaction. + // Once CS is set low to signal an SPI transaction has started, NINA sets the ready line high + // to indicate it has seen the CS transition to low. + wait_for_ready(self, false, 10000); + + while (!common_hal_busio_spi_try_lock(self->spi)) { + RUN_BACKGROUND_TASKS; + } + common_hal_busio_spi_configure(self->spi, 8000000, 0, 0, 8); + + common_hal_digitalio_digitalinout_set_value(self->cs, false); + wait_for_ready(self, true, 1000); +} + +// Internal routines that are also called from other classes. + +// Send command via SPI. Assumes spi_begin_transaction() has already been called. +void wifi_radio_send_command(wifi_radio_obj_t *self, uint8_t cmd, const uint8_t **params, const size_t *param_lens, size_t num_params) { + // Calculate packet size + size_t packet_len = 4; // START + CMD + NUM_PARAMS + END + for (size_t i = 0; i < num_params; i++) { + packet_len += 1 + param_lens[i]; // length byte + data + } + // Pad to 4-byte boundary + while (packet_len % 4 != 0) { + packet_len++; + } + + // Ensure buffer is large enough + if (packet_len > self->sendbuf_len) { + self->sendbuf = m_realloc(self->sendbuf, packet_len); + self->sendbuf_len = packet_len; + } + + // Build packet + self->sendbuf[0] = _START_CMD; + self->sendbuf[1] = cmd & ~_REPLY_FLAG; + self->sendbuf[2] = num_params; + + size_t ptr = 3; + for (size_t i = 0; i < num_params; i++) { + self->sendbuf[ptr++] = param_lens[i] & 0xFF; + memcpy(&self->sendbuf[ptr], params[i], param_lens[i]); + ptr += param_lens[i]; + } + self->sendbuf[ptr++] = _END_CMD; + + // Pad with zeros + while (ptr < packet_len) { + self->sendbuf[ptr++] = 0; + } + + spi_begin_transaction(self); + + // Wait for ready to go high (ready to receive) + wait_for_ready(self, true, 1000); + + common_hal_busio_spi_write(self->spi, self->sendbuf, packet_len); + + spi_end_transaction(self); +} + +// Read just one byte from SPI +uint8_t wifi_radio_read_byte(wifi_radio_obj_t *self) { + common_hal_busio_spi_read(self->spi, self->pbuf, 1, 0xFF); + return self->pbuf[0]; +} + +// Wait for specific byte +void wifi_radio_wait_spi_char(wifi_radio_obj_t *self, uint8_t desired) { + for (int i = 0; i < 10; i++) { + uint8_t r = wifi_radio_read_byte(self); + if (r == _ERR_CMD) { + mp_raise_msg(&mp_type_BrokenPipeError, MP_ERROR_TEXT("Error response to command")); + } + if (r == desired) { + return; + } + mp_hal_delay_ms(10); + } + mp_raise_msg(&mp_type_TimeoutError, MP_ERROR_TEXT("timeout waiting for byte")); +} + +// Check that next byte matches expected value. +void wifi_radio_check_data(wifi_radio_obj_t *self, uint8_t desired) { + uint8_t r = wifi_radio_read_byte(self); + if (r != desired) { + mp_raise_msg_varg(&mp_type_BrokenPipeError, MP_ERROR_TEXT("Expected %02x but got %02x"), desired, r); + } +} + +// Wait for and parse response +size_t wifi_radio_wait_response_cmd(wifi_radio_obj_t *self, uint8_t cmd, uint8_t **responses, size_t *response_lens, size_t max_responses) { + spi_begin_transaction(self); + + wifi_radio_wait_spi_char(self, _START_CMD); + wifi_radio_check_data(self, cmd | _REPLY_FLAG); + uint8_t num_responses = wifi_radio_read_byte(self); + + if (num_responses > max_responses) { + num_responses = max_responses; + } + + for (size_t i = 0; i < num_responses; i++) { + uint8_t param_len = wifi_radio_read_byte(self); + response_lens[i] = param_len; + responses[i] = m_malloc(param_len); + common_hal_busio_spi_read(self->spi, responses[i], param_len, 0xFF); + + } + + wifi_radio_check_data(self, _END_CMD); + + spi_end_transaction(self); + + return num_responses; +} + +size_t wifi_radio_send_command_get_response(wifi_radio_obj_t *self, uint8_t cmd, + const uint8_t **params, const size_t *param_lens, size_t num_params, + uint8_t **responses, size_t *response_lens, size_t max_responses) { + + wifi_radio_send_command(self, cmd, params, param_lens, num_params); + return wifi_radio_wait_response_cmd(self, cmd, responses, response_lens, max_responses); +} + + +////////////////////////////////////////////////////////////////////////////// +// common-hal routines called from shared-bindings/Radio.c + +void common_hal_wifi_radio_init_airlift(wifi_radio_obj_t *self, busio_spi_obj_t *spi, digitalio_digitalinout_obj_t *cs, digitalio_digitalinout_obj_t *ready, digitalio_digitalinout_obj_t *reset, digitalio_digitalinout_obj_t *gpio0) { +} + +mp_obj_t common_hal_wifi_radio_get_version(wifi_radio_obj_t *self) { + uint8_t *responses[1]; + size_t response_lens[1]; + + size_t num_resp = wifi_radio_send_command_get_response(self, _GET_FW_VERSION_CMD, NULL, NULL, 0, responses, response_lens, 1); + + if (num_resp > 0) { + return mp_obj_new_str_from_cstr((char *)responses[0]); + } else { + return mp_const_empty_bytes; + } +} + + +bool common_hal_wifi_radio_get_enabled(wifi_radio_obj_t *self) { + return self->started; +} + +void common_hal_wifi_radio_set_enabled(wifi_radio_obj_t *self, bool enabled) { +/* + if (self->started && !enabled) { + if (self->current_scan != NULL) { + common_hal_wifi_radio_stop_scanning_networks(self); + } + #if CIRCUITPY_MDNS + mdns_server_deinit_singleton(); + #endif + ESP_ERROR_CHECK(esp_wifi_stop()); + self->started = false; + return; + } + if (!self->started && enabled) { + ESP_ERROR_CHECK(esp_wifi_start()); + self->started = true; + common_hal_wifi_radio_set_tx_power(self, CIRCUITPY_WIFI_DEFAULT_TX_POWER); + return; + } +*/ +} + +mp_obj_t common_hal_wifi_radio_get_hostname(wifi_radio_obj_t *self) { + // const char *hostname = NULL; + // esp_netif_get_hostname(self->netif, &hostname); + // if (hostname == NULL) { + // return mp_const_none; + // } + // return mp_obj_new_str_from_cstr(hostname); + return mp_const_none; +} + +void common_hal_wifi_radio_set_hostname(wifi_radio_obj_t *self, const char *hostname) { + const uint8_t *params[1] = { (const uint8_t *)hostname }; + size_t param_lens[1] = { strlen(hostname) }; + uint8_t *responses[1]; + size_t response_lens[1]; + + size_t num_resp = wifi_radio_send_command_get_response(self, _SET_HOSTNAME, params, param_lens, 1, responses, response_lens, 1); + + if (num_resp > 0) { + uint8_t result = responses[0][0]; + m_free(responses[0]); + if (result != 1) { + mp_raise_OSError_msg(MP_ERROR_TEXT("Failed to set hostname")); + } + } +} + +mp_obj_t common_hal_wifi_radio_get_mac_address(wifi_radio_obj_t *self) { + const uint8_t param = 0xFF; + const uint8_t *params[1] = { ¶m }; + size_t param_lens[1] = { 1 }; + uint8_t *responses[1]; + size_t response_lens[1]; + uint8_t mac[6]; + + size_t num_resp = wifi_radio_send_command_get_response(self, _GET_MACADDR_CMD, params, param_lens, 1, responses, response_lens, 1); + + if (num_resp > 0 && response_lens[0] >= MAC_ADDRESS_LENGTH) { + // Reverse byte order (ESP32 returns it reversed) + for (int i = 0; i < MAC_ADDRESS_LENGTH; i++) { + mac[i] = responses[0][MAC_ADDRESS_LENGTH - 1 - i]; + } + m_free(responses[0]); + } else { + memset(mac, 0, MAC_ADDRESS_LENGTH); + } + return mp_obj_new_bytes(mac, MAC_ADDRESS_LENGTH); +} + +void common_hal_wifi_radio_set_mac_address(wifi_radio_obj_t *self, const uint8_t *mac) { + // if (!self->sta_mode) { + // mp_raise_RuntimeError(MP_ERROR_TEXT("Interface must be started")); + // } + // if ((mac[0] & 0b1) == 0b1) { + // mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid multicast MAC address")); + // } + // esp_wifi_set_mac(ESP_IF_WIFI_STA, mac); +} + +mp_float_t common_hal_wifi_radio_get_tx_power(wifi_radio_obj_t *self) { + // int8_t tx_power; + // esp_wifi_get_max_tx_power(&tx_power); + // return tx_power / 4.0f; + return 0.0f; +} + +void common_hal_wifi_radio_set_tx_power(wifi_radio_obj_t *self, const mp_float_t tx_power) { + // esp_wifi_set_max_tx_power(tx_power * 4.0f); +} + +wifi_power_management_t common_hal_wifi_radio_get_power_management(wifi_radio_obj_t *self) { + // wifi_ps_type_t ps; + // esp_err_t ret = esp_wifi_get_ps(&ps); + // if (ret == ESP_OK) { + // switch (ps) { + // case WIFI_PS_MIN_MODEM: + // return POWER_MANAGEMENT_MIN; + // case WIFI_PS_MAX_MODEM: + // return POWER_MANAGEMENT_MAX; + // case WIFI_PS_NONE: + // return POWER_MANAGEMENT_NONE; + // } + // } + return POWER_MANAGEMENT_MIN; +} + + +void common_hal_wifi_radio_set_power_management(wifi_radio_obj_t *self, wifi_power_management_t power_management) { + // switch (power_management) { + // case POWER_MANAGEMENT_MIN: + // esp_wifi_set_ps(WIFI_PS_MIN_MODEM); + // break; + // case POWER_MANAGEMENT_MAX: { + // // listen_interval is only used in this case. + // wifi_config_t *config = &self->sta_config; + // // This is a typical value seen in various examples. + // config->sta.listen_interval = 3; + // esp_wifi_set_ps(WIFI_PS_MAX_MODEM); + // esp_wifi_set_config(ESP_IF_WIFI_STA, config); + // } + // break; + // case POWER_MANAGEMENT_NONE: + // esp_wifi_set_ps(WIFI_PS_NONE); + // break; + // case POWER_MANAGEMENT_UNKNOWN: + // // This should be prevented in shared-bindings. + // break; + // } +} + +mp_obj_t common_hal_wifi_radio_get_mac_address_ap(wifi_radio_obj_t *self) { + // uint8_t mac[MAC_ADDRESS_LENGTH]; + // esp_wifi_get_mac(ESP_IF_WIFI_AP, mac); + // return mp_obj_new_bytes(mac, MAC_ADDRESS_LENGTH); + return mp_const_none; +} + +void common_hal_wifi_radio_set_mac_address_ap(wifi_radio_obj_t *self, const uint8_t *mac) { + // if (!self->ap_mode) { + // mp_raise_RuntimeError(MP_ERROR_TEXT("Interface must be started")); + // } + // if ((mac[0] & 0b1) == 0b1) { + // mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid multicast MAC address")); + // } + // esp_wifi_set_mac(ESP_IF_WIFI_AP, mac); +} + +mp_obj_t common_hal_wifi_radio_start_scanning_networks(wifi_radio_obj_t *self, uint8_t start_channel, uint8_t stop_channel) { + // if (self->current_scan != NULL) { + // mp_raise_RuntimeError(MP_ERROR_TEXT("Already scanning for wifi networks")); + // } + // if (!common_hal_wifi_radio_get_enabled(self)) { + // mp_raise_RuntimeError(MP_ERROR_TEXT("WiFi is not enabled")); + // } + // set_mode_station(self, true); + + // wifi_scannednetworks_obj_t *scan = mp_obj_malloc(wifi_scannednetworks_obj_t, &wifi_scannednetworks_type); + // self->current_scan = scan; + // scan->current_channel_index = 0; + // scan->start_channel = start_channel; + // scan->end_channel = stop_channel; + // scan->radio_event_group = self->event_group_handle; + // scan->done = false; + // scan->channel_scan_in_progress = false; + // wifi_scannednetworks_scan_next_channel(scan); + // return scan; + return mp_const_none; +} + +void common_hal_wifi_radio_stop_scanning_networks(wifi_radio_obj_t *self) { + // // Return early if self->current_scan is NULL to avoid hang + // if (self->current_scan == NULL) { + // return; + // } + // // Free the memory used to store the found aps. + // wifi_scannednetworks_deinit(self->current_scan); + // self->current_scan = NULL; +} + +void common_hal_wifi_radio_start_station(wifi_radio_obj_t *self) { + // set_mode_station(self, true); +} + +void common_hal_wifi_radio_stop_station(wifi_radio_obj_t *self) { + // set_mode_station(self, false); +} + +void common_hal_wifi_radio_start_ap(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, uint32_t authmode, uint8_t max_connections) { + // set_mode_ap(self, true); + + // uint8_t esp_authmode = 0; + // switch (authmode) { + // case AUTHMODE_OPEN: + // esp_authmode = WIFI_AUTH_OPEN; + // break; + // case AUTHMODE_WPA | AUTHMODE_PSK: + // esp_authmode = WIFI_AUTH_WPA_PSK; + // break; + // case AUTHMODE_WPA2 | AUTHMODE_PSK: + // esp_authmode = WIFI_AUTH_WPA2_PSK; + // break; + // case AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_PSK: + // esp_authmode = WIFI_AUTH_WPA_WPA2_PSK; + // break; + // default: + // mp_arg_error_invalid(MP_QSTR_authmode); + // break; + // } + + // wifi_config_t *config = &self->ap_config; + // memcpy(&config->ap.ssid, ssid, ssid_len); + // config->ap.ssid[ssid_len] = 0; + // memcpy(&config->ap.password, password, password_len); + // config->ap.password[password_len] = 0; + // config->ap.channel = channel; + // config->ap.authmode = esp_authmode; + + // mp_arg_validate_int_range(max_connections, 0, 10, MP_QSTR_max_connections); + + // config->ap.max_connection = max_connections; + + // esp_wifi_set_config(WIFI_IF_AP, config); +} + +bool common_hal_wifi_radio_get_ap_active(wifi_radio_obj_t *self) { + // return self->ap_mode && esp_netif_is_netif_up(self->ap_netif); + return mp_const_none; +} + +void common_hal_wifi_radio_stop_ap(wifi_radio_obj_t *self) { + // set_mode_ap(self, false); +} + +mp_obj_t common_hal_wifi_radio_get_stations_ap(wifi_radio_obj_t *self) { + // wifi_sta_list_t esp_sta_list; + // esp_err_t result; + + // result = esp_wifi_ap_get_sta_list(&esp_sta_list); + // if (result != ESP_OK) { + // return mp_const_none; + // } + + // esp_netif_pair_mac_ip_t mac_ip_pair[esp_sta_list.num]; + // for (int i = 0; i < esp_sta_list.num; i++) { + // memcpy(mac_ip_pair[i].mac, esp_sta_list.sta[i].mac, MAC_ADDRESS_LENGTH); + // mac_ip_pair[i].ip.addr = 0; + // } + + // result = esp_netif_dhcps_get_clients_by_mac(self->ap_netif, esp_sta_list.num, mac_ip_pair); + // if (result != ESP_OK) { + // return mp_const_none; + // } + + // mp_obj_t mp_sta_list = mp_obj_new_list(0, NULL); + // for (int i = 0; i < esp_sta_list.num; i++) { + // mp_obj_t elems[3] = { + // mp_obj_new_bytes(esp_sta_list.sta[i].mac, MAC_ADDRESS_LENGTH), + // MP_OBJ_NEW_SMALL_INT(esp_sta_list.sta[i].rssi), + // mp_const_none + // }; + + // if (mac_ip_pair[i].ip.addr) { + // elems[2] = common_hal_ipaddress_new_ipv4address(mac_ip_pair[i].ip.addr); + // } + + // mp_obj_list_append(mp_sta_list, namedtuple_make_new((const mp_obj_type_t *)&wifi_radio_station_type, 3, 0, elems)); + // } + + // return mp_sta_list; + return mp_const_none; +} + +wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, mp_float_t timeout, uint8_t *bssid, size_t bssid_len) { + // if (!common_hal_wifi_radio_get_enabled(self)) { + // mp_raise_RuntimeError(MP_ERROR_TEXT("WiFi is not enabled")); + // } + // wifi_config_t *config = &self->sta_config; + + // size_t timeout_ms = timeout * 1000; + // uint32_t start_time = common_hal_time_monotonic_ms(); + // uint32_t end_time = start_time + timeout_ms; + + // EventBits_t bits; + // // can't block since both bits are false after wifi_init + // // both bits are true after an existing connection stops + // bits = xEventGroupWaitBits(self->event_group_handle, + // WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT, + // pdTRUE, + // pdTRUE, + // 0); + // bool connected = ((bits & WIFI_CONNECTED_BIT) != 0) && + // !((bits & WIFI_DISCONNECTED_BIT) != 0); + // if (connected) { + // // SSIDs are up to 32 bytes. Assume it is null terminated if it is less. + // if (memcmp(ssid, config->sta.ssid, ssid_len) == 0 && + // (ssid_len == 32 || strlen((const char *)config->sta.ssid) == ssid_len)) { + // // Already connected to the desired network. + // return WIFI_RADIO_ERROR_NONE; + // } else { + // xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT); + // // Trying to switch networks so disconnect first. + // esp_wifi_disconnect(); + // do { + // RUN_BACKGROUND_TASKS; + // bits = xEventGroupWaitBits(self->event_group_handle, + // WIFI_DISCONNECTED_BIT, + // pdTRUE, + // pdTRUE, + // 0); + // } while ((bits & WIFI_DISCONNECTED_BIT) == 0 && !mp_hal_is_interrupted()); + // } + // } + // // explicitly clear bits since xEventGroupWaitBits may have timed out + // xEventGroupClearBits(self->event_group_handle, WIFI_CONNECTED_BIT); + // xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT); + // set_mode_station(self, true); + + // memcpy(&config->sta.ssid, ssid, ssid_len); + // if (ssid_len < 32) { + // config->sta.ssid[ssid_len] = 0; + // } + // memcpy(&config->sta.password, password, password_len); + // config->sta.password[password_len] = 0; + // config->sta.channel = channel; + // // From esp_wifi_types.h: + // // Generally, station_config.bssid_set needs to be 0; and it needs + // // to be 1 only when users need to check the MAC address of the AP + // if (bssid_len > 0) { + // memcpy(&config->sta.bssid, bssid, bssid_len); + // config->sta.bssid[bssid_len] = 0; + // config->sta.bssid_set = true; + // } else { + // config->sta.bssid_set = false; + // } + // // If channel is 0 (default/unset) and BSSID is not given, do a full scan instead of fast scan + // // This will ensure that the best AP in range is chosen automatically + // if ((config->sta.bssid_set == 0) && (config->sta.channel == 0)) { + // config->sta.scan_method = WIFI_ALL_CHANNEL_SCAN; + // } else { + // config->sta.scan_method = WIFI_FAST_SCAN; + // } + // esp_wifi_set_config(ESP_IF_WIFI_STA, config); + // self->starting_retries = 5; + // self->retries_left = 5; + // esp_wifi_connect(); + + // do { + // RUN_BACKGROUND_TASKS; + // bits = xEventGroupWaitBits(self->event_group_handle, + // WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT, + // pdTRUE, + // pdTRUE, + // 0); + // // Don't retry anymore if we're over our time budget. + // if (self->retries_left > 0 && common_hal_time_monotonic_ms() > end_time) { + // self->retries_left = 0; + // } + // } while ((bits & (WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT)) == 0 && !mp_hal_is_interrupted()); + + // if ((bits & WIFI_DISCONNECTED_BIT) != 0) { + // if ( + // (self->last_disconnect_reason == WIFI_REASON_AUTH_FAIL) || + // (self->last_disconnect_reason == WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT) || + // (self->last_disconnect_reason == WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY) || + // (self->last_disconnect_reason == WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD) + // ) { + // return WIFI_RADIO_ERROR_AUTH_FAIL; + // } else if (self->last_disconnect_reason == WIFI_REASON_NO_AP_FOUND) { + // return WIFI_RADIO_ERROR_NO_AP_FOUND; + // } + // return self->last_disconnect_reason; + // } else { + // // We're connected, allow us to retry if we get disconnected. + // self->retries_left = self->starting_retries; + // } + return WIFI_RADIO_ERROR_NONE; +} + +bool common_hal_wifi_radio_get_connected(wifi_radio_obj_t *self) { +// return self->sta_mode && esp_netif_is_netif_up(self->netif); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_ap_info(wifi_radio_obj_t *self) { + // if (!esp_netif_is_netif_up(self->netif)) { + // return mp_const_none; + // } + + // // Make sure the interface is in STA mode + // if (!self->sta_mode) { + // return mp_const_none; + // } + + // wifi_network_obj_t *ap_info = mp_obj_malloc(wifi_network_obj_t, &wifi_network_type); + // // From esp_wifi.h, the possible return values (typos theirs): + // // ESP_OK: succeed + // // ESP_ERR_WIFI_CONN: The station interface don't initialized + // // ESP_ERR_WIFI_NOT_CONNECT: The station is in disconnect status + // if (esp_wifi_sta_get_ap_info(&self->ap_info.record) != ESP_OK) { + // return mp_const_none; + // } else { + // if (strlen(self->ap_info.record.country.cc) == 0) { + // // Workaround to fill country related information in ap_info until ESP-IDF carries a fix + // // esp_wifi_sta_get_ap_info does not appear to fill wifi_country_t (e.g. country.cc) details + // // (IDFGH-4437) #6267 + // // Note: It is possible that Wi-Fi APs don't have a CC set, then even after this workaround + // // the element would remain empty. + // memset(&self->ap_info.record.country, 0, sizeof(wifi_country_t)); + // if (esp_wifi_get_country(&self->ap_info.record.country) != ESP_OK) { + // return mp_const_none; + // } + // } + // memcpy(&ap_info->record, &self->ap_info.record, sizeof(wifi_ap_record_t)); + // return MP_OBJ_FROM_PTR(ap_info); + // } + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_ipv4_gateway(wifi_radio_obj_t *self) { + // if (!esp_netif_is_netif_up(self->netif)) { + // return mp_const_none; + // } + // esp_netif_get_ip_info(self->netif, &self->ip_info); + // return common_hal_ipaddress_new_ipv4address(self->ip_info.gw.addr); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_ipv4_gateway_ap(wifi_radio_obj_t *self) { + // if (!esp_netif_is_netif_up(self->ap_netif)) { + // return mp_const_none; + // } + // esp_netif_get_ip_info(self->ap_netif, &self->ap_ip_info); + // return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.gw.addr); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_ipv4_subnet(wifi_radio_obj_t *self) { + // if (!esp_netif_is_netif_up(self->netif)) { + // return mp_const_none; + // } + // esp_netif_get_ip_info(self->netif, &self->ip_info); + // return common_hal_ipaddress_new_ipv4address(self->ip_info.netmask.addr); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self) { + // if (!esp_netif_is_netif_up(self->ap_netif)) { + // return mp_const_none; + // } + // esp_netif_get_ip_info(self->ap_netif, &self->ap_ip_info); + // return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.netmask.addr); + return mp_const_none; +} + +// static mp_obj_t common_hal_wifi_radio_get_addresses_netif(wifi_radio_obj_t *self, esp_netif_t *netif) { +// if (!esp_netif_is_netif_up(netif)) { +// return mp_const_empty_tuple; +// } +// esp_netif_ip_info_t ip_info; +// esp_netif_get_ip_info(netif, &ip_info); +// int n_addresses4 = ip_info.ip.addr != INADDR_NONE; + +// #if CIRCUITPY_SOCKETPOOL_IPV6 +// esp_ip6_addr_t addresses[LWIP_IPV6_NUM_ADDRESSES]; +// int n_addresses6 = esp_netif_get_all_ip6(netif, &addresses[0]); +// #else +// int n_addresses6 = 0; +// #endif +// int n_addresses = n_addresses4 + n_addresses6; +// mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(n_addresses, NULL)); + +// #if CIRCUITPY_SOCKETPOOL_IPV6 +// for (int i = 0; i < n_addresses6; i++) { +// result->items[i] = espaddr6_to_str(&addresses[i]); +// } +// #endif + +// if (n_addresses4) { +// result->items[n_addresses6] = espaddr4_to_str(&ip_info.ip); +// } + +// return MP_OBJ_FROM_PTR(result); +// } + +mp_obj_t common_hal_wifi_radio_get_addresses(wifi_radio_obj_t *self) { + // return common_hal_wifi_radio_get_addresses_netif(self, self->netif); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_addresses_ap(wifi_radio_obj_t *self) { + // return common_hal_wifi_radio_get_addresses_netif(self, self->ap_netif); + return mp_const_none; +} + +uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) { + // if (!esp_netif_is_netif_up(self->netif)) { + // return 0; + // } + // esp_netif_get_ip_info(self->netif, &self->ip_info); + // return self->ip_info.ip.addr; + return 42; +} + +mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) { + // if (!esp_netif_is_netif_up(self->netif)) { + // return mp_const_none; + // } + // esp_netif_get_ip_info(self->netif, &self->ip_info); + // return common_hal_ipaddress_new_ipv4address(self->ip_info.ip.addr); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self) { + // if (!esp_netif_is_netif_up(self->ap_netif)) { + // return mp_const_none; + // } + // esp_netif_get_ip_info(self->ap_netif, &self->ap_ip_info); + // return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.ip.addr); + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_ipv4_dns(wifi_radio_obj_t *self) { + // if (!esp_netif_is_netif_up(self->netif)) { + // return mp_const_none; + // } + + // esp_netif_get_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &self->dns_info); + + // if (self->dns_info.ip.type != ESP_IPADDR_TYPE_V4) { + // return mp_const_none; + // } + // // dns_info is of type esp_netif_dns_info_t, which is just ever so slightly + // // different than esp_netif_ip_info_t used for + // // common_hal_wifi_radio_get_ipv4_address (includes both ipv4 and 6), + // // so some extra jumping is required to get to the actual address + // return common_hal_ipaddress_new_ipv4address(self->dns_info.ip.u_addr.ip4.addr); + return mp_const_none; +} + +void common_hal_wifi_radio_set_ipv4_dns(wifi_radio_obj_t *self, mp_obj_t ipv4_dns_addr) { + // esp_netif_dns_info_t dns_addr; + // ipaddress_ipaddress_to_esp_idf_ip4(ipv4_dns_addr, &dns_addr.ip.u_addr.ip4); + // esp_netif_set_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &dns_addr); +} + +void common_hal_wifi_radio_start_dhcp_client(wifi_radio_obj_t *self, bool ipv4, bool ipv6) { + // if (ipv4) { + // esp_netif_dhcpc_start(self->netif); + // } else { + // esp_netif_dhcpc_stop(self->netif); + // } + // #if LWIP_IPV6_DHCP6 + // if (ipv6) { + // esp_netif_create_ip6_linklocal(self->netif); + // dhcp6_enable_stateless(esp_netif_get_netif_impl(self->netif)); + // } else { + // dhcp6_disable(esp_netif_get_netif_impl(self->netif)); + // } + // #else + // if (ipv6) { + // mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_ipv6); + // } + // #endif +} + +void common_hal_wifi_radio_stop_dhcp_client(wifi_radio_obj_t *self) { + // esp_netif_dhcpc_stop(self->netif); + // #if LWIP_IPV6_DHCP6 + // dhcp6_disable(esp_netif_get_netif_impl(self->netif)); + // #endif +} + +void common_hal_wifi_radio_start_dhcp_server(wifi_radio_obj_t *self) { + // esp_netif_dhcps_start(self->ap_netif); +} + +void common_hal_wifi_radio_stop_dhcp_server(wifi_radio_obj_t *self) { + // esp_netif_dhcps_stop(self->ap_netif); +} + +void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway, mp_obj_t ipv4_dns) { +// common_hal_wifi_radio_stop_dhcp_client(self); // Must stop station DHCP to set a manual address + +// esp_netif_ip_info_t ip_info; +// ipaddress_ipaddress_to_esp_idf_ip4(ipv4, &ip_info.ip); +// ipaddress_ipaddress_to_esp_idf_ip4(netmask, &ip_info.netmask); +// ipaddress_ipaddress_to_esp_idf_ip4(gateway, &ip_info.gw); + +// esp_netif_set_ip_info(self->netif, &ip_info); + +// if (ipv4_dns != MP_OBJ_NULL) { +// common_hal_wifi_radio_set_ipv4_dns(self, ipv4_dns); +// } +// } + +// void common_hal_wifi_radio_set_ipv4_address_ap(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway) { +// common_hal_wifi_radio_stop_dhcp_server(self); // Must stop access point DHCP to set a manual address + +// esp_netif_ip_info_t ip_info; +// ipaddress_ipaddress_to_esp_idf_ip4(ipv4, &ip_info.ip); +// ipaddress_ipaddress_to_esp_idf_ip4(netmask, &ip_info.netmask); +// ipaddress_ipaddress_to_esp_idf_ip4(gateway, &ip_info.gw); + +// esp_netif_set_ip_info(self->ap_netif, &ip_info); + +// common_hal_wifi_radio_start_dhcp_server(self); // restart access point DHCP +} + +// static void ping_success_cb(esp_ping_handle_t hdl, void *args) { +// wifi_radio_obj_t *self = (wifi_radio_obj_t *)args; +// esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &self->ping_elapsed_time, sizeof(self->ping_elapsed_time)); +// } + +mp_int_t common_hal_wifi_radio_ping(wifi_radio_obj_t *self, mp_obj_t ip_address, mp_float_t timeout) { + // esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG(); + // ipaddress_ipaddress_to_esp_idf(ip_address, &ping_config.target_addr); + // ping_config.count = 1; + + // // We must fetch ping information using the callback mechanism, because the session storage is freed when + // // the ping session is done, even before esp_ping_delete_session(). + // esp_ping_callbacks_t ping_callbacks = { + // .on_ping_success = ping_success_cb, + // .cb_args = (void *)self, + // }; + + // size_t timeout_ms = timeout * 1000; + + // // ESP-IDF creates a task to do the ping session. It shuts down when done, but only after a one second delay. + // // Calling common_hal_wifi_radio_ping() too fast will cause resource exhaustion. + // esp_ping_handle_t ping; + // if (esp_ping_new_session(&ping_config, &ping_callbacks, &ping) != ESP_OK) { + // // Wait for old task to go away and then try again. + // // Empirical testing shows we have to wait at least two seconds, despite the task + // // having a one-second timeout. + // common_hal_time_delay_ms(2000); + // // Return if interrupted now, to show the interruption as KeyboardInterrupt instead of the + // // IDF error. + // if (mp_hal_is_interrupted()) { + // return (uint32_t)(-1); + // } + // CHECK_ESP_RESULT(esp_ping_new_session(&ping_config, &ping_callbacks, &ping)); + // } + + // // Use all ones as a flag that the elapsed time was not set (ping failed or timed out). + // self->ping_elapsed_time = (uint32_t)(-1); + + // esp_ping_start(ping); + + // uint32_t start_time = common_hal_time_monotonic_ms(); + // while ((self->ping_elapsed_time == (uint32_t)(-1)) && + // (common_hal_time_monotonic_ms() - start_time < timeout_ms) && + // !mp_hal_is_interrupted()) { + // RUN_BACKGROUND_TASKS; + // } + // esp_ping_stop(ping); + // esp_ping_delete_session(ping); + + // return (mp_int_t)self->ping_elapsed_time; + return 0; +} + +void common_hal_wifi_radio_gc_collect(wifi_radio_obj_t *self) { + // Only bother to scan the actual object references. + gc_collect_ptr(self->current_scan); +} + +mp_obj_t common_hal_wifi_radio_get_dns(wifi_radio_obj_t *self) { + // if (!esp_netif_is_netif_up(self->netif)) { + // return mp_const_empty_tuple; + // } + + // esp_netif_get_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &self->dns_info); + + // if (self->dns_info.ip.type == ESP_IPADDR_TYPE_V4 && self->dns_info.ip.u_addr.ip4.addr == INADDR_NONE) { + // return mp_const_empty_tuple; + // } + + // mp_obj_t args[] = { + // espaddr_to_str(&self->dns_info.ip), + // }; + + // return mp_obj_new_tuple(1, args); + return mp_const_none; +} + +void common_hal_wifi_radio_set_dns(wifi_radio_obj_t *self, mp_obj_t dns_addrs_obj) { + // mp_int_t len = mp_obj_get_int(mp_obj_len(dns_addrs_obj)); + // mp_arg_validate_length_max(len, 1, MP_QSTR_dns); + // esp_netif_dns_info_t dns_info; + // if (len == 0) { + // // clear DNS server + // dns_info.ip.type = ESP_IPADDR_TYPE_V4; + // dns_info.ip.u_addr.ip4.addr = INADDR_NONE; + // } else { + // mp_obj_t dns_addr_obj = mp_obj_subscr(dns_addrs_obj, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_SENTINEL); + // struct sockaddr_storage addr_storage; + // socketpool_resolve_host_or_throw(AF_UNSPEC, SOCK_STREAM, mp_obj_str_get_str(dns_addr_obj), &addr_storage, 1); + // sockaddr_to_espaddr(&addr_storage, &dns_info.ip); + // } + // esp_netif_set_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &dns_info); +} diff --git a/devices/airlift/common-hal/wifi/Radio.h b/devices/airlift/common-hal/wifi/Radio.h new file mode 100644 index 0000000000000..ffb120d00c888 --- /dev/null +++ b/devices/airlift/common-hal/wifi/Radio.h @@ -0,0 +1,129 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/wifi/ScannedNetworks.h" +#include "shared-bindings/wifi/Network.h" + +// Connection modes +typedef enum { + ADAFRUIT_ESP32SPI_TCP_MODE = 0, + ADAFRUIT_ESP32SPI_UDP_MODE = 1, + ADAFRUIT_ESP32SPI_TLS_MODE = 2, +} adafruit_esp32spi_conn_mode_t; + +typedef struct { + mp_obj_base_t base; +} adafruit_esp32spi_esp_spicontrol_obj_t; +typedef struct { + mp_obj_base_t base; + wifi_scannednetworks_obj_t *current_scan; + uint32_t ping_elapsed_time; + bool started; + bool ap_mode; + bool sta_mode; + uint8_t retries_left; + uint8_t starting_retries; + uint8_t last_disconnect_reason; + + busio_spi_obj_t *spi; + digitalio_digitalinout_obj_t *cs; + digitalio_digitalinout_obj_t *ready; + digitalio_digitalinout_obj_t *reset; + digitalio_digitalinout_obj_t *gpio0; + uint8_t *sendbuf; + size_t sendbuf_len; + uint8_t buffer[10]; + uint8_t pbuf[1]; + int8_t tls_socket; // -1 if no TLS socket allocated +} wifi_radio_obj_t; + +extern void common_hal_wifi_radio_gc_collect(wifi_radio_obj_t *self); + +// ESP32 SPI protocol command constants +#define _START_CMD 0xE0 +#define _END_CMD 0xEE +#define _ERR_CMD 0xEF +#define _REPLY_FLAG 0x80 +#define _CMD_FLAG 0 + +// Command opcodes +#define _SET_NET_CMD 0x10 +#define _SET_PASSPHRASE_CMD 0x11 +#define _SET_IP_CONFIG 0x14 +#define _SET_DNS_CONFIG 0x15 +#define _SET_HOSTNAME 0x16 +#define _SET_AP_NET_CMD 0x18 +#define _SET_AP_PASSPHRASE_CMD 0x19 +#define _SET_DEBUG_CMD 0x1A + +#define _GET_CONN_STATUS_CMD 0x20 +#define _GET_IPADDR_CMD 0x21 +#define _GET_MACADDR_CMD 0x22 +#define _GET_CURR_SSID_CMD 0x23 +#define _GET_CURR_BSSID_CMD 0x24 +#define _GET_CURR_RSSI_CMD 0x25 +#define _GET_CURR_ENCT_CMD 0x26 + +#define _SCAN_NETWORKS 0x27 +#define _START_SERVER_TCP_CMD 0x28 +#define _GET_STATE_TCP_CMD 0x29 +#define _DATA_SENT_TCP_CMD 0x2A +#define _AVAIL_DATA_TCP_CMD 0x2B +#define _GET_DATA_TCP_CMD 0x2C +#define _START_CLIENT_TCP_CMD 0x2D +#define _STOP_CLIENT_TCP_CMD 0x2E +#define _GET_CLIENT_STATE_TCP_CMD 0x2F +#define _DISCONNECT_CMD 0x30 +#define _GET_IDX_RSSI_CMD 0x32 +#define _GET_IDX_ENCT_CMD 0x33 +#define _REQ_HOST_BY_NAME_CMD 0x34 +#define _GET_HOST_BY_NAME_CMD 0x35 +#define _START_SCAN_NETWORKS 0x36 +#define _GET_FW_VERSION_CMD 0x37 +#define _SEND_UDP_DATA_CMD 0x39 +#define _GET_REMOTE_DATA_CMD 0x3A +#define _GET_TIME 0x3B +#define _GET_IDX_BSSID_CMD 0x3C +#define _GET_IDX_CHAN_CMD 0x3D +#define _PING_CMD 0x3E +#define _GET_SOCKET_CMD 0x3F + +#define _SET_CLI_CERT 0x40 +#define _SET_PK 0x41 +#define _SEND_DATA_TCP_CMD 0x44 +#define _GET_DATABUF_TCP_CMD 0x45 +#define _INSERT_DATABUF_TCP_CMD 0x46 +#define _SET_ENT_IDENT_CMD 0x4A +#define _SET_ENT_UNAME_CMD 0x4B +#define _SET_ENT_PASSWD_CMD 0x4C +#define _SET_ENT_ENABLE_CMD 0x4F + +#define _SET_PIN_MODE_CMD 0x50 +#define _SET_DIGITAL_WRITE_CMD 0x51 +#define _SET_ANALOG_WRITE_CMD 0x52 +#define _SET_DIGITAL_READ_CMD 0x53 +#define _SET_ANALOG_READ_CMD 0x54 + +#define DEFAULT_SENDBUF_SIZE 256 +#define SOCKET_CHUNK_SIZE 64 + +// Communication with AirLift. +void wifi_radio_send_command(wifi_radio_obj_t *self, uint8_t cmd, + const uint8_t **params, const size_t *param_lens, size_t num_params); +uint8_t wifi_radio_read_byte(wifi_radio_obj_t *self); +void wifi_radio_wait_spi_char(wifi_radio_obj_t *self, uint8_t desired); +void wifi_radio_check_data(wifi_radio_obj_t *self, uint8_t desired); +size_t wifi_radio_wait_response_cmd(wifi_radio_obj_t *self, uint8_t cmd, + uint8_t **responses, size_t *response_lens, size_t max_responses); +size_t wifi_radio_send_command_get_response(wifi_radio_obj_t *self, uint8_t cmd, + const uint8_t **params, const size_t *param_lens, size_t num_params, + uint8_t **responses, size_t *response_lens, size_t max_responses); diff --git a/devices/airlift/common-hal/wifi/ScannedNetworks.c b/devices/airlift/common-hal/wifi/ScannedNetworks.c new file mode 100644 index 0000000000000..96c637fb6dee1 --- /dev/null +++ b/devices/airlift/common-hal/wifi/ScannedNetworks.c @@ -0,0 +1,159 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec +// SPDX-FileCopyrightText: Copyright (c) 2017 Glenn Ruben Bakke +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared/runtime/interrupt_char.h" +#include "py/gc.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "shared-bindings/wifi/__init__.h" +#include "shared-bindings/wifi/Network.h" +#include "shared-bindings/wifi/Radio.h" +#include "shared-bindings/wifi/ScannedNetworks.h" + +// static void wifi_scannednetworks_done(wifi_scannednetworks_obj_t *self) { +// self->done = true; +// if (self->results != NULL) { +// // Check to see if the heap is still active. If not, it'll be freed automatically. +// if (gc_alloc_possible()) { +// m_free(self->results); +// } +// self->results = NULL; +// } +// } + +// static bool wifi_scannednetworks_wait_for_scan(wifi_scannednetworks_obj_t *self) { +// EventBits_t bits = xEventGroupWaitBits(self->radio_event_group, +// WIFI_SCAN_DONE_BIT, +// pdTRUE, +// pdTRUE, +// 0); +// while ((bits & WIFI_SCAN_DONE_BIT) == 0 && !mp_hal_is_interrupted()) { +// RUN_BACKGROUND_TASKS; +// bits = xEventGroupWaitBits(self->radio_event_group, +// WIFI_SCAN_DONE_BIT, +// pdTRUE, +// pdTRUE, +// 0); +// } +// return !mp_hal_is_interrupted(); +// } + +mp_obj_t common_hal_wifi_scannednetworks_next(wifi_scannednetworks_obj_t *self) { + // if (self->done) { + // return mp_const_none; + // } + // // If we are scanning, wait and then load them. + // if (self->channel_scan_in_progress) { + // // We may have to scan more than one channel to get a result. + // while (!self->done) { + // if (!wifi_scannednetworks_wait_for_scan(self)) { + // wifi_scannednetworks_done(self); + // return mp_const_none; + // } + + // esp_wifi_scan_get_ap_num(&self->total_results); + // self->channel_scan_in_progress = false; + // if (self->total_results > 0) { + // break; + // } + // // If total_results is zero then we need to start a scan and wait again. + // wifi_scannednetworks_scan_next_channel(self); + // } + // // We not have found any more results so we're done. + // if (self->done) { + // return mp_const_none; + // } + // // If we need more space than we have, realloc. + // if (self->total_results > self->max_results) { + // wifi_ap_record_t *results = m_renew_maybe(wifi_ap_record_t, + // self->results, + // self->max_results, + // self->total_results, + // true /* allow move */); + // if (results != NULL) { + // self->results = results; + // self->max_results = self->total_results; + // } else { + // if (self->max_results == 0) { + // // No room for any results should error. + // mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Failed to allocate wifi scan memory")); + // } + // // Unable to allocate more results, so load what we can. + // self->total_results = self->max_results; + // } + // } + // esp_wifi_scan_get_ap_records(&self->total_results, self->results); + // self->channel_scan_in_progress = false; + // } + + // wifi_network_obj_t *entry = mp_obj_malloc(wifi_network_obj_t, &wifi_network_type); + // memcpy(&entry->record, &self->results[self->current_result], sizeof(wifi_ap_record_t)); + // self->current_result++; + + // // If we're returning our last network then start the next channel scan or + // // be done. + // if (self->current_result >= self->total_results) { + // wifi_scannednetworks_scan_next_channel(self); + // self->total_results = 0; + // self->current_result = 0; + // } + + // return MP_OBJ_FROM_PTR(entry); + return mp_const_none; +} + +// We don't do a linear scan so that we look at a variety of spectrum up front. +// static uint8_t scan_pattern[] = {6, 1, 11, 3, 9, 13, 2, 4, 8, 12, 5, 7, 10, 14, 0}; + +void wifi_scannednetworks_scan_next_channel(wifi_scannednetworks_obj_t *self) { + // // There is no channel 0, so use that as a flag to indicate we've run out of channels to scan. + // uint8_t next_channel = 0; + // while (self->current_channel_index < sizeof(scan_pattern)) { + // next_channel = scan_pattern[self->current_channel_index]; + // self->current_channel_index++; + // // Scan only channels that are in the specified range. + // if (self->start_channel <= next_channel && next_channel <= self->end_channel) { + // break; + // } + // } + // wifi_scan_config_t config = { 0 }; + // config.channel = next_channel; + // if (next_channel == 0) { + // wifi_scannednetworks_done(self); + // } else { + // esp_err_t result = esp_wifi_scan_start(&config, false); + // if (result != ESP_OK) { + // wifi_scannednetworks_done(self); + // } else { + // self->channel_scan_in_progress = true; + // } + // } +} + +void wifi_scannednetworks_deinit(wifi_scannednetworks_obj_t *self) { + // // if a scan is active, make sure and clean up the idf's buffer of results. + // if (self->channel_scan_in_progress) { + // esp_wifi_scan_stop(); + // if (wifi_scannednetworks_wait_for_scan(self)) { + // // Discard the scanned records, one at a time, to avoid memory leaks. + // uint16_t number; + // do { + // number = 1; + // wifi_ap_record_t record; + // esp_wifi_scan_get_ap_records(&number, &record); + // } while (number > 0); + // // TODO: available in ESP-IDF v5.0; do instead of the above. + // // Discard scan results. + // // esp_wifi_clear_ap_list(); + // self->channel_scan_in_progress = false; + // } + // } + // wifi_scannednetworks_done(self); +} diff --git a/devices/airlift/common-hal/wifi/ScannedNetworks.h b/devices/airlift/common-hal/wifi/ScannedNetworks.h new file mode 100644 index 0000000000000..7ea63c1e6263a --- /dev/null +++ b/devices/airlift/common-hal/wifi/ScannedNetworks.h @@ -0,0 +1,35 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "py/obj.h" + + +typedef struct { + mp_obj_base_t base; + uint8_t current_channel_index; +// EventGroupHandle_t radio_event_group; + + // Results from the last channel scan +// wifi_ap_record_t *results; + uint16_t current_result; + uint16_t total_results; + uint16_t max_results; + + // Limits on what channels to scan. + uint8_t start_channel; + uint8_t end_channel; // Inclusive + + bool done; + bool channel_scan_in_progress; +} wifi_scannednetworks_obj_t; + +void wifi_scannednetworks_scan_next_channel(wifi_scannednetworks_obj_t *self); +void wifi_scannednetworks_deinit(wifi_scannednetworks_obj_t *self); diff --git a/devices/airlift/common-hal/wifi/__init__.c b/devices/airlift/common-hal/wifi/__init__.c new file mode 100644 index 0000000000000..7804a857ab62f --- /dev/null +++ b/devices/airlift/common-hal/wifi/__init__.c @@ -0,0 +1,362 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "common-hal/wifi/__init__.h" +#include "shared-bindings/wifi/__init__.h" + +#include "shared-bindings/ipaddress/IPv4Address.h" +#include "shared-bindings/wifi/Monitor.h" +#include "shared-bindings/wifi/Radio.h" +#include "common-hal/socketpool/__init__.h" + +#include "py/gc.h" +#include "py/mpstate.h" +#include "py/runtime.h" + +wifi_radio_obj_t common_hal_wifi_radio_obj; + +#include "supervisor/port.h" +#include "supervisor/workflow.h" + +#if CIRCUITPY_STATUS_BAR +#include "supervisor/shared/status_bar.h" +#endif + +#define MAC_ADDRESS_LENGTH 6 + +// static void schedule_background_on_cp_core(void *arg) { +// #if CIRCUITPY_STATUS_BAR +// supervisor_status_bar_request_update(false); +// #endif + +// // CircuitPython's VM is run in a separate FreeRTOS task from wifi callbacks. So, we have to +// // notify the main task every time in case it's waiting for us. +// port_wake_main_task(); +// } + +// static void event_handler(void *arg, esp_event_base_t event_base, +// int32_t event_id, void *event_data) { +// // This runs on the PRO CORE! It cannot share CP interrupt enable/disable +// // directly. +// wifi_radio_obj_t *radio = arg; +// if (event_base == WIFI_EVENT) { +// switch (event_id) { +// case WIFI_EVENT_SCAN_DONE: +// ESP_LOGW(TAG, "scan"); +// xEventGroupSetBits(radio->event_group_handle, WIFI_SCAN_DONE_BIT); +// break; +// case WIFI_EVENT_AP_START: +// ESP_LOGW(TAG, "ap start"); +// break; +// case WIFI_EVENT_AP_STOP: +// ESP_LOGW(TAG, "ap stop"); +// break; +// case WIFI_EVENT_AP_STACONNECTED: +// break; +// case WIFI_EVENT_AP_STADISCONNECTED: +// break; +// case WIFI_EVENT_STA_START: +// ESP_LOGW(TAG, "sta start"); +// break; +// case WIFI_EVENT_STA_STOP: +// ESP_LOGW(TAG, "sta stop"); +// break; +// case WIFI_EVENT_STA_CONNECTED: +// ESP_LOGW(TAG, "connected"); +// break; +// case WIFI_EVENT_STA_DISCONNECTED: { +// ESP_LOGW(TAG, "disconnected"); +// wifi_event_sta_disconnected_t *d = (wifi_event_sta_disconnected_t *)event_data; +// uint8_t reason = d->reason; +// ESP_LOGW(TAG, "reason %d 0x%02x", reason, reason); +// if (radio->retries_left > 0 && +// reason != WIFI_REASON_AUTH_FAIL && +// reason != WIFI_REASON_NO_AP_FOUND && +// reason != WIFI_REASON_ASSOC_LEAVE) { +// radio->retries_left--; +// ESP_LOGI(TAG, "Retrying connect. %d retries remaining", radio->retries_left); +// esp_wifi_connect(); +// return; +// } + +// radio->last_disconnect_reason = reason; +// xEventGroupSetBits(radio->event_group_handle, WIFI_DISCONNECTED_BIT); +// break; +// } + +// // Cases to handle later. +// // case WIFI_EVENT_STA_AUTHMODE_CHANGE: +// default: { +// ESP_LOGW(TAG, "event %ld 0x%02ld", event_id, event_id); +// break; +// } +// } +// } + +// if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { +// ESP_LOGW(TAG, "got ip"); +// radio->retries_left = radio->starting_retries; +// xEventGroupSetBits(radio->event_group_handle, WIFI_CONNECTED_BIT); +// } +// // Use IPC to ensure we run schedule background on the same core as CircuitPython. +// #if defined(CONFIG_FREERTOS_UNICORE) && CONFIG_FREERTOS_UNICORE +// schedule_background_on_cp_core(NULL); +// #else +// // This only blocks until the start of the function. That's ok since the PRO +// // core shouldn't care what we do. +// esp_ipc_call(CONFIG_ESP_MAIN_TASK_AFFINITY, schedule_background_on_cp_core, NULL); +// #endif +// } + +// static bool wifi_inited; +// static bool wifi_ever_inited; +// static bool wifi_user_initiated; + +void common_hal_wifi_init(bool user_initiated) { + // wifi_radio_obj_t *self = &common_hal_wifi_radio_obj; + + // if (wifi_inited) { + // if (user_initiated && !wifi_user_initiated) { + // common_hal_wifi_radio_set_enabled(self, true); + // } + // return; + // } + // wifi_inited = true; + // wifi_user_initiated = user_initiated; + // common_hal_wifi_radio_obj.base.type = &wifi_radio_type; + + // if (!wifi_ever_inited) { + // ESP_ERROR_CHECK(esp_event_loop_create_default()); + // ESP_ERROR_CHECK(esp_netif_init()); + // wifi_ever_inited = true; + // } + + // self->netif = esp_netif_create_default_wifi_sta(); + // self->ap_netif = esp_netif_create_default_wifi_ap(); + // self->started = false; + + // // Even though we just called esp_netif_create_default_wifi_sta, + // // station mode isn't actually ready for use until esp_wifi_set_mode() + // // is called and the configuration is loaded via esp_wifi_set_config(). + // // Set both convenience flags to false so it's not forgotten. + // self->sta_mode = 0; + // self->ap_mode = 0; + + // self->event_group_handle = xEventGroupCreateStatic(&self->event_group); + // ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + // ESP_EVENT_ANY_ID, + // &event_handler, + // self, + // &self->handler_instance_all_wifi)); + // ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, + // IP_EVENT_STA_GOT_IP, + // &event_handler, + // self, + // &self->handler_instance_got_ip)); + + // wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); + // esp_err_t result = esp_wifi_init(&config); + // #ifdef CONFIG_ESP32_WIFI_NVS_ENABLED + // // Generally we don't use this because we store ssid and passwords ourselves in the filesystem. + // esp_err_t err = nvs_flash_init(); + // if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + // // NVS partition was truncated and needs to be erased + // // Retry nvs_flash_init + // ESP_ERROR_CHECK(nvs_flash_erase()); + // err = nvs_flash_init(); + // } + // ESP_ERROR_CHECK(err); + // ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH)); + // #else + // ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + // #endif + // if (result == ESP_ERR_NO_MEM) { + // if (gc_alloc_possible()) { + // mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Failed to allocate Wifi memory")); + // } + // ESP_LOGE(TAG, "Failed to allocate Wifi memory"); + // } else if (result != ESP_OK) { + // if (gc_alloc_possible()) { + // raise_esp_error(result); + // } + // ESP_LOGE(TAG, "WiFi error code: %x", result); + // return; + // } + // // Set the default lwip_local_hostname capped at 32 characters. We trim off + // // the start of the board name (likely manufacturer) because the end is + // // often more unique to the board. + // size_t board_len = MIN(32 - ((MAC_ADDRESS_LENGTH * 2) + 6), strlen(CIRCUITPY_BOARD_ID)); + // size_t board_trim = strlen(CIRCUITPY_BOARD_ID) - board_len; + // // Avoid double _ in the hostname. + // if (CIRCUITPY_BOARD_ID[board_trim] == '_') { + // board_trim++; + // } + + // char cpy_default_hostname[board_len + (MAC_ADDRESS_LENGTH * 2) + 6]; + // uint8_t mac[MAC_ADDRESS_LENGTH]; + // esp_wifi_get_mac(ESP_IF_WIFI_STA, mac); + // snprintf(cpy_default_hostname, sizeof(cpy_default_hostname), "cpy-%s-%02x%02x%02x%02x%02x%02x", CIRCUITPY_BOARD_ID + board_trim, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + // const char *default_lwip_local_hostname = cpy_default_hostname; + // ESP_ERROR_CHECK(esp_netif_set_hostname(self->netif, default_lwip_local_hostname)); + // // set station mode to avoid the default SoftAP + // common_hal_wifi_radio_start_station(self); + // // start wifi + // common_hal_wifi_radio_set_enabled(self, true); +} + +void wifi_user_reset(void) { + // if (wifi_user_initiated) { + // wifi_reset(); + // wifi_user_initiated = false; + // } +} + +void wifi_reset(void) { + // if (!wifi_inited) { + // return; + // } + // common_hal_wifi_monitor_deinit(MP_STATE_VM(wifi_monitor_singleton)); + // wifi_radio_obj_t *radio = &common_hal_wifi_radio_obj; + // common_hal_wifi_radio_set_enabled(radio, false); + // #ifndef CONFIG_IDF_TARGET_ESP32 + // ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, + // ESP_EVENT_ANY_ID, + // radio->handler_instance_all_wifi)); + // ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, + // IP_EVENT_STA_GOT_IP, + // radio->handler_instance_got_ip)); + // ESP_ERROR_CHECK(esp_wifi_deinit()); + // esp_netif_destroy(radio->netif); + // radio->netif = NULL; + // esp_netif_destroy(radio->ap_netif); + // radio->ap_netif = NULL; + // wifi_inited = false; + // #endif + // supervisor_workflow_request_background(); +} + +// void ipaddress_ipaddress_to_esp_idf(mp_obj_t ip_address, ip_addr_t *esp_ip_address) { +// if (mp_obj_is_type(ip_address, &ipaddress_ipv4address_type)) { +// ipaddress_ipaddress_to_esp_idf_ip4(ip_address, (esp_ip4_addr_t *)esp_ip_address); +// #if LWIP_IPV6 +// esp_ip_address->type = IPADDR_TYPE_V4; +// #endif +// } else { +// struct sockaddr_storage addr_storage; +// socketpool_resolve_host_or_throw(AF_UNSPEC, SOCK_STREAM, mp_obj_str_get_str(ip_address), &addr_storage, 1); +// sockaddr_to_espaddr(&addr_storage, (esp_ip_addr_t *)esp_ip_address); +// } +// } + +// void ipaddress_ipaddress_to_esp_idf_ip4(mp_obj_t ip_address, esp_ip4_addr_t *esp_ip_address) { +// if (!mp_obj_is_type(ip_address, &ipaddress_ipv4address_type)) { +// mp_raise_ValueError(MP_ERROR_TEXT("Only IPv4 addresses supported")); +// } +// mp_obj_t packed = common_hal_ipaddress_ipv4address_get_packed(ip_address); +// size_t len; +// const char *bytes = mp_obj_str_get_data(packed, &len); +// esp_netif_set_ip4_addr(esp_ip_address, bytes[0], bytes[1], bytes[2], bytes[3]); +// } + +void common_hal_wifi_gc_collect(void) { + common_hal_wifi_radio_gc_collect(&common_hal_wifi_radio_obj); +} + +// static mp_obj_t espaddrx_to_str(const void *espaddr, uint8_t esptype) { +// // char buf[IPADDR_STRLEN_MAX]; +// // inet_ntop(esptype == ESP_IPADDR_TYPE_V6 ? AF_INET6 : AF_INET, espaddr, buf, sizeof(buf)); +// // return mp_obj_new_str(buf, strlen(buf)); +// return mp_const_none; +// } + +// mp_obj_t espaddr_to_str(const esp_ip_addr_t *espaddr) { +// // return espaddrx_to_str(espaddr, espaddr->type); +// return mp_const_none; +// } + +// mp_obj_t espaddr4_to_str(const esp_ip4_addr_t *espaddr) { +// // return espaddrx_to_str(espaddr, ESP_IPADDR_TYPE_V4); +// return mp_const_none; +// } + +// mp_obj_t espaddr6_to_str(const esp_ip6_addr_t *espaddr) { +// // return espaddrx_to_str(espaddr, ESP_IPADDR_TYPE_V6); +// return mp_const_none; +// } + +// mp_obj_t sockaddr_to_str(const struct sockaddr_storage *sockaddr) { +// char buf[IPADDR_STRLEN_MAX]; +// #if CIRCUITPY_SOCKETPOOL_IPV6 +// if (sockaddr->ss_family == AF_INET6) { +// const struct sockaddr_in6 *addr6 = (const void *)sockaddr; +// inet_ntop(AF_INET6, &addr6->sin6_addr, buf, sizeof(buf)); +// } else +// #endif +// { +// const struct sockaddr_in *addr = (const void *)sockaddr; +// inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf)); +// } +// return mp_obj_new_str(buf, strlen(buf)); +// } + +// mp_obj_t sockaddr_to_tuple(const struct sockaddr_storage *sockaddr) { +// mp_obj_t args[4] = { +// sockaddr_to_str(sockaddr), +// }; +// int n = 2; +// #if CIRCUITPY_SOCKETPOOL_IPV6 +// if (sockaddr->ss_family == AF_INET6) { +// const struct sockaddr_in6 *addr6 = (const void *)sockaddr; +// args[1] = MP_OBJ_NEW_SMALL_INT(htons(addr6->sin6_port)); +// args[2] = MP_OBJ_NEW_SMALL_INT(addr6->sin6_flowinfo); +// args[3] = MP_OBJ_NEW_SMALL_INT(addr6->sin6_scope_id); +// n = 4; +// } else +// #endif +// { +// const struct sockaddr_in *addr = (const void *)sockaddr; +// args[1] = MP_OBJ_NEW_SMALL_INT(htons(addr->sin_port)); +// } +// return mp_obj_new_tuple(n, args); +// } + +// void sockaddr_to_espaddr(const struct sockaddr_storage *sockaddr, esp_ip_addr_t *espaddr) { +// #if CIRCUITPY_SOCKETPOOL_IPV6 +// MP_STATIC_ASSERT(IPADDR_TYPE_V4 == ESP_IPADDR_TYPE_V4); +// MP_STATIC_ASSERT(IPADDR_TYPE_V6 == ESP_IPADDR_TYPE_V6); +// MP_STATIC_ASSERT(sizeof(ip_addr_t) == sizeof(esp_ip_addr_t)); +// MP_STATIC_ASSERT(offsetof(ip_addr_t, u_addr) == offsetof(esp_ip_addr_t, u_addr)); +// MP_STATIC_ASSERT(offsetof(ip_addr_t, type) == offsetof(esp_ip_addr_t, type)); +// if (sockaddr->ss_family == AF_INET6) { +// const struct sockaddr_in6 *addr6 = (const void *)sockaddr; +// MP_STATIC_ASSERT(sizeof(espaddr->u_addr.ip6.addr) == sizeof(addr6->sin6_addr)); +// memcpy(&espaddr->u_addr.ip6.addr, &addr6->sin6_addr, sizeof(espaddr->u_addr.ip6.addr)); +// espaddr->u_addr.ip6.zone = addr6->sin6_scope_id; +// espaddr->type = ESP_IPADDR_TYPE_V6; +// } else +// #endif +// { +// const struct sockaddr_in *addr = (const void *)sockaddr; +// MP_STATIC_ASSERT(sizeof(espaddr->u_addr.ip4.addr) == sizeof(addr->sin_addr)); +// memcpy(&espaddr->u_addr.ip4.addr, &addr->sin_addr, sizeof(espaddr->u_addr.ip4.addr)); +// espaddr->type = ESP_IPADDR_TYPE_V4; +// } +// } + +// void espaddr_to_sockaddr(const esp_ip_addr_t *espaddr, struct sockaddr_storage *sockaddr, int port) { +// #if CIRCUITPY_SOCKETPOOL_IPV6 +// if (espaddr->type == ESP_IPADDR_TYPE_V6) { +// struct sockaddr_in6 *addr6 = (void *)sockaddr; +// memcpy(&addr6->sin6_addr, &espaddr->u_addr.ip6.addr, sizeof(espaddr->u_addr.ip6.addr)); +// addr6->sin6_scope_id = espaddr->u_addr.ip6.zone; +// } else +// #endif +// { +// struct sockaddr_in *addr = (void *)sockaddr; +// memcpy(&addr->sin_addr, &espaddr->u_addr.ip4.addr, sizeof(espaddr->u_addr.ip4.addr)); +// } +// } diff --git a/devices/airlift/common-hal/wifi/__init__.h b/devices/airlift/common-hal/wifi/__init__.h new file mode 100644 index 0000000000000..110fcae04c05f --- /dev/null +++ b/devices/airlift/common-hal/wifi/__init__.h @@ -0,0 +1,24 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +struct sockaddr_storage; + +void wifi_reset(void); + +// void ipaddress_ipaddress_to_esp_idf(mp_obj_t ip_address, ip_addr_t *esp_ip_address); +// void ipaddress_ipaddress_to_esp_idf_ip4(mp_obj_t ip_address, esp_ip4_addr_t *esp_ip_address); + +// mp_obj_t sockaddr_to_str(const struct sockaddr_storage *addr); +// mp_obj_t sockaddr_to_tuple(const struct sockaddr_storage *addr); +// mp_obj_t espaddr_to_str(const esp_ip_addr_t *espaddr); +// mp_obj_t espaddr4_to_str(const esp_ip4_addr_t *espaddr); +// mp_obj_t espaddr6_to_str(const esp_ip6_addr_t *espaddr); +// void sockaddr_to_espaddr(const struct sockaddr_storage *sockaddr, esp_ip_addr_t *espaddr); +// void espaddr_to_sockaddr(const esp_ip_addr_t *espaddr, struct sockaddr_storage *sockaddr, int port); diff --git a/docs/shared_bindings_matrix.py b/docs/shared_bindings_matrix.py index b4fe4ff7e2464..135b2db5d2cf2 100644 --- a/docs/shared_bindings_matrix.py +++ b/docs/shared_bindings_matrix.py @@ -72,7 +72,7 @@ "adafruit_bus_device": "CIRCUITPY_BUSDEVICE", "adafruit_pixelbuf": "CIRCUITPY_PIXELBUF", "array": "CIRCUITPY_ARRAY", - # always available, so depend on something that's always 1. + # builtins are always available, so depend on something that's always 1. "builtins": "CIRCUITPY", "builtins.pow3": "CIRCUITPY_BUILTINS_POW3", "busio.SPI": "CIRCUITPY_BUSIO_SPI", @@ -90,6 +90,8 @@ "terminalio": "CIRCUITPY_DISPLAYIO", "usb": "CIRCUITPY_PYUSB", "socketpool.socketpool.AF_INET6": "CIRCUITPY_SOCKETPOOL_IPV6", + "wifi (native)": "CIRCUITPY_WIFI_NATIVE", + "wifi (AirLift co-processor)": "CIRCUITPY_WIFI_AIRLIFT", } MODULES_NOT_IN_BINDINGS = ["binascii", "errno", "json", "re", "ulab"] diff --git a/frozen/Adafruit_CircuitPython_AHTx0 b/frozen/Adafruit_CircuitPython_AHTx0 index 8c61ed111fc83..ff95dd5f3d018 160000 --- a/frozen/Adafruit_CircuitPython_AHTx0 +++ b/frozen/Adafruit_CircuitPython_AHTx0 @@ -1 +1 @@ -Subproject commit 8c61ed111fc83e4e1703cf5e014e645f4dbbef32 +Subproject commit ff95dd5f3d0186c5cdc8bd8cb34ac22ac2e2225d diff --git a/frozen/Adafruit_CircuitPython_APDS9960 b/frozen/Adafruit_CircuitPython_APDS9960 index f05a7239131dc..00127a75d22f0 160000 --- a/frozen/Adafruit_CircuitPython_APDS9960 +++ b/frozen/Adafruit_CircuitPython_APDS9960 @@ -1 +1 @@ -Subproject commit f05a7239131dc05df949e49c1bb5732529a864bf +Subproject commit 00127a75d22f035096ea9317ad57c74c6a9b4232 diff --git a/frozen/Adafruit_CircuitPython_BLE b/frozen/Adafruit_CircuitPython_BLE index 6744b6869e4c3..1acb303cc7f63 160000 --- a/frozen/Adafruit_CircuitPython_BLE +++ b/frozen/Adafruit_CircuitPython_BLE @@ -1 +1 @@ -Subproject commit 6744b6869e4c3112610132bcac23535ae5ef3983 +Subproject commit 1acb303cc7f63a752c9fb87655d2ec478e564be2 diff --git a/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center b/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center index e162713efa578..476082b43c9e5 160000 --- a/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center +++ b/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center @@ -1 +1 @@ -Subproject commit e162713efa578b9967f7ec921b129362036571b3 +Subproject commit 476082b43c9e5971da20a320a05546a8285d4891 diff --git a/frozen/Adafruit_CircuitPython_Bitmap_Font b/frozen/Adafruit_CircuitPython_Bitmap_Font index 13c6a39a58e28..5ca3f55f2e393 160000 --- a/frozen/Adafruit_CircuitPython_Bitmap_Font +++ b/frozen/Adafruit_CircuitPython_Bitmap_Font @@ -1 +1 @@ -Subproject commit 13c6a39a58e28030f3651a90e116c1ab30f2035b +Subproject commit 5ca3f55f2e39302c787ca93f95276e8269024038 diff --git a/frozen/Adafruit_CircuitPython_BusDevice b/frozen/Adafruit_CircuitPython_BusDevice index baa6bcafa2251..afe91665e4389 160000 --- a/frozen/Adafruit_CircuitPython_BusDevice +++ b/frozen/Adafruit_CircuitPython_BusDevice @@ -1 +1 @@ -Subproject commit baa6bcafa22512ac56f343c7d124f3b029861c33 +Subproject commit afe91665e438947bd3d88ba4a0f937ec58ff1035 diff --git a/frozen/Adafruit_CircuitPython_CircuitPlayground b/frozen/Adafruit_CircuitPython_CircuitPlayground index 65be0763beda7..d093fed40590a 160000 --- a/frozen/Adafruit_CircuitPython_CircuitPlayground +++ b/frozen/Adafruit_CircuitPython_CircuitPlayground @@ -1 +1 @@ -Subproject commit 65be0763beda780d3a1a8c4c49b033628bc54d28 +Subproject commit d093fed40590af312e44b1efa8d88ecaef9aaed4 diff --git a/frozen/Adafruit_CircuitPython_ConnectionManager b/frozen/Adafruit_CircuitPython_ConnectionManager index 2c85f3b98d081..95f39faaa647b 160000 --- a/frozen/Adafruit_CircuitPython_ConnectionManager +++ b/frozen/Adafruit_CircuitPython_ConnectionManager @@ -1 +1 @@ -Subproject commit 2c85f3b98d08102d2494195074ad836fc3020610 +Subproject commit 95f39faaa647b4215f615603368a453742423a09 diff --git a/frozen/Adafruit_CircuitPython_Crickit b/frozen/Adafruit_CircuitPython_Crickit index 722f7937bfb0c..efeb183228ff9 160000 --- a/frozen/Adafruit_CircuitPython_Crickit +++ b/frozen/Adafruit_CircuitPython_Crickit @@ -1 +1 @@ -Subproject commit 722f7937bfb0c02340dcf737ebf37cc4ecf86b83 +Subproject commit efeb183228ff9640aec5938f9c2305766579dc25 diff --git a/frozen/Adafruit_CircuitPython_DRV2605 b/frozen/Adafruit_CircuitPython_DRV2605 index 616d61c7495e5..f120d56222166 160000 --- a/frozen/Adafruit_CircuitPython_DRV2605 +++ b/frozen/Adafruit_CircuitPython_DRV2605 @@ -1 +1 @@ -Subproject commit 616d61c7495e5dadc6b77ea9fce07a3861580534 +Subproject commit f120d56222166af85b33e8e9c70eff6aec2e4828 diff --git a/frozen/Adafruit_CircuitPython_DS3231 b/frozen/Adafruit_CircuitPython_DS3231 index 62cc4dc49b587..a5d94eee49d32 160000 --- a/frozen/Adafruit_CircuitPython_DS3231 +++ b/frozen/Adafruit_CircuitPython_DS3231 @@ -1 +1 @@ -Subproject commit 62cc4dc49b587fad935368ed60b9ba1433250fdc +Subproject commit a5d94eee49d324bad474847749c3d481a1f7c908 diff --git a/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 b/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 index 89463c9bd81aa..4b382e8986db3 160000 --- a/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 +++ b/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 @@ -1 +1 @@ -Subproject commit 89463c9bd81aaf43a14fd4f3c7bdbb75d4e48b40 +Subproject commit 4b382e8986db36eaef558fec67be543205f268b2 diff --git a/frozen/Adafruit_CircuitPython_Display_Text b/frozen/Adafruit_CircuitPython_Display_Text index 727a1022e140b..7d1f187aac8e8 160000 --- a/frozen/Adafruit_CircuitPython_Display_Text +++ b/frozen/Adafruit_CircuitPython_Display_Text @@ -1 +1 @@ -Subproject commit 727a1022e140b971a2f4bde5e6571dd327f6785d +Subproject commit 7d1f187aac8e899e791324cc78633bf4f32c984b diff --git a/frozen/Adafruit_CircuitPython_DotStar b/frozen/Adafruit_CircuitPython_DotStar index 8d19e1b23cbe6..4b0ba649e5abd 160000 --- a/frozen/Adafruit_CircuitPython_DotStar +++ b/frozen/Adafruit_CircuitPython_DotStar @@ -1 +1 @@ -Subproject commit 8d19e1b23cbe6c1d17a29f321d06b16d21909b92 +Subproject commit 4b0ba649e5abdebead5b9a47a6c695d67c2c25fa diff --git a/frozen/Adafruit_CircuitPython_ESP32SPI b/frozen/Adafruit_CircuitPython_ESP32SPI index 3fcea236876b0..063b90c8706dd 160000 --- a/frozen/Adafruit_CircuitPython_ESP32SPI +++ b/frozen/Adafruit_CircuitPython_ESP32SPI @@ -1 +1 @@ -Subproject commit 3fcea236876b05d09ebf95f43cec6016667ccf84 +Subproject commit 063b90c8706ddef97cc4abf9cb78e0cc09ff3c6c diff --git a/frozen/Adafruit_CircuitPython_FakeRequests b/frozen/Adafruit_CircuitPython_FakeRequests index ff942eaae8351..020121e90c630 160000 --- a/frozen/Adafruit_CircuitPython_FakeRequests +++ b/frozen/Adafruit_CircuitPython_FakeRequests @@ -1 +1 @@ -Subproject commit ff942eaae835144f7269d8206adf506c99f699f4 +Subproject commit 020121e90c6306147f91b8079b75f3d14ff86138 diff --git a/frozen/Adafruit_CircuitPython_FocalTouch b/frozen/Adafruit_CircuitPython_FocalTouch index f20c13bdffa9b..2fb86313db340 160000 --- a/frozen/Adafruit_CircuitPython_FocalTouch +++ b/frozen/Adafruit_CircuitPython_FocalTouch @@ -1 +1 @@ -Subproject commit f20c13bdffa9b586c648f331851f427368a995ae +Subproject commit 2fb86313db3408e57b1fbfbc56359ccb4f16f38b diff --git a/frozen/Adafruit_CircuitPython_HID b/frozen/Adafruit_CircuitPython_HID index 788e46ca2cb2f..d06b8b812caef 160000 --- a/frozen/Adafruit_CircuitPython_HID +++ b/frozen/Adafruit_CircuitPython_HID @@ -1 +1 @@ -Subproject commit 788e46ca2cb2febddac83e0c660972598d6a8a27 +Subproject commit d06b8b812caef3ae2eebb662f4e57ca306ce3219 diff --git a/frozen/Adafruit_CircuitPython_HTTPServer b/frozen/Adafruit_CircuitPython_HTTPServer index e234a4940504b..c43147a016ffd 160000 --- a/frozen/Adafruit_CircuitPython_HTTPServer +++ b/frozen/Adafruit_CircuitPython_HTTPServer @@ -1 +1 @@ -Subproject commit e234a4940504b4fb21c7338d774ec273260ef672 +Subproject commit c43147a016ffd13c57a0923730bc6a83afefb4ad diff --git a/frozen/Adafruit_CircuitPython_IRRemote b/frozen/Adafruit_CircuitPython_IRRemote index 98bd8fad8cd65..b92d69304212e 160000 --- a/frozen/Adafruit_CircuitPython_IRRemote +++ b/frozen/Adafruit_CircuitPython_IRRemote @@ -1 +1 @@ -Subproject commit 98bd8fad8cd65f481b8808e5de8cdbf62d0dd300 +Subproject commit b92d69304212ee57a5f008317fcc4ebaf75ddebb diff --git a/frozen/Adafruit_CircuitPython_IS31FL3731 b/frozen/Adafruit_CircuitPython_IS31FL3731 index 1babff02ea87f..a0d701892d8be 160000 --- a/frozen/Adafruit_CircuitPython_IS31FL3731 +++ b/frozen/Adafruit_CircuitPython_IS31FL3731 @@ -1 +1 @@ -Subproject commit 1babff02ea87f5c4863aed20be0da553d76e9600 +Subproject commit a0d701892d8bef096d80f1117bee718cecb380ff diff --git a/frozen/Adafruit_CircuitPython_ImageLoad b/frozen/Adafruit_CircuitPython_ImageLoad index 67532099f7a57..135b0e4478b34 160000 --- a/frozen/Adafruit_CircuitPython_ImageLoad +++ b/frozen/Adafruit_CircuitPython_ImageLoad @@ -1 +1 @@ -Subproject commit 67532099f7a574f08955b31efb7c1ca5cc3f143c +Subproject commit 135b0e4478b34e1271e6bd87fa6d8efa0bef64b5 diff --git a/frozen/Adafruit_CircuitPython_LC709203F b/frozen/Adafruit_CircuitPython_LC709203F index 7fe15ca666bd3..b007bcae07b34 160000 --- a/frozen/Adafruit_CircuitPython_LC709203F +++ b/frozen/Adafruit_CircuitPython_LC709203F @@ -1 +1 @@ -Subproject commit 7fe15ca666bd3730a17e13bb29ff884092345b5f +Subproject commit b007bcae07b346fd28aaee770dcabc9dde698c67 diff --git a/frozen/Adafruit_CircuitPython_LED_Animation b/frozen/Adafruit_CircuitPython_LED_Animation index 515553f0b6cb1..8af05705962e8 160000 --- a/frozen/Adafruit_CircuitPython_LED_Animation +++ b/frozen/Adafruit_CircuitPython_LED_Animation @@ -1 +1 @@ -Subproject commit 515553f0b6cb1290b92965f8e4e8beab9e83bcf1 +Subproject commit 8af05705962e8bb7d2f8003e6a70916a9a51b863 diff --git a/frozen/Adafruit_CircuitPython_LIS3DH b/frozen/Adafruit_CircuitPython_LIS3DH index a3c33eff7c03e..640b18ec1bfd7 160000 --- a/frozen/Adafruit_CircuitPython_LIS3DH +++ b/frozen/Adafruit_CircuitPython_LIS3DH @@ -1 +1 @@ -Subproject commit a3c33eff7c03e7c1f1e896a08f4878f5db6a6cbf +Subproject commit 640b18ec1bfd71e0a70f7ff3b8784043cd2d2671 diff --git a/frozen/Adafruit_CircuitPython_LSM6DS b/frozen/Adafruit_CircuitPython_LSM6DS index e7da74fd8d7fd..2f50836f4bf0d 160000 --- a/frozen/Adafruit_CircuitPython_LSM6DS +++ b/frozen/Adafruit_CircuitPython_LSM6DS @@ -1 +1 @@ -Subproject commit e7da74fd8d7fddd9515e975be5479596283a719c +Subproject commit 2f50836f4bf0d9e48e4b8e046ba4d4167ad6dbdc diff --git a/frozen/Adafruit_CircuitPython_MIDI b/frozen/Adafruit_CircuitPython_MIDI index 2bc5554857727..c4e693c2d4904 160000 --- a/frozen/Adafruit_CircuitPython_MIDI +++ b/frozen/Adafruit_CircuitPython_MIDI @@ -1 +1 @@ -Subproject commit 2bc555485772743b70f620fe939048020924a605 +Subproject commit c4e693c2d4904d885cf842efc25687ccaccbabfa diff --git a/frozen/Adafruit_CircuitPython_MPU6050 b/frozen/Adafruit_CircuitPython_MPU6050 index bb5100733f339..05a0c3b72279d 160000 --- a/frozen/Adafruit_CircuitPython_MPU6050 +++ b/frozen/Adafruit_CircuitPython_MPU6050 @@ -1 +1 @@ -Subproject commit bb5100733f339dcad24df7d32eeeb492023b5059 +Subproject commit 05a0c3b72279db9fa2431308a77e6ab7ba040c8a diff --git a/frozen/Adafruit_CircuitPython_Motor b/frozen/Adafruit_CircuitPython_Motor index 610c42f118704..89facc69a405a 160000 --- a/frozen/Adafruit_CircuitPython_Motor +++ b/frozen/Adafruit_CircuitPython_Motor @@ -1 +1 @@ -Subproject commit 610c42f1187045fb962807ac8d895e66e2612298 +Subproject commit 89facc69a405ae83702ce566414adc39d46068f1 diff --git a/frozen/Adafruit_CircuitPython_NeoPixel b/frozen/Adafruit_CircuitPython_NeoPixel index 1a9523574b68c..0ba2f2122a54a 160000 --- a/frozen/Adafruit_CircuitPython_NeoPixel +++ b/frozen/Adafruit_CircuitPython_NeoPixel @@ -1 +1 @@ -Subproject commit 1a9523574b68cc205c151aaf080909348df7417c +Subproject commit 0ba2f2122a54a71b1bc3576f87b1ba7dfc9db11e diff --git a/frozen/Adafruit_CircuitPython_PCF8563 b/frozen/Adafruit_CircuitPython_PCF8563 index 3f40c877acbbd..74bb72d1c607e 160000 --- a/frozen/Adafruit_CircuitPython_PCF8563 +++ b/frozen/Adafruit_CircuitPython_PCF8563 @@ -1 +1 @@ -Subproject commit 3f40c877acbbda0ef82336c18f3620ce1b9013f5 +Subproject commit 74bb72d1c607e44cf0d5349c466acd34863c11b4 diff --git a/frozen/Adafruit_CircuitPython_Pixel_Framebuf b/frozen/Adafruit_CircuitPython_Pixel_Framebuf index d355df47c0d5c..1db789cf99429 160000 --- a/frozen/Adafruit_CircuitPython_Pixel_Framebuf +++ b/frozen/Adafruit_CircuitPython_Pixel_Framebuf @@ -1 +1 @@ -Subproject commit d355df47c0d5c1f80da01c86d585223988f30a33 +Subproject commit 1db789cf99429e27d740279000788edc794d9d0d diff --git a/frozen/Adafruit_CircuitPython_PortalBase b/frozen/Adafruit_CircuitPython_PortalBase index 25fc43dd67ae9..d26e2324de496 160000 --- a/frozen/Adafruit_CircuitPython_PortalBase +++ b/frozen/Adafruit_CircuitPython_PortalBase @@ -1 +1 @@ -Subproject commit 25fc43dd67ae95a8e62173e90c3069502194873a +Subproject commit d26e2324de496761e0aa72abc30ba07cdce8814b diff --git a/frozen/Adafruit_CircuitPython_ProgressBar b/frozen/Adafruit_CircuitPython_ProgressBar index 9fa23112cea1a..6ba9d9d991ada 160000 --- a/frozen/Adafruit_CircuitPython_ProgressBar +++ b/frozen/Adafruit_CircuitPython_ProgressBar @@ -1 +1 @@ -Subproject commit 9fa23112cea1a8db2b9b87cf2156cc4b039440a7 +Subproject commit 6ba9d9d991ada6c0cea6a32bd64595cfd37e06b2 diff --git a/frozen/Adafruit_CircuitPython_RFM69 b/frozen/Adafruit_CircuitPython_RFM69 index 62cb5cf6fbeb9..07be137bf5bda 160000 --- a/frozen/Adafruit_CircuitPython_RFM69 +++ b/frozen/Adafruit_CircuitPython_RFM69 @@ -1 +1 @@ -Subproject commit 62cb5cf6fbeb9943bf7b2db4fc614f9b40830bf3 +Subproject commit 07be137bf5bda7a0469225c9cbb09b9a0aa08791 diff --git a/frozen/Adafruit_CircuitPython_RFM9x b/frozen/Adafruit_CircuitPython_RFM9x index 5b8a9ae0ace6b..609aafb018b1c 160000 --- a/frozen/Adafruit_CircuitPython_RFM9x +++ b/frozen/Adafruit_CircuitPython_RFM9x @@ -1 +1 @@ -Subproject commit 5b8a9ae0ace6bd5f3a0860c95c3c389a09e4b59b +Subproject commit 609aafb018b1cf5b7f60f2a7c961b827dce7468e diff --git a/frozen/Adafruit_CircuitPython_Register b/frozen/Adafruit_CircuitPython_Register index 98faa16a0dd6c..96d0a4774f552 160000 --- a/frozen/Adafruit_CircuitPython_Register +++ b/frozen/Adafruit_CircuitPython_Register @@ -1 +1 @@ -Subproject commit 98faa16a0dd6c63a2726b291a53fde760a0fcabb +Subproject commit 96d0a4774f5525b926c131618e436b8e5c218a2f diff --git a/frozen/Adafruit_CircuitPython_Requests b/frozen/Adafruit_CircuitPython_Requests index 1479169b59d06..5e646b244cf36 160000 --- a/frozen/Adafruit_CircuitPython_Requests +++ b/frozen/Adafruit_CircuitPython_Requests @@ -1 +1 @@ -Subproject commit 1479169b59d069b15384da64645f1e2d711a4679 +Subproject commit 5e646b244cf36f879f15aaf77a270e4c7e6e8336 diff --git a/frozen/Adafruit_CircuitPython_SD b/frozen/Adafruit_CircuitPython_SD index dfbb9fd6ae297..ee4d73293c8d0 160000 --- a/frozen/Adafruit_CircuitPython_SD +++ b/frozen/Adafruit_CircuitPython_SD @@ -1 +1 @@ -Subproject commit dfbb9fd6ae297d6246554ea44e6c318970de98af +Subproject commit ee4d73293c8d059cd0c8bcf46758e62f5393cbee diff --git a/frozen/Adafruit_CircuitPython_SHT4x b/frozen/Adafruit_CircuitPython_SHT4x index 9da248ca94426..26a0a407d43bd 160000 --- a/frozen/Adafruit_CircuitPython_SHT4x +++ b/frozen/Adafruit_CircuitPython_SHT4x @@ -1 +1 @@ -Subproject commit 9da248ca944264cbc4278c1732f901f3e1229231 +Subproject commit 26a0a407d43bd6208deffdf577e214d899855c0e diff --git a/frozen/Adafruit_CircuitPython_SSD1306 b/frozen/Adafruit_CircuitPython_SSD1306 index 2d7fd1fd8f7bb..d75b4d593cd18 160000 --- a/frozen/Adafruit_CircuitPython_SSD1306 +++ b/frozen/Adafruit_CircuitPython_SSD1306 @@ -1 +1 @@ -Subproject commit 2d7fd1fd8f7bb1b83d60926a28ab01ffaf67fa3b +Subproject commit d75b4d593cd184cbea5e237f5212cd9122d46263 diff --git a/frozen/Adafruit_CircuitPython_SSD1680 b/frozen/Adafruit_CircuitPython_SSD1680 index c22e6d097b44c..d6aa01c4f8fa1 160000 --- a/frozen/Adafruit_CircuitPython_SSD1680 +++ b/frozen/Adafruit_CircuitPython_SSD1680 @@ -1 +1 @@ -Subproject commit c22e6d097b44c6e9612ff6b866ebec569796e6f5 +Subproject commit d6aa01c4f8fa1004430bfcdd4db2219183425693 diff --git a/frozen/Adafruit_CircuitPython_ST7789 b/frozen/Adafruit_CircuitPython_ST7789 index 1060cf1753df0..0f7269267c0d1 160000 --- a/frozen/Adafruit_CircuitPython_ST7789 +++ b/frozen/Adafruit_CircuitPython_ST7789 @@ -1 +1 @@ -Subproject commit 1060cf1753df0024a95070132045357ddd9ce559 +Subproject commit 0f7269267c0d17ada34926333bbda4021e5d7cb3 diff --git a/frozen/Adafruit_CircuitPython_SimpleIO b/frozen/Adafruit_CircuitPython_SimpleIO index 054d2643744fe..d5278d246bcf6 160000 --- a/frozen/Adafruit_CircuitPython_SimpleIO +++ b/frozen/Adafruit_CircuitPython_SimpleIO @@ -1 +1 @@ -Subproject commit 054d2643744fe78fed3c4c8b371ced26c8ab2ebe +Subproject commit d5278d246bcf658ef5d44e7658c956fac29bd9e1 diff --git a/frozen/Adafruit_CircuitPython_SimpleMath b/frozen/Adafruit_CircuitPython_SimpleMath index da27f05235713..33f82828598a3 160000 --- a/frozen/Adafruit_CircuitPython_SimpleMath +++ b/frozen/Adafruit_CircuitPython_SimpleMath @@ -1 +1 @@ -Subproject commit da27f05235713bc8e79cf3a005d11bab920e13bb +Subproject commit 33f82828598a3a10c73dfa50601fef4beac40be8 diff --git a/frozen/Adafruit_CircuitPython_Thermistor b/frozen/Adafruit_CircuitPython_Thermistor index f109e9d847b7f..2b45967cc5283 160000 --- a/frozen/Adafruit_CircuitPython_Thermistor +++ b/frozen/Adafruit_CircuitPython_Thermistor @@ -1 +1 @@ -Subproject commit f109e9d847b7f8ba8545a2b6be8d0c3dd6a8a280 +Subproject commit 2b45967cc5283e71b7826f6a158d8c8556dde287 diff --git a/frozen/Adafruit_CircuitPython_Ticks b/frozen/Adafruit_CircuitPython_Ticks index 83695404ab734..6e159f899b017 160000 --- a/frozen/Adafruit_CircuitPython_Ticks +++ b/frozen/Adafruit_CircuitPython_Ticks @@ -1 +1 @@ -Subproject commit 83695404ab734eb60d32c9e96784b9bd90c58a1a +Subproject commit 6e159f899b017e920a6058a6b16735af8a6e852e diff --git a/frozen/Adafruit_CircuitPython_UC8151D b/frozen/Adafruit_CircuitPython_UC8151D index b9bd61a0bbef1..4ebf9c2854376 160000 --- a/frozen/Adafruit_CircuitPython_UC8151D +++ b/frozen/Adafruit_CircuitPython_UC8151D @@ -1 +1 @@ -Subproject commit b9bd61a0bbef1f4705abb831739d4888f1dcec95 +Subproject commit 4ebf9c2854376a06766a6ae4732a4537a766fb75 diff --git a/frozen/Adafruit_CircuitPython_Wave b/frozen/Adafruit_CircuitPython_Wave index d302cd78d29ef..6fba948b024ec 160000 --- a/frozen/Adafruit_CircuitPython_Wave +++ b/frozen/Adafruit_CircuitPython_Wave @@ -1 +1 @@ -Subproject commit d302cd78d29ef821faa1c18482a0b20fdca1d4ee +Subproject commit 6fba948b024ec210b3cf1f1b068b3eebc82fe8d4 diff --git a/frozen/Adafruit_CircuitPython_Wiznet5k b/frozen/Adafruit_CircuitPython_Wiznet5k index ea7b97c5b1faa..736241c7a22f8 160000 --- a/frozen/Adafruit_CircuitPython_Wiznet5k +++ b/frozen/Adafruit_CircuitPython_Wiznet5k @@ -1 +1 @@ -Subproject commit ea7b97c5b1faa2db4b54d7f6d9a61b545b49f383 +Subproject commit 736241c7a22f86dcf8ff73a77c4536cedfdc4cdd diff --git a/frozen/Adafruit_CircuitPython_asyncio b/frozen/Adafruit_CircuitPython_asyncio index e69ac03dccfd8..24818f817f511 160000 --- a/frozen/Adafruit_CircuitPython_asyncio +++ b/frozen/Adafruit_CircuitPython_asyncio @@ -1 +1 @@ -Subproject commit e69ac03dccfd87ccaf3655dc751331ff922f525f +Subproject commit 24818f817f5118f59aa696a04776049c179c0f4f diff --git a/frozen/Adafruit_CircuitPython_framebuf b/frozen/Adafruit_CircuitPython_framebuf index 8a94ddb7257be..0fedf2f308ed6 160000 --- a/frozen/Adafruit_CircuitPython_framebuf +++ b/frozen/Adafruit_CircuitPython_framebuf @@ -1 +1 @@ -Subproject commit 8a94ddb7257bebfb856fe476d9b851dfa63ce443 +Subproject commit 0fedf2f308ed6b3e8261661e4810e613f33d7171 diff --git a/frozen/Adafruit_CircuitPython_seesaw b/frozen/Adafruit_CircuitPython_seesaw index d01642926b80a..94c541f45313d 160000 --- a/frozen/Adafruit_CircuitPython_seesaw +++ b/frozen/Adafruit_CircuitPython_seesaw @@ -1 +1 @@ -Subproject commit d01642926b80a46762f1dd76dcdd5aefb0d95bc8 +Subproject commit 94c541f45313dc7eb98a4cd1a6c3af39f001cc49 diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index 861ef37464633..f374ff9e2351c 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -127,6 +127,7 @@ CIRCUITPY_SPITARGET ?= 1 CIRCUITPY_SYNTHIO_MAX_CHANNELS = 12 CIRCUITPY_ULAB_OPTIMIZE_SIZE ?= 1 CIRCUITPY_WATCHDOG ?= 1 +CIRCUITPY_WIFI_AIRLIFT ?= $(HAS_1MB_FLASH) ifeq ($(CHIP_VARIANT),SAMD51G19A) # No I2S on SAMD51G diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index 8302726aad7a4..986c4e17dde4a 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -435,7 +435,7 @@ SRC_C += \ shared/netutils/netutils.c \ peripherals/$(IDF_TARGET)/pins.c -ifeq ($(CIRCUITPY_SSL),1) +ifeq ($(CIRCUITPY_SSL_NATIVE),1) SRC_C += common-hal/ssl/crt_bundle.c endif diff --git a/ports/espressif/boards/makerfabs_tft7/mpconfigboard.mk b/ports/espressif/boards/makerfabs_tft7/mpconfigboard.mk index 1797cafbce450..06ae72c0bef97 100644 --- a/ports/espressif/boards/makerfabs_tft7/mpconfigboard.mk +++ b/ports/espressif/boards/makerfabs_tft7/mpconfigboard.mk @@ -21,6 +21,6 @@ CIRCUITPY_DOTCLOCKFRAMEBUFFER = 1 #CIRCUITPY_USB_DEVICE=0 #CIRCUITPY_BUILD_EXTENSIONS = bin,uf2 #UF2_BOOTLOADER = 1 -#CIRCUITPY_WIFI=1 +#CIRCUITPY_WIFI_NATIVE=1 #CIRCUITPY_WEB_WORKFLOW=1 #OPTIMIZATION_FLAGS = -Os diff --git a/ports/espressif/common-hal/socketpool/Socket.c b/ports/espressif/common-hal/socketpool/Socket.c index 5128e93c8589e..f5cd496515b27 100644 --- a/ports/espressif/common-hal/socketpool/Socket.c +++ b/ports/espressif/common-hal/socketpool/Socket.c @@ -13,7 +13,7 @@ #include "shared-bindings/socketpool/SocketPool.h" #include "common-hal/socketpool/__init__.h" #include "common-hal/wifi/__init__.h" -#if CIRCUITPY_SSL +#if CIRCUITPY_SSL_NATIVE #include "shared-bindings/ssl/SSLSocket.h" #include "shared-module/ssl/SSLSocket.h" #endif diff --git a/ports/espressif/common-hal/wifi/Radio.c b/ports/espressif/common-hal/wifi/Radio.c index dc5311bb8adc8..7f87806e20ce5 100644 --- a/ports/espressif/common-hal/wifi/Radio.c +++ b/ports/espressif/common-hal/wifi/Radio.c @@ -75,6 +75,10 @@ static void set_mode_ap(wifi_radio_obj_t *self, bool state) { self->ap_mode = state; } +const char *common_hal_wifi_radio_get_version(wifi_radio_obj_t *self) { + return esp_idf_get_version(); +} + bool common_hal_wifi_radio_get_enabled(wifi_radio_obj_t *self) { return self->started; } diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index c3585c7e39984..58d2da68075ea 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -49,8 +49,8 @@ CIRCUITPY_4MB_FLASH_LARGE_USER_FS_LAYOUT ?= 0 # Enable more features CIRCUITPY_FULL_BUILD ?= 1 -# If SSL is enabled, it's mbedtls -CIRCUITPY_SSL_MBEDTLS = 1 +# If SSL is enabled for CYW43 or Ethernet, it's mbedtls +CIRCUITPY_SSL_MBEDTLS = $(CIRCUITPY_SSL_NATIVE) # Never use our copy of MBEDTLS CIRCUITPY_HASHLIB_MBEDTLS_ONLY = 0 @@ -93,7 +93,7 @@ CIRCUITPY_SETTABLE_PROCESSOR_FREQUENCY ?= 1 CIRCUITPY_SYNTHIO_MAX_CHANNELS ?= 12 CIRCUITPY_TOUCHIO_USE_NATIVE ?= 1 CIRCUITPY_WATCHDOG ?= 1 -CIRCUITPY_WIFI ?= 1 +CIRCUITPY_WIFI_NATIVE ?= 1 CIRCUITPY_SOCKETPOOL_IPV6 ?= 1 # Conditionally turn off modules/features per chip type @@ -268,7 +268,7 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 # Features CIRCUITPY_USB_DEVICE = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 1 -CIRCUITPY_WIFI = 0 +CIRCUITPY_WIFI_NATIVE = 0 CIRCUITPY_MAX3421E = 0 @@ -281,8 +281,8 @@ CIRCUITPY_AUDIOIO = 0 # No wifi # TODO: Support ESP32-C6 coprocessor on some boards. CIRCUITPY_BLEIO_NATIVE = 0 -CIRCUITPY_WIFI = 0 -CIRCUITPY_SSL = 0 +CIRCUITPY_WIFI_NATIVE = 0 +CIRCUITPY_SSL_NATIVE = 0 CIRCUITPY_TOUCHIO = 1 CIRCUITPY_TOUCHIO_USE_NATIVE = 0 diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 192a91717169a..fbbaca2f1414d 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -91,7 +91,7 @@ #include "esp32/rom/efuse.h" #endif -#if CIRCUITPY_SSL +#if CIRCUITPY_SSL_NATIVE #include "shared-module/ssl/__init__.h" #endif @@ -392,7 +392,7 @@ void reset_port(void) { rtc_reset(); #endif - #if CIRCUITPY_SOCKETPOOL + #if CIRCUITPY_SOCKETPOOL_NATIVE socketpool_user_reset(); #endif @@ -507,7 +507,7 @@ void port_idle_until_interrupt(void) { } } -#if CIRCUITPY_WIFI +#if CIRCUITPY_WIFI_NATIVE void port_boot_info(void) { uint8_t mac[6]; esp_wifi_get_mac(ESP_IF_WIFI_STA, mac); diff --git a/ports/raspberrypi/boards/cytron_edu_pico_w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_pico_w/mpconfigboard.mk index 384b0060df87e..927b0df2dadf0 100644 --- a/ports/raspberrypi/boards/cytron_edu_pico_w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/cytron_edu_pico_w/mpconfigboard.mk @@ -11,12 +11,12 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WIFI_NATIVE = 1 CIRCUITPY_PICODVI = 1 diff --git a/ports/raspberrypi/boards/pajenicko_picopad/mpconfigboard.mk b/ports/raspberrypi/boards/pajenicko_picopad/mpconfigboard.mk index 764e41d4d9154..6a4592e4207c9 100644 --- a/ports/raspberrypi/boards/pajenicko_picopad/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pajenicko_picopad/mpconfigboard.mk @@ -16,12 +16,12 @@ CIRCUITPY_AUDIOIO = 1 CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WIFI_NATIVE = 1 CIRCUITPY_PICODVI = 1 diff --git a/ports/raspberrypi/boards/pimoroni_badger2040w/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_badger2040w/mpconfigboard.mk index dc70c55195913..b3953a551c261 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2040w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pimoroni_badger2040w/mpconfigboard.mk @@ -12,12 +12,12 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WIFI_NATIVE = 1 CIRCUITPY_PICODVI = 0 CIRCUITPY_USB_HOST = 0 diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/mpconfigboard.mk index 69b23a72be738..2a76e78df352f 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/mpconfigboard.mk @@ -11,12 +11,13 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 -CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_WIFI_NATIVE = 1 + CIRCUITPY_PICODVI = 0 CIRCUITPY_USB_HOST = 0 diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/mpconfigboard.mk index ee04517499ee5..d4898949a169b 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/mpconfigboard.mk @@ -11,12 +11,13 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 -CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_WIFI_NATIVE = 1 + CIRCUITPY_PICODVI = 0 CIRCUITPY_USB_HOST = 0 diff --git a/ports/raspberrypi/boards/pimoroni_pico_dv_base_w/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_pico_dv_base_w/mpconfigboard.mk index 9218192d08333..d31537bc0a21a 100644 --- a/ports/raspberrypi/boards/pimoroni_pico_dv_base_w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pimoroni_pico_dv_base_w/mpconfigboard.mk @@ -11,12 +11,12 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WIFI_NATIVE = 1 CIRCUITPY_PICODVI = 1 diff --git a/ports/raspberrypi/boards/pimoroni_pico_plus2w/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_pico_plus2w/mpconfigboard.mk index b06c1246d3521..2a4df0edaa27e 100644 --- a/ports/raspberrypi/boards/pimoroni_pico_plus2w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pimoroni_pico_plus2w/mpconfigboard.mk @@ -12,12 +12,12 @@ EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 -CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_WIFI_NATIVE = 1 # PIO clock divider set to 2 (default), consider changing if TM2 gSPI # becomes unreliable. diff --git a/ports/raspberrypi/boards/pimoroni_plasma2040w/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_plasma2040w/mpconfigboard.mk index 442991fa12622..34d7e62ef7b75 100644 --- a/ports/raspberrypi/boards/pimoroni_plasma2040w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pimoroni_plasma2040w/mpconfigboard.mk @@ -11,12 +11,13 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 -CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_WIFI_NATIVE = 1 + CIRCUITPY_PICODVI = 0 CIRCUITPY_USB_HOST = 0 diff --git a/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.mk index 8563f0d0034c3..cdd5103177678 100644 --- a/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.mk @@ -12,12 +12,13 @@ EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 -CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_WIFI_NATIVE = 1 + CIRCUITPY_PICODVI = 0 CIRCUITPY_USB_HOST = 0 diff --git a/ports/raspberrypi/boards/raspberry_pi_pico2_w/mpconfigboard.mk b/ports/raspberrypi/boards/raspberry_pi_pico2_w/mpconfigboard.mk index e1407c16854a4..127b1febb35cc 100644 --- a/ports/raspberrypi/boards/raspberry_pi_pico2_w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/raspberry_pi_pico2_w/mpconfigboard.mk @@ -12,12 +12,12 @@ EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WIFI_NATIVE = 1 CFLAGS += \ -DCYW43_PIN_WL_DYNAMIC=0 \ diff --git a/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.mk b/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.mk index e658db65f4da5..af6f8415e3b59 100644 --- a/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/raspberry_pi_pico_w/mpconfigboard.mk @@ -13,12 +13,12 @@ CIRCUITPY_USB_HOST = 0 CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 -CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_WIFI_NATIVE = 1 CFLAGS += \ diff --git a/ports/raspberrypi/boards/sparkfun_thing_plus_rp2350/mpconfigboard.mk b/ports/raspberrypi/boards/sparkfun_thing_plus_rp2350/mpconfigboard.mk index 6e81a229961da..d692c604b9049 100644 --- a/ports/raspberrypi/boards/sparkfun_thing_plus_rp2350/mpconfigboard.mk +++ b/ports/raspberrypi/boards/sparkfun_thing_plus_rp2350/mpconfigboard.mk @@ -16,12 +16,12 @@ CIRCUITPY_USB_HOST = 0 CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 -CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 -CIRCUITPY_WEB_WORKFLOW = 1 CIRCUITPY_MDNS = 1 -CIRCUITPY_SOCKETPOOL = 1 -CIRCUITPY_WIFI = 1 +CIRCUITPY_SOCKETPOOL_NATIVE = 1 +CIRCUITPY_SSL_NATIVE = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_WIFI_NATIVE = 1 CFLAGS += \ diff --git a/ports/raspberrypi/boards/wiznet_w5100s_evb_pico/mpconfigboard.mk b/ports/raspberrypi/boards/wiznet_w5100s_evb_pico/mpconfigboard.mk index 3ab04a098335f..9ce40cb9742c6 100644 --- a/ports/raspberrypi/boards/wiznet_w5100s_evb_pico/mpconfigboard.mk +++ b/ports/raspberrypi/boards/wiznet_w5100s_evb_pico/mpconfigboard.mk @@ -10,7 +10,7 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_FLOPPYIO = 0 -CIRCUITPY_SSL = 1 +CIRCUITPY_SSL_NATIVE = 1 CIRCUITPY_USB_HOST = 0 # The default is -O3. Change to -O2 because the build was overflowing. diff --git a/ports/raspberrypi/boards/wiznet_w5100s_evb_pico2/mpconfigboard.mk b/ports/raspberrypi/boards/wiznet_w5100s_evb_pico2/mpconfigboard.mk index 3e3542120e362..576a8665db51e 100644 --- a/ports/raspberrypi/boards/wiznet_w5100s_evb_pico2/mpconfigboard.mk +++ b/ports/raspberrypi/boards/wiznet_w5100s_evb_pico2/mpconfigboard.mk @@ -10,7 +10,7 @@ CHIP_FAMILY = rp2 EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" CIRCUITPY__EVE = 1 -CIRCUITPY_SSL = 1 +CIRCUITPY_SSL_NATIVE = 1 # The default is -O3. Change to -O2 because the build was overflowing. OPTIMIZATION_FLAGS = -O2 diff --git a/ports/raspberrypi/boards/wiznet_w5500_evb_pico/mpconfigboard.mk b/ports/raspberrypi/boards/wiznet_w5500_evb_pico/mpconfigboard.mk index 4a8343963bc98..9f3a2a3cc6419 100644 --- a/ports/raspberrypi/boards/wiznet_w5500_evb_pico/mpconfigboard.mk +++ b/ports/raspberrypi/boards/wiznet_w5500_evb_pico/mpconfigboard.mk @@ -10,7 +10,7 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 CIRCUITPY_FLOPPYIO = 0 -CIRCUITPY_SSL = 1 +CIRCUITPY_SSL_NATIVE = 1 CIRCUITPY_USB_HOST = 0 # The default is -O3. Change to -O2 because the build was overflowing. diff --git a/ports/raspberrypi/boards/wiznet_w5500_evb_pico2/mpconfigboard.mk b/ports/raspberrypi/boards/wiznet_w5500_evb_pico2/mpconfigboard.mk index c10c7aa7eb892..d688c61e3f729 100644 --- a/ports/raspberrypi/boards/wiznet_w5500_evb_pico2/mpconfigboard.mk +++ b/ports/raspberrypi/boards/wiznet_w5500_evb_pico2/mpconfigboard.mk @@ -10,7 +10,7 @@ CHIP_FAMILY = rp2 EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" CIRCUITPY__EVE = 1 -CIRCUITPY_SSL = 1 +CIRCUITPY_SSL_NATIVE = 1 # The default is -O3. Change to -O2 because the build was overflowing. OPTIMIZATION_FLAGS = -O2 diff --git a/ports/raspberrypi/common-hal/wifi/Radio.c b/ports/raspberrypi/common-hal/wifi/Radio.c index 3c22c3548d191..bfd94cae07230 100644 --- a/ports/raspberrypi/common-hal/wifi/Radio.c +++ b/ports/raspberrypi/common-hal/wifi/Radio.c @@ -52,6 +52,13 @@ NORETURN static void ro_attribute(qstr attr) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q is read-only for this board"), attr); } +const char *common_hal_wifi_radio_get_version(wifi_radio_obj_t *self) { + return "cyw43-driver " + MP_STRINGIFY(CYW43_VERSION_MAJOR) "." + MP_STRINGIFY(CYW43_VERSION_MINOR) "." + MP_STRINGIFY(CYW43_VERSION_MICRO); +} + bool common_hal_wifi_radio_get_enabled(wifi_radio_obj_t *self) { return self->enabled; } diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 5cfbdfa66a32b..dcf2819ec9cd0 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -27,7 +27,7 @@ #include "shared-module/ssl/__init__.h" #endif -#if CIRCUITPY_WIFI +#if CIRCUITPY_WIFI_NATIVE #include "common-hal/wifi/__init__.h" #endif @@ -449,7 +449,7 @@ void reset_port(void) { watchdog_reset(); #endif - #if CIRCUITPY_WIFI + #if CIRCUITPY_WIFI_NATIVE wifi_reset(); #endif } diff --git a/ports/zephyr-cp/common-hal/socketpool/Socket.c b/ports/zephyr-cp/common-hal/socketpool/Socket.c index 857526a12debd..4814d375959d8 100644 --- a/ports/zephyr-cp/common-hal/socketpool/Socket.c +++ b/ports/zephyr-cp/common-hal/socketpool/Socket.c @@ -12,7 +12,7 @@ #include "shared-bindings/socketpool/SocketPool.h" #include "common-hal/socketpool/__init__.h" #include "common-hal/wifi/__init__.h" -#if CIRCUITPY_SSL +#if CIRCUITPY_SSL_NATIVE #include "shared-bindings/ssl/SSLSocket.h" #include "shared-module/ssl/SSLSocket.h" #endif diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 56c836cb3a490..a732e9b932b3c 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -201,6 +201,9 @@ endif ifeq ($(CIRCUITPY_DISPLAYIO),1) SRC_PATTERNS += displayio/% endif +ifeq ($(CIRCUITPY_DOTCLOCKFRAMEBUFFER),1) +SRC_PATTERNS += dotclockframebuffer/% +endif ifeq ($(CIRCUITPY_DUALBANK),1) SRC_PATTERNS += dualbank/% endif @@ -351,9 +354,6 @@ endif ifeq ($(CIRCUITPY_RGBMATRIX),1) SRC_PATTERNS += rgbmatrix/% endif -ifeq ($(CIRCUITPY_DOTCLOCKFRAMEBUFFER),1) -SRC_PATTERNS += dotclockframebuffer/% -endif ifeq ($(CIRCUITPY_RP2PIO),1) SRC_PATTERNS += rp2pio/% endif @@ -375,13 +375,13 @@ endif ifeq ($(CIRCUITPY_SHARPDISPLAY),1) SRC_PATTERNS += sharpdisplay/% endif -ifeq ($(CIRCUITPY_SOCKETPOOL),1) +ifeq ($(CIRCUITPY_SOCKETPOOL_NATIVE),1) SRC_PATTERNS += socketpool/% endif ifeq ($(CIRCUITPY_SPITARGET),1) SRC_PATTERNS += spitarget/% endif -ifeq ($(CIRCUITPY_SSL),1) +ifeq ($(CIRCUITPY_SSL_NATIVE),1) SRC_PATTERNS += ssl/% endif ifeq ($(CIRCUITPY_STAGE),1) @@ -459,14 +459,14 @@ endif ifeq ($(CIRCUITPY_WATCHDOG),1) SRC_PATTERNS += watchdog/% endif -ifeq ($(CIRCUITPY_WIFI),1) +ifeq ($(CIRCUITPY_WIFI_NATIVE),1) SRC_PATTERNS += wifi/% endif ifeq ($(CIRCUITPY_ZLIB),1) SRC_PATTERNS += zlib/% endif -# All possible sources are listed here, and are filtered by SRC_PATTERNS in SRC_COMMON_HAL +# All possible sources in ports/*/common-hal are listed here, and are filtered by SRC_PATTERNS in SRC_COMMON_HAL. SRC_COMMON_HAL_ALL = \ _bleio/Adapter.c \ _bleio/Attribute.c \ @@ -580,10 +580,10 @@ SRC_COMMON_HAL = $(filter $(SRC_PATTERNS), $(SRC_COMMON_HAL_ALL)) ifeq ($(CIRCUITPY_BLEIO_HCI),1) # HCI device-specific HAL and helper sources. -SRC_DEVICES_HAL += \ +SRC_DEVICES_HAL_BLEIO_HCI += \ _bleio/att.c \ _bleio/hci.c \ - _bleio/Adapter.c \ + _bleio/Adapter.c \ _bleio/Attribute.c \ _bleio/Characteristic.c \ _bleio/CharacteristicBuffer.c \ @@ -594,10 +594,28 @@ SRC_DEVICES_HAL += \ _bleio/UUID.c \ _bleio/__init__.c # HCI device-specific bindings. -SRC_DEVICES_BINDINGS += \ +SRC_DEVICES_BINDINGS_BLEIO_HCI += \ supervisor/bluetooth.c endif +ifeq ($(CIRCUITPY_WIFI_AIRLIFT),1) +# Add device-specific includes to search path. +INC += -I$(TOP)/devices/airlift +# Airlift-specific HAL and helper sources +SRC_DEVICES_HAL_WIFI_AIRLIFT += \ + socketpool/Socket.c \ + socketpool/SocketPool.c \ + socketpool/__init__.c \ + ssl/__init__.c \ + ssl/SSLContext.c \ + ssl/SSLSocket.c \ + wifi/Monitor.c \ + wifi/Network.c \ + wifi/Radio.c \ + wifi/ScannedNetworks.c \ + wifi/__init__.c +endif + # These don't have corresponding files in each port but are still located in # shared-bindings to make it clear what the contents of the modules are. # All possible sources are listed here, and are filtered by SRC_PATTERNS. @@ -652,6 +670,22 @@ SRC_BINDINGS_ENUMS += \ _bleio/__init__.c endif +ifeq ($(CIRCUITPY_WIFI_AIRLIFT),1) +# Common wifi-related bindings used by AirLift. +SRC_BINDINGS_ENUMS += \ + socketpool/Socket.c \ + socketpool/SocketPool.c \ + socketpool/__init__.c \ + ssl/SSLContext.c \ + ssl/SSLSocket.c \ + ssl/__init__.c \ + wifi/Monitor.c \ + wifi/Network.c \ + wifi/Radio.c \ + wifi/ScannedNetworks.c \ + wifi/__init__.c +endif + ifeq ($(CIRCUITPY_SAFEMODE_PY),1) SRC_BINDINGS_ENUMS += \ supervisor/SafeModeReason.c @@ -809,18 +843,20 @@ SRC_SHARED_MODULE_ALL = \ watchdog/__init__.c \ zlib/__init__.c \ -# All possible sources are listed here, and are filtered by SRC_PATTERNS. +# All possible sources in shared-module/ are listed here, and are filtered by SRC_PATTERNS. SRC_SHARED_MODULE = $(filter $(SRC_PATTERNS), $(SRC_SHARED_MODULE_ALL)) SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \ $(addprefix shared-bindings/, $(SRC_BINDINGS_ENUMS)) \ $(addprefix common-hal/, $(SRC_COMMON_HAL)) \ - $(addprefix devices/ble_hci/common-hal/, $(SRC_DEVICES_HAL)) \ - $(addprefix devices/ble_hci/, $(SRC_DEVICES_BINDINGS)) + $(addprefix devices/airlift/common-hal/, $(SRC_DEVICES_HAL_WIFI_AIRLIFT)) \ + $(addprefix devices/airlift/, $(SRC_DEVICES_BINDINGS_WIFI_AIRLIFT)) \ + $(addprefix devices/ble_hci/common-hal/, $(SRC_DEVICES_HAL_BLEIO_HCI)) \ + $(addprefix devices/ble_hci/, $(SRC_DEVICES_BINDINGS_BLEIO_HCI)) \ SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \ $(addprefix shared-module/, $(SRC_SHARED_MODULE)) \ - $(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL)) + $(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL)) \ # There may be duplicates between SRC_COMMON_HAL_EXPANDED and SRC_SHARED_MODULE_EXPANDED, # because a few modules have files both in common-hal/ and shared-module/. diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 9bbe5691dfb3b..3a2908639ba35 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -393,7 +393,7 @@ CFLAGS += -DCIRCUITPY_MATH=$(CIRCUITPY_MATH) CIRCUITPY_MAX3421E ?= 0 CFLAGS += -DCIRCUITPY_MAX3421E=$(CIRCUITPY_MAX3421E) -CIRCUITPY_MDNS ?= $(CIRCUITPY_WIFI) +CIRCUITPY_MDNS ?= $(CIRCUITPY_WIFI_NATIVE) CFLAGS += -DCIRCUITPY_MDNS=$(CIRCUITPY_MDNS) CIRCUITPY_MEMORYMAP ?= 0 @@ -534,17 +534,34 @@ CFLAGS += -DCIRCUITPY_SHARPDISPLAY=$(CIRCUITPY_SHARPDISPLAY) CIRCUITPY_SKIP_SAFE_MODE_WAIT ?= 0 CFLAGS += -DCIRCUITPY_SKIP_SAFE_MODE_WAIT=$(CIRCUITPY_SKIP_SAFE_MODE_WAIT) -CIRCUITPY_SOCKETPOOL ?= $(CIRCUITPY_WIFI) +CIRCUITPY_SOCKETPOOL_AIRLIFT ?= $(CIRCUITPY_WIFI_AIRLIFT) +CFLAGS += -DCIRCUITPY_WIFI_AIRLIFT=$(CIRCUITPY_SOCKETPOOL_AIRLIFT) + +CIRCUITPY_SOCKETPOOL_NATIVE ?= 0 +CFLAGS += -DCIRCUITPY_SOCKETPOOL_NATIVE=$(CIRCUITPY_SOCKETPOOL_NATIVE) + +ifeq ($(CIRCUITPY_SOCKETPOOL_AIRLIFT)$(CIRCUITPY_SOCKETPOOL_NATIVE),11) +$(error "CIRCUITPY_SOCKETPOOL_AIRLIFT and CIRCUITPY_SOCKETPOOL_NATIVE cannot both be enabled") +endif + +CIRCUITPY_SOCKETPOOL ?= $(call enable-if-any,$(CIRCUITPY_SOCKETPOOL_AIRLIFT) $(CIRCUITPY_SOCKETPOOL_NATIVE)) CFLAGS += -DCIRCUITPY_SOCKETPOOL=$(CIRCUITPY_SOCKETPOOL) +CIRCUITPY_SOCKETPOOL_IPV6 ?= 0 +CFLAGS += -DCIRCUITPY_SOCKETPOOL_IPV6=$(CIRCUITPY_SOCKETPOOL_IPV6) + CIRCUITPY_SPITARGET ?= 0 CFLAGS += -DCIRCUITPY_SPITARGET=$(CIRCUITPY_SPITARGET) -CIRCUITPY_SOCKETPOOL_IPV6 ?= 0 -CFLAGS += -DCIRCUITPY_SOCKETPOOL_IPV6=$(CIRCUITPY_SOCKETPOOL_IPV6) +CIRCUITPY_SSL_AIRLIFT ?= $(CIRCUITPY_WIFI_AIRLIFT) +CFLAGS += -DCIRCUITPY_WIFI_AIRLIFT=$(CIRCUITPY_SSL_AIRLIFT) -CIRCUITPY_SSL ?= $(CIRCUITPY_WIFI) -CFLAGS += -DCIRCUITPY_SSL=$(CIRCUITPY_SSL) +CIRCUITPY_SSL_NATIVE ?= 0 +CFLAGS += -DCIRCUITPY_SSL_NATIVE=$(CIRCUITPY_SSL_NATIVE) + +ifeq ($(CIRCUITPY_SSL_AIRLIFT)$(CIRCUITPY_SSL_NATIVE),11) +$(error "CIRCUITPY_SSL_AIRLIFT and CIRCUITPY_SSL_NATIVE cannot both be enabled") +endif CIRCUITPY_SSL_MBEDTLS ?= 0 CFLAGS += -DCIRCUITPY_SSL_MBEDTLS=$(CIRCUITPY_SSL_MBEDTLS) @@ -718,12 +735,24 @@ CFLAGS += -DCIRCUITPY_WARNINGS=$(CIRCUITPY_WARNINGS) CIRCUITPY_WATCHDOG ?= 0 CFLAGS += -DCIRCUITPY_WATCHDOG=$(CIRCUITPY_WATCHDOG) -CIRCUITPY_WIFI ?= 0 -CFLAGS += -DCIRCUITPY_WIFI=$(CIRCUITPY_WIFI) - -CIRCUITPY_WEB_WORKFLOW ?= $(CIRCUITPY_WIFI) +CIRCUITPY_WEB_WORKFLOW ?= $(CIRCUITPY_WIFI_NATIVE) CFLAGS += -DCIRCUITPY_WEB_WORKFLOW=$(CIRCUITPY_WEB_WORKFLOW) +# AirLift co-processor WiFi support is on boards where it will fit. +CIRCUITPY_WIFI_AIRLIFT ?= 0 +CFLAGS += -DCIRCUITPY_WIFI_AIRLIFT=$(CIRCUITPY_WIFI_AIRLIFT) + +# Native WiFi support (on chip or port-specific co-processor (e.g. RP2xxx and CYW43) is off by default. +CIRCUITPY_WIFI_NATIVE ?= 0 +CFLAGS += -DCIRCUITPY_WIFI_NATIVE=$(CIRCUITPY_WIFI_NATIVE) + +ifeq ($(CIRCUITPY_WIFI_AIRLIFT)$(CIRCUITPY_WIFI_NATIVE),11) +$(error "CIRCUITPY_WIFI_AIRLIFT and CIRCUITPY_WIFI_NATIVE cannot both be enabled") +endif + +CIRCUITPY_WIFI ?= $(call enable-if-any,$(CIRCUITPY_WIFI_AIRLIFT) $(CIRCUITPY_WIFI_NATIVE)) +CFLAGS += -DCIRCUITPY_WIFI=$(CIRCUITPY_WIFI) + CIRCUITPY_WIFI_RADIO_SETTABLE_MAC_ADDRESS?= 1 CFLAGS += -DCIRCUITPY_WIFI_RADIO_SETTABLE_MAC_ADDRESS=$(CIRCUITPY_WIFI_RADIO_SETTABLE_MAC_ADDRESS) diff --git a/shared-bindings/wifi/Radio.c b/shared-bindings/wifi/Radio.c index 9a7d445fd0473..3e196735ad9fb 100644 --- a/shared-bindings/wifi/Radio.c +++ b/shared-bindings/wifi/Radio.c @@ -77,6 +77,64 @@ static void validate_hex_password(const uint8_t *buf, size_t len) { //| ... //| +#if CIRCUITPY_WIFI_AIRLIFT +//| def init_airlift( +//| spi: busio.SPI, +//| cs: digitalio.DigitalInOut, +//| ready: digitalio.DigitalInOut, +//| reset: digitalio.DigitalInOut, +//| gpio0: Optional[digitalio.DigitalInOut] = None, +//| ) -> None: +//| """Create an ESP32 SPI WiFi control object. +//| +//| :param busio.SPI spi: The SPI bus to use +//| :param digitalio.DigitalInOut cs: Chip select pin +//| :param digitalio.DigitalInOut ready: Ready pin +//| :param digitalio.DigitalInOut reset: Reset pin +//| :param digitalio.DigitalInOut gpio0: Optional GPIO0 pin for boot mode control +//| """ +static mp_obj_t wifi_radio_init_airlift(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_spi, ARG_cs, ARG_ready, ARG_reset, ARG_gpio0, }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_spi, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_cs, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_ready, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_reset, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_gpio0, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + 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); + + busio_spi_obj_t *spi = + mp_arg_validate_type(args[ARG_spi].u_obj, &busio_spi_type, MP_QSTR_spi); + digitalio_digitalinout_obj_t *cs = + mp_arg_validate_type(args[ARG_cs].u_obj, &digitalio_digitalinout_type, MP_QSTR_cs); + digitalio_digitalinout_obj_t *ready = + mp_arg_validate_type(args[ARG_ready].u_obj, &digitalio_digitalinout_type, MP_QSTR_ready); + digitalio_digitalinout_obj_t *reset = + mp_arg_validate_type(args[ARG_reset].u_obj, &digitalio_digitalinout_type, MP_QSTR_reset); + digitalio_digitalinout_obj_t *gpio0 = + mp_arg_validate_type_or_none(args[ARG_gpio0].u_obj, &digitalio_digitalinout_type, MP_QSTR_gpio0); + + common_hal_wifi_radio_init_airlift(self, spi, cs, ready, reset, gpio0 == mp_const_none ? NULL : gpio0); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_init_airlift_obj, 4, wifi_radio_init_airlift); +#endif + +//| version: str +//| """A string described the version of the underlying WiFi software (read-only).""" +static mp_obj_t wifi_radio_get_version(mp_obj_t self) { + return common_hal_wifi_radio_get_version(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_version_obj, wifi_radio_get_version); + +MP_PROPERTY_GETTER(wifi_radio_version_obj, + (mp_obj_t)&wifi_radio_get_version_obj); + //| enabled: bool //| """``True`` when the wifi radio is enabled. //| If you set the value to ``False``, any open sockets will be closed. @@ -831,6 +889,10 @@ static mp_obj_t wifi_radio_ping(size_t n_args, const mp_obj_t *pos_args, mp_map_ static MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_ping_obj, 1, wifi_radio_ping); static const mp_rom_map_elem_t wifi_radio_locals_dict_table[] = { + #if CIRCUITPY_WIFI_AIRLIFT + { MP_ROM_QSTR(MP_QSTR_init_airlift), MP_ROM_PTR(&wifi_radio_init_airlift_obj) }, + #endif + { MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&wifi_radio_version_obj) }, { MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&wifi_radio_enabled_obj) }, { MP_ROM_QSTR(MP_QSTR_hostname), MP_ROM_PTR(&wifi_radio_hostname_obj) }, diff --git a/shared-bindings/wifi/Radio.h b/shared-bindings/wifi/Radio.h index d3c12344b8706..8e471d08449d0 100644 --- a/shared-bindings/wifi/Radio.h +++ b/shared-bindings/wifi/Radio.h @@ -8,6 +8,8 @@ #include +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/wifi/PowerManagement.h" #include "common-hal/wifi/Radio.h" @@ -61,14 +63,16 @@ typedef enum { WIFI_RADIO_NO_AP_FOUND_IN_RSSI_THRESHOLD = 212, } wifi_radio_error_t; +extern void common_hal_wifi_radio_init_airlift(wifi_radio_obj_t *self, busio_spi_obj_t *spi, digitalio_digitalinout_obj_t *cs, digitalio_digitalinout_obj_t *ready, digitalio_digitalinout_obj_t *reset, digitalio_digitalinout_obj_t *gpio0); + +extern mp_obj_t common_hal_wifi_radio_get_version(wifi_radio_obj_t *self); + extern bool common_hal_wifi_radio_get_enabled(wifi_radio_obj_t *self); extern void common_hal_wifi_radio_set_enabled(wifi_radio_obj_t *self, bool enabled); extern mp_obj_t common_hal_wifi_radio_get_hostname(wifi_radio_obj_t *self); extern void common_hal_wifi_radio_set_hostname(wifi_radio_obj_t *self, const char *hostname); - -extern void wifi_radio_get_mac_address(wifi_radio_obj_t *self, uint8_t *mac); extern mp_obj_t common_hal_wifi_radio_get_mac_address(wifi_radio_obj_t *self); extern void common_hal_wifi_radio_set_mac_address(wifi_radio_obj_t *self, const uint8_t *mac); extern mp_obj_t common_hal_wifi_radio_get_mac_address_ap(wifi_radio_obj_t *self); From e9e41d289a28e8fac4b74657fc735e97b478025d Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 6 Dec 2025 23:33:45 -0500 Subject: [PATCH 2/4] wip; scanning working except for AuthMode get/set hostname power mgmt, ip config; compiles wip compiles SSID connection working fix authmode in scanned results getaddrinfo fix incorrect const in socket recv API wip: progress on socket ops wip wip; removing heap ops; still need to do sock ops HTTP fetches work wip ssl HTTPS fetching works wip: manage nina client and server UDP working both ways --- .../airlift/common-hal/socketpool/Socket.c | 1059 +++++------ .../airlift/common-hal/socketpool/Socket.h | 57 +- .../common-hal/socketpool/SocketPool.c | 175 +- .../common-hal/socketpool/SocketPool.h | 4 + .../airlift/common-hal/socketpool/__init__.c | 2 +- .../airlift/common-hal/socketpool/__init__.h | 5 - devices/airlift/common-hal/ssl/SSLContext.c | 22 +- devices/airlift/common-hal/ssl/SSLContext.h | 4 +- devices/airlift/common-hal/ssl/SSLSocket.c | 478 +---- devices/airlift/common-hal/ssl/SSLSocket.h | 23 +- devices/airlift/common-hal/ssl/__init__.c | 3 +- devices/airlift/common-hal/wifi/Monitor.c | 110 +- devices/airlift/common-hal/wifi/Monitor.h | 4 - devices/airlift/common-hal/wifi/Network.c | 145 +- devices/airlift/common-hal/wifi/Network.h | 9 +- devices/airlift/common-hal/wifi/Radio.c | 1613 ++++++++++------- devices/airlift/common-hal/wifi/Radio.h | 264 ++- .../airlift/common-hal/wifi/ScannedNetworks.c | 242 ++- .../airlift/common-hal/wifi/ScannedNetworks.h | 25 +- devices/airlift/common-hal/wifi/__init__.c | 329 +--- devices/airlift/common-hal/wifi/__init__.h | 11 - locale/circuitpython.pot | 24 +- ports/atmel-samd/peripherals | 2 +- .../espressif/common-hal/socketpool/Socket.c | 13 +- ports/espressif/common-hal/wifi/Network.c | 5 +- ports/espressif/common-hal/wifi/Radio.c | 8 +- ports/espressif/common-hal/wifi/__init__.c | 2 - .../common-hal/socketpool/Socket.c | 15 +- ports/raspberrypi/common-hal/wifi/Network.c | 5 +- ports/raspberrypi/common-hal/wifi/Radio.c | 4 +- .../zephyr-cp/common-hal/socketpool/Socket.c | 7 +- ports/zephyr-cp/common-hal/wifi/Radio.c | 4 +- ports/zephyr-cp/common-hal/wifi/__init__.c | 2 - py/circuitpy_defns.mk | 7 +- py/circuitpy_mpconfig.mk | 5 +- shared-bindings/audiofreeverb/Freeverb.c | 7 +- shared-bindings/busio/SPI.c | 1 + shared-bindings/ipaddress/IPv4Address.c | 1 + shared-bindings/ipaddress/IPv4Address.h | 9 +- shared-bindings/ipaddress/__init__.c | 41 - shared-bindings/ipaddress/__init__.h | 6 - shared-bindings/socketpool/Socket.c | 7 +- shared-bindings/socketpool/Socket.h | 13 +- shared-bindings/socketpool/__init__.c | 3 +- shared-bindings/ssl/SSLSocket.c | 8 +- shared-bindings/ssl/__init__.c | 3 +- shared-bindings/util.c | 15 + shared-bindings/util.h | 1 + shared-bindings/wifi/AuthMode.h | 7 + shared-bindings/wifi/Network.c | 10 +- shared-bindings/wifi/Radio.c | 188 +- shared-bindings/wifi/Radio.h | 8 + shared-bindings/wifi/__init__.c | 7 +- shared-bindings/wifi/__init__.h | 1 + shared-module/ipaddress/IPv4Address.c | 76 + shared-module/ipaddress/__init__.c | 9 - supervisor/port_heap.h | 3 + supervisor/shared/port.c | 16 + 58 files changed, 2413 insertions(+), 2714 deletions(-) diff --git a/devices/airlift/common-hal/socketpool/Socket.c b/devices/airlift/common-hal/socketpool/Socket.c index 1765d8c0e0c4b..0389894e0787f 100644 --- a/devices/airlift/common-hal/socketpool/Socket.c +++ b/devices/airlift/common-hal/socketpool/Socket.c @@ -8,703 +8,562 @@ #include "shared/runtime/interrupt_char.h" #include "py/mperrno.h" +///TEMP +#include "py/mphal.h" #include "py/runtime.h" +#include "shared-bindings/ipaddress/IPv4Address.h" #include "shared-bindings/socketpool/SocketPool.h" +#include "shared-bindings/wifi/Radio.h" #include "common-hal/socketpool/__init__.h" #include "common-hal/wifi/__init__.h" -#if CIRCUITPY_SSL_NATIVE -#include "shared-bindings/ssl/SSLSocket.h" -#include "shared-module/ssl/SSLSocket.h" -#endif +// #if CIRCUITPY_SSL +// #include "shared-bindings/ssl/SSLSocket.h" +// #include "shared-module/ssl/SSLSocket.h" +// #endif #include "supervisor/port.h" #include "supervisor/shared/tick.h" #include "supervisor/workflow.h" -// void socketpool_resolve_host_or_throw(int family, int type, const char *hostname, struct sockaddr_storage *addr, int port) { -// // struct addrinfo *result_i; -// // const struct addrinfo hints = { -// // .ai_family = family, -// // .ai_socktype = type, -// // }; -// // int error = socketpool_getaddrinfo_common(hostname, port, &hints, &result_i); -// // if (error != 0 || result_i == NULL) { -// // common_hal_socketpool_socketpool_raise_gaierror_noname(); -// // } -// // memcpy(addr, result_i->ai_addr, sizeof(struct sockaddr_storage)); -// // lwip_freeaddrinfo(result_i); -// } - -// static void resolve_host_or_throw(socketpool_socket_obj_t *self, const char *hostname, struct sockaddr_storage *addr, int port) { -// // socketpool_resolve_host_or_throw(self->family, self->type, hostname, addr, port); -// } - -// StackType_t socket_select_stack[2 * configMINIMAL_STACK_SIZE]; - -/* Socket state table: - * 0 := Closed (unused) - * 1 := Open - * 2 := Closing (remove from rfds) - * Index into socket_fd_state is calculated from actual lwip fd. idx := fd - LWIP_SOCKET_OFFSET -*/ -#define FDSTATE_CLOSED 0 -#define FDSTATE_OPEN 1 -#define FDSTATE_CLOSING 2 -// static uint8_t socket_fd_state[CONFIG_LWIP_MAX_SOCKETS]; - -// How long to wait between checks for a socket to connect. -#define SOCKET_CONNECT_POLL_INTERVAL_MS 100 - -// static socketpool_socket_obj_t *user_socket[CONFIG_LWIP_MAX_SOCKETS]; -// StaticTask_t socket_select_task_buffer; -// TaskHandle_t socket_select_task_handle; -// static int socket_change_fd = -1; - -// static void socket_select_task(void *arg) { -// uint64_t signal; -// fd_set readfds; -// fd_set excptfds; - -// while (true) { -// FD_ZERO(&readfds); -// FD_ZERO(&excptfds); -// FD_SET(socket_change_fd, &readfds); -// int max_fd = socket_change_fd; -// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { -// if ((socket_fd_state[i] == FDSTATE_OPEN) && (user_socket[i] == NULL)) { -// int sockfd = i + LWIP_SOCKET_OFFSET; -// max_fd = MAX(max_fd, sockfd); -// FD_SET(sockfd, &readfds); -// FD_SET(sockfd, &excptfds); -// } -// } - -// int num_triggered = select(max_fd + 1, &readfds, NULL, &excptfds, NULL); -// // Hard error (or someone closed a socket on another thread) -// if (num_triggered == -1) { -// assert(errno == EBADF); -// continue; -// } - -// assert(num_triggered > 0); - -// // Notice event trigger -// if (FD_ISSET(socket_change_fd, &readfds)) { -// read(socket_change_fd, &signal, sizeof(signal)); -// num_triggered--; -// } - -// // Handle active FDs, close the dead ones -// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { -// int sockfd = i + LWIP_SOCKET_OFFSET; -// if (socket_fd_state[i] != FDSTATE_CLOSED) { -// if (FD_ISSET(sockfd, &readfds) || FD_ISSET(sockfd, &excptfds)) { -// if (socket_fd_state[i] == FDSTATE_CLOSING) { -// socket_fd_state[i] = FDSTATE_CLOSED; -// num_triggered--; -// } -// } -// } -// } - -// if (num_triggered > 0) { -// // Wake up CircuitPython by queuing request -// supervisor_workflow_request_background(); -// ulTaskNotifyTake(pdTRUE, portMAX_DELAY); -// } -// } - -// close(socket_change_fd); -// socket_change_fd = -1; -// vTaskDelete(NULL); -// } - void socket_user_reset(void) { - // if (socket_change_fd < 0) { - // esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT(); - // ESP_ERROR_CHECK(esp_vfs_eventfd_register(&config)); - // - // // Clear initial socket states - // for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { - // socket_fd_state[i] = FDSTATE_CLOSED; - // user_socket[i] = NULL; - // } - // socket_change_fd = eventfd(0, 0); - // // Run this at the same priority as CP so that the web workflow background task can be - // // queued while CP is running. Both tasks can still sleep and, therefore, sleep overall. - // socket_select_task_handle = xTaskCreateStaticPinnedToCore(socket_select_task, - // "socket_select", - // 2 * configMINIMAL_STACK_SIZE, - // NULL, - // uxTaskPriorityGet(NULL), - // socket_select_stack, - // &socket_select_task_buffer, - // xPortGetCoreID()); - // } else { - // // Not init - close open user sockets - // for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { - // if ((socket_fd_state[i] == FDSTATE_OPEN) && user_socket[i]) { - // common_hal_socketpool_socket_close(user_socket[i]); - // } - // } - // } +} + +NORETURN static void raise_failed(void) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); } // Unblock select task (ok if not blocked yet) void socketpool_socket_poll_resume(void) { - // if (socket_select_task_handle) { - // xTaskNotifyGive(socket_select_task_handle); - // } } -// The writes below send an event to the socket select task so that it redoes the -// select with the new open socket set. - -// static bool register_open_socket(int fd) { -// // if (fd < FD_SETSIZE) { -// // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_OPEN; -// // user_socket[fd - LWIP_SOCKET_OFFSET] = NULL; -// // -// // uint64_t signal = 1; -// // write(socket_change_fd, &signal, sizeof(signal)); -// // socketpool_socket_poll_resume(); -// // return true; -// // } -// // return false; -// return 0; -// } +// The SOCKETPOOL_ constants are actually the same as the LWIP constants, but it's +// possible they might not be, so map them, just in case. +static int socketpool_type_to_airlift_type(socketpool_socketpool_sock_t type) { + switch (type) { + case SOCKETPOOL_SOCK_STREAM: + return SOCK_STREAM; + case SOCKETPOOL_SOCK_DGRAM: + return SOCK_DGRAM; + case SOCKETPOOL_SOCK_RAW: + return SOCK_RAW; + default: + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_type); + } +} -// static void mark_user_socket(int fd, socketpool_socket_obj_t *obj) { -// // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_OPEN; -// // user_socket[fd - LWIP_SOCKET_OFFSET] = obj; -// // // No need to wakeup select task +// // The SOCKETPOOL_ constants are actually the same as the LWIP constants, but it's +// // possible they might not be, so map them, just in case. +// static socketpool_socketpool_sock_t airlift_type_to_socketpool_type(int type) { +// switch (type) { +// case SOCK_STREAM: +// return SOCKETPOOL_SOCK_STREAM; +// case SOCK_DGRAM: +// return SOCKETPOOL_SOCK_DGRAM; +// case SOCK_RAW: +// return SOCKETPOOL_SOCK_RAW; +// default: +// mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_type); +// } // } -// static bool _socketpool_socket(socketpool_socketpool_obj_t *self, -// socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, -// int proto, -// socketpool_socket_obj_t *sock) { -// int addr_family; -// int ipproto; - -// if (family == SOCKETPOOL_AF_INET) { -// addr_family = AF_INET; -// ipproto = IPPROTO_IP; -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// } else { // INET6 -// addr_family = AF_INET6; -// ipproto = IPPROTO_IPV6; -// #endif -// } -// int socket_type; -// if (type == SOCKETPOOL_SOCK_STREAM) { -// socket_type = SOCK_STREAM; -// } else if (type == SOCKETPOOL_SOCK_DGRAM) { -// socket_type = SOCK_DGRAM; -// } else { // SOCKETPOOL_SOCK_RAW -// socket_type = SOCK_RAW; -// ipproto = proto; -// } -// sock->type = socket_type; -// sock->family = addr_family; -// sock->ipproto = ipproto; -// sock->pool = self; -// sock->timeout_ms = (uint)-1; - -// // Create LWIP socket -// int socknum = -1; -// socknum = lwip_socket(sock->family, sock->type, sock->ipproto); -// if (socknum < 0) { -// return false; -// } +static bool _socketpool_socket(socketpool_socketpool_obj_t *self, + socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, + int proto, + socketpool_socket_obj_t *sock) { -// sock->num = socknum; -// // Sockets should be nonblocking in most cases -// lwip_fcntl(socknum, F_SETFL, O_NONBLOCK); + sock->proto = IPPROTO_IP; -// return true; -// } + // The SOCKETPOOL_ constants are actually the same as the LWIP constants, but it's + // possible they might not be, so map them, just in case. + switch (family) { + case SOCKETPOOL_AF_INET: + sock->family = AF_INET; + break; + default: + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_family); + } + + sock->type = socketpool_type_to_airlift_type(type); + + sock->socketpool = self; + sock->timeout_ms = SOCKET_BLOCK_FOREVER; + + uint8_t socket_num; + uint8_t *responses[1] = { &socket_num }; + size_t response_lengths[1] = { 1 }; + + size_t num_responses = wifi_radio_send_command_get_response(self->radio, GET_SOCKET_CMD, + NULL, NULL, LENGTHS_8, 0, + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0) { + raise_failed(); + } + + sock->num = socket_num; + sock->bound = false; + sock->connected = false; + sock->client_started = false; + sock->server_started = false; + + return sock->num != NO_SOCKET; + +} // special entry for workflow listener (register system socket) bool socketpool_socket(socketpool_socketpool_obj_t *self, socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, int proto, socketpool_socket_obj_t *sock) { - // - // if (!_socketpool_socket(self, family, type, proto, sock)) { - // return false; - // } - // - // // This shouldn't happen since we have room for the same number of sockets as LWIP. - // if (!register_open_socket(sock->num)) { - // lwip_close(sock->num); - // return false; - // } - // return true; - return 0; + + return _socketpool_socket(self, family, type, proto, sock); } socketpool_socket_obj_t *common_hal_socketpool_socket(socketpool_socketpool_obj_t *self, socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, int proto) { - // switch (family) { - // #if CIRCUITPY_SOCKETPOOL_IPV6 - // case SOCKETPOOL_AF_INET6: - // #endif - // case SOCKETPOOL_AF_INET: - // break; - // default: - // mp_raise_NotImplementedError(MP_ERROR_TEXT("Unsupported socket type")); - // } - // - // socketpool_socket_obj_t *sock = mp_obj_malloc_with_finaliser(socketpool_socket_obj_t, &socketpool_socket_type); - // - // if (!_socketpool_socket(self, family, type, proto, sock)) { - // mp_raise_RuntimeError(MP_ERROR_TEXT("Out of sockets")); - // } - // mark_user_socket(sock->num, sock); - // return sock; - return NULL; + socketpool_socket_obj_t *sock = mp_obj_malloc_with_finaliser(socketpool_socket_obj_t, &socketpool_socket_type); + + if (!_socketpool_socket(self, family, type, proto, sock)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Out of sockets")); + } + return sock; +} + +// Polling values, specific to NINA-FW. +#define SOCKET_POLL_RD (0x01) +#define SOCKET_POLL_WR (0x02) +#define SOCKET_POLL_ERR (0x04) +#define SOCKET_POLL_FAIL (0x80) + +static uint8_t socketpool_socket_poll(socketpool_socket_obj_t *self) { + // TODO: not gonna work on the mapped socket number. + mp_raise_NotImplementedError(NULL); +} + +// Return, via parameters, the IP address and port of the connection. +// Return false if not connected. +static bool socketpool_socket_get_connection_info(socketpool_socket_obj_t *self, uint8_t ipv4[IPV4_LENGTH], uint16_t *port) { + const uint8_t *params[1] = { &self->num }; + size_t param_lengths[1] = { 1 }; + + *port = 0; + memset(ipv4, 0, IPV4_LENGTH); + uint8_t *responses[3] = { ipv4, (uint8_t *)&port }; + size_t response_lengths[3] = { IPV4_LENGTH, sizeof(port) }; + + size_t num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, GET_REMOTE_DATA_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0) { + raise_failed(); + } + + return port != 0; +} + + + +static airlift_socket_status_t socketpool_socket_status(socketpool_socket_obj_t *self) { + const uint8_t *params[1] = { &self->num }; + size_t param_lengths[1] = { 1 }; + + uint8_t result; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { 1 }; + + size_t num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, GET_CLIENT_STATE_TCP_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0) { + raise_failed(); + } + + return result; } int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, socketpool_socket_obj_t *accepted) { - // struct sockaddr_storage peer_addr; - // socklen_t socklen = sizeof(peer_addr); - // int newsoc = -1; - // bool timed_out = false; - // uint64_t start_ticks = supervisor_ticks_ms64(); - // - // // Allow timeouts and interrupts - // while (newsoc == -1 && !timed_out) { - // if (self->timeout_ms != (uint)-1 && self->timeout_ms != 0) { - // timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; - // } - // RUN_BACKGROUND_TASKS; - // newsoc = lwip_accept(self->num, (struct sockaddr *)&peer_addr, &socklen); - // // In non-blocking mode, fail instead of timing out - // if (newsoc == -1 && (self->timeout_ms == 0 || mp_hal_is_interrupted())) { - // return -MP_EAGAIN; - // } - // } - // - // if (timed_out) { - // return -ETIMEDOUT; - // } - // - // if (newsoc < 0) { - // return -MP_EBADF; - // } - // - // // We got a socket. New client socket will not be non-blocking by default, so make it non-blocking. - // lwip_fcntl(newsoc, F_SETFL, O_NONBLOCK); - // - // if (accepted != NULL) { - // // Error if called with open socket object. - // assert(common_hal_socketpool_socket_get_closed(accepted)); - // - // // Register if system socket - // if (!register_open_socket(newsoc)) { - // lwip_close(newsoc); - // return -MP_EBADF; - // } - // - // // Replace the old accepted socket with the new one. - // accepted->num = newsoc; - // accepted->pool = self->pool; - // accepted->connected = true; - // accepted->type = self->type; - // } - // - // if (peer_out) { - // *peer_out = sockaddr_to_tuple(&peer_addr); - // } - // - // return newsoc; - return 0; + if (self->type != SOCK_STREAM) { + return -MP_EOPNOTSUPP; + } + + if (common_hal_socketpool_socket_get_closed(self)) { + return -MP_EBADF; + } + + const uint8_t *params[1] = { &self->num }; + size_t param_lengths[1] = { 1 }; + + uint8_t accept_socket_num; + uint8_t *responses[1] = { &accept_socket_num }; + size_t response_lengths[1] = { 1 }; + + size_t num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, AVAIL_DATA_TCP_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0) { + raise_failed(); + } + if (accept_socket_num == NO_SOCKET) { + return -EBADF; + } + + return accept_socket_num; } socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out) { - // // Set the socket type only after the socketpool_socket_accept succeeds, so that the - // // finaliser is not called on a bad socket. - // socketpool_socket_obj_t *sock = mp_obj_malloc_with_finaliser(socketpool_socket_obj_t, NULL); - // int newsoc = socketpool_socket_accept(self, peer_out, NULL); - // - // if (newsoc > 0) { - // // Create the socket - // mark_user_socket(newsoc, sock); - // sock->base.type = &socketpool_socket_type; - // sock->num = newsoc; - // sock->pool = self->pool; - // sock->connected = true; - // sock->type = self->type; - // - // return sock; - // } else { - // mp_raise_OSError(-newsoc); - // return NULL; - // } - return NULL; + socketpool_socket_obj_t *accepted = mp_obj_malloc_with_finaliser(socketpool_socket_obj_t, NULL); + socketpool_socket_reset(accepted); + + int ret = socketpool_socket_accept(self, peer_out, accepted); + if (ret < 0) { + mp_raise_OSError(-ret); + } + + return accepted; } -size_t common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, +int common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port) { - // struct sockaddr_storage bind_addr; - // const char *broadcast = ""; - // - // bind_addr.ss_family = self->family; - // - // #if CIRCUITPY_SOCKETPOOL_IPV6 - // if (self->family == AF_INET6) { - // struct sockaddr_in6 *addr6 = (void *)&bind_addr; - // addr6->sin6_port = htons(port); - // // no ipv6 broadcast - // if (hostlen == 0) { - // memset(&addr6->sin6_addr, 0, sizeof(addr6->sin6_addr)); - // } else { - // socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, port); - // } - // } else - // #endif - // { - // struct sockaddr_in *addr4 = (void *)&bind_addr; - // addr4->sin_port = htons(port); - // if (hostlen == 0) { - // addr4->sin_addr.s_addr = IPADDR_ANY; - // } else if (hostlen == strlen(broadcast) && - // memcmp(host, broadcast, strlen(broadcast)) == 0) { - // addr4->sin_addr.s_addr = IPADDR_BROADCAST; - // } else { - // socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, port); - // } - // } - // - // int result = lwip_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); - // if (result == 0) { - // return 0; - // } - // return errno; + if (self->server_started) { + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_socket); + } + if (self->bound) { + // Same as CPython. + mp_raise_OSError(MP_EINVAL); + } + + // Validate the host name (which might a numeric IP string) to an IPv4 address first. + uint8_t ipv4[IPV4_LENGTH]; + if (!socketpool_gethostbyname_ipv4(self->socketpool, host, ipv4)) { + // Could not resolve hostname. + common_hal_socketpool_socketpool_raise_gaierror_noname(); + } + + self->bound = true; + memcpy(self->hostname, host, hostlen); + self->hostname_len = hostlen; return 0; } void socketpool_socket_close(socketpool_socket_obj_t *self) { - // #if CIRCUITPY_SSL - // if (self->ssl_socket) { - // ssl_sslsocket_obj_t *ssl_socket = self->ssl_socket; - // self->ssl_socket = NULL; - // common_hal_ssl_sslsocket_close(ssl_socket); - // return; - // } - // #endif - // self->connected = false; - // int fd = self->num; - // // Ignore bogus/closed sockets - // if (fd >= LWIP_SOCKET_OFFSET) { - // if (user_socket[fd - LWIP_SOCKET_OFFSET] == NULL) { - // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_CLOSING; - // lwip_shutdown(fd, SHUT_RDWR); - // lwip_close(fd); + if (self->client_started) { + socketpool_socket_stop_client(self); + } + + if (self->server_started) { + // TODO: how to shut down server? + } +} + +void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self) { + socketpool_socket_close(self); +} + +void socketpool_socket_start_client_mode(socketpool_socket_obj_t *self, + const char *host, size_t hostlen, uint32_t port, airlift_conn_mode_t mode) { + + mp_arg_validate_length_max(hostlen, MAX_HOSTNAME_LENGTH, MP_QSTR_hostname); + + // if (self->client_started) { + // // Is this client already started on the given host and port? If so, nothing need be done. + // if (self->hostname_len == hostlen && + // strncasecmp((char *)self->hostname, host, hostlen) == 0 && + // self->port == port) { + // return; // } else { - // lwip_shutdown(fd, SHUT_RDWR); - // lwip_close(fd); - // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_CLOSED; - // user_socket[fd - LWIP_SOCKET_OFFSET] = NULL; + // // Otherwise, stop the current client, so we can restart it with a different address. + // socketpool_socket_stop_client(self); // } // } - // self->num = -1; + + uint8_t unused_ipv4[IPV4_LENGTH] = { 0 }; + uint8_t port_bytes[2]; + be_uint16_to_uint8_bytes((uint16_t)port, port_bytes); + uint8_t conn_mode = mode; + + const uint8_t *params[5] = { (uint8_t *)host, unused_ipv4, port_bytes, &self->num, &conn_mode }; + size_t param_lengths[5] = { hostlen, 4, 2, 1, 1 }; + + uint8_t result; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { 1 }; + + size_t num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, START_CLIENT_TCP_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), self->timeout_ms); + + if (num_responses == 0 || result != 1) { + mp_raise_ConnectionError(MP_ERROR_TEXT("Failed")); + } + + // Update any previous IP and port with the requested ones. + // The memcpy won't overflow because hostlen has been validated above. + memcpy(self->hostname, host, hostlen); + self->hostname_len = hostlen; + self->port = port; + self->client_started = true; } -void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self) { - // socketpool_socket_close(self); +void socketpool_socket_stop_client(socketpool_socket_obj_t *self) { + const uint8_t *params[1] = { &self->num }; + + if (!self->client_started) { + return; + } + + size_t param_lengths[1] = { 1 }; + + uint8_t result; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { 1 }; + + size_t num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, STOP_CLIENT_TCP_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + // Don't raise an error if VM isn't running, because socket close is the socket finaliser. + if ((num_responses == 0 || result != 1) && vm_is_running()) { + raise_failed(); + } + + self->client_started = false; +} + +void socketpool_socket_start_server_mode(socketpool_socket_obj_t *self, airlift_conn_mode_t mode) { + // Once bind() is called, the ipv4 and port won't change, so a restart will not change anything. + if (self->server_started) { + return; + } + + uint8_t port_bytes[2]; + be_uint16_to_uint8_bytes((uint16_t)self->port, port_bytes); + uint8_t conn_mode = mode; + + const uint8_t *params[5] = { self->hostname, port_bytes, &self->num, &conn_mode }; + size_t param_lengths[5] = { self->hostname_len, 4, 2, 1, 1 }; + + uint8_t result; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { 1 }; + + size_t num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, START_SERVER_TCP_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), self->timeout_ms); + + if (num_responses == 0 || result != 1) { + mp_raise_ConnectionError(MP_ERROR_TEXT("Failed")); + } + + self->server_started = true; } void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port) { - // struct sockaddr_storage addr; - // resolve_host_or_throw(self, host, &addr, port); - // - // // Replace above with function call ----- - // - // // Emulate SO_CONTIMEO, which is not implemented by lwip. - // // All our sockets are non-blocking, so we check the timeout ourselves. - // - // int result = -1; - // result = lwip_connect(self->num, (struct sockaddr *)&addr, addr.s2_len); - // - // if (result == 0) { - // // Connected immediately. - // self->connected = true; - // return; - // } - // - // if (result < 0 && errno != EINPROGRESS) { - // // Some error happened; error is in errno. - // mp_raise_OSError(errno); - // return; - // } - // - // struct timeval timeout = { - // .tv_sec = 0, - // .tv_usec = SOCKET_CONNECT_POLL_INTERVAL_MS * 1000, - // }; - // - // // Keep checking, using select(), until timeout expires, at short intervals. - // // This allows ctrl-C interrupts to be detected and background tasks to run. - // mp_uint_t timeout_left = self->timeout_ms; - // - // while (timeout_left > 0) { - // RUN_BACKGROUND_TASKS; - // // Allow ctrl-C interrupt - // if (mp_hal_is_interrupted()) { - // return; - // } - // - // fd_set fds; - // FD_ZERO(&fds); - // FD_SET(self->num, &fds); - // - // result = select(self->num + 1, NULL, &fds, NULL, &timeout); - // if (result == 0) { - // // No change to fd's after waiting for timeout, so try again if some time is still left. - // // Don't wrap below 0, because we're using a uint. - // if (timeout_left < SOCKET_CONNECT_POLL_INTERVAL_MS) { - // timeout_left = 0; - // } else { - // timeout_left -= SOCKET_CONNECT_POLL_INTERVAL_MS; - // } - // continue; - // } - // - // if (result < 0) { - // // Some error happened when doing select(); error is in errno. - // mp_raise_OSError(errno); - // } - // - // // select() indicated the socket is writable. Check if any connection error occurred. - // int error_code = 0; - // socklen_t socklen = sizeof(error_code); - // result = getsockopt(self->num, SOL_SOCKET, SO_ERROR, &error_code, &socklen); - // if (result < 0 || error_code != 0) { - // mp_raise_OSError(error_code); - // } - // self->connected = true; - // return; - // } - // - // // No connection after timeout. The connection attempt is not stopped. - // // This imitates what happens in Python. - // mp_raise_OSError(ETIMEDOUT); + socketpool_socket_start_client_mode(self, host, hostlen, port, + self->type == SOCK_STREAM ? AIRLIFT_TCP_MODE : AIRLIFT_UDP_MODE); } bool common_hal_socketpool_socket_get_closed(socketpool_socket_obj_t *self) { - // return self->num < 0; - return 0; + return socketpool_socket_status(self) == SOCKET_CLOSED; } bool common_hal_socketpool_socket_get_connected(socketpool_socket_obj_t *self) { - // return self->connected; - return 0; + return socketpool_socket_status(self) == SOCKET_ESTABLISHED; } bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *self, int backlog) { - // return lwip_listen(self->num, backlog) == 0; - return 0; + if (self->type != SOCK_STREAM) { + // Same as CPython. + mp_raise_OSError(MP_EOPNOTSUPP); + } + if (!self->bound) { + // Other impls will assign an address and port, but AirLift can't do that. + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q() without %q()"), MP_QSTR_listen, MP_QSTR_bind); + } + socketpool_socket_start_server_mode(self, AIRLIFT_TCP_MODE); + return true; } mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len, mp_obj_t *source_out) { - // - // struct sockaddr_storage 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) { - // mp_raise_OSError(ETIMEDOUT); - // } - // - // if (received < 0) { - // mp_raise_BrokenPipeError(); - // return 0; - // } - // - // if (source_out) { - // *source_out = sockaddr_to_tuple(&source_addr); - // } - // - // return received; - return 0; + uint8_t ipv4[IPV4_LENGTH]; + uint16_t port; + + if (self->type != SOCK_DGRAM) { + mp_raise_ValueError(MP_ERROR_TEXT("Unsupported socket type")); + } + + const mp_uint_t bytes_received = common_hal_socketpool_socket_recv_into(self, buf, len); + + if (!socketpool_socket_get_connection_info(self, ipv4, &port)) { + raise_failed(); + } + + // Pass back a tuple of stringified IP address and port. + mp_obj_t items[2] = { + ipv4_bytes_to_str(ipv4), + MP_OBJ_NEW_SMALL_INT(port), + }; + *source_out = mp_obj_new_tuple(MP_ARRAY_SIZE(items), items); + + return bytes_received; } -int socketpool_socket_recv_into(socketpool_socket_obj_t *self, - const uint8_t *buf, uint32_t len) { - // int received = 0; - // bool timed_out = false; - // - // if (self->num != -1) { - // // LWIP Socket - // uint64_t start_ticks = supervisor_ticks_ms64(); - // received = -1; - // while (received == -1 && - // !timed_out) { - // 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_recv(self->num, (void *)buf, len, 0); - // // In non-blocking mode, fail instead of looping - // if (received < 1 && self->timeout_ms == 0) { - // if ((received == 0) || (errno == ENOTCONN)) { - // self->connected = false; - // return -MP_ENOTCONN; - // } - // return -MP_EAGAIN; - // } - // // Check this after going through the loop once so it can make - // // progress while interrupted. - // if (mp_hal_is_interrupted()) { - // if (received == -1) { - // return -MP_EAGAIN; - // } - // break; - // } - // } - // } else { - // return -MP_EBADF; - // } - // - // if (timed_out) { - // return -ETIMEDOUT; - // } - // return received; - return 0; +int socketpool_socket_recv_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len) { + const uint8_t *params[2] = { &self->num, (uint8_t *)&len }; + size_t param_lengths[2] = { 1, 2 }; + + const uint64_t start_time = supervisor_ticks_ms64(); + + uint8_t *responses[1] = { buf }; + while (true) { + // This will be the max length read. We need to re-initialize each time we read, because its + // value is changed by wifi_radio_send_command_get_response(). + size_t response_lengths[1] = { len }; + + size_t num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, GET_DATABUF_TCP_CMD, + params, param_lengths, LENGTHS_16, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_16, MP_ARRAY_SIZE(responses), self->timeout_ms); + + if (num_responses == 0) { + return -1; + } + + const size_t num_read = response_lengths[0]; + if (num_read > 0) { + return num_read; + } + + // Give up if timeout was exceeded, or ctrl-C was typed. + if ((self->timeout_ms != SOCKET_BLOCK_FOREVER && + supervisor_ticks_ms64() - start_time >= self->timeout_ms) || + mp_hal_is_interrupted()) { + return 0; + } + RUN_BACKGROUND_TASKS; + mp_hal_delay_ms(500); + } } -mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) { - // int received = socketpool_socket_recv_into(self, buf, len); - // if (received < 0) { - // mp_raise_OSError(-received); - // } - // return received; - return 0; +mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len) { + int received = socketpool_socket_recv_into(self, buf, len); + if (received < 0) { + raise_failed(); + } + return received; } int 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 { - // sent = -MP_EBADF; - // } - // - // if (sent < 0) { - // if (errno == ECONNRESET || errno == ENOTCONN) { - // self->connected = false; - // } - // return -errno; - // } - // - // return sent; - return 0; + const uint8_t *params[2] = { &self->num, buf }; + size_t param_lengths[2] = { 1, len }; + + size_t num_responses; + + // Will only be SOCK_STREAM or SOCK_DGRAM. + switch (self->type) { + case SOCK_STREAM: { + uint8_t bytes_sent[2]; + uint8_t *responses[1] = { bytes_sent }; + size_t response_lengths[1] = { sizeof(bytes_sent) }; + + num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, SEND_DATA_TCP_CMD, + params, param_lengths, LENGTHS_16, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), self->timeout_ms); + if (num_responses == 0) { + return -1; + } + return le_uint8_bytes_to_uint16(bytes_sent); + } + + case SOCK_DGRAM: { + uint8_t result; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { 1 }; + + num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, INSERT_DATABUF_TCP_CMD, + params, param_lengths, LENGTHS_16, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), self->timeout_ms); + if (num_responses == 0 || result != 1) { + return -1; + } + + const uint8_t *send_udp_data_params[1] = { &self->num }; + size_t send_udp_data_param_lengths[1] = { 1 }; + num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, SEND_UDP_DATA_CMD, + send_udp_data_params, send_udp_data_param_lengths, LENGTHS_8, MP_ARRAY_SIZE(send_udp_data_params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), self->timeout_ms); + if (num_responses == 0 || result != 1) { + return -1; + } + + // Using DATA_SENT_TCP_CMD is not necessary. It's a no-op on the AirLift side. + return len; + } + + default: + // This shouldn't happen. + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), self->type); + } } mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) { - // int sent = socketpool_socket_send(self, buf, len); - // - // if (sent < 0) { - // mp_raise_OSError(-sent); - // } - // return sent; - return 0; + int sent = socketpool_socket_send(self, buf, len); + + if (sent < 0) { + raise_failed(); + } + return sent; } 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) { - // - // struct sockaddr_storage addr; - // resolve_host_or_throw(self, host, &addr, port); - // - // int bytes_sent = lwip_sendto(self->num, buf, len, 0, (struct sockaddr *)&addr, addr.s2_len); - // if (bytes_sent < 0) { - // mp_raise_BrokenPipeError(); - // return 0; - // } - // return bytes_sent; - return 0; + if (self->type != SOCK_DGRAM) { + mp_raise_ValueError(MP_ERROR_TEXT("Unsupported socket type")); + } + socketpool_socket_start_client_mode(self, host, hostlen, port, AIRLIFT_UDP_MODE); + return common_hal_socketpool_socket_send(self, buf, len); } void common_hal_socketpool_socket_settimeout(socketpool_socket_obj_t *self, uint32_t timeout_ms) { - // self->timeout_ms = timeout_ms; + // 0 means non-blocking. (uint32_t) -1 means block forever. + self->timeout_ms = timeout_ms; } mp_int_t common_hal_socketpool_socket_get_type(socketpool_socket_obj_t *self) { - // return self->type; - return 0; + return self->type; } int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int level, int optname, const void *value, size_t optlen) { - // int err = lwip_setsockopt(self->num, level, optname, value, optlen); - // if (err != 0) { - // return -errno; - // } - // return 0; - return 0; + mp_raise_NotImplementedError(NULL); // TODO } -bool common_hal_socketpool_readable(socketpool_socket_obj_t *self) { - // struct timeval immediate = {0, 0}; - // - // fd_set fds; - // FD_ZERO(&fds); - // FD_SET(self->num, &fds); - // int num_triggered = select(self->num + 1, &fds, NULL, &fds, &immediate); - // - // // including returning true in the error case - // return num_triggered != 0; - return 0; +bool common_hal_socketpool_socket_readable(socketpool_socket_obj_t *self) { + return socketpool_socket_poll(self) & SOCKET_POLL_RD; } -bool common_hal_socketpool_writable(socketpool_socket_obj_t *self) { - // struct timeval immediate = {0, 0}; - // - // fd_set fds; - // FD_ZERO(&fds); - // FD_SET(self->num, &fds); - // int num_triggered = select(self->num + 1, NULL, &fds, &fds, &immediate); - // - // // including returning true in the error case - // return num_triggered != 0; - return 0; +bool common_hal_socketpool_socket_writable(socketpool_socket_obj_t *self) { + return socketpool_socket_poll(self) & SOCKET_POLL_WR; } void socketpool_socket_move(socketpool_socket_obj_t *self, socketpool_socket_obj_t *sock) { - // *sock = *self; - // self->connected = false; - // self->num = -1; + *sock = *self; + self->connected = false; + self->num = NO_SOCKET; } void socketpool_socket_reset(socketpool_socket_obj_t *self) { - // if (self->base.type == &socketpool_socket_type) { - // return; - // } - // self->base.type = &socketpool_socket_type; - // self->connected = false; - // self->num = -1; + self->num = NO_SOCKET; + self->bound = false; + self->connected = false; + self->client_started = false; + self->server_started = false; + self->connected = false; } diff --git a/devices/airlift/common-hal/socketpool/Socket.h b/devices/airlift/common-hal/socketpool/Socket.h index 92cb486b23a83..af00ebb859b3d 100644 --- a/devices/airlift/common-hal/socketpool/Socket.h +++ b/devices/airlift/common-hal/socketpool/Socket.h @@ -9,21 +9,64 @@ #include "py/obj.h" #include "common-hal/socketpool/SocketPool.h" +#include "shared-bindings/wifi/Radio.h" + +typedef enum { + SOCKET_CLOSED = 0, + SOCKET_LISTEN = 1, + SOCKET_SYN_SENT = 2, + SOCKET_SYN_RCVD = 3, + SOCKET_ESTABLISHED = 4, + SOCKET_FIN_WAIT_1 = 5, + SOCKET_FIN_WAIT_2 = 6, + SOCKET_CLOSE_WAIT = 7, + SOCKET_CLOSING = 8, + SOCKET_LAST_ACK = 9, + SOCKET_TIME_WAIT = 10, +} airlift_socket_status_t; typedef struct ssl_sslsocket_obj ssl_sslsocket_obj_t; typedef struct { mp_obj_base_t base; - int num; - int type; - int family; - int ipproto; - bool connected; - socketpool_socketpool_obj_t *pool; + socketpool_socketpool_obj_t *socketpool; ssl_sslsocket_obj_t *ssl_socket; - mp_uint_t timeout_ms; + mp_uint_t timeout_ms; // SOCKET_BLOCK_FOREVER is (mp_uint_t)-1. + size_t hostname_len; + uint8_t hostname[MAX_HOSTNAME_LENGTH + 1]; + mp_uint_t port; + uint8_t num; + uint8_t type; + uint8_t family; + uint8_t proto; + bool connected; + bool bound; + bool client_started; + bool server_started; } socketpool_socket_obj_t; +#define AIRLIFT_SOCKET_DEFAULT_TIMEOUT (3000) + +#define IPPROTO_IP 0 +#define IPPROTO_ICMP 1 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 + +#define AF_UNSPEC 0 +#define AF_INET 2 + +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 + +// Used both internally and by AirLift. +#define NO_SOCKET 255 + void socket_user_reset(void); // Unblock workflow socket select thread (platform specific) void socketpool_socket_poll_resume(void); + +void socketpool_socket_start_client_mode(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port, airlift_conn_mode_t mode); +void socketpool_socket_stop_client(socketpool_socket_obj_t *self); + +void socketpool_socket_start_server_mode(socketpool_socket_obj_t *self, airlift_conn_mode_t mode); diff --git a/devices/airlift/common-hal/socketpool/SocketPool.c b/devices/airlift/common-hal/socketpool/SocketPool.c index 30f8fece5fc41..f737e4eea4cc2 100644 --- a/devices/airlift/common-hal/socketpool/SocketPool.c +++ b/devices/airlift/common-hal/socketpool/SocketPool.c @@ -8,117 +8,86 @@ #include "common-hal/socketpool/Socket.h" #include "py/runtime.h" +#include "shared-bindings/ipaddress/IPv4Address.h" #include "shared-bindings/wifi/__init__.h" #include "common-hal/socketpool/__init__.h" void common_hal_socketpool_socketpool_construct(socketpool_socketpool_obj_t *self, mp_obj_t radio) { - // if (radio != MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj)) { - // mp_raise_ValueError(MP_ERROR_TEXT("SocketPool can only be used with wifi.radio")); - // } + if (radio != MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj)) { + mp_raise_ValueError(MP_ERROR_TEXT("SocketPool can only be used with wifi.radio")); + } + // Not really needed, but more convenient. + self->radio = radio; } // common_hal_socketpool_socket is in socketpool/Socket.c to centralize open socket tracking. -// int socketpool_getaddrinfo_common(const char *host, int service, const struct addrinfo *hints, struct addrinfo **res) { -// // As of 2022, the version of lwip in esp-idf does not handle the -// // trailing-dot syntax of domain names, so emulate it. -// // Remove this once https://github.com/espressif/esp-idf/issues/10013 has -// // been implemented -// if (host) { -// size_t strlen_host = strlen(host); -// if (strlen_host && host[strlen_host - 1] == '.') { -// mp_obj_t nodot = mp_obj_new_str(host, strlen_host - 1); -// host = mp_obj_str_get_str(nodot); -// } -// } - -// char service_buf[6]; -// snprintf(service_buf, sizeof(service_buf), "%d", service); - -// return lwip_getaddrinfo(host, service_buf, hints, res); -// } - -// static mp_obj_t format_address(const struct sockaddr *addr, int family) { -// char ip_str[IPADDR_STRLEN_MAX]; // big enough for any supported address type -// const struct sockaddr_in *a = (void *)addr; - -// switch (family) { -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// case AF_INET6: -// inet_ntop(family, &((const struct sockaddr_in6 *)a)->sin6_addr, ip_str, sizeof(ip_str)); -// break; -// #endif -// default: -// case AF_INET: -// inet_ntop(family, &((const struct sockaddr_in *)a)->sin_addr, ip_str, sizeof(ip_str)); -// break; -// } -// return mp_obj_new_str(ip_str, strlen(ip_str)); -// return mp_const_none; -// } - -// static mp_obj_t convert_sockaddr(const struct addrinfo *ai, int port) { -// // #if CIRCUITPY_SOCKETPOOL_IPV6 -// // mp_int_t n_tuple = ai->ai_family == AF_INET6 ? 4 : 2; -// // #else -// // mp_int_t n_tuple = 2; -// // #endif -// // mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(n_tuple, NULL)); -// // result->items[0] = format_address(ai->ai_addr, ai->ai_family); -// // result->items[1] = MP_OBJ_NEW_SMALL_INT(port); -// // #if CIRCUITPY_SOCKETPOOL_IPV6 -// // if (ai->ai_family == AF_INET6) { -// // const struct sockaddr_in6 *ai6 = (void *)ai->ai_addr; -// // result->items[2] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_flowinfo); -// // result->items[3] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_scope_id); -// // } -// // #endif -// // return result; -// return mp_const_none; -// } - -// static mp_obj_t convert_addrinfo(const struct addrinfo *ai, int port) { -// // MP_STATIC_ASSERT(AF_INET == SOCKETPOOL_AF_INET); -// // #if CIRCUITPY_SOCKETPOOL_IPV6 -// // MP_STATIC_ASSERT(AF_INET6 == SOCKETPOOL_AF_INET6); -// // #endif -// // // MP_STATIC_ASSERT(AF_UNSPEC == SOCKETPOOL_AF_UNSPEC); -// // mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL)); -// // result->items[0] = MP_OBJ_NEW_SMALL_INT(ai->ai_family); -// // result->items[1] = MP_OBJ_NEW_SMALL_INT(ai->ai_socktype); -// // result->items[2] = MP_OBJ_NEW_SMALL_INT(ai->ai_protocol); -// // result->items[3] = ai->ai_canonname ? mp_obj_new_str(ai->ai_canonname, strlen(ai->ai_canonname)) : MP_OBJ_NEW_QSTR(MP_QSTR_); -// // result->items[4] = convert_sockaddr(ai, port); -// // return result; -// return mp_const_none; -// } +bool socketpool_gethostbyname_ipv4(socketpool_socketpool_obj_t *self, const char *host, uint8_t ipv4[4]) { + const uint8_t *req_host_params[1] = { (uint8_t *)host }; + size_t req_host_param_lengths[1] = { strlen(host) }; + + uint8_t result = 0; + uint8_t *req_host_responses[1] = { &result }; + size_t req_host_response_lengths[1] = { 1 }; + + // If host is a numeric IP address, AirLift will just parse and return the address. + + size_t num_responses = wifi_radio_send_command_get_response(self->radio, REQ_HOST_BY_NAME_CMD, + req_host_params, req_host_param_lengths, LENGTHS_8, MP_ARRAY_SIZE(req_host_params), + req_host_responses, req_host_response_lengths, LENGTHS_8, MP_ARRAY_SIZE(req_host_responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses >= 1) { + if (result == 0) { + return false; + } + + uint8_t *get_host_responses[1] = { ipv4 }; + size_t get_host_response_lengths[1] = { IPV4_LENGTH }; + + // Now actually get the name. + num_responses = wifi_radio_send_command_get_response(self->radio, GET_HOST_BY_NAME_CMD, + NULL, NULL, LENGTHS_8, 0, + get_host_responses, get_host_response_lengths, LENGTHS_8, MP_ARRAY_SIZE(get_host_responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + if (num_responses == 1) { + return true; + } + } + + return false; +} + +static mp_obj_t socketpool_socketpool_gethostbyname_str(socketpool_socketpool_obj_t *self, const char *host) { + uint8_t ipv4[4] = { 0 }; + + if (!socketpool_gethostbyname_ipv4(self, host, ipv4)) { + // Could not resolve or parse hostname. + return mp_const_none; + } + + vstr_t vstr; + mp_print_t print; + vstr_init_print(&vstr, 16, &print); + mp_printf(&print, "%d.%d.%d.%d", ipv4[0], ipv4[1], ipv4[2], ipv4[3]); + return mp_obj_new_str_from_vstr(&vstr); +} mp_obj_t common_hal_socketpool_getaddrinfo_raise(socketpool_socketpool_obj_t *self, const char *host, int port, int family, int type, int proto, int flags) { - // const struct addrinfo hints = { - // .ai_flags = flags, - // .ai_family = family, - // .ai_protocol = proto, - // .ai_socktype = type, - // }; - // - // struct addrinfo *res = NULL; - // int err = socketpool_getaddrinfo_common(host, port, &hints, &res); - // if (err != 0 || res == NULL) { - // common_hal_socketpool_socketpool_raise_gaierror_noname(); - // } - // - // nlr_buf_t nlr; - // if (nlr_push(&nlr) == 0) { - // mp_obj_t result = mp_obj_new_list(0, NULL); - // for (struct addrinfo *ai = res; ai; ai = ai->ai_next) { - // mp_obj_list_append(result, convert_addrinfo(ai, port)); - // } - // nlr_pop(); - // lwip_freeaddrinfo(res); - // return result; - // } else { - // lwip_freeaddrinfo(res); - // nlr_raise(MP_OBJ_FROM_PTR(nlr.ret_val)); - // } - return mp_const_none; + mp_obj_t ip_str = socketpool_socketpool_gethostbyname_str(self, host); + if (ip_str == mp_const_none) { + // Could not resolve hostname. + common_hal_socketpool_socketpool_raise_gaierror_noname(); + } + + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL)); + tuple->items[0] = MP_OBJ_NEW_SMALL_INT(SOCKETPOOL_AF_INET); + tuple->items[1] = MP_OBJ_NEW_SMALL_INT(SOCKETPOOL_SOCK_STREAM); + tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0); + tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_); + mp_obj_tuple_t *sockaddr = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + sockaddr->items[0] = ip_str; + sockaddr->items[1] = MP_OBJ_NEW_SMALL_INT(port); + tuple->items[4] = MP_OBJ_FROM_PTR(sockaddr); + return mp_obj_new_list(1, (mp_obj_t *)&tuple); } diff --git a/devices/airlift/common-hal/socketpool/SocketPool.h b/devices/airlift/common-hal/socketpool/SocketPool.h index 66113c41bda53..04d2fe0854a98 100644 --- a/devices/airlift/common-hal/socketpool/SocketPool.h +++ b/devices/airlift/common-hal/socketpool/SocketPool.h @@ -7,7 +7,11 @@ #pragma once #include "py/obj.h" +#include "shared-bindings/wifi/Radio.h" typedef struct { mp_obj_base_t base; + wifi_radio_obj_t *radio; } socketpool_socketpool_obj_t; + +bool socketpool_gethostbyname_ipv4(socketpool_socketpool_obj_t *self, const char *host, uint8_t ipv4[IPV4_LENGTH]); diff --git a/devices/airlift/common-hal/socketpool/__init__.c b/devices/airlift/common-hal/socketpool/__init__.c index 3d004370ddb25..ac260523b3160 100644 --- a/devices/airlift/common-hal/socketpool/__init__.c +++ b/devices/airlift/common-hal/socketpool/__init__.c @@ -9,5 +9,5 @@ #include "common-hal/socketpool/Socket.h" void socketpool_user_reset(void) { - // socket_user_reset(); + socket_user_reset(); } diff --git a/devices/airlift/common-hal/socketpool/__init__.h b/devices/airlift/common-hal/socketpool/__init__.h index 118a6a5e16d1b..bdbe682b41b59 100644 --- a/devices/airlift/common-hal/socketpool/__init__.h +++ b/devices/airlift/common-hal/socketpool/__init__.h @@ -5,8 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -struct addrinfo; - -// int socketpool_getaddrinfo_common(const char *host, int service, const struct addrinfo *hints, struct addrinfo **res); -// void socketpool_resolve_host_or_throw(int family, int type, const char *hostname, struct sockaddr_storage *addr, int port); diff --git a/devices/airlift/common-hal/ssl/SSLContext.c b/devices/airlift/common-hal/ssl/SSLContext.c index aef51fab1489b..17b5f63b2e464 100644 --- a/devices/airlift/common-hal/ssl/SSLContext.c +++ b/devices/airlift/common-hal/ssl/SSLContext.c @@ -11,34 +11,28 @@ #include "py/stream.h" void common_hal_ssl_sslcontext_construct(ssl_sslcontext_obj_t *self) { - // common_hal_ssl_sslcontext_set_default_verify_paths(self); + common_hal_ssl_sslcontext_set_default_verify_paths(self); } void common_hal_ssl_sslcontext_load_verify_locations(ssl_sslcontext_obj_t *self, const char *cadata) { - // self->crt_bundle_attach = NULL; - // self->use_global_ca_store = false; - // self->cacert_buf = (const unsigned char *)cadata; - // self->cacert_bytes = *cadata ? strlen(cadata) + 1 : 0; + mp_raise_NotImplementedError(NULL); } void common_hal_ssl_sslcontext_set_default_verify_paths(ssl_sslcontext_obj_t *self) { - // self->crt_bundle_attach = crt_bundle_attach; - // self->use_global_ca_store = true; - // self->cacert_buf = NULL; - // self->cacert_bytes = 0; + // The default paths are what's built in to NINA-FW, so nothing need be done. } bool common_hal_ssl_sslcontext_get_check_hostname(ssl_sslcontext_obj_t *self) { - // return self->check_name; - return false; + return true; } void common_hal_ssl_sslcontext_set_check_hostname(ssl_sslcontext_obj_t *self, bool value) { - // self->check_name = value; + if (!value) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q must be %q"), MP_QSTR_check_hostname, MP_QSTR_true); + } } void common_hal_ssl_sslcontext_load_cert_chain(ssl_sslcontext_obj_t *self, mp_buffer_info_t *cert_buf, mp_buffer_info_t *key_buf) { - // self->cert_buf = *cert_buf; - // self->key_buf = *key_buf; + mp_raise_NotImplementedError(NULL); } diff --git a/devices/airlift/common-hal/ssl/SSLContext.h b/devices/airlift/common-hal/ssl/SSLContext.h index d420803d1611c..99243b6729004 100644 --- a/devices/airlift/common-hal/ssl/SSLContext.h +++ b/devices/airlift/common-hal/ssl/SSLContext.h @@ -10,8 +10,8 @@ typedef struct { mp_obj_base_t base; - bool check_name, use_global_ca_store; - const unsigned char *cacert_buf; + // bool check_name, use_global_ca_store; + // const unsigned char *cacert_buf; // size_t cacert_bytes; // int (*crt_bundle_attach)(mbedtls_ssl_config *conf); // mp_buffer_info_t cert_buf, key_buf; diff --git a/devices/airlift/common-hal/ssl/SSLSocket.c b/devices/airlift/common-hal/ssl/SSLSocket.c index ab329e445b60b..6d721eb12cda3 100644 --- a/devices/airlift/common-hal/ssl/SSLSocket.c +++ b/devices/airlift/common-hal/ssl/SSLSocket.c @@ -1,8 +1,6 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2016 Linaro Ltd. -// SPDX-FileCopyrightText: Copyright (c) 2019 Paul Sokolovsky -// SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries // // SPDX-License-Identifier: MIT @@ -20,452 +18,116 @@ #include "shared-bindings/socketpool/enum.h" -#define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR) - -// static NORETURN void mbedtls_raise_error(int err) { -// // _mbedtls_ssl_send and _mbedtls_ssl_recv (below) turn positive error codes from the -// // underlying socket into negative codes to pass them through mbedtls. Here we turn them -// // positive again so they get interpreted as the OSError they really are. The -// // cut-off of -256 is a bit hacky, sigh. -// if (err < 0 && err > -256) { -// mp_raise_OSError(-err); -// } - -// if (err == MBEDTLS_ERR_SSL_WANT_WRITE || err == MBEDTLS_ERR_SSL_WANT_READ) { -// mp_raise_OSError(MP_EWOULDBLOCK); -// } - -// #if defined(MBEDTLS_ERROR_C) -// // Including mbedtls_strerror takes about 1.5KB due to the error strings. -// // MBEDTLS_ERROR_C is the define used by mbedtls to conditionally include mbedtls_strerror. -// // It is set/unset in the MBEDTLS_CONFIG_FILE which is defined in the Makefile. - -// // Try to allocate memory for the message -// #define ERR_STR_MAX 80 // mbedtls_strerror truncates if it doesn't fit -// mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); -// byte *o_str_buf = m_malloc_without_collect(ERR_STR_MAX); -// if (o_str == NULL || o_str_buf == NULL) { -// mp_raise_OSError(err); -// } - -// // print the error message into the allocated buffer -// mbedtls_strerror(err, (char *)o_str_buf, ERR_STR_MAX); -// size_t len = strlen((char *)o_str_buf); - -// // Put the exception object together -// o_str->base.type = &mp_type_str; -// o_str->data = o_str_buf; -// o_str->len = len; -// o_str->hash = qstr_compute_hash(o_str->data, o_str->len); -// // raise -// mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(err), MP_OBJ_FROM_PTR(o_str)}; -// nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args)); -// #else -// // mbedtls is compiled without error strings so we simply return the err number -// mp_raise_OSError(err); // err is typically a large negative number -// #endif -// } - -// Because ssl_socket_send and ssl_socket_recv_into are callbacks from mbedtls code, -// it is not OK to exit them by raising an exception (nlr_jump'ing through -// foreign code is not permitted). Instead, preserve the error number of any OSError -// and turn anything else into -MP_EINVAL. -// static int call_method_errno(size_t n_args, const mp_obj_t *args) { -// nlr_buf_t nlr; -// mp_int_t result = -MP_EINVAL; -// if (nlr_push(&nlr) == 0) { -// mp_obj_t obj_result = mp_call_method_n_kw(n_args, 0, args); -// result = (obj_result == mp_const_none) ? 0 : mp_obj_get_int(obj_result); -// nlr_pop(); -// return result; -// } else { -// mp_obj_t exc = MP_OBJ_FROM_PTR(nlr.ret_val); -// if (nlr_push(&nlr) == 0) { -// result = -mp_obj_get_int(mp_load_attr(exc, MP_QSTR_errno)); -// nlr_pop(); -// } -// } -// return result; -// } - -// static int ssl_socket_send(ssl_sslsocket_obj_t *self, const byte *buf, size_t len) { -// mp_obj_array_t mv; -// mp_obj_memoryview_init(&mv, 'B', 0, len, (void *)buf); - -// self->send_args[2] = MP_OBJ_FROM_PTR(&mv); -// return call_method_errno(1, self->send_args); -// } - -// static int ssl_socket_recv_into(ssl_sslsocket_obj_t *self, byte *buf, size_t len) { -// mp_obj_array_t mv; -// mp_obj_memoryview_init(&mv, 'B' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW, 0, len, buf); - -// self->recv_into_args[2] = MP_OBJ_FROM_PTR(&mv); -// return call_method_errno(1, self->recv_into_args); -// } - -// static void ssl_socket_connect(ssl_sslsocket_obj_t *self, mp_obj_t addr_in) { -// self->connect_args[2] = addr_in; -// mp_call_method_n_kw(1, 0, self->connect_args); -// } - -// static void ssl_socket_bind(ssl_sslsocket_obj_t *self, mp_obj_t addr_in) { -// self->bind_args[2] = addr_in; -// mp_call_method_n_kw(1, 0, self->bind_args); -// } - -// static void ssl_socket_close(ssl_sslsocket_obj_t *self) { -// // swallow any exception raised by the underlying close method. -// // This is not ideal. However, it avoids printing "MemoryError:" -// // when attempting to close a userspace socket object during gc_sweep_all -// nlr_buf_t nlr; -// if (nlr_push(&nlr) == 0) { -// mp_call_method_n_kw(0, 0, self->close_args); -// nlr_pop(); -// } else { -// nlr_pop(); -// } -// } - -// static void ssl_socket_setsockopt(ssl_sslsocket_obj_t *self, mp_obj_t level_obj, mp_obj_t opt_obj, mp_obj_t optval_obj) { -// self->setsockopt_args[2] = level_obj; -// self->setsockopt_args[3] = opt_obj; -// self->setsockopt_args[4] = optval_obj; -// mp_call_method_n_kw(3, 0, self->setsockopt_args); -// } - -// static void ssl_socket_settimeout(ssl_sslsocket_obj_t *self, mp_obj_t timeout_obj) { -// self->settimeout_args[2] = timeout_obj; -// mp_call_method_n_kw(1, 0, self->settimeout_args); -// } - -// static void ssl_socket_listen(ssl_sslsocket_obj_t *self, mp_int_t backlog) { -// self->listen_args[2] = MP_OBJ_NEW_SMALL_INT(backlog); -// mp_call_method_n_kw(1, 0, self->listen_args); -// } - -// static mp_obj_t ssl_socket_accept(ssl_sslsocket_obj_t *self) { -// // return mp_call_method_n_kw(0, 0, self->accept_args); -// return mp_const_none; -// } - -// static int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) { -// ssl_sslsocket_obj_t *self = (ssl_sslsocket_obj_t *)ctx; - -// mp_int_t out_sz = ssl_socket_send(self, buf, len); -// DEBUG_PRINT("socket_send() -> %d", out_sz); -// if (out_sz < 0) { -// int err = -out_sz; -// DEBUG_PRINT("sock_stream->write() -> %d nonblocking? %d", out_sz, mp_is_nonblocking_error(err)); -// if (mp_is_nonblocking_error(err)) { -// return MBEDTLS_ERR_SSL_WANT_WRITE; -// } -// } -// return out_sz; -// } - -// static int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) { -// ssl_sslsocket_obj_t *self = (ssl_sslsocket_obj_t *)ctx; +ssl_sslsocket_obj_t *common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t *self, + mp_obj_t socket, bool server_side, const char *server_hostname) { -// mp_int_t out_sz = ssl_socket_recv_into(self, buf, len); -// DEBUG_PRINT("socket_recv() -> %d", out_sz); -// if (out_sz < 0) { -// int err = -out_sz; -// if (mp_is_nonblocking_error(err)) { -// return MBEDTLS_ERR_SSL_WANT_READ; -// } -// } -// return out_sz; -// } + if (server_side) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q not implemented"), MP_QSTR_server_side); + } + mp_arg_validate_length_max(strlen(server_hostname), MAX_HOSTNAME_LENGTH, MP_QSTR_hostname); -ssl_sslsocket_obj_t *common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t *self, - mp_obj_t socket, bool server_side, const char *server_hostname) { - // - // mp_int_t socket_type = mp_obj_get_int(mp_load_attr(socket, MP_QSTR_type)); - // if (socket_type != SOCKETPOOL_SOCK_STREAM) { - // mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid socket for TLS")); - // } - // - // ssl_sslsocket_obj_t *o = mp_obj_malloc_with_finaliser(ssl_sslsocket_obj_t, &ssl_sslsocket_type); - // o->ssl_context = self; - // o->sock_obj = socket; - // o->poll_mask = 0; - // - // mp_load_method(socket, MP_QSTR_accept, o->accept_args); - // mp_load_method(socket, MP_QSTR_bind, o->bind_args); - // mp_load_method(socket, MP_QSTR_close, o->close_args); - // mp_load_method(socket, MP_QSTR_connect, o->connect_args); - // mp_load_method(socket, MP_QSTR_listen, o->listen_args); - // mp_load_method(socket, MP_QSTR_recv_into, o->recv_into_args); - // mp_load_method(socket, MP_QSTR_send, o->send_args); - // mp_load_method(socket, MP_QSTR_settimeout, o->settimeout_args); - // mp_load_method(socket, MP_QSTR_setsockopt, o->setsockopt_args); - // - // mbedtls_ssl_init(&o->ssl); - // mbedtls_ssl_config_init(&o->conf); - // mbedtls_x509_crt_init(&o->cacert); - // mbedtls_x509_crt_init(&o->cert); - // mbedtls_pk_init(&o->pkey); - // mbedtls_ctr_drbg_init(&o->ctr_drbg); - // #ifdef MBEDTLS_DEBUG_C - // // Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose - // mbedtls_debug_set_threshold(4); - // #endif - // - // mbedtls_entropy_init(&o->entropy); - // const byte seed[] = "upy"; - // int ret = mbedtls_ctr_drbg_seed(&o->ctr_drbg, mbedtls_entropy_func, &o->entropy, seed, sizeof(seed)); - // if (ret != 0) { - // goto cleanup; - // } - // - // ret = mbedtls_ssl_config_defaults(&o->conf, - // server_side ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, - // MBEDTLS_SSL_TRANSPORT_STREAM, - // MBEDTLS_SSL_PRESET_DEFAULT); - // if (ret != 0) { - // goto cleanup; - // } - // - // if (self->crt_bundle_attach != NULL) { - // mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_REQUIRED); - // self->crt_bundle_attach(&o->conf); - // } else if (self->cacert_buf && self->cacert_bytes) { - // ret = mbedtls_x509_crt_parse(&o->cacert, self->cacert_buf, self->cacert_bytes); - // if (ret != 0) { - // goto cleanup; - // } - // mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_REQUIRED); - // mbedtls_ssl_conf_ca_chain(&o->conf, &o->cacert, NULL); - // - // } else { - // mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_NONE); - // } - // mbedtls_ssl_conf_rng(&o->conf, mbedtls_ctr_drbg_random, &o->ctr_drbg); - // #ifdef MBEDTLS_DEBUG_C - // mbedtls_ssl_conf_dbg(&o->conf, mbedtls_debug, NULL); - // #endif - // - // ret = mbedtls_ssl_setup(&o->ssl, &o->conf); - // if (ret != 0) { - // goto cleanup; - // } - // - // if (server_hostname != NULL) { - // ret = mbedtls_ssl_set_hostname(&o->ssl, server_hostname); - // if (ret != 0) { - // goto cleanup; - // } - // } - // - // mbedtls_ssl_set_bio(&o->ssl, o, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL); - // - // if (self->cert_buf.buf != NULL) { - // #if MBEDTLS_VERSION_MAJOR >= 3 - // ret = mbedtls_pk_parse_key(&o->pkey, self->key_buf.buf, self->key_buf.len + 1, NULL, 0, urandom_adapter, NULL); - // #else - // ret = mbedtls_pk_parse_key(&o->pkey, self->key_buf.buf, self->key_buf.len + 1, NULL, 0); - // #endif - // if (ret != 0) { - // goto cleanup; - // } - // ret = mbedtls_x509_crt_parse(&o->cert, self->cert_buf.buf, self->cert_buf.len + 1); - // if (ret != 0) { - // goto cleanup; - // } - // - // ret = mbedtls_ssl_conf_own_cert(&o->conf, &o->cert, &o->pkey); - // if (ret != 0) { - // goto cleanup; - // } - // } - // return o; - // cleanup: - // mbedtls_pk_free(&o->pkey); - // mbedtls_x509_crt_free(&o->cert); - // mbedtls_x509_crt_free(&o->cacert); - // mbedtls_ssl_free(&o->ssl); - // mbedtls_ssl_config_free(&o->conf); - // mbedtls_ctr_drbg_free(&o->ctr_drbg); - // mbedtls_entropy_free(&o->entropy); - // - // if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) { - // mp_raise_type(&mp_type_MemoryError); - // } else if (ret == MBEDTLS_ERR_PK_BAD_INPUT_DATA) { - // mp_raise_ValueError(MP_ERROR_TEXT("invalid key")); - // } else if (ret == MBEDTLS_ERR_X509_BAD_INPUT_DATA) { - // mp_raise_ValueError(MP_ERROR_TEXT("invalid cert")); - // } else { - // mbedtls_raise_error(ret); - // } - return NULL; + ssl_sslsocket_obj_t *ssl_socket = mp_obj_malloc(ssl_sslsocket_obj_t, &ssl_sslsocket_type); + ssl_socket->socket = MP_OBJ_TO_PTR(socket); + // Max length already validated above, so this is safe. + strcpy(ssl_socket->hostname, server_hostname); + ssl_socket->server_side = server_side; + return ssl_socket; } mp_uint_t common_hal_ssl_sslsocket_recv_into(ssl_sslsocket_obj_t *self, uint8_t *buf, mp_uint_t len) { - // self->poll_mask = 0; - // int ret = mbedtls_ssl_read(&self->ssl, buf, len); - // DEBUG_PRINT("recv_into mbedtls_ssl_read() -> %d\n", ret); - // if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { - // DEBUG_PRINT("returning %d\n", 0); - // // end of stream - // return 0; - // } - // if (ret >= 0) { - // DEBUG_PRINT("returning %d\n", ret); - // return ret; - // } - // if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { - // self->poll_mask = MP_STREAM_POLL_WR; - // } - // DEBUG_PRINT("raising errno [error case] %d\n", ret); - // mbedtls_raise_error(ret); - return 0; + return common_hal_socketpool_socket_recv_into(self->socket, buf, len); } mp_uint_t common_hal_ssl_sslsocket_send(ssl_sslsocket_obj_t *self, const uint8_t *buf, mp_uint_t len) { - // self->poll_mask = 0; - // int ret = mbedtls_ssl_write(&self->ssl, buf, len); - // DEBUG_PRINT("send mbedtls_ssl_write() -> %d\n", ret); - // if (ret >= 0) { - // DEBUG_PRINT("returning %d\n", ret); - // return ret; - // } - // if (ret == MBEDTLS_ERR_SSL_WANT_READ) { - // self->poll_mask = MP_STREAM_POLL_RD; - // } - // DEBUG_PRINT("raising errno [error case] %d\n", ret); - // mbedtls_raise_error(ret); - return 0; + return common_hal_socketpool_socket_send(self->socket, buf, len); } void common_hal_ssl_sslsocket_bind(ssl_sslsocket_obj_t *self, mp_obj_t addr_in) { - // ssl_socket_bind(self, addr_in); + mp_obj_t *addr_items; + mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); + + size_t hostlen; + const char *host = mp_obj_str_get_data(addr_items[0], &hostlen); + mp_int_t port = mp_arg_validate_int_min(mp_obj_get_int(addr_items[1]), 0, MP_QSTR_port); + + common_hal_socketpool_socket_bind(self->socket, host, hostlen, port); } void common_hal_ssl_sslsocket_close(ssl_sslsocket_obj_t *self) { - // if (self->closed) { - // return; - // } - // self->closed = true; - // ssl_socket_close(self); - // mbedtls_pk_free(&self->pkey); - // mbedtls_x509_crt_free(&self->cert); - // mbedtls_x509_crt_free(&self->cacert); - // mbedtls_ssl_free(&self->ssl); - // mbedtls_ssl_config_free(&self->conf); - // mbedtls_ctr_drbg_free(&self->ctr_drbg); - // mbedtls_entropy_free(&self->entropy); + // TODO: MORE?? + common_hal_socketpool_socket_close(self->socket); } -// static void do_handshake(ssl_sslsocket_obj_t *self) { -// int ret; -// while ((ret = mbedtls_ssl_handshake(&self->ssl)) != 0) { -// if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { -// goto cleanup; -// } -// RUN_BACKGROUND_TASKS; -// if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { -// mp_handle_pending(true); -// } -// mp_hal_delay_ms(1); -// } - -// return; - -// cleanup: -// self->closed = true; -// mbedtls_pk_free(&self->pkey); -// mbedtls_x509_crt_free(&self->cert); -// mbedtls_x509_crt_free(&self->cacert); -// mbedtls_ssl_free(&self->ssl); -// mbedtls_ssl_config_free(&self->conf); -// mbedtls_ctr_drbg_free(&self->ctr_drbg); -// mbedtls_entropy_free(&self->entropy); +void common_hal_ssl_sslsocket_connect(ssl_sslsocket_obj_t *self, mp_obj_t addr_in) { + mp_obj_t *addr_items; + mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); -// if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) { -// mp_raise_type(&mp_type_MemoryError); -// } else if (ret == MBEDTLS_ERR_PK_BAD_INPUT_DATA) { -// mp_raise_ValueError(MP_ERROR_TEXT("invalid key")); -// } else if (ret == MBEDTLS_ERR_X509_BAD_INPUT_DATA) { -// mp_raise_ValueError(MP_ERROR_TEXT("invalid cert")); -// } else { -// mbedtls_raise_error(ret); -// } -// } + size_t hostlen; + const char *host = mp_obj_str_get_data(addr_items[0], &hostlen); + mp_int_t port = mp_arg_validate_int_min(mp_obj_get_int(addr_items[1]), 0, MP_QSTR_port); -void common_hal_ssl_sslsocket_connect(ssl_sslsocket_obj_t *self, mp_obj_t addr_in) { - // ssl_socket_connect(self, addr_in); - // do_handshake(self); + socketpool_socket_start_client_mode(self->socket, host, hostlen, (uint32_t)port, AIRLIFT_TLS_MODE); } bool common_hal_ssl_sslsocket_get_closed(ssl_sslsocket_obj_t *self) { - // return self->closed; - return false; + return common_hal_socketpool_socket_get_closed(self->socket); } bool common_hal_ssl_sslsocket_get_connected(ssl_sslsocket_obj_t *self) { - // return !self->closed; - return false; + return common_hal_socketpool_socket_get_connected(self->socket); } void common_hal_ssl_sslsocket_listen(ssl_sslsocket_obj_t *self, int backlog) { - // return ssl_socket_listen(self, backlog); + mp_raise_NotImplementedError(NULL); } mp_obj_t common_hal_ssl_sslsocket_accept(ssl_sslsocket_obj_t *self) { - // mp_obj_t accepted = ssl_socket_accept(self); - // mp_obj_t sock = mp_obj_subscr(accepted, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_SENTINEL); - // ssl_sslsocket_obj_t *sslsock = common_hal_ssl_sslcontext_wrap_socket(self->ssl_context, sock, true, NULL); - // do_handshake(sslsock); - // mp_obj_t peer = mp_obj_subscr(accepted, MP_OBJ_NEW_SMALL_INT(1), MP_OBJ_SENTINEL); - // mp_obj_t tuple_contents[2]; - // tuple_contents[0] = MP_OBJ_FROM_PTR(sslsock); - // tuple_contents[1] = peer; - // return mp_obj_new_tuple(2, tuple_contents); - return mp_const_none; + mp_raise_NotImplementedError(NULL); } void common_hal_ssl_sslsocket_setsockopt(ssl_sslsocket_obj_t *self, mp_obj_t level_obj, mp_obj_t optname_obj, mp_obj_t optval_obj) { - // ssl_socket_setsockopt(self, level_obj, optname_obj, optval_obj); + mp_int_t level = mp_obj_get_int(level_obj); + mp_int_t optname = mp_obj_get_int(optname_obj); + + const void *optval; + mp_uint_t optlen; + mp_int_t val; + if (mp_obj_is_integer(optval_obj)) { + val = mp_obj_get_int_truncated(optval_obj); + optval = &val; + optlen = sizeof(val); + } else { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(optval_obj, &bufinfo, MP_BUFFER_READ); + optval = bufinfo.buf; + optlen = bufinfo.len; + } + + int _errno = common_hal_socketpool_socket_setsockopt(self->socket, level, optname, optval, optlen); + if (_errno < 0) { + mp_raise_OSError(-_errno); + } } void common_hal_ssl_sslsocket_settimeout(ssl_sslsocket_obj_t *self, mp_obj_t timeout_obj) { - // ssl_socket_settimeout(self, timeout_obj); + mp_uint_t timeout_ms; + if (timeout_obj == mp_const_none) { + timeout_ms = SOCKET_BLOCK_FOREVER; + } else { + #if MICROPY_PY_BUILTINS_FLOAT + timeout_ms = 1000 * mp_obj_get_float(timeout_obj); + #else + timeout_ms = 1000 * mp_obj_get_int(timeout_obj); + #endif + } + common_hal_socketpool_socket_settimeout(self->socket, timeout_ms); } -// static bool poll_common(ssl_sslsocket_obj_t *self, uintptr_t arg) { -// // Take into account that the library might have buffered data already -// int has_pending = 0; -// if (arg & MP_STREAM_POLL_RD) { -// has_pending = mbedtls_ssl_check_pending(&self->ssl); -// if (has_pending) { -// // Shortcut if we only need to read and we have buffered data, no need to go to the underlying socket -// return true; -// } -// } - -// // If the library signaled us that it needs reading or writing, only -// // check that direction -// if (self->poll_mask && (arg & MP_STREAM_POLL_RDWR)) { -// arg = (arg & ~MP_STREAM_POLL_RDWR) | self->poll_mask; -// } - -// // If direction the library needed is available, return a fake -// // result to the caller so that it reenters a read or a write to -// // allow the handshake to progress -// const mp_stream_p_t *stream_p = mp_get_stream_raise(self->sock_obj, MP_STREAM_OP_IOCTL); -// int errcode; -// mp_int_t ret = stream_p->ioctl(self->sock_obj, MP_STREAM_POLL, arg, &errcode); -// return ret != 0; -// } - bool common_hal_ssl_sslsocket_readable(ssl_sslsocket_obj_t *self) { - // return poll_common(self, MP_STREAM_POLL_RD); - return false; + return common_hal_socketpool_socket_readable(self->socket); } bool common_hal_ssl_sslsocket_writable(ssl_sslsocket_obj_t *self) { - // return poll_common(self, MP_STREAM_POLL_WR); - return false; + return common_hal_socketpool_socket_writable(self->socket); } diff --git a/devices/airlift/common-hal/ssl/SSLSocket.h b/devices/airlift/common-hal/ssl/SSLSocket.h index 2af8039142452..918a82ab9a1d5 100644 --- a/devices/airlift/common-hal/ssl/SSLSocket.h +++ b/devices/airlift/common-hal/ssl/SSLSocket.h @@ -8,27 +8,12 @@ #pragma once #include "py/obj.h" +#include "shared-bindings/socketpool/Socket.h" typedef struct ssl_sslsocket_obj { mp_obj_base_t base; - mp_obj_t sock_obj; - // ssl_sslcontext_obj_t *ssl_context; - // mbedtls_entropy_context entropy; - // mbedtls_ctr_drbg_context ctr_drbg; - // mbedtls_ssl_context ssl; - // mbedtls_ssl_config conf; - // mbedtls_x509_crt cacert; - // mbedtls_x509_crt cert; - // mbedtls_pk_context pkey; - uintptr_t poll_mask; + socketpool_socket_obj_t *socket; + char hostname[MAX_HOSTNAME_LENGTH + 1]; bool closed; - mp_obj_t accept_args[2]; - mp_obj_t bind_args[3]; - mp_obj_t close_args[2]; - mp_obj_t connect_args[3]; - mp_obj_t listen_args[3]; - mp_obj_t recv_into_args[3]; - mp_obj_t send_args[3]; - mp_obj_t setsockopt_args[5]; - mp_obj_t settimeout_args[3]; + bool server_side; } ssl_sslsocket_obj_t; diff --git a/devices/airlift/common-hal/ssl/__init__.c b/devices/airlift/common-hal/ssl/__init__.c index 3fe609f466741..c4b99294a39cd 100644 --- a/devices/airlift/common-hal/ssl/__init__.c +++ b/devices/airlift/common-hal/ssl/__init__.c @@ -10,9 +10,8 @@ #include "shared-bindings/ssl/SSLContext.h" void common_hal_ssl_create_default_context(ssl_sslcontext_obj_t *self) { - // common_hal_ssl_sslcontext_construct(self); + common_hal_ssl_sslcontext_construct(self); } void ssl_reset(void) { - // crt_bundle_detach(NULL); } diff --git a/devices/airlift/common-hal/wifi/Monitor.c b/devices/airlift/common-hal/wifi/Monitor.c index 6c9309964ffad..a683b1821dec3 100644 --- a/devices/airlift/common-hal/wifi/Monitor.c +++ b/devices/airlift/common-hal/wifi/Monitor.c @@ -22,130 +22,38 @@ typedef struct { signed rssi; } monitor_packet_t; -// static void wifi_monitor_cb(void *recv_buf, wifi_promiscuous_pkt_type_t type) { -// wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)recv_buf; - -// // prepare packet -// monitor_packet_t packet = { -// .channel = pkt->rx_ctrl.channel, -// .length = pkt->rx_ctrl.sig_len, -// .rssi = pkt->rx_ctrl.rssi, -// }; - -// // for now, the monitor only dumps the length of the MISC type frame -// if (type != WIFI_PKT_MISC && !pkt->rx_ctrl.rx_state) { -// packet.length -= MONITOR_PAYLOAD_FCS_LEN; -// packet.payload = malloc(packet.length); -// if (packet.payload) { -// memcpy(packet.payload, pkt->payload, packet.length); -// wifi_monitor_obj_t *self = MP_STATE_VM(wifi_monitor_singleton); -// if (self->queue) { -// // send packet -// if (xQueueSendFromISR(self->queue, &packet, NULL) != pdTRUE) { -// self->lost++; -// free(packet.payload); -// ESP_LOGE(TAG, "packet queue full"); -// } -// } -// } else { -// ESP_LOGE(TAG, "not enough memory for packet"); -// } -// } -// } - void common_hal_wifi_monitor_construct(wifi_monitor_obj_t *self, uint8_t channel, size_t queue) { - // mp_rom_error_text_t monitor_mode_init_error = MP_ERROR_TEXT("monitor init failed"); - - // self->queue = xQueueCreate(queue, sizeof(monitor_packet_t)); - // if (!self->queue) { - // mp_raise_RuntimeError(monitor_mode_init_error); - // } - - // // start wifi promicuous mode - // wifi_promiscuous_filter_t wifi_filter = { - // .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT, - // }; - // esp_wifi_set_promiscuous_filter(&wifi_filter); - // esp_wifi_set_promiscuous_rx_cb(wifi_monitor_cb); - // if (esp_wifi_set_promiscuous(true) != ESP_OK) { - // mp_raise_RuntimeError(monitor_mode_init_error); - // } - // esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); - - // self->channel = channel; - // self->queue_length = queue; + mp_raise_NotImplementedError(NULL); } bool common_hal_wifi_monitor_deinited(void) { - // bool enabled; - // return (esp_wifi_get_promiscuous(&enabled) == ESP_ERR_WIFI_NOT_INIT) ? true : !enabled; - return false; + mp_raise_NotImplementedError(NULL); } void common_hal_wifi_monitor_deinit(wifi_monitor_obj_t *self) { - // if (common_hal_wifi_monitor_deinited()) { - // return; - // } - - // // disable wifi promiscuous mode - // esp_wifi_set_promiscuous(false); - - // // make sure to free all resources in the left items - // UBaseType_t left_items = uxQueueMessagesWaiting(self->queue); - // monitor_packet_t packet; - // while (left_items--) { - // xQueueReceive(self->queue, &packet, MONITOR_QUEUE_TIMEOUT_TICK); - // free(packet.payload); - // } - // vQueueDelete(self->queue); - // self->queue = NULL; + mp_raise_NotImplementedError(NULL); } void common_hal_wifi_monitor_set_channel(wifi_monitor_obj_t *self, uint8_t channel) { - // self->channel = channel; - // esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); + mp_raise_NotImplementedError(NULL); } mp_obj_t common_hal_wifi_monitor_get_channel(wifi_monitor_obj_t *self) { - // return MP_OBJ_NEW_SMALL_INT(self->channel); - return mp_const_none; + mp_raise_NotImplementedError(NULL); } mp_obj_t common_hal_wifi_monitor_get_queue(wifi_monitor_obj_t *self) { - // return mp_obj_new_int_from_uint(self->queue_length); - return mp_const_none; + mp_raise_NotImplementedError(NULL); } mp_obj_t common_hal_wifi_monitor_get_lost(wifi_monitor_obj_t *self) { - // size_t lost = self->lost; - // self->lost = 0; - // return mp_obj_new_int_from_uint(lost); - return mp_const_none; + mp_raise_NotImplementedError(NULL); } mp_obj_t common_hal_wifi_monitor_get_queued(wifi_monitor_obj_t *self) { - // return mp_obj_new_int_from_uint(uxQueueMessagesWaiting(self->queue)); - return mp_const_none; + mp_raise_NotImplementedError(NULL); } mp_obj_t common_hal_wifi_monitor_get_packet(wifi_monitor_obj_t *self) { - // monitor_packet_t packet; - - // if (xQueueReceive(self->queue, &packet, MONITOR_QUEUE_TIMEOUT_TICK) != pdTRUE) { - // return (mp_obj_t)&mp_const_empty_dict_obj; - // } - - // mp_obj_dict_t *dict = MP_OBJ_TO_PTR(mp_obj_new_dict(4)); - - // mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_CH), MP_OBJ_NEW_SMALL_INT(packet.channel)); - - // mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_LEN), MP_OBJ_NEW_SMALL_INT(packet.length)); - - // mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_RAW), mp_obj_new_bytes(packet.payload, packet.length)); - // free(packet.payload); - - // mp_obj_dict_store(dict, cp_enum_find(&wifi_packet_type, PACKET_RSSI), MP_OBJ_NEW_SMALL_INT(packet.rssi)); - - // return MP_OBJ_FROM_PTR(dict); - return mp_const_none; + mp_raise_NotImplementedError(NULL); } diff --git a/devices/airlift/common-hal/wifi/Monitor.h b/devices/airlift/common-hal/wifi/Monitor.h index 4e05cc36e80de..080aa8e6071fa 100644 --- a/devices/airlift/common-hal/wifi/Monitor.h +++ b/devices/airlift/common-hal/wifi/Monitor.h @@ -10,8 +10,4 @@ typedef struct { mp_obj_base_t base; - uint8_t channel; - size_t lost; - size_t queue_length; -// QueueHandle_t queue; } wifi_monitor_obj_t; diff --git a/devices/airlift/common-hal/wifi/Network.c b/devices/airlift/common-hal/wifi/Network.c index 461c6e3a67fae..3adf9c4ca0292 100644 --- a/devices/airlift/common-hal/wifi/Network.c +++ b/devices/airlift/common-hal/wifi/Network.c @@ -9,74 +9,111 @@ #include "shared-bindings/wifi/Network.h" #include "shared-bindings/wifi/AuthMode.h" -mp_obj_t common_hal_wifi_network_get_ssid(wifi_network_obj_t *self) { - // const char *cstr = (const char *)self->record.ssid; - // return mp_obj_new_str(cstr, strlen(cstr)); - return mp_const_none; +// From esp_wifi_types_generic.h. Needs to match the enum used to build nina-fw. +typedef enum { + WIFI_AUTH_OPEN = 0, /**< Authenticate mode : open */ + WIFI_AUTH_WEP, /**< Authenticate mode : WEP */ + WIFI_AUTH_WPA_PSK, /**< Authenticate mode : WPA_PSK */ + WIFI_AUTH_WPA2_PSK, /**< Authenticate mode : WPA2_PSK */ + WIFI_AUTH_WPA_WPA2_PSK, /**< Authenticate mode : WPA_WPA2_PSK */ + WIFI_AUTH_ENTERPRISE, /**< Authenticate mode : Wi-Fi EAP security, treated the same as WIFI_AUTH_WPA2_ENTERPRISE */ + WIFI_AUTH_WPA2_ENTERPRISE = WIFI_AUTH_ENTERPRISE, /**< Authenticate mode : WPA2-Enterprise security */ + WIFI_AUTH_WPA3_PSK, /**< Authenticate mode : WPA3_PSK */ + WIFI_AUTH_WPA2_WPA3_PSK, /**< Authenticate mode : WPA2_WPA3_PSK */ + WIFI_AUTH_WAPI_PSK, /**< Authenticate mode : WAPI_PSK */ + WIFI_AUTH_OWE, /**< Authenticate mode : OWE */ + WIFI_AUTH_WPA3_ENT_192, /**< Authenticate mode : WPA3_ENT_SUITE_B_192_BIT */ + WIFI_AUTH_WPA3_EXT_PSK, /**< This authentication mode will yield same result as WIFI_AUTH_WPA3_PSK and not recommended to be used. It will be deprecated in future, please use WIFI_AUTH_WPA3_PSK instead. */ + WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE, /**< This authentication mode will yield same result as WIFI_AUTH_WPA3_PSK and not recommended to be used. It will be deprecated in future, please use WIFI_AUTH_WPA3_PSK instead.*/ + WIFI_AUTH_DPP, /**< Authenticate mode : DPP */ + WIFI_AUTH_WPA3_ENTERPRISE, /**< Authenticate mode : WPA3-Enterprise Only Mode */ + WIFI_AUTH_WPA2_WPA3_ENTERPRISE, /**< Authenticate mode : WPA3-Enterprise Transition Mode */ + WIFI_AUTH_WPA_ENTERPRISE, /**< Authenticate mode : WPA-Enterprise security */ + WIFI_AUTH_MAX +} wifi_auth_mode_t; + +// Lookup table to convert authmodes. + +typedef struct { + uint8_t esp_authmode; + wifi_authmode_t wifi_authmode; +} esp_authmode_to_wifi_authmode_t; + +esp_authmode_to_wifi_authmode_t esp_authmode_to_wifi_authmode[] = { + { WIFI_AUTH_OPEN, AUTHMODE_OPEN }, + { WIFI_AUTH_WEP, AUTHMODE_WEP }, + { WIFI_AUTH_WPA_PSK, AUTHMODE_WPA | AUTHMODE_PSK }, + { WIFI_AUTH_WPA_WPA2_PSK, AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_PSK }, + { WIFI_AUTH_WPA_ENTERPRISE, AUTHMODE_WPA | AUTHMODE_ENTERPRISE }, + { WIFI_AUTH_WPA2_PSK, AUTHMODE_WPA2 | AUTHMODE_PSK }, + {WIFI_AUTH_WPA2_WPA3_PSK, AUTHMODE_WPA2 | AUTHMODE_WPA3 | AUTHMODE_PSK }, + // Same as WIFI_AUTH_ENTERPRISE. + { WIFI_AUTH_WPA2_ENTERPRISE, AUTHMODE_WPA2 | AUTHMODE_ENTERPRISE }, + { WIFI_AUTH_WPA2_WPA3_ENTERPRISE, AUTHMODE_WPA2 | AUTHMODE_WPA3 | AUTHMODE_ENTERPRISE }, + { WIFI_AUTH_WPA3_PSK, AUTHMODE_WPA3 | AUTHMODE_PSK }, + { WIFI_AUTH_WPA3_ENTERPRISE, AUTHMODE_WPA3 | AUTHMODE_ENTERPRISE }, +}; + +typedef struct { + wifi_authmode_t wifi_authmode; + const cp_enum_obj_t *authmode_obj; +} wifi_authmode_to_authmode_obj_t; + +wifi_authmode_to_authmode_obj_t wifi_authmode_to_authmode_obj[] = { + { AUTHMODE_OPEN, &authmode_OPEN_obj }, + { AUTHMODE_WEP, &authmode_WEP_obj }, + { AUTHMODE_PSK, &authmode_PSK_obj }, + { AUTHMODE_WPA, &authmode_WPA_obj }, + { AUTHMODE_WPA2, &authmode_WPA2_obj }, + { AUTHMODE_WPA3, &authmode_WPA3_obj }, + { AUTHMODE_ENTERPRISE, &authmode_ENTERPRISE_obj }, +}; + +mp_obj_t esp_authmode_to_wifi_authmode_tuple(uint8_t esp_authmode) { + wifi_authmode_t wifi_authmode_mask = 0; + // First convert the esp_authmode from AirLift to a bitmask wifi_authmode_t. + for (size_t i = 0; i < MP_ARRAY_SIZE(esp_authmode_to_wifi_authmode); i++) { + if (esp_authmode_to_wifi_authmode[i].esp_authmode == esp_authmode) { + wifi_authmode_mask = esp_authmode_to_wifi_authmode[i].wifi_authmode; + break; + } + } + + // Then, make a tuple of all the AuthMode objects + // corresponding to the bits set in the authmode bitmask. + mp_obj_t authmode_objs[MP_ARRAY_SIZE(wifi_authmode_to_authmode_obj)]; + size_t len = 0; + for (size_t i = 0; i < MP_ARRAY_SIZE(wifi_authmode_to_authmode_obj); i++) { + if (wifi_authmode_to_authmode_obj[i].wifi_authmode & wifi_authmode_mask) { + // Bit is set in bitmask. + authmode_objs[len++] = MP_OBJ_FROM_PTR(wifi_authmode_to_authmode_obj[i].authmode_obj); + } + } + + // Save the objs as a tuple. + return mp_obj_new_tuple(len, authmode_objs); } -#define MAC_ADDRESS_LENGTH 6 +mp_obj_t common_hal_wifi_network_get_ssid(wifi_network_obj_t *self) { + return self->ssid; +} mp_obj_t common_hal_wifi_network_get_bssid(wifi_network_obj_t *self) { - // return mp_obj_new_bytes(self->record.bssid, MAC_ADDRESS_LENGTH); - return mp_const_none; + return self->bssid; } mp_obj_t common_hal_wifi_network_get_rssi(wifi_network_obj_t *self) { - // return mp_obj_new_int(self->record.rssi); - return mp_const_none; + return self->rssi; } mp_obj_t common_hal_wifi_network_get_channel(wifi_network_obj_t *self) { - // return mp_obj_new_int(self->record.primary); - return mp_const_none; + return self->channel; } mp_obj_t common_hal_wifi_network_get_country(wifi_network_obj_t *self) { - // const char *cstr = (const char *)self->record.country.cc; - // // 2 instead of strlen(cstr) as this gives us only the country-code - // return mp_obj_new_str(cstr, 2); - return mp_const_none; + return self->country; } mp_obj_t common_hal_wifi_network_get_authmode(wifi_network_obj_t *self) { - // uint32_t authmode_mask = 0; - // switch (self->record.authmode) { - // case WIFI_AUTH_OPEN: - // authmode_mask = AUTHMODE_OPEN; - // break; - // case WIFI_AUTH_WEP: - // authmode_mask = AUTHMODE_WEP; - // break; - // case WIFI_AUTH_WPA_PSK: - // authmode_mask = AUTHMODE_WPA | AUTHMODE_PSK; - // break; - // case WIFI_AUTH_WPA2_PSK: - // authmode_mask = AUTHMODE_WPA2 | AUTHMODE_PSK; - // break; - // case WIFI_AUTH_WPA_WPA2_PSK: - // authmode_mask = AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_PSK; - // break; - // case WIFI_AUTH_WPA2_ENTERPRISE: - // authmode_mask = AUTHMODE_WPA2 | AUTHMODE_ENTERPRISE; - // break; - // case WIFI_AUTH_WPA3_PSK: - // authmode_mask = AUTHMODE_WPA3 | AUTHMODE_PSK; - // break; - // case WIFI_AUTH_WPA2_WPA3_PSK: - // authmode_mask = AUTHMODE_WPA2 | AUTHMODE_WPA3 | AUTHMODE_PSK; - // break; - // default: - // break; - // } - // mp_obj_t authmode_list = mp_obj_new_list(0, NULL); - // if (authmode_mask != 0) { - // for (uint8_t i = 0; i < 32; i++) { - // if ((authmode_mask >> i) & 1) { - // mp_obj_list_append(authmode_list, cp_enum_find(&wifi_authmode_type, 1 << i)); - // } - // } - // } - // return authmode_list; - return mp_const_none; + return self->authmode; } diff --git a/devices/airlift/common-hal/wifi/Network.h b/devices/airlift/common-hal/wifi/Network.h index 368f92b9126c4..83fceefb5b367 100644 --- a/devices/airlift/common-hal/wifi/Network.h +++ b/devices/airlift/common-hal/wifi/Network.h @@ -10,5 +10,12 @@ typedef struct { mp_obj_base_t base; -// wifi_ap_record_t record; + mp_obj_t ssid; + mp_obj_t bssid; + mp_obj_t rssi; + mp_obj_t channel; + mp_obj_t country; + mp_obj_t authmode; } wifi_network_obj_t; + +mp_obj_t esp_authmode_to_wifi_authmode_tuple(uint8_t esp_authmode); diff --git a/devices/airlift/common-hal/wifi/Radio.c b/devices/airlift/common-hal/wifi/Radio.c index b12177bd49d12..19cecbfa26dd8 100644 --- a/devices/airlift/common-hal/wifi/Radio.c +++ b/devices/airlift/common-hal/wifi/Radio.c @@ -4,9 +4,7 @@ // // SPDX-License-Identifier: MIT -#include "shared-bindings/wifi/Radio.h" -#include "shared-bindings/wifi/Network.h" - +#include #include #include "common-hal/wifi/__init__.h" @@ -15,157 +13,441 @@ #include "py/mphal.h" #include "py/obj.h" #include "py/runtime.h" + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/ipaddress/IPv4Address.h" -#include "shared-bindings/wifi/ScannedNetworks.h" -#include "shared-bindings/wifi/AuthMode.h" +#include "shared-bindings/ipaddress/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/time/__init__.h" -#include "shared-module/ipaddress/__init__.h" +#include "shared-bindings/wifi/AuthMode.h" +#include "shared-bindings/wifi/Network.h" +#include "shared-bindings/wifi/Radio.h" +#include "shared-bindings/wifi/ScannedNetworks.h" + #include "common-hal/socketpool/__init__.h" -#if CIRCUITPY_MDNS -#include "common-hal/mdns/Server.h" -#endif -#define MAC_ADDRESS_LENGTH 6 +// Print out SPI traffic with AirLift. +#define DEBUG_AIRLIFT 1 + +#if DEBUG_AIRLIFT +static const char *command_name(uint8_t command) { + switch (command) { + case START_CMD: + return "START_CMD"; + case END_CMD: + return "END_CMD"; + case ERR_CMD: + return "ERR_CMD"; + case REPLY_FLAG: + return "REPLY_FLAG"; + case CMD_FLAG: + return "CMD_FLAG"; + case SET_NET_CMD: + return "SET_NET_CMD"; + case SET_PASSPHRASE_CMD: + return "SET_PASSPHRASE_CMD"; + case SET_KEY_CMD: + return "SET_KEY_CMD"; + // case TEST_CMD: return "TEST_CMD"; // Not implemented + case SET_IP_CONFIG: + return "SET_IP_CONFIG"; + case SET_DNS_CONFIG: + return "SET_DNS_CONFIG"; + case SET_HOSTNAME: + return "SET_HOSTNAME"; + case SET_POWER_MODE_CMD: + return "SET_POWER_MODE_CMD"; + case SET_AP_NET_CMD: + return "SET_AP_NET_CMD"; + case SET_AP_PASSPHRASE_CMD: + return "SET_AP_PASSPHRASE_CMD"; + case SET_DEBUG_CMD: + return "SET_DEBUG_CMD"; + case GET_TEMPERATURE_CMD: + return "GET_TEMPERATURE_CMD"; + case GET_DNS_CONFIG_CMD: + return "GET_DNS_CONFIG_CMD"; + case GET_REASON_CODE_CMD: + return "GET_REASON_CODE_CMD"; + case GET_CONN_STATUS_CMD: + return "GET_CONN_STATUS_CMD"; + case GET_IPADDR_CMD: + return "GET_IPADDR_CMD"; + case GET_MACADDR_CMD: + return "GET_MACADDR_CMD"; + case GET_CURR_SSID_CMD: + return "GET_CURR_SSID_CMD"; + case GET_CURR_BSSID_CMD: + return "GET_CURR_BSSID_CMD"; + case GET_CURR_RSSI_CMD: + return "GET_CURR_RSSI_CMD"; + case GET_CURR_ENCT_CMD: + return "GET_CURR_ENCT_CMD"; + case SCAN_NETWORKS: + return "SCAN_NETWORKS"; + case START_SERVER_TCP_CMD: + return "START_SERVER_TCP_CMD"; + case GET_STATE_TCP_CMD: + return "GET_STATE_TCP_CMD"; + case DATA_SENT_TCP_CMD: + return "DATA_SENT_TCP_CMD"; + case AVAIL_DATA_TCP_CMD: + return "AVAIL_DATA_TCP_CMD"; + case GET_DATA_TCP_CMD: + return "GET_DATA_TCP_CMD"; + case START_CLIENT_TCP_CMD: + return "START_CLIENT_TCP_CMD"; + case STOP_CLIENT_TCP_CMD: + return "STOP_CLIENT_TCP_CMD"; + case GET_CLIENT_STATE_TCP_CMD: + return "GET_CLIENT_STATE_TCP_CMD"; + case DISCONNECT_CMD: + return "DISCONNECT_CMD"; + // case GET_IDX_SSID_CMD: return "GET_IDX_SSID_CMD"; // Not implemented. + case GET_IDX_RSSI_CMD: + return "GET_IDX_RSSI_CMD"; + case GET_IDX_ENCT_CMD: + return "GET_IDX_ENCT_CMD"; + case REQ_HOST_BY_NAME_CMD: + return "REQ_HOST_BY_NAME_CMD"; + case GET_HOST_BY_NAME_CMD: + return "GET_HOST_BY_NAME_CMD"; + case START_SCAN_NETWORKS: + return "START_SCAN_NETWORKS"; + case GET_FW_VERSION_CMD: + return "GET_FW_VERSION_CMD"; + // case GET_TEST_CMD: return "GET_TEST_CMD"; // Not implemented. + case SEND_UDP_DATA_CMD: + return "SEND_UDP_DATA_CMD"; + case GET_REMOTE_DATA_CMD: + return "GET_REMOTE_DATA_CMD"; + case GET_TIME: + return "GET_TIME"; + case GET_IDX_BSSID_CMD: + return "GET_IDX_BSSID_CMD"; + case GET_IDX_CHAN_CMD: + return "GET_IDX_CHAN_CMD"; + case PING_CMD: + return "PING_CMD"; + case GET_SOCKET_CMD: + return "GET_SOCKET_CMD"; + case SET_CLI_CERT: + return "SET_CLI_CERT"; + case SET_PK: + return "SET_PK"; + case SEND_DATA_TCP_CMD: + return "SEND_DATA_TCP_CMD"; + case GET_DATABUF_TCP_CMD: + return "GET_DATABUF_TCP_CMD"; + case INSERT_DATABUF_TCP_CMD: + return "INSERT_DATABUF_TCP_CMD"; + case SET_ENT_IDENT_CMD: + return "SET_ENT_IDENT_CMD"; + case SET_ENT_UNAME_CMD: + return "SET_ENT_UNAME_CMD"; + case SET_ENT_PASSWD_CMD: + return "SET_ENT_PASSWD_CMD"; + case SET_ENT_ENABLE_CMD: + return "SET_ENT_ENABLE_CMD"; + case SET_DIGITAL_WRITE_CMD: + return "SET_DIGITAL_WRITE_CMD"; + case SET_ANALOG_WRITE_CMD: + return "SET_ANALOG_WRITE_CMD"; + case SET_DIGITAL_READ_CMD: + return "SET_DIGITAL_READ_CMD"; + case SET_ANALOG_READ_CMD: + return "SET_ANALOG_READ_CMD"; + case WRITE_FILE: + return "WRITE_FILE"; + case READ_FILE: + return "READ_FILE"; + case DELETE_FILE: + return "DELETE_FILE"; + case EXISTS_FILE: + return "EXISTS_FILE"; + case DOWNLOAD_FILE: + return "DOWNLOAD_FILE"; + case APPLY_OTA_COMMAND: + return "APPLY_OTA_COMMAND"; + case RENAME_FILE: + return "RENAME_FILE"; + case DOWNLOAD_OTA: + return "DOWNLOAD_OTA"; + case SOCKET_SOCKET_CMD: + return "SOCKET_SOCKET_CMD"; + case SOCKET_CLOSE_CMD: + return "SOCKET_CLOSE_CMD"; + case SOCKET_ERRNO_CMD: + return "SOCKET_ERRNO_CMD"; + case SOCKET_BIND_CMD: + return "SOCKET_BIND_CMD"; + case SOCKET_LISTEN_CMD: + return "SOCKET_LISTEN_CMD"; + case SOCKET_ACCEPT_CMD: + return "SOCKET_ACCEPT_CMD"; + case SOCKET_CONNECT_CMD: + return "SOCKET_CONNECT_CMD"; + case SOCKET_SEND_CMD: + return "SOCKET_SEND_CMD"; + case SOCKET_RECV_CMD: + return "SOCKET_RECV_CMD"; + case SOCKET_SENDTO_CMD: + return "SOCKET_SENDTO_CMD"; + case SOCKET_RECVFROM_CMD: + return "SOCKET_RECVFROM_CMD"; + case SOCKET_IOCTL_CMD: + return "SOCKET_IOCTL_CMD"; + case SOCKET_POLL_CMD: + return "SOCKET_POLL_CMD"; + case SOCKET_SETSOCKOPT_CMD: + return "SOCKET_SETSOCKOPT_CMD"; + case SOCKET_GETSOCKOPT_CMD: + return "SOCKET_GETSOCKOPT_CMD"; + case SOCKET_GETPEERNAME_CMD: + return "SOCKET_GETPEERNAME_CMD"; + default: + return "*UNKNOWN COMMAND*"; + } +} +#endif // DEBUG_AIRLIFT // Release CS and lock if held. static void spi_end_transaction(wifi_radio_obj_t *self) { if (common_hal_busio_spi_has_lock(self->spi)) { - common_hal_digitalio_digitalinout_set_value(self->cs, true); + common_hal_digitalio_digitalinout_set_value(self->cs_dio, true); common_hal_busio_spi_unlock(self->spi); } } -// Wait for ready pin to become low or high. Raise an exception if a timeout is exceeded. -static void wait_for_ready(wifi_radio_obj_t *self, bool value, uint32_t timeout_ticks) { +// Wait for ready pin to become low or high. +// Terminate the spi transaction if in progress, and raise an exception if a timeout is exceeded. +// +static void wait_for_ready_raise(wifi_radio_obj_t *self, bool value, uint32_t timeout_ms) { uint64_t start = mp_hal_ticks_ms(); - while ((mp_hal_ticks_ms() - start) < timeout_ticks) { - if (common_hal_digitalio_digitalinout_get_value(self->ready) == value) { + while ((mp_hal_ticks_ms() - start) < timeout_ms) { + if (common_hal_digitalio_digitalinout_get_value(self->ready_dio) == value) { return; } RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + break; + } } - // Timeout. Give up SPI control. spi_end_transaction(self); mp_raise_msg_varg(&mp_type_TimeoutError, MP_ERROR_TEXT("timeout waiting for ready %q"), value ? MP_QSTR_True : MP_QSTR_False); } // Wait for co-processor to be ready, then grab lock and CS. -static void spi_begin_transaction(wifi_radio_obj_t *self) { +static bool spi_begin_transaction(wifi_radio_obj_t *self, uint32_t timeout_ms) { + // SPI might have been deinited out from under us.. + if (!self->spi || common_hal_busio_spi_deinited(self->spi)) { + return false; + } // The ready line is set low when the NINA firmware is ready to start an SPI transaction. // Once CS is set low to signal an SPI transaction has started, NINA sets the ready line high // to indicate it has seen the CS transition to low. - wait_for_ready(self, false, 10000); + // Wait for the previous command to complete. + wait_for_ready_raise(self, false, timeout_ms); while (!common_hal_busio_spi_try_lock(self->spi)) { RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + break; + } } common_hal_busio_spi_configure(self->spi, 8000000, 0, 0, 8); - common_hal_digitalio_digitalinout_set_value(self->cs, false); - wait_for_ready(self, true, 1000); + common_hal_digitalio_digitalinout_set_value(self->cs_dio, false); + // This is not a variable timeout, because we are not waiting for a command to complete. + wait_for_ready_raise(self, true, AIRLIFT_DEFAULT_TIMEOUT_MS); + return true; } -// Internal routines that are also called from other classes. - -// Send command via SPI. Assumes spi_begin_transaction() has already been called. -void wifi_radio_send_command(wifi_radio_obj_t *self, uint8_t cmd, const uint8_t **params, const size_t *param_lens, size_t num_params) { - // Calculate packet size - size_t packet_len = 4; // START + CMD + NUM_PARAMS + END - for (size_t i = 0; i < num_params; i++) { - packet_len += 1 + param_lens[i]; // length byte + data - } - // Pad to 4-byte boundary - while (packet_len % 4 != 0) { - packet_len++; - } - - // Ensure buffer is large enough - if (packet_len > self->sendbuf_len) { - self->sendbuf = m_realloc(self->sendbuf, packet_len); - self->sendbuf_len = packet_len; +static void check_for_enabled(wifi_radio_obj_t *self) { + if (!common_hal_wifi_radio_get_enabled(self)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("WiFi is not enabled")); } +} - // Build packet - self->sendbuf[0] = _START_CMD; - self->sendbuf[1] = cmd & ~_REPLY_FLAG; - self->sendbuf[2] = num_params; +// AirLift communication routines, some of which are also called from other classes. - size_t ptr = 3; - for (size_t i = 0; i < num_params; i++) { - self->sendbuf[ptr++] = param_lens[i] & 0xFF; - memcpy(&self->sendbuf[ptr], params[i], param_lens[i]); - ptr += param_lens[i]; +// Read byte_length bytes from SPI. +static void wifi_radio_read(wifi_radio_obj_t *self, uint8_t *bytes, size_t byte_length) { + if (!common_hal_busio_spi_read(self->spi, bytes, byte_length, 0xFF)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); } - self->sendbuf[ptr++] = _END_CMD; +} - // Pad with zeros - while (ptr < packet_len) { - self->sendbuf[ptr++] = 0; +// Read just one byte from SPI. +static uint8_t wifi_radio_read_byte(wifi_radio_obj_t *self) { + uint8_t one_byte; + if (common_hal_busio_spi_read(self->spi, &one_byte, 1, 0xFF)) { + return one_byte; + } else { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); } +} - spi_begin_transaction(self); - - // Wait for ready to go high (ready to receive) - wait_for_ready(self, true, 1000); - - common_hal_busio_spi_write(self->spi, self->sendbuf, packet_len); - - spi_end_transaction(self); +// Send just one byte from SPI +static bool wifi_radio_write_byte(wifi_radio_obj_t *self, uint8_t b) { + return common_hal_busio_spi_write(self->spi, &b, 1); } -// Read just one byte from SPI -uint8_t wifi_radio_read_byte(wifi_radio_obj_t *self) { - common_hal_busio_spi_read(self->spi, self->pbuf, 1, 0xFF); - return self->pbuf[0]; +static bool wifi_radio_write(wifi_radio_obj_t *self, const uint8_t *bytes, size_t byte_length) { + return common_hal_busio_spi_write(self->spi, bytes, byte_length); } // Wait for specific byte -void wifi_radio_wait_spi_char(wifi_radio_obj_t *self, uint8_t desired) { +static void wifi_radio_wait_spi_char(wifi_radio_obj_t *self, uint8_t desired) { for (int i = 0; i < 10; i++) { uint8_t r = wifi_radio_read_byte(self); - if (r == _ERR_CMD) { - mp_raise_msg(&mp_type_BrokenPipeError, MP_ERROR_TEXT("Error response to command")); + if (r == ERR_CMD) { + spi_end_transaction(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("Error response to AirLift command")); } if (r == desired) { return; } mp_hal_delay_ms(10); } - mp_raise_msg(&mp_type_TimeoutError, MP_ERROR_TEXT("timeout waiting for byte")); + spi_end_transaction(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("timeout waiting for byte")); } // Check that next byte matches expected value. -void wifi_radio_check_data(wifi_radio_obj_t *self, uint8_t desired) { +static void wifi_radio_read_and_check_byte(wifi_radio_obj_t *self, uint8_t desired) { uint8_t r = wifi_radio_read_byte(self); if (r != desired) { - mp_raise_msg_varg(&mp_type_BrokenPipeError, MP_ERROR_TEXT("Expected %02x but got %02x"), desired, r); + spi_end_transaction(self); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Expected %02x but got %02x"), desired, r); } } +// Send command via SPI. +bool wifi_radio_send_command(wifi_radio_obj_t *self, uint8_t cmd, + const uint8_t **params, const size_t *param_lengths, lengths_size_t param_lengths_size, size_t num_params) { + + if (!spi_begin_transaction(self, AIRLIFT_DEFAULT_TIMEOUT_MS)) { + return false; + } + + // Send command opcode and number of params. + const uint8_t command_begin[] = { + START_CMD, + cmd & ~REPLY_FLAG, + num_params, + }; + wifi_radio_write(self, command_begin, sizeof(command_begin)); + size_t num_bytes_sent = sizeof(command_begin); + + #if DEBUG_AIRLIFT + PLAT_PRINTF(">cmd %s (%02x)\n", command_name(cmd), cmd); + #endif // DEBUG_AIRLIFT + + for (size_t i = 0; i < num_params; i++) { + switch (param_lengths_size) { + case LENGTHS_16: + // Length is sent big-endian. + wifi_radio_write_byte(self, (param_lengths[i] >> 8) & 0xFF); + num_bytes_sent++; + [[fallthrough]]; + case LENGTHS_8: + wifi_radio_write_byte(self, param_lengths[i] & 0xFF); + num_bytes_sent++; + break; + } + + wifi_radio_write(self, params[i], param_lengths[i]); + num_bytes_sent += param_lengths[i]; + + #if DEBUG_AIRLIFT + PLAT_PRINTF(">>param #%d (length: %d) --x", i, param_lengths[i]); + for (size_t j = 0; j < param_lengths[i]; j++) { + PLAT_PRINTF(" %02x", params[i][j]); + } + PLAT_PRINTF(" --d"); + for (size_t j = 0; j < param_lengths[i]; j++) { + PLAT_PRINTF(" %d", params[i][j]); + } + PLAT_PRINTF(" --c \""); + for (size_t j = 0; j < param_lengths[i]; j++) { + PLAT_PRINTF("%c", params[i][j]); + } + PLAT_PRINTF("\"\n"); + #endif // DEBUG_AIRLIFT + } + + while (num_bytes_sent++ % 4 != 0) { + wifi_radio_write_byte(self, 0x00); + } + + spi_end_transaction(self); + return true; +} + // Wait for and parse response -size_t wifi_radio_wait_response_cmd(wifi_radio_obj_t *self, uint8_t cmd, uint8_t **responses, size_t *response_lens, size_t max_responses) { - spi_begin_transaction(self); +size_t wifi_radio_wait_response_cmd(wifi_radio_obj_t *self, uint8_t cmd, + uint8_t **responses, size_t *response_lengths, lengths_size_t response_lengths_size, size_t max_responses, + uint32_t response_timeout_ms) { + spi_begin_transaction(self, response_timeout_ms); - wifi_radio_wait_spi_char(self, _START_CMD); - wifi_radio_check_data(self, cmd | _REPLY_FLAG); + wifi_radio_wait_spi_char(self, START_CMD); + wifi_radio_read_and_check_byte(self, cmd | REPLY_FLAG); uint8_t num_responses = wifi_radio_read_byte(self); if (num_responses > max_responses) { num_responses = max_responses; } + if (num_responses == 0) { + PLAT_PRINTF("spi, responses[i], param_len, 0xFF); + size_t response_length = wifi_radio_read_byte(self); + // Two-byte response lengths are big-endian. + if (response_lengths_size == LENGTHS_16) { + response_length = (response_length << 8) | wifi_radio_read_byte(self); + } + // Don't overflow the supplied buffer. + size_t read_length = MIN(response_length, response_lengths[i]); + wifi_radio_read(self, responses[i], read_length); + // Update the passed-in length with what was actually read, so the caller knows how many bytes + // were read for each response. + response_lengths[i] = read_length; + + // Read and discard bytes that didn't fit in buffer. + // TODO report error? + if (read_length > response_lengths[i]) { + #if DEBUG_AIRLIFT + PLAT_PRINTF(" 0) { + wifi_radio_read_byte(self); + } + } + #if DEBUG_AIRLIFT + PLAT_PRINTF(" 0 && response_lengths[0] >= MAC_ADDRESS_LENGTH) { + return true; + } else { + memset(mac, 0, MAC_ADDRESS_LENGTH); + return false; + } } +static bool wifi_radio_get_address_info(wifi_radio_obj_t *self, + uint8_t ipv4[IPV4_LENGTH], uint8_t netmask[IPV4_LENGTH], uint8_t gateway[IPV4_LENGTH]) { + const uint8_t ignored = 0xFF; + const uint8_t *params[1] = { &ignored }; + size_t param_lengths[1] = { 1 }; + + uint8_t *responses[3] = { ipv4, netmask, gateway }; + size_t response_lengths[3] = { IPV4_LENGTH, IPV4_LENGTH, IPV4_LENGTH }; + + size_t num_responses = wifi_radio_send_command_get_response(self, GET_IPADDR_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 3) { + return true; + } else { + return false; + } +} + +// Fetch up to two DNS addresses, into a passed C array of IPv4Address objects. +static void wifi_radio_get_dns_config(wifi_radio_obj_t *self, mp_obj_t dns_ipv4_addresses[], size_t *num_dns) { + const uint8_t ignored = 0xFF; + const uint8_t *params[1] = { &ignored }; + size_t param_lengths[1] = { 1 }; + + uint8_t dns_ipv4[2][IPV4_LENGTH] = { 0 }; + uint8_t *responses[2] = { dns_ipv4[0], dns_ipv4[1] }; + size_t response_lengths[2] = { IPV4_LENGTH, IPV4_LENGTH }; + + size_t num_responses = wifi_radio_send_command_get_response(self, GET_DNS_CONFIG_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 2) { + for (size_t dns_i = 0; dns_i < 2; dns_i++) { + // Only count the non-zero IP addresses. + for (size_t octet_i = 0; octet_i < IPV4_LENGTH; octet_i++) { + if (dns_ipv4[dns_i][octet_i] != 0) { + (*num_dns)++; + dns_ipv4_addresses[dns_i] = common_hal_ipaddress_new_ipv4address_from_bytes(dns_ipv4[dns_i]); + break; + } + } + } + } +} + +static void wifi_radio_reset(wifi_radio_obj_t *self) { + if (self->gpio0_dio) { + common_hal_digitalio_digitalinout_switch_to_output(self->gpio0_dio, true, DRIVE_MODE_PUSH_PULL); + } + + common_hal_digitalio_digitalinout_set_value(self->cs_dio, true); + common_hal_digitalio_digitalinout_set_value(self->reset_dio, false); + mp_hal_delay_ms(10); + common_hal_digitalio_digitalinout_set_value(self->reset_dio, true); + mp_hal_delay_ms(750); // Wait for Airlift to boot. + + if (self->gpio0_dio) { + common_hal_digitalio_digitalinout_switch_to_input(self->gpio0_dio, PULL_NONE); + } +} + +wl_status_t wifi_radio_get_connection_status(wifi_radio_obj_t *self) { + uint8_t connection_status; + + uint8_t *responses[1] = { &connection_status }; + size_t response_lengths[1] = { 1 }; + + size_t num_responses = wifi_radio_send_command_get_response(self, GET_CONN_STATUS_CMD, + NULL, NULL, LENGTHS_8, 0, + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses > 0) { + return connection_status; + } else { + return WL_NO_SHIELD; + } +} ////////////////////////////////////////////////////////////////////////////// // common-hal routines called from shared-bindings/Radio.c -void common_hal_wifi_radio_init_airlift(wifi_radio_obj_t *self, busio_spi_obj_t *spi, digitalio_digitalinout_obj_t *cs, digitalio_digitalinout_obj_t *ready, digitalio_digitalinout_obj_t *reset, digitalio_digitalinout_obj_t *gpio0) { +bool common_hal_wifi_radio_deinited(wifi_radio_obj_t *self) { + return self->spi == NULL; +} + +void common_hal_wifi_radio_mark_deinit(wifi_radio_obj_t *self) { + self->enabled = false; + self->spi = NULL; +} + +void common_hal_wifi_radio_deinit(wifi_radio_obj_t *self) { + if (common_hal_wifi_radio_deinited(self)) { + return; + } + common_hal_wifi_radio_mark_deinit(self); +} + +void common_hal_wifi_radio_init_airlift( + wifi_radio_obj_t *self, + busio_spi_obj_t *spi, + digitalio_digitalinout_obj_t *cs, + digitalio_digitalinout_obj_t *ready, + digitalio_digitalinout_obj_t *reset, + digitalio_digitalinout_obj_t *gpio0) { + + self->spi = spi; + self->cs_dio = cs; + self->ready_dio = ready; + self->reset_dio = reset; + self->gpio0_dio = (gpio0 != mp_const_none) ? gpio0 : NULL; + self->tls_socket = -1; + self->hostname = MP_OBJ_NEW_QSTR(MP_QSTR_); + // Default is not to minimize power. + self->power_management = POWER_MANAGEMENT_NONE; + + common_hal_digitalio_digitalinout_switch_to_output(self->cs_dio, true, DRIVE_MODE_PUSH_PULL); + common_hal_digitalio_digitalinout_switch_to_input(self->ready_dio, PULL_NONE); + common_hal_digitalio_digitalinout_switch_to_output(self->reset_dio, true, DRIVE_MODE_PUSH_PULL); + + if (self->gpio0_dio) { + common_hal_digitalio_digitalinout_switch_to_input(self->gpio0_dio, PULL_NONE); + } + + // Perform initial reset + wifi_radio_reset(self); + self->enabled = true; + + // There is no NINA-FW command to get the hostname, so set a default hostname to begin with. + // NINA-FW sets one, but it includes the co-processor chip type, and we don't know the chip type, + // so instead set a more general name. The partial mac address suffix is the same. + + uint8_t mac[MAC_ADDRESS_LENGTH]; + if (wifi_radio_get_mac_address(self, mac)) { + char default_hostname[32]; + snprintf(default_hostname, sizeof(default_hostname), "AirLift-%02X%02X%02X", mac[3], mac[4], mac[5]); + common_hal_wifi_radio_set_hostname(self, default_hostname); + } } mp_obj_t common_hal_wifi_radio_get_version(wifi_radio_obj_t *self) { - uint8_t *responses[1]; - size_t response_lens[1]; + check_for_enabled(self); - size_t num_resp = wifi_radio_send_command_get_response(self, _GET_FW_VERSION_CMD, NULL, NULL, 0, responses, response_lens, 1); + uint8_t firmware_version[AIRLIFT_MAX_FIRMWARE_VERSION_LENGTH]; + uint8_t *responses[1] = { firmware_version }; + size_t response_lengths[1] = { AIRLIFT_MAX_FIRMWARE_VERSION_LENGTH }; - if (num_resp > 0) { - return mp_obj_new_str_from_cstr((char *)responses[0]); + size_t num_responses = wifi_radio_send_command_get_response(self, GET_FW_VERSION_CMD, + NULL, NULL, LENGTHS_8, 0, + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses > 0) { + return mp_obj_new_str((char *)firmware_version, response_lengths[0]); } else { return mp_const_empty_bytes; } @@ -202,709 +655,585 @@ mp_obj_t common_hal_wifi_radio_get_version(wifi_radio_obj_t *self) { bool common_hal_wifi_radio_get_enabled(wifi_radio_obj_t *self) { - return self->started; + return self->enabled; } void common_hal_wifi_radio_set_enabled(wifi_radio_obj_t *self, bool enabled) { -/* - if (self->started && !enabled) { - if (self->current_scan != NULL) { - common_hal_wifi_radio_stop_scanning_networks(self); + if (enabled != self->enabled) { + if (enabled) { + // Do a fresh reset to disable anything going on now. } - #if CIRCUITPY_MDNS - mdns_server_deinit_singleton(); - #endif - ESP_ERROR_CHECK(esp_wifi_stop()); - self->started = false; - return; + self->enabled = enabled; } - if (!self->started && enabled) { - ESP_ERROR_CHECK(esp_wifi_start()); - self->started = true; - common_hal_wifi_radio_set_tx_power(self, CIRCUITPY_WIFI_DEFAULT_TX_POWER); - return; - } -*/ } mp_obj_t common_hal_wifi_radio_get_hostname(wifi_radio_obj_t *self) { - // const char *hostname = NULL; - // esp_netif_get_hostname(self->netif, &hostname); - // if (hostname == NULL) { - // return mp_const_none; - // } - // return mp_obj_new_str_from_cstr(hostname); - return mp_const_none; + check_for_enabled(self); + + return self->hostname; } void common_hal_wifi_radio_set_hostname(wifi_radio_obj_t *self, const char *hostname) { const uint8_t *params[1] = { (const uint8_t *)hostname }; - size_t param_lens[1] = { strlen(hostname) }; - uint8_t *responses[1]; - size_t response_lens[1]; + size_t param_lengths[1] = { strlen(hostname) }; - size_t num_resp = wifi_radio_send_command_get_response(self, _SET_HOSTNAME, params, param_lens, 1, responses, response_lens, 1); + uint8_t result = 0; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { 1 }; - if (num_resp > 0) { - uint8_t result = responses[0][0]; - m_free(responses[0]); - if (result != 1) { - mp_raise_OSError_msg(MP_ERROR_TEXT("Failed to set hostname")); - } + size_t num_responses = wifi_radio_send_command_get_response(self, SET_HOSTNAME, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0 || result != 1) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); } + + // Remember this so we can return it when asked. + self->hostname = mp_obj_new_str_from_cstr(hostname); } mp_obj_t common_hal_wifi_radio_get_mac_address(wifi_radio_obj_t *self) { - const uint8_t param = 0xFF; - const uint8_t *params[1] = { ¶m }; - size_t param_lens[1] = { 1 }; - uint8_t *responses[1]; - size_t response_lens[1]; - uint8_t mac[6]; - - size_t num_resp = wifi_radio_send_command_get_response(self, _GET_MACADDR_CMD, params, param_lens, 1, responses, response_lens, 1); + check_for_enabled(self); - if (num_resp > 0 && response_lens[0] >= MAC_ADDRESS_LENGTH) { - // Reverse byte order (ESP32 returns it reversed) - for (int i = 0; i < MAC_ADDRESS_LENGTH; i++) { - mac[i] = responses[0][MAC_ADDRESS_LENGTH - 1 - i]; - } - m_free(responses[0]); + uint8_t mac[MAC_ADDRESS_LENGTH]; + if (wifi_radio_get_mac_address(self, mac)) { + return mp_obj_new_bytes(mac, MAC_ADDRESS_LENGTH); } else { - memset(mac, 0, MAC_ADDRESS_LENGTH); + return mp_const_none; } - return mp_obj_new_bytes(mac, MAC_ADDRESS_LENGTH); } void common_hal_wifi_radio_set_mac_address(wifi_radio_obj_t *self, const uint8_t *mac) { - // if (!self->sta_mode) { - // mp_raise_RuntimeError(MP_ERROR_TEXT("Interface must be started")); - // } - // if ((mac[0] & 0b1) == 0b1) { - // mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid multicast MAC address")); - // } - // esp_wifi_set_mac(ESP_IF_WIFI_STA, mac); + mp_raise_NotImplementedError(NULL); } mp_float_t common_hal_wifi_radio_get_tx_power(wifi_radio_obj_t *self) { - // int8_t tx_power; - // esp_wifi_get_max_tx_power(&tx_power); - // return tx_power / 4.0f; - return 0.0f; + return 20.0f; } void common_hal_wifi_radio_set_tx_power(wifi_radio_obj_t *self, const mp_float_t tx_power) { - // esp_wifi_set_max_tx_power(tx_power * 4.0f); + mp_raise_NotImplementedError(NULL); } wifi_power_management_t common_hal_wifi_radio_get_power_management(wifi_radio_obj_t *self) { - // wifi_ps_type_t ps; - // esp_err_t ret = esp_wifi_get_ps(&ps); - // if (ret == ESP_OK) { - // switch (ps) { - // case WIFI_PS_MIN_MODEM: - // return POWER_MANAGEMENT_MIN; - // case WIFI_PS_MAX_MODEM: - // return POWER_MANAGEMENT_MAX; - // case WIFI_PS_NONE: - // return POWER_MANAGEMENT_NONE; - // } - // } - return POWER_MANAGEMENT_MIN; -} + check_for_enabled(self); + return self->power_management; +} void common_hal_wifi_radio_set_power_management(wifi_radio_obj_t *self, wifi_power_management_t power_management) { - // switch (power_management) { - // case POWER_MANAGEMENT_MIN: - // esp_wifi_set_ps(WIFI_PS_MIN_MODEM); - // break; - // case POWER_MANAGEMENT_MAX: { - // // listen_interval is only used in this case. - // wifi_config_t *config = &self->sta_config; - // // This is a typical value seen in various examples. - // config->sta.listen_interval = 3; - // esp_wifi_set_ps(WIFI_PS_MAX_MODEM); - // esp_wifi_set_config(ESP_IF_WIFI_STA, config); - // } - // break; - // case POWER_MANAGEMENT_NONE: - // esp_wifi_set_ps(WIFI_PS_NONE); - // break; - // case POWER_MANAGEMENT_UNKNOWN: - // // This should be prevented in shared-bindings. - // break; - // } + check_for_enabled(self); + + uint8_t mode; + switch (power_management) { + case POWER_MANAGEMENT_MIN: + mode = 1; + break; + case POWER_MANAGEMENT_NONE: + mode = 0; + break; + case POWER_MANAGEMENT_MAX: + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q not implemented"), MP_QSTR_POWER_MANAGEMENT_MAX); + break; + case POWER_MANAGEMENT_UNKNOWN: + // This should be prevented in shared-bindings. + return; + } + + const uint8_t *params[1] = { &mode }; + size_t param_lengths[1] = { 1 }; + uint8_t result = 0; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { sizeof(result) }; + + size_t num_responses = wifi_radio_send_command_get_response(self, SET_POWER_MODE_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0 || result != 1) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); + } + + self->power_management = power_management; } mp_obj_t common_hal_wifi_radio_get_mac_address_ap(wifi_radio_obj_t *self) { - // uint8_t mac[MAC_ADDRESS_LENGTH]; - // esp_wifi_get_mac(ESP_IF_WIFI_AP, mac); - // return mp_obj_new_bytes(mac, MAC_ADDRESS_LENGTH); - return mp_const_none; + mp_raise_NotImplementedError(NULL); } void common_hal_wifi_radio_set_mac_address_ap(wifi_radio_obj_t *self, const uint8_t *mac) { - // if (!self->ap_mode) { - // mp_raise_RuntimeError(MP_ERROR_TEXT("Interface must be started")); - // } - // if ((mac[0] & 0b1) == 0b1) { - // mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid multicast MAC address")); - // } - // esp_wifi_set_mac(ESP_IF_WIFI_AP, mac); + mp_raise_NotImplementedError(NULL); } +// start_channel and stop_channel are not implemented by AirLift. mp_obj_t common_hal_wifi_radio_start_scanning_networks(wifi_radio_obj_t *self, uint8_t start_channel, uint8_t stop_channel) { - // if (self->current_scan != NULL) { - // mp_raise_RuntimeError(MP_ERROR_TEXT("Already scanning for wifi networks")); - // } - // if (!common_hal_wifi_radio_get_enabled(self)) { - // mp_raise_RuntimeError(MP_ERROR_TEXT("WiFi is not enabled")); - // } - // set_mode_station(self, true); - - // wifi_scannednetworks_obj_t *scan = mp_obj_malloc(wifi_scannednetworks_obj_t, &wifi_scannednetworks_type); - // self->current_scan = scan; - // scan->current_channel_index = 0; - // scan->start_channel = start_channel; - // scan->end_channel = stop_channel; - // scan->radio_event_group = self->event_group_handle; - // scan->done = false; - // scan->channel_scan_in_progress = false; - // wifi_scannednetworks_scan_next_channel(scan); - // return scan; - return mp_const_none; + check_for_enabled(self); + + uint8_t result = 0; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { sizeof(result) }; + + size_t num_responses = wifi_radio_send_command_get_response(self, START_SCAN_NETWORKS, + NULL, NULL, LENGTHS_8, 0, + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0 || result != 1) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); + } + + if (self->current_scan) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Already scanning for wifi networks")); + } + if (!common_hal_wifi_radio_get_enabled(self)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("WiFi is not enabled")); + } + + return wifi_scannednetworks_do_scan(self); } void common_hal_wifi_radio_stop_scanning_networks(wifi_radio_obj_t *self) { - // // Return early if self->current_scan is NULL to avoid hang - // if (self->current_scan == NULL) { - // return; - // } - // // Free the memory used to store the found aps. - // wifi_scannednetworks_deinit(self->current_scan); - // self->current_scan = NULL; + // Nothing to do: the scan is done all at once. } void common_hal_wifi_radio_start_station(wifi_radio_obj_t *self) { - // set_mode_station(self, true); + common_hal_wifi_radio_set_enabled(self, true); } void common_hal_wifi_radio_stop_station(wifi_radio_obj_t *self) { - // set_mode_station(self, false); + common_hal_wifi_radio_set_enabled(self, false); } void common_hal_wifi_radio_start_ap(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, uint32_t authmode, uint8_t max_connections) { - // set_mode_ap(self, true); - - // uint8_t esp_authmode = 0; - // switch (authmode) { - // case AUTHMODE_OPEN: - // esp_authmode = WIFI_AUTH_OPEN; - // break; - // case AUTHMODE_WPA | AUTHMODE_PSK: - // esp_authmode = WIFI_AUTH_WPA_PSK; - // break; - // case AUTHMODE_WPA2 | AUTHMODE_PSK: - // esp_authmode = WIFI_AUTH_WPA2_PSK; - // break; - // case AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_PSK: - // esp_authmode = WIFI_AUTH_WPA_WPA2_PSK; - // break; - // default: - // mp_arg_error_invalid(MP_QSTR_authmode); - // break; - // } - - // wifi_config_t *config = &self->ap_config; - // memcpy(&config->ap.ssid, ssid, ssid_len); - // config->ap.ssid[ssid_len] = 0; - // memcpy(&config->ap.password, password, password_len); - // config->ap.password[password_len] = 0; - // config->ap.channel = channel; - // config->ap.authmode = esp_authmode; - - // mp_arg_validate_int_range(max_connections, 0, 10, MP_QSTR_max_connections); - - // config->ap.max_connection = max_connections; - - // esp_wifi_set_config(WIFI_IF_AP, config); + mp_raise_NotImplementedError(NULL); } bool common_hal_wifi_radio_get_ap_active(wifi_radio_obj_t *self) { - // return self->ap_mode && esp_netif_is_netif_up(self->ap_netif); - return mp_const_none; + return mp_const_false; } void common_hal_wifi_radio_stop_ap(wifi_radio_obj_t *self) { - // set_mode_ap(self, false); + // Doing nothing when not started is the same behavior on other ports. } mp_obj_t common_hal_wifi_radio_get_stations_ap(wifi_radio_obj_t *self) { - // wifi_sta_list_t esp_sta_list; - // esp_err_t result; - - // result = esp_wifi_ap_get_sta_list(&esp_sta_list); - // if (result != ESP_OK) { - // return mp_const_none; - // } - - // esp_netif_pair_mac_ip_t mac_ip_pair[esp_sta_list.num]; - // for (int i = 0; i < esp_sta_list.num; i++) { - // memcpy(mac_ip_pair[i].mac, esp_sta_list.sta[i].mac, MAC_ADDRESS_LENGTH); - // mac_ip_pair[i].ip.addr = 0; - // } - - // result = esp_netif_dhcps_get_clients_by_mac(self->ap_netif, esp_sta_list.num, mac_ip_pair); - // if (result != ESP_OK) { - // return mp_const_none; - // } - - // mp_obj_t mp_sta_list = mp_obj_new_list(0, NULL); - // for (int i = 0; i < esp_sta_list.num; i++) { - // mp_obj_t elems[3] = { - // mp_obj_new_bytes(esp_sta_list.sta[i].mac, MAC_ADDRESS_LENGTH), - // MP_OBJ_NEW_SMALL_INT(esp_sta_list.sta[i].rssi), - // mp_const_none - // }; - - // if (mac_ip_pair[i].ip.addr) { - // elems[2] = common_hal_ipaddress_new_ipv4address(mac_ip_pair[i].ip.addr); - // } - - // mp_obj_list_append(mp_sta_list, namedtuple_make_new((const mp_obj_type_t *)&wifi_radio_station_type, 3, 0, elems)); - // } - - // return mp_sta_list; return mp_const_none; } wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, mp_float_t timeout, uint8_t *bssid, size_t bssid_len) { - // if (!common_hal_wifi_radio_get_enabled(self)) { - // mp_raise_RuntimeError(MP_ERROR_TEXT("WiFi is not enabled")); - // } - // wifi_config_t *config = &self->sta_config; - - // size_t timeout_ms = timeout * 1000; - // uint32_t start_time = common_hal_time_monotonic_ms(); - // uint32_t end_time = start_time + timeout_ms; - - // EventBits_t bits; - // // can't block since both bits are false after wifi_init - // // both bits are true after an existing connection stops - // bits = xEventGroupWaitBits(self->event_group_handle, - // WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT, - // pdTRUE, - // pdTRUE, - // 0); - // bool connected = ((bits & WIFI_CONNECTED_BIT) != 0) && - // !((bits & WIFI_DISCONNECTED_BIT) != 0); - // if (connected) { - // // SSIDs are up to 32 bytes. Assume it is null terminated if it is less. - // if (memcmp(ssid, config->sta.ssid, ssid_len) == 0 && - // (ssid_len == 32 || strlen((const char *)config->sta.ssid) == ssid_len)) { - // // Already connected to the desired network. - // return WIFI_RADIO_ERROR_NONE; - // } else { - // xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT); - // // Trying to switch networks so disconnect first. - // esp_wifi_disconnect(); - // do { - // RUN_BACKGROUND_TASKS; - // bits = xEventGroupWaitBits(self->event_group_handle, - // WIFI_DISCONNECTED_BIT, - // pdTRUE, - // pdTRUE, - // 0); - // } while ((bits & WIFI_DISCONNECTED_BIT) == 0 && !mp_hal_is_interrupted()); - // } - // } - // // explicitly clear bits since xEventGroupWaitBits may have timed out - // xEventGroupClearBits(self->event_group_handle, WIFI_CONNECTED_BIT); - // xEventGroupClearBits(self->event_group_handle, WIFI_DISCONNECTED_BIT); - // set_mode_station(self, true); - - // memcpy(&config->sta.ssid, ssid, ssid_len); - // if (ssid_len < 32) { - // config->sta.ssid[ssid_len] = 0; - // } - // memcpy(&config->sta.password, password, password_len); - // config->sta.password[password_len] = 0; - // config->sta.channel = channel; - // // From esp_wifi_types.h: - // // Generally, station_config.bssid_set needs to be 0; and it needs - // // to be 1 only when users need to check the MAC address of the AP - // if (bssid_len > 0) { - // memcpy(&config->sta.bssid, bssid, bssid_len); - // config->sta.bssid[bssid_len] = 0; - // config->sta.bssid_set = true; - // } else { - // config->sta.bssid_set = false; - // } - // // If channel is 0 (default/unset) and BSSID is not given, do a full scan instead of fast scan - // // This will ensure that the best AP in range is chosen automatically - // if ((config->sta.bssid_set == 0) && (config->sta.channel == 0)) { - // config->sta.scan_method = WIFI_ALL_CHANNEL_SCAN; - // } else { - // config->sta.scan_method = WIFI_FAST_SCAN; - // } - // esp_wifi_set_config(ESP_IF_WIFI_STA, config); - // self->starting_retries = 5; - // self->retries_left = 5; - // esp_wifi_connect(); - - // do { - // RUN_BACKGROUND_TASKS; - // bits = xEventGroupWaitBits(self->event_group_handle, - // WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT, - // pdTRUE, - // pdTRUE, - // 0); - // // Don't retry anymore if we're over our time budget. - // if (self->retries_left > 0 && common_hal_time_monotonic_ms() > end_time) { - // self->retries_left = 0; - // } - // } while ((bits & (WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT)) == 0 && !mp_hal_is_interrupted()); - - // if ((bits & WIFI_DISCONNECTED_BIT) != 0) { - // if ( - // (self->last_disconnect_reason == WIFI_REASON_AUTH_FAIL) || - // (self->last_disconnect_reason == WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT) || - // (self->last_disconnect_reason == WIFI_REASON_NO_AP_FOUND_W_COMPATIBLE_SECURITY) || - // (self->last_disconnect_reason == WIFI_REASON_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD) - // ) { - // return WIFI_RADIO_ERROR_AUTH_FAIL; - // } else if (self->last_disconnect_reason == WIFI_REASON_NO_AP_FOUND) { - // return WIFI_RADIO_ERROR_NO_AP_FOUND; - // } - // return self->last_disconnect_reason; - // } else { - // // We're connected, allow us to retry if we get disconnected. - // self->retries_left = self->starting_retries; - // } - return WIFI_RADIO_ERROR_NONE; + if (bssid_len > 0) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q not implemented"), MP_QSTR_bssid); + } + + uint8_t result = 0; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { sizeof(result) }; + size_t num_responses; + + if (password_len > 0) { + const uint8_t *params[2] = { ssid, password }; + size_t param_lengths[2] = { ssid_len, password_len }; + + num_responses = wifi_radio_send_command_get_response(self, SET_PASSPHRASE_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + } else { + const uint8_t *params[1] = { ssid }; + size_t param_lengths[1] = { ssid_len }; + + num_responses = wifi_radio_send_command_get_response(self, SET_NET_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + } + + if (num_responses == 0 || result != 1) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); + } + + // Wait for connection to be established. + uint64_t timeout_ms = timeout * 1000; + if (timeout_ms == 0) { + timeout_ms = 8000; + } + uint64_t end_time = common_hal_time_monotonic_ms() + timeout_ms; + while (common_hal_time_monotonic_ms() < end_time) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + break; + } + mp_hal_delay_ms(200); + + wl_status_t status = wifi_radio_get_connection_status(self); + switch (status) { + case WL_NO_SHIELD: + return WIFI_RADIO_ERROR_NO_RADIO; + + case WL_NO_SSID_AVAIL: + return WIFI_RADIO_ERROR_NO_AP_FOUND; + + case WL_STOPPED: + case WL_IDLE_STATUS: + case WL_SCAN_COMPLETED: + continue; + + case WL_CONNECTED: + return WIFI_RADIO_ERROR_NONE; + + case WL_CONNECT_FAILED: + case WL_CONNECTION_LOST: + case WL_DISCONNECTED: + return WIFI_RADIO_ERROR_CONNECTION_FAIL; + + default: + return WIFI_RADIO_ERROR_UNSPECIFIED; + } + } + + // We either timed out or got interrupted. + return WIFI_RADIO_ERROR_UNSPECIFIED; } bool common_hal_wifi_radio_get_connected(wifi_radio_obj_t *self) { -// return self->sta_mode && esp_netif_is_netif_up(self->netif); - return mp_const_none; + return wifi_radio_get_connection_status(self) == WL_CONNECTED; } mp_obj_t common_hal_wifi_radio_get_ap_info(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - // return mp_const_none; - // } - - // // Make sure the interface is in STA mode - // if (!self->sta_mode) { - // return mp_const_none; - // } - - // wifi_network_obj_t *ap_info = mp_obj_malloc(wifi_network_obj_t, &wifi_network_type); - // // From esp_wifi.h, the possible return values (typos theirs): - // // ESP_OK: succeed - // // ESP_ERR_WIFI_CONN: The station interface don't initialized - // // ESP_ERR_WIFI_NOT_CONNECT: The station is in disconnect status - // if (esp_wifi_sta_get_ap_info(&self->ap_info.record) != ESP_OK) { - // return mp_const_none; - // } else { - // if (strlen(self->ap_info.record.country.cc) == 0) { - // // Workaround to fill country related information in ap_info until ESP-IDF carries a fix - // // esp_wifi_sta_get_ap_info does not appear to fill wifi_country_t (e.g. country.cc) details - // // (IDFGH-4437) #6267 - // // Note: It is possible that Wi-Fi APs don't have a CC set, then even after this workaround - // // the element would remain empty. - // memset(&self->ap_info.record.country, 0, sizeof(wifi_country_t)); - // if (esp_wifi_get_country(&self->ap_info.record.country) != ESP_OK) { - // return mp_const_none; - // } - // } - // memcpy(&ap_info->record, &self->ap_info.record, sizeof(wifi_ap_record_t)); - // return MP_OBJ_FROM_PTR(ap_info); - // } - return mp_const_none; + if (!common_hal_wifi_radio_get_enabled(self)) { + return mp_const_none; + } + + // Create a Network object and fill it in. + wifi_network_obj_t *network = mp_obj_malloc(wifi_network_obj_t, &wifi_network_type); + + const uint8_t ignored = 0xFF; + const uint8_t *params[1] = { &ignored }; + size_t param_lengths[1] = { 1 }; + + { + uint8_t ssid[MAX_SSID_LENGTH] = { 0 }; + uint8_t *responses[1] = { ssid }; + size_t response_lengths[1] = { sizeof(ssid) }; + + network->ssid = mp_const_none; + if (wifi_radio_send_command_get_response(self, GET_CURR_SSID_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS) > 0) { + network->ssid = mp_obj_new_str((const char *)ssid, response_lengths[0]); + } + } + + { + uint8_t bssid[MAC_ADDRESS_LENGTH] = { 0 }; + uint8_t *responses[1] = { bssid }; + size_t response_lengths[1] = { sizeof(bssid) }; + + network->ssid = mp_const_none; + if (wifi_radio_send_command_get_response(self, GET_CURR_BSSID_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS) > 0) { + network->bssid = mp_obj_new_bytes(bssid, MAC_ADDRESS_LENGTH); + } + } + + { + int32_t rssi = 0; + uint8_t *responses[1] = { (uint8_t *)&rssi }; + size_t response_lengths[1] = { sizeof(rssi) }; + + network->rssi = mp_const_none; + if (wifi_radio_send_command_get_response(self, GET_CURR_RSSI_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS) > 0) { + network->rssi = mp_obj_new_int(rssi); + } + } + + // Channel is not available for the current AP we are using. + network->channel = mp_const_none; + + { + uint8_t esp_authmode = 0; + uint8_t *responses[1] = { &esp_authmode }; + size_t response_lengths[1] = { sizeof(esp_authmode) }; + + if (wifi_radio_send_command_get_response(self, GET_CURR_ENCT_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS) > 0) { + + // Convert AirLift authmode to tuple of wifi.AuthMode objects. + network->authmode = esp_authmode_to_wifi_authmode_tuple(esp_authmode); + } + } + + // country code not available on AirLift. + network->country = MP_OBJ_NEW_QSTR(MP_QSTR_); + + return network; } mp_obj_t common_hal_wifi_radio_get_ipv4_gateway(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - // return mp_const_none; - // } - // esp_netif_get_ip_info(self->netif, &self->ip_info); - // return common_hal_ipaddress_new_ipv4address(self->ip_info.gw.addr); - return mp_const_none; + check_for_enabled(self); + + uint8_t gateway[IPV4_LENGTH]; + if (wifi_radio_get_address_info(self, NULL, NULL, gateway)) { + return common_hal_ipaddress_new_ipv4address_from_bytes(gateway); + } else { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); + } } mp_obj_t common_hal_wifi_radio_get_ipv4_gateway_ap(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->ap_netif)) { - // return mp_const_none; - // } - // esp_netif_get_ip_info(self->ap_netif, &self->ap_ip_info); - // return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.gw.addr); return mp_const_none; } mp_obj_t common_hal_wifi_radio_get_ipv4_subnet(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - // return mp_const_none; - // } - // esp_netif_get_ip_info(self->netif, &self->ip_info); - // return common_hal_ipaddress_new_ipv4address(self->ip_info.netmask.addr); - return mp_const_none; + check_for_enabled(self); + + uint8_t mask[IPV4_LENGTH]; + if (wifi_radio_get_address_info(self, NULL, mask, NULL)) { + return common_hal_ipaddress_new_ipv4address_from_bytes(mask); + } else { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); + } } mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->ap_netif)) { - // return mp_const_none; - // } - // esp_netif_get_ip_info(self->ap_netif, &self->ap_ip_info); - // return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.netmask.addr); return mp_const_none; } -// static mp_obj_t common_hal_wifi_radio_get_addresses_netif(wifi_radio_obj_t *self, esp_netif_t *netif) { -// if (!esp_netif_is_netif_up(netif)) { -// return mp_const_empty_tuple; -// } -// esp_netif_ip_info_t ip_info; -// esp_netif_get_ip_info(netif, &ip_info); -// int n_addresses4 = ip_info.ip.addr != INADDR_NONE; - -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// esp_ip6_addr_t addresses[LWIP_IPV6_NUM_ADDRESSES]; -// int n_addresses6 = esp_netif_get_all_ip6(netif, &addresses[0]); -// #else -// int n_addresses6 = 0; -// #endif -// int n_addresses = n_addresses4 + n_addresses6; -// mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(n_addresses, NULL)); - -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// for (int i = 0; i < n_addresses6; i++) { -// result->items[i] = espaddr6_to_str(&addresses[i]); -// } -// #endif - -// if (n_addresses4) { -// result->items[n_addresses6] = espaddr4_to_str(&ip_info.ip); -// } - -// return MP_OBJ_FROM_PTR(result); -// } - mp_obj_t common_hal_wifi_radio_get_addresses(wifi_radio_obj_t *self) { - // return common_hal_wifi_radio_get_addresses_netif(self, self->netif); - return mp_const_none; + check_for_enabled(self); + + vstr_t vstr; + mp_print_t print; + vstr_init_print(&vstr, 16, &print); + mp_obj_print_helper(&print, common_hal_wifi_radio_get_ipv4_address(self), PRINT_STR); + return mp_obj_new_tuple(1, mp_obj_new_str_from_vstr(&vstr)); } mp_obj_t common_hal_wifi_radio_get_addresses_ap(wifi_radio_obj_t *self) { - // return common_hal_wifi_radio_get_addresses_netif(self, self->ap_netif); - return mp_const_none; + return mp_const_empty_tuple; } uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - // return 0; - // } - // esp_netif_get_ip_info(self->netif, &self->ip_info); - // return self->ip_info.ip.addr; - return 42; + check_for_enabled(self); + + uint8_t ip[IPV4_LENGTH] = { 0 }; + wifi_radio_get_address_info(self, ip, NULL, NULL); + return ipv4_bytes_to_uint32(ip); } mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - // return mp_const_none; - // } - // esp_netif_get_ip_info(self->netif, &self->ip_info); - // return common_hal_ipaddress_new_ipv4address(self->ip_info.ip.addr); - return mp_const_none; + check_for_enabled(self); + + uint8_t ip[IPV4_LENGTH]; + if (wifi_radio_get_address_info(self, ip, NULL, NULL)) { + return common_hal_ipaddress_new_ipv4address_from_bytes(ip); + } else { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); + } } mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->ap_netif)) { - // return mp_const_none; - // } - // esp_netif_get_ip_info(self->ap_netif, &self->ap_ip_info); - // return common_hal_ipaddress_new_ipv4address(self->ap_ip_info.ip.addr); return mp_const_none; } mp_obj_t common_hal_wifi_radio_get_ipv4_dns(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - // return mp_const_none; - // } - - // esp_netif_get_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &self->dns_info); - - // if (self->dns_info.ip.type != ESP_IPADDR_TYPE_V4) { - // return mp_const_none; - // } - // // dns_info is of type esp_netif_dns_info_t, which is just ever so slightly - // // different than esp_netif_ip_info_t used for - // // common_hal_wifi_radio_get_ipv4_address (includes both ipv4 and 6), - // // so some extra jumping is required to get to the actual address - // return common_hal_ipaddress_new_ipv4address(self->dns_info.ip.u_addr.ip4.addr); - return mp_const_none; + check_for_enabled(self); + + mp_obj_t dns[2]; + size_t count; + + wifi_radio_get_dns_config(self, dns, &count); + if (count == 0) { + return mp_const_none; + } + + // Return just the first DNS entry. + return dns[0]; } void common_hal_wifi_radio_set_ipv4_dns(wifi_radio_obj_t *self, mp_obj_t ipv4_dns_addr) { - // esp_netif_dns_info_t dns_addr; - // ipaddress_ipaddress_to_esp_idf_ip4(ipv4_dns_addr, &dns_addr.ip.u_addr.ip4); - // esp_netif_set_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &dns_addr); + check_for_enabled(self); + + uint8_t valid_params_arg = 0; + + uint8_t dns_bytes[IPV4_LENGTH]; + uint8_t zero_dns_bytes[IPV4_LENGTH] = {0}; + ipaddress_ipv4address_to_bytes(ipv4_dns_addr, dns_bytes); + + const uint8_t *params[3] = { &valid_params_arg, dns_bytes, zero_dns_bytes }; + size_t param_lengths[3] = { 1, IPV4_LENGTH, IPV4_LENGTH }; + uint8_t *responses[1]; + size_t response_lengths[1]; + + size_t num_responses = wifi_radio_send_command_get_response(self, SET_DNS_CONFIG, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0 || responses[0][0] != 1) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); + } } void common_hal_wifi_radio_start_dhcp_client(wifi_radio_obj_t *self, bool ipv4, bool ipv6) { - // if (ipv4) { - // esp_netif_dhcpc_start(self->netif); - // } else { - // esp_netif_dhcpc_stop(self->netif); - // } - // #if LWIP_IPV6_DHCP6 - // if (ipv6) { - // esp_netif_create_ip6_linklocal(self->netif); - // dhcp6_enable_stateless(esp_netif_get_netif_impl(self->netif)); - // } else { - // dhcp6_disable(esp_netif_get_netif_impl(self->netif)); - // } - // #else - // if (ipv6) { - // mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_ipv6); - // } - // #endif + if (ipv6) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_ipv6); + } } void common_hal_wifi_radio_stop_dhcp_client(wifi_radio_obj_t *self) { - // esp_netif_dhcpc_stop(self->netif); - // #if LWIP_IPV6_DHCP6 - // dhcp6_disable(esp_netif_get_netif_impl(self->netif)); - // #endif + // not controllable } void common_hal_wifi_radio_start_dhcp_server(wifi_radio_obj_t *self) { - // esp_netif_dhcps_start(self->ap_netif); + mp_raise_NotImplementedError(NULL); } void common_hal_wifi_radio_stop_dhcp_server(wifi_radio_obj_t *self) { - // esp_netif_dhcps_stop(self->ap_netif); + mp_raise_NotImplementedError(NULL); } void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway, mp_obj_t ipv4_dns) { -// common_hal_wifi_radio_stop_dhcp_client(self); // Must stop station DHCP to set a manual address + check_for_enabled(self); -// esp_netif_ip_info_t ip_info; -// ipaddress_ipaddress_to_esp_idf_ip4(ipv4, &ip_info.ip); -// ipaddress_ipaddress_to_esp_idf_ip4(netmask, &ip_info.netmask); -// ipaddress_ipaddress_to_esp_idf_ip4(gateway, &ip_info.gw); + uint8_t valid_params_arg = 0; -// esp_netif_set_ip_info(self->netif, &ip_info); + uint8_t ipv4_bytes[IPV4_LENGTH]; + ipaddress_ipv4address_to_bytes(ipv4, ipv4_bytes); -// if (ipv4_dns != MP_OBJ_NULL) { -// common_hal_wifi_radio_set_ipv4_dns(self, ipv4_dns); -// } -// } + uint8_t netmask_bytes[IPV4_LENGTH]; + ipaddress_ipv4address_to_bytes(netmask, netmask_bytes); -// void common_hal_wifi_radio_set_ipv4_address_ap(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway) { -// common_hal_wifi_radio_stop_dhcp_server(self); // Must stop access point DHCP to set a manual address + uint8_t gateway_bytes[IPV4_LENGTH]; + ipaddress_ipv4address_to_bytes(gateway, gateway_bytes); -// esp_netif_ip_info_t ip_info; -// ipaddress_ipaddress_to_esp_idf_ip4(ipv4, &ip_info.ip); -// ipaddress_ipaddress_to_esp_idf_ip4(netmask, &ip_info.netmask); -// ipaddress_ipaddress_to_esp_idf_ip4(gateway, &ip_info.gw); + // Notice order is different. + const uint8_t *params[4] = { &valid_params_arg, ipv4_bytes, gateway_bytes, netmask_bytes }; + size_t param_lengths[4] = { 1, IPV4_LENGTH, IPV4_LENGTH, IPV4_LENGTH }; -// esp_netif_set_ip_info(self->ap_netif, &ip_info); + uint8_t result = 0; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { sizeof(result) }; + + size_t num_responses = wifi_radio_send_command_get_response(self, SET_IP_CONFIG, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0 || result != 1) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); + } -// common_hal_wifi_radio_start_dhcp_server(self); // restart access point DHCP + if (ipv4_dns != mp_const_none) { + common_hal_wifi_radio_set_ipv4_dns(self, ipv4_dns); + } } -// static void ping_success_cb(esp_ping_handle_t hdl, void *args) { -// wifi_radio_obj_t *self = (wifi_radio_obj_t *)args; -// esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &self->ping_elapsed_time, sizeof(self->ping_elapsed_time)); -// } +void common_hal_wifi_radio_set_ipv4_address_ap(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway) { + mp_raise_NotImplementedError(NULL); +} mp_int_t common_hal_wifi_radio_ping(wifi_radio_obj_t *self, mp_obj_t ip_address, mp_float_t timeout) { - // esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG(); - // ipaddress_ipaddress_to_esp_idf(ip_address, &ping_config.target_addr); - // ping_config.count = 1; - - // // We must fetch ping information using the callback mechanism, because the session storage is freed when - // // the ping session is done, even before esp_ping_delete_session(). - // esp_ping_callbacks_t ping_callbacks = { - // .on_ping_success = ping_success_cb, - // .cb_args = (void *)self, - // }; - - // size_t timeout_ms = timeout * 1000; - - // // ESP-IDF creates a task to do the ping session. It shuts down when done, but only after a one second delay. - // // Calling common_hal_wifi_radio_ping() too fast will cause resource exhaustion. - // esp_ping_handle_t ping; - // if (esp_ping_new_session(&ping_config, &ping_callbacks, &ping) != ESP_OK) { - // // Wait for old task to go away and then try again. - // // Empirical testing shows we have to wait at least two seconds, despite the task - // // having a one-second timeout. - // common_hal_time_delay_ms(2000); - // // Return if interrupted now, to show the interruption as KeyboardInterrupt instead of the - // // IDF error. - // if (mp_hal_is_interrupted()) { - // return (uint32_t)(-1); - // } - // CHECK_ESP_RESULT(esp_ping_new_session(&ping_config, &ping_callbacks, &ping)); - // } - - // // Use all ones as a flag that the elapsed time was not set (ping failed or timed out). - // self->ping_elapsed_time = (uint32_t)(-1); - - // esp_ping_start(ping); - - // uint32_t start_time = common_hal_time_monotonic_ms(); - // while ((self->ping_elapsed_time == (uint32_t)(-1)) && - // (common_hal_time_monotonic_ms() - start_time < timeout_ms) && - // !mp_hal_is_interrupted()) { - // RUN_BACKGROUND_TASKS; - // } - // esp_ping_stop(ping); - // esp_ping_delete_session(ping); - - // return (mp_int_t)self->ping_elapsed_time; - return 0; + check_for_enabled(self); + + uint8_t ip_bytes[IPV4_LENGTH]; + ipaddress_ipv4address_to_bytes(ip_address, ip_bytes); + const uint8_t ttl = 250; + const uint8_t *params[2] = { ip_bytes, &ttl }; + size_t param_lengths[2] = { IPV4_LENGTH, 1 }; + + uint8_t ping_msecs_bytes[2] = { 0 }; + uint8_t *responses[1] = { ping_msecs_bytes }; + size_t response_lengths[1] = { sizeof(ping_msecs_bytes) }; + + size_t num_responses = wifi_radio_send_command_get_response(self, PING_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses > 0 && response_lengths[0] >= 2) { + // Returned as little-endian, unlike data length values, which are big-endian. + uint16_t ms = le_uint8_bytes_to_uint16(ping_msecs_bytes); + return ms; + } + + return -1; } void common_hal_wifi_radio_gc_collect(wifi_radio_obj_t *self) { - // Only bother to scan the actual object references. - gc_collect_ptr(self->current_scan); + gc_collect_ptr(self); } mp_obj_t common_hal_wifi_radio_get_dns(wifi_radio_obj_t *self) { - // if (!esp_netif_is_netif_up(self->netif)) { - // return mp_const_empty_tuple; - // } + check_for_enabled(self); - // esp_netif_get_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &self->dns_info); + mp_obj_t dns[2]; + size_t count; - // if (self->dns_info.ip.type == ESP_IPADDR_TYPE_V4 && self->dns_info.ip.u_addr.ip4.addr == INADDR_NONE) { - // return mp_const_empty_tuple; - // } + wifi_radio_get_dns_config(self, dns, &count); - // mp_obj_t args[] = { - // espaddr_to_str(&self->dns_info.ip), - // }; + // Convert the returned IPv4Address objects to strings. + // Return a tuple of those strings. - // return mp_obj_new_tuple(1, args); - return mp_const_none; + mp_obj_t str[2]; + for (size_t i = 0; i < count; i++) { + vstr_t vstr; + mp_print_t print; + vstr_init_print(&vstr, 16, &print); + mp_obj_print_helper(&print, dns[i], PRINT_STR); + str[i] = mp_obj_new_str_from_vstr(&vstr); + } + + return mp_obj_new_tuple(count, str); } +// Takes a sequence of strings of IP addresses. +// TODO: It could take host names, but that's not handled by raspberrypi right now. void common_hal_wifi_radio_set_dns(wifi_radio_obj_t *self, mp_obj_t dns_addrs_obj) { - // mp_int_t len = mp_obj_get_int(mp_obj_len(dns_addrs_obj)); - // mp_arg_validate_length_max(len, 1, MP_QSTR_dns); - // esp_netif_dns_info_t dns_info; - // if (len == 0) { - // // clear DNS server - // dns_info.ip.type = ESP_IPADDR_TYPE_V4; - // dns_info.ip.u_addr.ip4.addr = INADDR_NONE; - // } else { - // mp_obj_t dns_addr_obj = mp_obj_subscr(dns_addrs_obj, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_SENTINEL); - // struct sockaddr_storage addr_storage; - // socketpool_resolve_host_or_throw(AF_UNSPEC, SOCK_STREAM, mp_obj_str_get_str(dns_addr_obj), &addr_storage, 1); - // sockaddr_to_espaddr(&addr_storage, &dns_info.ip); - // } - // esp_netif_set_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &dns_info); + check_for_enabled(self); + + mp_int_t len = mp_obj_get_int(mp_obj_len(dns_addrs_obj)); + mp_arg_validate_length_max(len, 2, MP_QSTR_dns); + + uint8_t zero_byte = 0; + // Two 4-byte sequences. + uint8_t dns8[2][IPV4_LENGTH] = {0}; + + for (mp_int_t i = 0; i < len; i++) { + uint32_t dns32; + mp_obj_t dns_addr_obj = mp_obj_subscr(dns_addrs_obj, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_SENTINEL); + mp_arg_validate_type_string(dns_addr_obj, MP_QSTR_dns); + GET_STR_DATA_LEN(dns_addr_obj, str_data, str_len); + if (!ipaddress_parse_ipv4address((const char *)str_data, str_len, &dns32)) { + mp_raise_ValueError(MP_ERROR_TEXT("Not a valid IP string")); + } + ipv4_uint32_to_bytes(dns32, dns8[i]); + } + + const uint8_t *params[3] = { &zero_byte, dns8[0], dns8[1] }; + size_t param_lengths[3] = { 1, IPV4_LENGTH, IPV4_LENGTH }; + + uint8_t result = 0; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { sizeof(result) }; + + size_t num_responses = wifi_radio_send_command_get_response(self, SET_DNS_CONFIG, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0 || result != 1) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed")); + } } diff --git a/devices/airlift/common-hal/wifi/Radio.h b/devices/airlift/common-hal/wifi/Radio.h index ffb120d00c888..dee1d61249ccd 100644 --- a/devices/airlift/common-hal/wifi/Radio.h +++ b/devices/airlift/common-hal/wifi/Radio.h @@ -11,119 +11,203 @@ #include "shared-bindings/busio/SPI.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/wifi/ScannedNetworks.h" -#include "shared-bindings/wifi/Network.h" + +// Forward declarations due to mutually referenced typedef struct definitions. +typedef struct wifi_scannednetworks_obj_t wifi_scannednetworks_obj_t; // Connection modes typedef enum { - ADAFRUIT_ESP32SPI_TCP_MODE = 0, - ADAFRUIT_ESP32SPI_UDP_MODE = 1, - ADAFRUIT_ESP32SPI_TLS_MODE = 2, -} adafruit_esp32spi_conn_mode_t; + AIRLIFT_TCP_MODE = 0, + AIRLIFT_UDP_MODE = 1, + AIRLIFT_TLS_MODE = 2, +} airlift_conn_mode_t; -typedef struct { - mp_obj_base_t base; -} adafruit_esp32spi_esp_spicontrol_obj_t; -typedef struct { +typedef struct wifi_radio_obj_t { mp_obj_base_t base; wifi_scannednetworks_obj_t *current_scan; uint32_t ping_elapsed_time; - bool started; + bool enabled; bool ap_mode; bool sta_mode; uint8_t retries_left; uint8_t starting_retries; uint8_t last_disconnect_reason; + char *hostname; + wifi_power_management_t power_management; busio_spi_obj_t *spi; - digitalio_digitalinout_obj_t *cs; - digitalio_digitalinout_obj_t *ready; - digitalio_digitalinout_obj_t *reset; - digitalio_digitalinout_obj_t *gpio0; - uint8_t *sendbuf; - size_t sendbuf_len; - uint8_t buffer[10]; - uint8_t pbuf[1]; + digitalio_digitalinout_obj_t *cs_dio; + digitalio_digitalinout_obj_t *ready_dio; + digitalio_digitalinout_obj_t *reset_dio; + digitalio_digitalinout_obj_t *gpio0_dio; int8_t tls_socket; // -1 if no TLS socket allocated } wifi_radio_obj_t; extern void common_hal_wifi_radio_gc_collect(wifi_radio_obj_t *self); // ESP32 SPI protocol command constants -#define _START_CMD 0xE0 -#define _END_CMD 0xEE -#define _ERR_CMD 0xEF -#define _REPLY_FLAG 0x80 -#define _CMD_FLAG 0 +#define START_CMD 0xE0 +#define END_CMD 0xEE +#define ERR_CMD 0xEF +#define REPLY_FLAG 0x80 +#define CMD_FLAG 0 // Command opcodes -#define _SET_NET_CMD 0x10 -#define _SET_PASSPHRASE_CMD 0x11 -#define _SET_IP_CONFIG 0x14 -#define _SET_DNS_CONFIG 0x15 -#define _SET_HOSTNAME 0x16 -#define _SET_AP_NET_CMD 0x18 -#define _SET_AP_PASSPHRASE_CMD 0x19 -#define _SET_DEBUG_CMD 0x1A - -#define _GET_CONN_STATUS_CMD 0x20 -#define _GET_IPADDR_CMD 0x21 -#define _GET_MACADDR_CMD 0x22 -#define _GET_CURR_SSID_CMD 0x23 -#define _GET_CURR_BSSID_CMD 0x24 -#define _GET_CURR_RSSI_CMD 0x25 -#define _GET_CURR_ENCT_CMD 0x26 - -#define _SCAN_NETWORKS 0x27 -#define _START_SERVER_TCP_CMD 0x28 -#define _GET_STATE_TCP_CMD 0x29 -#define _DATA_SENT_TCP_CMD 0x2A -#define _AVAIL_DATA_TCP_CMD 0x2B -#define _GET_DATA_TCP_CMD 0x2C -#define _START_CLIENT_TCP_CMD 0x2D -#define _STOP_CLIENT_TCP_CMD 0x2E -#define _GET_CLIENT_STATE_TCP_CMD 0x2F -#define _DISCONNECT_CMD 0x30 -#define _GET_IDX_RSSI_CMD 0x32 -#define _GET_IDX_ENCT_CMD 0x33 -#define _REQ_HOST_BY_NAME_CMD 0x34 -#define _GET_HOST_BY_NAME_CMD 0x35 -#define _START_SCAN_NETWORKS 0x36 -#define _GET_FW_VERSION_CMD 0x37 -#define _SEND_UDP_DATA_CMD 0x39 -#define _GET_REMOTE_DATA_CMD 0x3A -#define _GET_TIME 0x3B -#define _GET_IDX_BSSID_CMD 0x3C -#define _GET_IDX_CHAN_CMD 0x3D -#define _PING_CMD 0x3E -#define _GET_SOCKET_CMD 0x3F - -#define _SET_CLI_CERT 0x40 -#define _SET_PK 0x41 -#define _SEND_DATA_TCP_CMD 0x44 -#define _GET_DATABUF_TCP_CMD 0x45 -#define _INSERT_DATABUF_TCP_CMD 0x46 -#define _SET_ENT_IDENT_CMD 0x4A -#define _SET_ENT_UNAME_CMD 0x4B -#define _SET_ENT_PASSWD_CMD 0x4C -#define _SET_ENT_ENABLE_CMD 0x4F - -#define _SET_PIN_MODE_CMD 0x50 -#define _SET_DIGITAL_WRITE_CMD 0x51 -#define _SET_ANALOG_WRITE_CMD 0x52 -#define _SET_DIGITAL_READ_CMD 0x53 -#define _SET_ANALOG_READ_CMD 0x54 - -#define DEFAULT_SENDBUF_SIZE 256 -#define SOCKET_CHUNK_SIZE 64 +#define SET_NET_CMD 0x10 +#define SET_PASSPHRASE_CMD 0x11 +#define SET_KEY_CMD 0x12 +// #define TEST_CMD 0x13 // not implemented +#define SET_IP_CONFIG 0x14 +#define SET_DNS_CONFIG 0x15 +#define SET_HOSTNAME 0x16 +#define SET_POWER_MODE_CMD 0x17 +#define SET_AP_NET_CMD 0x18 +#define SET_AP_PASSPHRASE_CMD 0x19 +#define SET_DEBUG_CMD 0x1A +#define GET_TEMPERATURE_CMD 0x1B +#define GET_DNS_CONFIG_CMD 0x1E +#define GET_REASON_CODE_CMD 0x1F + +#define GET_CONN_STATUS_CMD 0x20 +#define GET_IPADDR_CMD 0x21 +#define GET_MACADDR_CMD 0x22 +#define GET_CURR_SSID_CMD 0x23 +#define GET_CURR_BSSID_CMD 0x24 +#define GET_CURR_RSSI_CMD 0x25 +#define GET_CURR_ENCT_CMD 0x26 +#define SCAN_NETWORKS 0x27 +#define START_SERVER_TCP_CMD 0x28 +#define GET_STATE_TCP_CMD 0x29 +#define DATA_SENT_TCP_CMD 0x2A +#define AVAIL_DATA_TCP_CMD 0x2B +#define GET_DATA_TCP_CMD 0x2C +#define START_CLIENT_TCP_CMD 0x2D +#define STOP_CLIENT_TCP_CMD 0x2E +#define GET_CLIENT_STATE_TCP_CMD 0x2F + +#define DISCONNECT_CMD 0x30 +// GET_IDX_SSID_CMD 0x31 // not implemented +#define GET_IDX_RSSI_CMD 0x32 +#define GET_IDX_ENCT_CMD 0x33 +#define REQ_HOST_BY_NAME_CMD 0x34 +#define GET_HOST_BY_NAME_CMD 0x35 +#define START_SCAN_NETWORKS 0x36 +#define GET_FW_VERSION_CMD 0x37 +// GET_TEST_CMD 0x38 // not implemented +#define SEND_UDP_DATA_CMD 0x39 +#define GET_REMOTE_DATA_CMD 0x3A +#define GET_TIME 0x3B +#define GET_IDX_BSSID_CMD 0x3C +#define GET_IDX_CHAN_CMD 0x3D +#define PING_CMD 0x3E +#define GET_SOCKET_CMD 0x3F + +#define SET_CLI_CERT 0x40 +// Adafruit +#define SET_PK 0x41 +#define SEND_DATA_TCP_CMD 0x44 +#define GET_DATABUF_TCP_CMD 0x45 +#define INSERT_DATABUF_TCP_CMD 0x46 +// Adafruit +#define SET_ENT_IDENT_CMD 0x4A +#define SET_ENT_UNAME_CMD 0x4B +#define SET_ENT_PASSWD_CMD 0x4C +#define SET_ENT_ENABLE_CMD 0x4F + +#define SET_PIN_MODE_CMD 0x50 +#define SET_DIGITAL_WRITE_CMD 0x51 +#define SET_ANALOG_WRITE_CMD 0x52 +#define SET_DIGITAL_READ_CMD 0x53 +#define SET_ANALOG_READ_CMD 0x54 + +// Not implemented in Adafruit NINA-FW +#define WRITE_FILE 0x60 +#define READ_FILE 0x61 +#define DELETE_FILE 0x62 +#define EXISTS_FILE 0x63 +#define DOWNLOAD_FILE 0x64 +#define APPLY_OTA_COMMAND 0x65 +#define RENAME_FILE 0x66 +#define DOWNLOAD_OTA 0x67 + +// Low-level socket operations. +#define SOCKET_SOCKET_CMD 0x70 +#define SOCKET_CLOSE_CMD 0x71 +#define SOCKET_ERRNO_CMD 0x72 +#define SOCKET_BIND_CMD 0x73 +#define SOCKET_LISTEN_CMD 0x74 +#define SOCKET_ACCEPT_CMD 0x75 +#define SOCKET_CONNECT_CMD 0x76 +#define SOCKET_SEND_CMD 0x77 +#define SOCKET_RECV_CMD 0x78 +#define SOCKET_SENDTO_CMD 0x79 +#define SOCKET_RECVFROM_CMD 0x7A +#define SOCKET_IOCTL_CMD 0x7B +#define SOCKET_POLL_CMD 0x7C +#define SOCKET_SETSOCKOPT_CMD 0x7D +#define SOCKET_GETSOCKOPT_CMD 0x7E +#define SOCKET_GETPEERNAME_CMD 0x7F + +typedef enum { + WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library + WL_STOPPED = 254, + WL_IDLE_STATUS = 0, + WL_NO_SSID_AVAIL = 1, + WL_SCAN_COMPLETED = 2, + WL_CONNECTED = 3, + WL_CONNECT_FAILED = 4, + WL_CONNECTION_LOST = 5, + WL_DISCONNECTED = 6 +} wl_status_t; + +#define AIRLIFT_DEFAULT_TIMEOUT_MS (1000) + +// Maximum is 10 in NINA-FW. +#define AIRLIFT_MAX_NETWORKS (10) +// SSID does not include a terminating null byte, because the length is passed. +#define MAX_SSID_LENGTH (32) +// NINA-FW allows this length internally. Does not include trailing null. +#define MAX_HOSTNAME_LENGTH (255) +#define IPV4_LENGTH (4) +#define AIRLIFT_MAX_FIRMWARE_VERSION_LENGTH (6) + +// Param and response lengths can be 8-bit, or 16-bit big-endian. +// Most are 8-bit, but potentially large data buffers have 16-bit lengths. +typedef enum { + LENGTHS_8 = 8, + LENGTHS_16 = 16, +} lengths_size_t; // Communication with AirLift. -void wifi_radio_send_command(wifi_radio_obj_t *self, uint8_t cmd, - const uint8_t **params, const size_t *param_lens, size_t num_params); -uint8_t wifi_radio_read_byte(wifi_radio_obj_t *self); -void wifi_radio_wait_spi_char(wifi_radio_obj_t *self, uint8_t desired); -void wifi_radio_check_data(wifi_radio_obj_t *self, uint8_t desired); +bool wifi_radio_send_command(wifi_radio_obj_t *self, uint8_t cmd, + const uint8_t **params, const size_t *param_lengths, lengths_size_t param_lengths_size, size_t num_params); size_t wifi_radio_wait_response_cmd(wifi_radio_obj_t *self, uint8_t cmd, - uint8_t **responses, size_t *response_lens, size_t max_responses); + uint8_t **responses, size_t *response_lengths, lengths_size_t response_lengths_size, size_t max_responses, + uint32_t response_timeout_ms); size_t wifi_radio_send_command_get_response(wifi_radio_obj_t *self, uint8_t cmd, - const uint8_t **params, const size_t *param_lens, size_t num_params, - uint8_t **responses, size_t *response_lens, size_t max_responses); + const uint8_t **params, const size_t *param_lengths, lengths_size_t param_lengths_size_t, size_t num_params, + uint8_t **responses, size_t *response_lengths, lengths_size_t response_lengths_size, size_t max_responses, + uint32_t response_timeout_ms); +wl_status_t wifi_radio_get_connection_status(wifi_radio_obj_t *self); + +inline void be_uint16_to_uint8_bytes(uint16_t value, uint8_t *bytes) { + bytes[0] = value >> 8; + bytes[1] = value & 0xff; +} + +inline uint16_t be_uint8_bytes_to_uint16(uint8_t *bytes) { + return bytes[1] | (bytes[0] << 8); +} + +inline void le_uint16_to_uint8_bytes(uint16_t value, uint8_t *bytes) { + bytes[0] = value & 0xff; + bytes[1] = value >> 8; +} + +inline uint16_t le_uint8_bytes_to_uint16(uint8_t *bytes) { + return bytes[0] | (bytes[1] << 8); +} + +inline uint32_t le_uint8_bytes_to_uint32(uint8_t *bytes) { + return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); +} diff --git a/devices/airlift/common-hal/wifi/ScannedNetworks.c b/devices/airlift/common-hal/wifi/ScannedNetworks.c index 96c637fb6dee1..cdf86ce32304c 100644 --- a/devices/airlift/common-hal/wifi/ScannedNetworks.c +++ b/devices/airlift/common-hal/wifi/ScannedNetworks.c @@ -1,8 +1,6 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries -// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec -// SPDX-FileCopyrightText: Copyright (c) 2017 Glenn Ruben Bakke +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries // // SPDX-License-Identifier: MIT @@ -13,147 +11,119 @@ #include "py/objstr.h" #include "py/runtime.h" #include "shared-bindings/wifi/__init__.h" +#include "shared-bindings/wifi/AuthMode.h" #include "shared-bindings/wifi/Network.h" #include "shared-bindings/wifi/Radio.h" #include "shared-bindings/wifi/ScannedNetworks.h" -// static void wifi_scannednetworks_done(wifi_scannednetworks_obj_t *self) { -// self->done = true; -// if (self->results != NULL) { -// // Check to see if the heap is still active. If not, it'll be freed automatically. -// if (gc_alloc_possible()) { -// m_free(self->results); -// } -// self->results = NULL; -// } -// } - -// static bool wifi_scannednetworks_wait_for_scan(wifi_scannednetworks_obj_t *self) { -// EventBits_t bits = xEventGroupWaitBits(self->radio_event_group, -// WIFI_SCAN_DONE_BIT, -// pdTRUE, -// pdTRUE, -// 0); -// while ((bits & WIFI_SCAN_DONE_BIT) == 0 && !mp_hal_is_interrupted()) { -// RUN_BACKGROUND_TASKS; -// bits = xEventGroupWaitBits(self->radio_event_group, -// WIFI_SCAN_DONE_BIT, -// pdTRUE, -// pdTRUE, -// 0); -// } -// return !mp_hal_is_interrupted(); -// } +mp_obj_t wifi_scannednetworks_do_scan(wifi_radio_obj_t *self) { + uint8_t ssids[AIRLIFT_MAX_NETWORKS][MAX_SSID_LENGTH]; + uint8_t *network_responses[AIRLIFT_MAX_NETWORKS]; + size_t network_response_lengths[AIRLIFT_MAX_NETWORKS]; + // Set the maximum size for each SSID response. They may be shorter. + for (size_t i = 0; i < AIRLIFT_MAX_NETWORKS; i++) { + network_responses[i] = ssids[i]; + network_response_lengths[i] = MAX_SSID_LENGTH; + } -mp_obj_t common_hal_wifi_scannednetworks_next(wifi_scannednetworks_obj_t *self) { - // if (self->done) { - // return mp_const_none; - // } - // // If we are scanning, wait and then load them. - // if (self->channel_scan_in_progress) { - // // We may have to scan more than one channel to get a result. - // while (!self->done) { - // if (!wifi_scannednetworks_wait_for_scan(self)) { - // wifi_scannednetworks_done(self); - // return mp_const_none; - // } - - // esp_wifi_scan_get_ap_num(&self->total_results); - // self->channel_scan_in_progress = false; - // if (self->total_results > 0) { - // break; - // } - // // If total_results is zero then we need to start a scan and wait again. - // wifi_scannednetworks_scan_next_channel(self); - // } - // // We not have found any more results so we're done. - // if (self->done) { - // return mp_const_none; - // } - // // If we need more space than we have, realloc. - // if (self->total_results > self->max_results) { - // wifi_ap_record_t *results = m_renew_maybe(wifi_ap_record_t, - // self->results, - // self->max_results, - // self->total_results, - // true /* allow move */); - // if (results != NULL) { - // self->results = results; - // self->max_results = self->total_results; - // } else { - // if (self->max_results == 0) { - // // No room for any results should error. - // mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Failed to allocate wifi scan memory")); - // } - // // Unable to allocate more results, so load what we can. - // self->total_results = self->max_results; - // } - // } - // esp_wifi_scan_get_ap_records(&self->total_results, self->results); - // self->channel_scan_in_progress = false; - // } - - // wifi_network_obj_t *entry = mp_obj_malloc(wifi_network_obj_t, &wifi_network_type); - // memcpy(&entry->record, &self->results[self->current_result], sizeof(wifi_ap_record_t)); - // self->current_result++; - - // // If we're returning our last network then start the next channel scan or - // // be done. - // if (self->current_result >= self->total_results) { - // wifi_scannednetworks_scan_next_channel(self); - // self->total_results = 0; - // self->current_result = 0; - // } - - // return MP_OBJ_FROM_PTR(entry); - return mp_const_none; + size_t num_networks = wifi_radio_send_command_get_response(self, SCAN_NETWORKS, + NULL, NULL, LENGTHS_8, 0, + network_responses, network_response_lengths, LENGTHS_8, AIRLIFT_MAX_NETWORKS, +// AIRLIFT_DEFAULT_TIMEOUT_MS); + 10000); + + // Now fetch each network's details and store them in Network objects. + uint8_t network_idx; + size_t param_lengths[1] = { 1 }; + const uint8_t *params[1] = { &network_idx }; + + mp_obj_t networks[AIRLIFT_MAX_NETWORKS]; + + for (network_idx = 0; network_idx < num_networks; network_idx++) { + wifi_network_obj_t *network = mp_obj_malloc(wifi_network_obj_t, &wifi_network_type); + networks[network_idx] = network; + + network->ssid = mp_obj_new_str( + (const char *)network_responses[network_idx], + network_response_lengths[network_idx]); + + + { + uint8_t bssid[MAC_ADDRESS_LENGTH] = { 0 }; + uint8_t *responses[1] = { bssid }; + size_t response_lengths[1] = { sizeof(bssid) }; + + network->bssid = mp_const_none; + if (wifi_radio_send_command_get_response(self, GET_IDX_BSSID_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS) > 0) { + network->bssid = mp_obj_new_bytes(bssid, MAC_ADDRESS_LENGTH); + } + } + + { + int32_t rssi = 0; + uint8_t *responses[1] = { (uint8_t *)&rssi }; + size_t response_lengths[1] = { sizeof(rssi) }; + + network->rssi = mp_const_none; + if (wifi_radio_send_command_get_response(self, GET_IDX_RSSI_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS) > 0) { + network->rssi = mp_obj_new_int(rssi); + } + } + + { + uint8_t channel = 0; + uint8_t *responses[1] = { &channel }; + size_t response_lengths[1] = { sizeof(channel) }; + + network->channel = mp_const_none; + if (wifi_radio_send_command_get_response(self, GET_IDX_CHAN_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS) > 0) { + network->channel = mp_obj_new_int(channel); + } + } + + { + uint8_t esp_authmode = 0; + uint8_t *responses[1] = { &esp_authmode }; + size_t response_lengths[1] = { sizeof(esp_authmode) }; + + if (wifi_radio_send_command_get_response(self, GET_IDX_ENCT_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS) > 0) { + + // Convert AirLift authmode to tuple of wifi.AuthMode objects. + network->authmode = esp_authmode_to_wifi_authmode_tuple(esp_authmode); + } + + // country code not supported on AirLift. + network->country = MP_OBJ_NEW_QSTR(MP_QSTR_); + } + } + + // Create a ScannedNetworks obj and populate it with the results of the scan. + wifi_scannednetworks_obj_t *scanned_networks = + mp_obj_malloc(wifi_scannednetworks_obj_t, &wifi_scannednetworks_type); + scanned_networks->networks = mp_obj_new_tuple(num_networks, networks); + + return MP_OBJ_FROM_PTR(scanned_networks); } -// We don't do a linear scan so that we look at a variety of spectrum up front. -// static uint8_t scan_pattern[] = {6, 1, 11, 3, 9, 13, 2, 4, 8, 12, 5, 7, 10, 14, 0}; - -void wifi_scannednetworks_scan_next_channel(wifi_scannednetworks_obj_t *self) { - // // There is no channel 0, so use that as a flag to indicate we've run out of channels to scan. - // uint8_t next_channel = 0; - // while (self->current_channel_index < sizeof(scan_pattern)) { - // next_channel = scan_pattern[self->current_channel_index]; - // self->current_channel_index++; - // // Scan only channels that are in the specified range. - // if (self->start_channel <= next_channel && next_channel <= self->end_channel) { - // break; - // } - // } - // wifi_scan_config_t config = { 0 }; - // config.channel = next_channel; - // if (next_channel == 0) { - // wifi_scannednetworks_done(self); - // } else { - // esp_err_t result = esp_wifi_scan_start(&config, false); - // if (result != ESP_OK) { - // wifi_scannednetworks_done(self); - // } else { - // self->channel_scan_in_progress = true; - // } - // } +mp_obj_t common_hal_wifi_scannednetworks_next(wifi_scannednetworks_obj_t *self) { + if (self->next_network_index >= self->networks->len) { + return mp_const_none; + } + + return self->networks->items[self->next_network_index++]; } void wifi_scannednetworks_deinit(wifi_scannednetworks_obj_t *self) { - // // if a scan is active, make sure and clean up the idf's buffer of results. - // if (self->channel_scan_in_progress) { - // esp_wifi_scan_stop(); - // if (wifi_scannednetworks_wait_for_scan(self)) { - // // Discard the scanned records, one at a time, to avoid memory leaks. - // uint16_t number; - // do { - // number = 1; - // wifi_ap_record_t record; - // esp_wifi_scan_get_ap_records(&number, &record); - // } while (number > 0); - // // TODO: available in ESP-IDF v5.0; do instead of the above. - // // Discard scan results. - // // esp_wifi_clear_ap_list(); - // self->channel_scan_in_progress = false; - // } - // } - // wifi_scannednetworks_done(self); } diff --git a/devices/airlift/common-hal/wifi/ScannedNetworks.h b/devices/airlift/common-hal/wifi/ScannedNetworks.h index 7ea63c1e6263a..81ea5f03b3079 100644 --- a/devices/airlift/common-hal/wifi/ScannedNetworks.h +++ b/devices/airlift/common-hal/wifi/ScannedNetworks.h @@ -10,26 +10,19 @@ #include #include "py/obj.h" +#include "py/objtuple.h" +#include "shared-bindings/wifi/Radio.h" -typedef struct { +typedef struct wifi_scannednetworks_obj_t { mp_obj_base_t base; - uint8_t current_channel_index; -// EventGroupHandle_t radio_event_group; - - // Results from the last channel scan -// wifi_ap_record_t *results; - uint16_t current_result; - uint16_t total_results; - uint16_t max_results; + mp_obj_tuple_t *networks; + size_t next_network_index; +} wifi_scannednetworks_obj_t; - // Limits on what channels to scan. - uint8_t start_channel; - uint8_t end_channel; // Inclusive - bool done; - bool channel_scan_in_progress; -} wifi_scannednetworks_obj_t; +// Forward declaration due to mutually referenced typedef struct definitions. +typedef struct wifi_radio_obj_t wifi_radio_obj_t; -void wifi_scannednetworks_scan_next_channel(wifi_scannednetworks_obj_t *self); +mp_obj_t wifi_scannednetworks_do_scan(wifi_radio_obj_t *self); void wifi_scannednetworks_deinit(wifi_scannednetworks_obj_t *self); diff --git a/devices/airlift/common-hal/wifi/__init__.c b/devices/airlift/common-hal/wifi/__init__.c index 7804a857ab62f..011c78b5b8b0b 100644 --- a/devices/airlift/common-hal/wifi/__init__.c +++ b/devices/airlift/common-hal/wifi/__init__.c @@ -4,208 +4,31 @@ // // SPDX-License-Identifier: MIT -#include "common-hal/wifi/__init__.h" -#include "shared-bindings/wifi/__init__.h" - -#include "shared-bindings/ipaddress/IPv4Address.h" -#include "shared-bindings/wifi/Monitor.h" -#include "shared-bindings/wifi/Radio.h" -#include "common-hal/socketpool/__init__.h" - -#include "py/gc.h" -#include "py/mpstate.h" #include "py/runtime.h" -wifi_radio_obj_t common_hal_wifi_radio_obj; - -#include "supervisor/port.h" -#include "supervisor/workflow.h" - -#if CIRCUITPY_STATUS_BAR -#include "supervisor/shared/status_bar.h" -#endif - -#define MAC_ADDRESS_LENGTH 6 - -// static void schedule_background_on_cp_core(void *arg) { -// #if CIRCUITPY_STATUS_BAR -// supervisor_status_bar_request_update(false); -// #endif - -// // CircuitPython's VM is run in a separate FreeRTOS task from wifi callbacks. So, we have to -// // notify the main task every time in case it's waiting for us. -// port_wake_main_task(); -// } - -// static void event_handler(void *arg, esp_event_base_t event_base, -// int32_t event_id, void *event_data) { -// // This runs on the PRO CORE! It cannot share CP interrupt enable/disable -// // directly. -// wifi_radio_obj_t *radio = arg; -// if (event_base == WIFI_EVENT) { -// switch (event_id) { -// case WIFI_EVENT_SCAN_DONE: -// ESP_LOGW(TAG, "scan"); -// xEventGroupSetBits(radio->event_group_handle, WIFI_SCAN_DONE_BIT); -// break; -// case WIFI_EVENT_AP_START: -// ESP_LOGW(TAG, "ap start"); -// break; -// case WIFI_EVENT_AP_STOP: -// ESP_LOGW(TAG, "ap stop"); -// break; -// case WIFI_EVENT_AP_STACONNECTED: -// break; -// case WIFI_EVENT_AP_STADISCONNECTED: -// break; -// case WIFI_EVENT_STA_START: -// ESP_LOGW(TAG, "sta start"); -// break; -// case WIFI_EVENT_STA_STOP: -// ESP_LOGW(TAG, "sta stop"); -// break; -// case WIFI_EVENT_STA_CONNECTED: -// ESP_LOGW(TAG, "connected"); -// break; -// case WIFI_EVENT_STA_DISCONNECTED: { -// ESP_LOGW(TAG, "disconnected"); -// wifi_event_sta_disconnected_t *d = (wifi_event_sta_disconnected_t *)event_data; -// uint8_t reason = d->reason; -// ESP_LOGW(TAG, "reason %d 0x%02x", reason, reason); -// if (radio->retries_left > 0 && -// reason != WIFI_REASON_AUTH_FAIL && -// reason != WIFI_REASON_NO_AP_FOUND && -// reason != WIFI_REASON_ASSOC_LEAVE) { -// radio->retries_left--; -// ESP_LOGI(TAG, "Retrying connect. %d retries remaining", radio->retries_left); -// esp_wifi_connect(); -// return; -// } - -// radio->last_disconnect_reason = reason; -// xEventGroupSetBits(radio->event_group_handle, WIFI_DISCONNECTED_BIT); -// break; -// } - -// // Cases to handle later. -// // case WIFI_EVENT_STA_AUTHMODE_CHANGE: -// default: { -// ESP_LOGW(TAG, "event %ld 0x%02ld", event_id, event_id); -// break; -// } -// } -// } - -// if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { -// ESP_LOGW(TAG, "got ip"); -// radio->retries_left = radio->starting_retries; -// xEventGroupSetBits(radio->event_group_handle, WIFI_CONNECTED_BIT); -// } -// // Use IPC to ensure we run schedule background on the same core as CircuitPython. -// #if defined(CONFIG_FREERTOS_UNICORE) && CONFIG_FREERTOS_UNICORE -// schedule_background_on_cp_core(NULL); -// #else -// // This only blocks until the start of the function. That's ok since the PRO -// // core shouldn't care what we do. -// esp_ipc_call(CONFIG_ESP_MAIN_TASK_AFFINITY, schedule_background_on_cp_core, NULL); -// #endif -// } +#include "shared-bindings/wifi/__init__.h" +#include "shared-bindings/wifi/Radio.h" // static bool wifi_inited; // static bool wifi_ever_inited; // static bool wifi_user_initiated; +wifi_radio_obj_t common_hal_wifi_radio_obj; + void common_hal_wifi_init(bool user_initiated) { - // wifi_radio_obj_t *self = &common_hal_wifi_radio_obj; + wifi_radio_obj_t *radio = &common_hal_wifi_radio_obj; // if (wifi_inited) { // if (user_initiated && !wifi_user_initiated) { - // common_hal_wifi_radio_set_enabled(self, true); + // common_hal_wifi_radio_set_enabled(radio, true); // } // return; // } // wifi_inited = true; // wifi_user_initiated = user_initiated; - // common_hal_wifi_radio_obj.base.type = &wifi_radio_type; - - // if (!wifi_ever_inited) { - // ESP_ERROR_CHECK(esp_event_loop_create_default()); - // ESP_ERROR_CHECK(esp_netif_init()); - // wifi_ever_inited = true; - // } - - // self->netif = esp_netif_create_default_wifi_sta(); - // self->ap_netif = esp_netif_create_default_wifi_ap(); - // self->started = false; - // // Even though we just called esp_netif_create_default_wifi_sta, - // // station mode isn't actually ready for use until esp_wifi_set_mode() - // // is called and the configuration is loaded via esp_wifi_set_config(). - // // Set both convenience flags to false so it's not forgotten. - // self->sta_mode = 0; - // self->ap_mode = 0; - - // self->event_group_handle = xEventGroupCreateStatic(&self->event_group); - // ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, - // ESP_EVENT_ANY_ID, - // &event_handler, - // self, - // &self->handler_instance_all_wifi)); - // ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, - // IP_EVENT_STA_GOT_IP, - // &event_handler, - // self, - // &self->handler_instance_got_ip)); - - // wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); - // esp_err_t result = esp_wifi_init(&config); - // #ifdef CONFIG_ESP32_WIFI_NVS_ENABLED - // // Generally we don't use this because we store ssid and passwords ourselves in the filesystem. - // esp_err_t err = nvs_flash_init(); - // if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { - // // NVS partition was truncated and needs to be erased - // // Retry nvs_flash_init - // ESP_ERROR_CHECK(nvs_flash_erase()); - // err = nvs_flash_init(); - // } - // ESP_ERROR_CHECK(err); - // ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH)); - // #else - // ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); - // #endif - // if (result == ESP_ERR_NO_MEM) { - // if (gc_alloc_possible()) { - // mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Failed to allocate Wifi memory")); - // } - // ESP_LOGE(TAG, "Failed to allocate Wifi memory"); - // } else if (result != ESP_OK) { - // if (gc_alloc_possible()) { - // raise_esp_error(result); - // } - // ESP_LOGE(TAG, "WiFi error code: %x", result); - // return; - // } - // // Set the default lwip_local_hostname capped at 32 characters. We trim off - // // the start of the board name (likely manufacturer) because the end is - // // often more unique to the board. - // size_t board_len = MIN(32 - ((MAC_ADDRESS_LENGTH * 2) + 6), strlen(CIRCUITPY_BOARD_ID)); - // size_t board_trim = strlen(CIRCUITPY_BOARD_ID) - board_len; - // // Avoid double _ in the hostname. - // if (CIRCUITPY_BOARD_ID[board_trim] == '_') { - // board_trim++; - // } - - // char cpy_default_hostname[board_len + (MAC_ADDRESS_LENGTH * 2) + 6]; - // uint8_t mac[MAC_ADDRESS_LENGTH]; - // esp_wifi_get_mac(ESP_IF_WIFI_STA, mac); - // snprintf(cpy_default_hostname, sizeof(cpy_default_hostname), "cpy-%s-%02x%02x%02x%02x%02x%02x", CIRCUITPY_BOARD_ID + board_trim, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - - // const char *default_lwip_local_hostname = cpy_default_hostname; - // ESP_ERROR_CHECK(esp_netif_set_hostname(self->netif, default_lwip_local_hostname)); - // // set station mode to avoid the default SoftAP - // common_hal_wifi_radio_start_station(self); - // // start wifi - // common_hal_wifi_radio_set_enabled(self, true); + radio->base.type = &wifi_radio_type; + common_hal_wifi_radio_mark_deinit(radio); } void wifi_user_reset(void) { @@ -219,144 +42,8 @@ void wifi_reset(void) { // if (!wifi_inited) { // return; // } - // common_hal_wifi_monitor_deinit(MP_STATE_VM(wifi_monitor_singleton)); - // wifi_radio_obj_t *radio = &common_hal_wifi_radio_obj; - // common_hal_wifi_radio_set_enabled(radio, false); - // #ifndef CONFIG_IDF_TARGET_ESP32 - // ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, - // ESP_EVENT_ANY_ID, - // radio->handler_instance_all_wifi)); - // ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, - // IP_EVENT_STA_GOT_IP, - // radio->handler_instance_got_ip)); - // ESP_ERROR_CHECK(esp_wifi_deinit()); - // esp_netif_destroy(radio->netif); - // radio->netif = NULL; - // esp_netif_destroy(radio->ap_netif); - // radio->ap_netif = NULL; - // wifi_inited = false; - // #endif - // supervisor_workflow_request_background(); } -// void ipaddress_ipaddress_to_esp_idf(mp_obj_t ip_address, ip_addr_t *esp_ip_address) { -// if (mp_obj_is_type(ip_address, &ipaddress_ipv4address_type)) { -// ipaddress_ipaddress_to_esp_idf_ip4(ip_address, (esp_ip4_addr_t *)esp_ip_address); -// #if LWIP_IPV6 -// esp_ip_address->type = IPADDR_TYPE_V4; -// #endif -// } else { -// struct sockaddr_storage addr_storage; -// socketpool_resolve_host_or_throw(AF_UNSPEC, SOCK_STREAM, mp_obj_str_get_str(ip_address), &addr_storage, 1); -// sockaddr_to_espaddr(&addr_storage, (esp_ip_addr_t *)esp_ip_address); -// } -// } - -// void ipaddress_ipaddress_to_esp_idf_ip4(mp_obj_t ip_address, esp_ip4_addr_t *esp_ip_address) { -// if (!mp_obj_is_type(ip_address, &ipaddress_ipv4address_type)) { -// mp_raise_ValueError(MP_ERROR_TEXT("Only IPv4 addresses supported")); -// } -// mp_obj_t packed = common_hal_ipaddress_ipv4address_get_packed(ip_address); -// size_t len; -// const char *bytes = mp_obj_str_get_data(packed, &len); -// esp_netif_set_ip4_addr(esp_ip_address, bytes[0], bytes[1], bytes[2], bytes[3]); -// } - void common_hal_wifi_gc_collect(void) { common_hal_wifi_radio_gc_collect(&common_hal_wifi_radio_obj); } - -// static mp_obj_t espaddrx_to_str(const void *espaddr, uint8_t esptype) { -// // char buf[IPADDR_STRLEN_MAX]; -// // inet_ntop(esptype == ESP_IPADDR_TYPE_V6 ? AF_INET6 : AF_INET, espaddr, buf, sizeof(buf)); -// // return mp_obj_new_str(buf, strlen(buf)); -// return mp_const_none; -// } - -// mp_obj_t espaddr_to_str(const esp_ip_addr_t *espaddr) { -// // return espaddrx_to_str(espaddr, espaddr->type); -// return mp_const_none; -// } - -// mp_obj_t espaddr4_to_str(const esp_ip4_addr_t *espaddr) { -// // return espaddrx_to_str(espaddr, ESP_IPADDR_TYPE_V4); -// return mp_const_none; -// } - -// mp_obj_t espaddr6_to_str(const esp_ip6_addr_t *espaddr) { -// // return espaddrx_to_str(espaddr, ESP_IPADDR_TYPE_V6); -// return mp_const_none; -// } - -// mp_obj_t sockaddr_to_str(const struct sockaddr_storage *sockaddr) { -// char buf[IPADDR_STRLEN_MAX]; -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// if (sockaddr->ss_family == AF_INET6) { -// const struct sockaddr_in6 *addr6 = (const void *)sockaddr; -// inet_ntop(AF_INET6, &addr6->sin6_addr, buf, sizeof(buf)); -// } else -// #endif -// { -// const struct sockaddr_in *addr = (const void *)sockaddr; -// inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf)); -// } -// return mp_obj_new_str(buf, strlen(buf)); -// } - -// mp_obj_t sockaddr_to_tuple(const struct sockaddr_storage *sockaddr) { -// mp_obj_t args[4] = { -// sockaddr_to_str(sockaddr), -// }; -// int n = 2; -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// if (sockaddr->ss_family == AF_INET6) { -// const struct sockaddr_in6 *addr6 = (const void *)sockaddr; -// args[1] = MP_OBJ_NEW_SMALL_INT(htons(addr6->sin6_port)); -// args[2] = MP_OBJ_NEW_SMALL_INT(addr6->sin6_flowinfo); -// args[3] = MP_OBJ_NEW_SMALL_INT(addr6->sin6_scope_id); -// n = 4; -// } else -// #endif -// { -// const struct sockaddr_in *addr = (const void *)sockaddr; -// args[1] = MP_OBJ_NEW_SMALL_INT(htons(addr->sin_port)); -// } -// return mp_obj_new_tuple(n, args); -// } - -// void sockaddr_to_espaddr(const struct sockaddr_storage *sockaddr, esp_ip_addr_t *espaddr) { -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// MP_STATIC_ASSERT(IPADDR_TYPE_V4 == ESP_IPADDR_TYPE_V4); -// MP_STATIC_ASSERT(IPADDR_TYPE_V6 == ESP_IPADDR_TYPE_V6); -// MP_STATIC_ASSERT(sizeof(ip_addr_t) == sizeof(esp_ip_addr_t)); -// MP_STATIC_ASSERT(offsetof(ip_addr_t, u_addr) == offsetof(esp_ip_addr_t, u_addr)); -// MP_STATIC_ASSERT(offsetof(ip_addr_t, type) == offsetof(esp_ip_addr_t, type)); -// if (sockaddr->ss_family == AF_INET6) { -// const struct sockaddr_in6 *addr6 = (const void *)sockaddr; -// MP_STATIC_ASSERT(sizeof(espaddr->u_addr.ip6.addr) == sizeof(addr6->sin6_addr)); -// memcpy(&espaddr->u_addr.ip6.addr, &addr6->sin6_addr, sizeof(espaddr->u_addr.ip6.addr)); -// espaddr->u_addr.ip6.zone = addr6->sin6_scope_id; -// espaddr->type = ESP_IPADDR_TYPE_V6; -// } else -// #endif -// { -// const struct sockaddr_in *addr = (const void *)sockaddr; -// MP_STATIC_ASSERT(sizeof(espaddr->u_addr.ip4.addr) == sizeof(addr->sin_addr)); -// memcpy(&espaddr->u_addr.ip4.addr, &addr->sin_addr, sizeof(espaddr->u_addr.ip4.addr)); -// espaddr->type = ESP_IPADDR_TYPE_V4; -// } -// } - -// void espaddr_to_sockaddr(const esp_ip_addr_t *espaddr, struct sockaddr_storage *sockaddr, int port) { -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// if (espaddr->type == ESP_IPADDR_TYPE_V6) { -// struct sockaddr_in6 *addr6 = (void *)sockaddr; -// memcpy(&addr6->sin6_addr, &espaddr->u_addr.ip6.addr, sizeof(espaddr->u_addr.ip6.addr)); -// addr6->sin6_scope_id = espaddr->u_addr.ip6.zone; -// } else -// #endif -// { -// struct sockaddr_in *addr = (void *)sockaddr; -// memcpy(&addr->sin_addr, &espaddr->u_addr.ip4.addr, sizeof(espaddr->u_addr.ip4.addr)); -// } -// } diff --git a/devices/airlift/common-hal/wifi/__init__.h b/devices/airlift/common-hal/wifi/__init__.h index 110fcae04c05f..2cfdcb54b3197 100644 --- a/devices/airlift/common-hal/wifi/__init__.h +++ b/devices/airlift/common-hal/wifi/__init__.h @@ -11,14 +11,3 @@ struct sockaddr_storage; void wifi_reset(void); - -// void ipaddress_ipaddress_to_esp_idf(mp_obj_t ip_address, ip_addr_t *esp_ip_address); -// void ipaddress_ipaddress_to_esp_idf_ip4(mp_obj_t ip_address, esp_ip4_addr_t *esp_ip_address); - -// mp_obj_t sockaddr_to_str(const struct sockaddr_storage *addr); -// mp_obj_t sockaddr_to_tuple(const struct sockaddr_storage *addr); -// mp_obj_t espaddr_to_str(const esp_ip_addr_t *espaddr); -// mp_obj_t espaddr4_to_str(const esp_ip4_addr_t *espaddr); -// mp_obj_t espaddr6_to_str(const esp_ip6_addr_t *espaddr); -// void sockaddr_to_espaddr(const struct sockaddr_storage *sockaddr, esp_ip_addr_t *espaddr); -// void espaddr_to_sockaddr(const esp_ip_addr_t *espaddr, struct sockaddr_storage *sockaddr, int port); diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 35fc94513a547..0e754e5fc6015 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -111,6 +111,7 @@ msgid "%q in %q must be of type %q, not %q" msgstr "" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -171,6 +172,10 @@ msgstr "" msgid "%q must be %d-%d" msgstr "" +#: shared-bindings/audiofreeverb/Freeverb.c +msgid "%q must be %q" +msgstr "" + #: shared-bindings/busdisplay/BusDisplay.c msgid "%q must be 1 when %q is True" msgstr "" @@ -834,10 +839,6 @@ msgstr "" msgid "Clock unit in use" msgstr "" -#: ports/espressif/common-hal/mipidsi/Display.c -msgid "Color depth must be 16 or 24" -msgstr "" - #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -982,6 +983,10 @@ msgstr "" msgid "FFT is implemented for linear arrays only" msgstr "" +#: shared-bindings/wifi/Radio.c +msgid "Failed" +msgstr "" + #: shared-bindings/ps2io/Ps2.c msgid "Failed sending command." msgstr "" @@ -1218,7 +1223,7 @@ msgid "Insufficient stream input buffer" msgstr "" #: ports/espressif/common-hal/wifi/Radio.c -#: ports/zephyr-cp/common-hal/wifi/Radio.c +#: ports/zephyr-cp/common-hal/wifi/Radio.c shared-bindings/wifi/Radio.c msgid "Interface must be started" msgstr "" @@ -1271,6 +1276,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c @@ -2677,10 +2683,6 @@ msgstr "" msgid "bits must be 32 or less" msgstr "" -#: shared-bindings/audiofreeverb/Freeverb.c -msgid "bits_per_sample must be 16" -msgstr "" - #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c #: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c @@ -4123,10 +4125,6 @@ msgstr "" msgid "rsplit(None,n)" msgstr "" -#: shared-bindings/audiofreeverb/Freeverb.c -msgid "samples_signed must be true" -msgstr "" - #: ports/atmel-samd/common-hal/audiobusio/PDMIn.c #: ports/raspberrypi/common-hal/audiobusio/PDMIn.c msgid "sampling rate out of range" diff --git a/ports/atmel-samd/peripherals b/ports/atmel-samd/peripherals index 863e615ff98c3..da0a1d7dccb34 160000 --- a/ports/atmel-samd/peripherals +++ b/ports/atmel-samd/peripherals @@ -1 +1 @@ -Subproject commit 863e615ff98c3a8aa904e87a157553559c933b81 +Subproject commit da0a1d7dccb34b7fa07738e7a8ce25118c88d1a3 diff --git a/ports/espressif/common-hal/socketpool/Socket.c b/ports/espressif/common-hal/socketpool/Socket.c index f5cd496515b27..f34f9b8b4058e 100644 --- a/ports/espressif/common-hal/socketpool/Socket.c +++ b/ports/espressif/common-hal/socketpool/Socket.c @@ -346,7 +346,7 @@ socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_o } } -size_t common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, +int common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port) { struct sockaddr_storage bind_addr; const char *broadcast = ""; @@ -547,8 +547,7 @@ mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *se return received; } -int socketpool_socket_recv_into(socketpool_socket_obj_t *self, - const uint8_t *buf, uint32_t len) { +int socketpool_socket_recv_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len) { int received = 0; bool timed_out = false; @@ -562,7 +561,7 @@ int 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, 0); + received = lwip_recv(self->num, buf, len, 0); // In non-blocking mode, fail instead of looping if (received < 1 && self->timeout_ms == 0) { if ((received == 0) || (errno == ENOTCONN)) { @@ -590,7 +589,7 @@ int socketpool_socket_recv_into(socketpool_socket_obj_t *self, return received; } -mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) { +mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len) { int received = socketpool_socket_recv_into(self, buf, len); if (received < 0) { mp_raise_OSError(-received); @@ -658,7 +657,7 @@ int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int l return 0; } -bool common_hal_socketpool_readable(socketpool_socket_obj_t *self) { +bool common_hal_socketpool_socket_readable(socketpool_socket_obj_t *self) { struct timeval immediate = {0, 0}; fd_set fds; @@ -670,7 +669,7 @@ bool common_hal_socketpool_readable(socketpool_socket_obj_t *self) { return num_triggered != 0; } -bool common_hal_socketpool_writable(socketpool_socket_obj_t *self) { +bool common_hal_socketpool_socket_writable(socketpool_socket_obj_t *self) { struct timeval immediate = {0, 0}; fd_set fds; diff --git a/ports/espressif/common-hal/wifi/Network.c b/ports/espressif/common-hal/wifi/Network.c index 5e29cad319391..b7a918a8a5f22 100644 --- a/ports/espressif/common-hal/wifi/Network.c +++ b/ports/espressif/common-hal/wifi/Network.c @@ -6,16 +6,15 @@ #include -#include "shared-bindings/wifi/Network.h" #include "shared-bindings/wifi/AuthMode.h" +#include "shared-bindings/wifi/Radio.h" +#include "shared-bindings/wifi/Network.h" mp_obj_t common_hal_wifi_network_get_ssid(wifi_network_obj_t *self) { const char *cstr = (const char *)self->record.ssid; return mp_obj_new_str(cstr, strlen(cstr)); } -#define MAC_ADDRESS_LENGTH 6 - mp_obj_t common_hal_wifi_network_get_bssid(wifi_network_obj_t *self) { return mp_obj_new_bytes(self->record.bssid, MAC_ADDRESS_LENGTH); } diff --git a/ports/espressif/common-hal/wifi/Radio.c b/ports/espressif/common-hal/wifi/Radio.c index 7f87806e20ce5..c178a7270c3c7 100644 --- a/ports/espressif/common-hal/wifi/Radio.c +++ b/ports/espressif/common-hal/wifi/Radio.c @@ -35,8 +35,6 @@ #include "common-hal/mdns/Server.h" #endif -#define MAC_ADDRESS_LENGTH 6 - static void set_mode_station(wifi_radio_obj_t *self, bool state) { wifi_mode_t next_mode; if (state) { @@ -75,8 +73,8 @@ static void set_mode_ap(wifi_radio_obj_t *self, bool state) { self->ap_mode = state; } -const char *common_hal_wifi_radio_get_version(wifi_radio_obj_t *self) { - return esp_idf_get_version(); +mp_obj_t common_hal_wifi_radio_get_version(wifi_radio_obj_t *self) { + return mp_obj_new_str_from_cstr(esp_get_idf_version()); } bool common_hal_wifi_radio_get_enabled(wifi_radio_obj_t *self) { @@ -627,7 +625,7 @@ void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv esp_netif_set_ip_info(self->netif, &ip_info); - if (ipv4_dns != MP_OBJ_NULL) { + if (ipv4_dns != mp_const_none) { common_hal_wifi_radio_set_ipv4_dns(self, ipv4_dns); } } diff --git a/ports/espressif/common-hal/wifi/__init__.c b/ports/espressif/common-hal/wifi/__init__.c index 0eacd2bab3ec0..067d308cee9e4 100644 --- a/ports/espressif/common-hal/wifi/__init__.c +++ b/ports/espressif/common-hal/wifi/__init__.c @@ -40,8 +40,6 @@ wifi_radio_obj_t common_hal_wifi_radio_obj; #include "nvs_flash.h" #endif -#define MAC_ADDRESS_LENGTH 6 - static const char *TAG = "CP wifi"; static void schedule_background_on_cp_core(void *arg) { diff --git a/ports/raspberrypi/common-hal/socketpool/Socket.c b/ports/raspberrypi/common-hal/socketpool/Socket.c index aeab1498510a0..a2ad4829d02c0 100644 --- a/ports/raspberrypi/common-hal/socketpool/Socket.c +++ b/ports/raspberrypi/common-hal/socketpool/Socket.c @@ -889,7 +889,7 @@ socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_o return MP_OBJ_FROM_PTR(accepted); } -size_t common_hal_socketpool_socket_bind(socketpool_socket_obj_t *socket, +int common_hal_socketpool_socket_bind(socketpool_socket_obj_t *socket, const char *host, size_t hostlen, uint32_t port) { // get address @@ -1127,20 +1127,19 @@ mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *so return ret; } -int socketpool_socket_recv_into(socketpool_socket_obj_t *socket, - const uint8_t *buf, uint32_t len) { +int socketpool_socket_recv_into(socketpool_socket_obj_t *socket, uint8_t *buf, uint32_t len) { mp_uint_t ret = 0; int _errno = 0; switch (socket->type) { case SOCKETPOOL_SOCK_STREAM: { - ret = lwip_tcp_receive(socket, (byte *)buf, len, &_errno); + ret = lwip_tcp_receive(socket, buf, len, &_errno); break; } case SOCKETPOOL_SOCK_DGRAM: #if MICROPY_PY_LWIP_SOCK_RAW case SOCKETPOOL_SOCK_RAW: #endif - ret = lwip_raw_udp_receive(socket, (byte *)buf, len, NULL, &_errno); + ret = lwip_raw_udp_receive(socket, buf, len, NULL, &_errno); break; } if (ret == (unsigned)-1) { @@ -1149,7 +1148,7 @@ int socketpool_socket_recv_into(socketpool_socket_obj_t *socket, return ret; } -mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) { +mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len) { int received = socketpool_socket_recv_into(self, buf, len); if (received < 0) { mp_raise_OSError(-received); @@ -1255,7 +1254,7 @@ int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int l return -MP_EOPNOTSUPP; } -bool common_hal_socketpool_readable(socketpool_socket_obj_t *self) { +bool common_hal_socketpool_socket_readable(socketpool_socket_obj_t *self) { MICROPY_PY_LWIP_ENTER; @@ -1275,7 +1274,7 @@ bool common_hal_socketpool_readable(socketpool_socket_obj_t *self) { return result; } -bool common_hal_socketpool_writable(socketpool_socket_obj_t *self) { +bool common_hal_socketpool_socket_writable(socketpool_socket_obj_t *self) { bool result = false; MICROPY_PY_LWIP_ENTER; diff --git a/ports/raspberrypi/common-hal/wifi/Network.c b/ports/raspberrypi/common-hal/wifi/Network.c index 1cd3effbd0f73..bedb79ca6f42d 100644 --- a/ports/raspberrypi/common-hal/wifi/Network.c +++ b/ports/raspberrypi/common-hal/wifi/Network.c @@ -6,16 +6,15 @@ #include -#include "shared-bindings/wifi/Network.h" #include "shared-bindings/wifi/AuthMode.h" +#include "shared-bindings/wifi/Network.h" +#include "shared-bindings/wifi/Radio.h" mp_obj_t common_hal_wifi_network_get_ssid(wifi_network_obj_t *self) { const char *cstr = (const char *)self->record.ssid; return mp_obj_new_str(cstr, self->record.ssid_len); } -#define MAC_ADDRESS_LENGTH 6 - mp_obj_t common_hal_wifi_network_get_bssid(wifi_network_obj_t *self) { return mp_obj_new_bytes(self->record.bssid, MAC_ADDRESS_LENGTH); } diff --git a/ports/raspberrypi/common-hal/wifi/Radio.c b/ports/raspberrypi/common-hal/wifi/Radio.c index bfd94cae07230..6cc09ab954b09 100644 --- a/ports/raspberrypi/common-hal/wifi/Radio.c +++ b/ports/raspberrypi/common-hal/wifi/Radio.c @@ -32,8 +32,6 @@ #include "shared/netutils/dhcpserver.h" -#define MAC_ADDRESS_LENGTH 6 - #define NETIF_STA (&cyw43_state.netif[CYW43_ITF_STA]) #define NETIF_AP (&cyw43_state.netif[CYW43_ITF_AP]) @@ -483,7 +481,7 @@ void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv ipaddress_ipaddress_to_lwip(netmask, &netmask_addr); ipaddress_ipaddress_to_lwip(gateway, &gateway_addr); netif_set_addr(NETIF_STA, &ipv4_addr, &netmask_addr, &gateway_addr); - if (ipv4_dns != MP_OBJ_NULL) { + if (ipv4_dns != mp_const_none) { common_hal_wifi_radio_set_ipv4_dns(self, ipv4_dns); } } diff --git a/ports/zephyr-cp/common-hal/socketpool/Socket.c b/ports/zephyr-cp/common-hal/socketpool/Socket.c index 4814d375959d8..1804348f292ae 100644 --- a/ports/zephyr-cp/common-hal/socketpool/Socket.c +++ b/ports/zephyr-cp/common-hal/socketpool/Socket.c @@ -543,8 +543,7 @@ mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *se return received; } -int socketpool_socket_recv_into(socketpool_socket_obj_t *self, - const uint8_t *buf, uint32_t len) { +int socketpool_socket_recv_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len) { int received = 0; bool timed_out = false; @@ -558,7 +557,7 @@ int 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, 0); + // received = lwip_recv(self->num, buf, len, 0); // In non-blocking mode, fail instead of looping if (received < 1 && self->timeout_ms == 0) { if ((received == 0) || (errno == ENOTCONN)) { @@ -586,7 +585,7 @@ int socketpool_socket_recv_into(socketpool_socket_obj_t *self, return received; } -mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) { +mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len) { int received = socketpool_socket_recv_into(self, buf, len); if (received < 0) { mp_raise_OSError(-received); diff --git a/ports/zephyr-cp/common-hal/wifi/Radio.c b/ports/zephyr-cp/common-hal/wifi/Radio.c index 726b406b3ca89..3433b15f8b002 100644 --- a/ports/zephyr-cp/common-hal/wifi/Radio.c +++ b/ports/zephyr-cp/common-hal/wifi/Radio.c @@ -33,8 +33,6 @@ #include "common-hal/mdns/Server.h" #endif -#define MAC_ADDRESS_LENGTH 6 - // static void set_mode_station(wifi_radio_obj_t *self, bool state) { // wifi_mode_t next_mode; // if (state) { @@ -661,7 +659,7 @@ void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv // esp_netif_set_ip_info(self->netif, &ip_info); - // if (ipv4_dns != MP_OBJ_NULL) { + // if (ipv4_dns != mp_const_none) { // common_hal_wifi_radio_set_ipv4_dns(self, ipv4_dns); // } } diff --git a/ports/zephyr-cp/common-hal/wifi/__init__.c b/ports/zephyr-cp/common-hal/wifi/__init__.c index 57f073a9cbb08..26b844c0c6eaf 100644 --- a/ports/zephyr-cp/common-hal/wifi/__init__.c +++ b/ports/zephyr-cp/common-hal/wifi/__init__.c @@ -29,8 +29,6 @@ wifi_radio_obj_t common_hal_wifi_radio_obj; #include #include -#define MAC_ADDRESS_LENGTH 6 - static void schedule_background_on_cp_core(void *arg) { #if CIRCUITPY_STATUS_BAR supervisor_status_bar_request_update(false); diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index a732e9b932b3c..9835531199d93 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -74,7 +74,7 @@ endif CIRCUITPY_LTO ?= 0 CIRCUITPY_LTO_PARTITION ?= balanced ifeq ($(CIRCUITPY_LTO),1) -CFLAGS += -flto=jobserver -flto-partition=$(CIRCUITPY_LTO_PARTITION) -DCIRCUITPY_LTO=1 +CFLAGS += -flto=auto -flto-partition=$(CIRCUITPY_LTO_PARTITION) -DCIRCUITPY_LTO=1 else CFLAGS += -DCIRCUITPY_LTO=0 endif @@ -683,7 +683,10 @@ SRC_BINDINGS_ENUMS += \ wifi/Network.c \ wifi/Radio.c \ wifi/ScannedNetworks.c \ - wifi/__init__.c + wifi/__init__.c \ + wifi/AuthMode.c \ + wifi/Packet.c \ + wifi/PowerManagement.c endif ifeq ($(CIRCUITPY_SAFEMODE_PY),1) diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 3a2908639ba35..c71598052a869 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -537,7 +537,7 @@ CFLAGS += -DCIRCUITPY_SKIP_SAFE_MODE_WAIT=$(CIRCUITPY_SKIP_SAFE_MODE_WAIT) CIRCUITPY_SOCKETPOOL_AIRLIFT ?= $(CIRCUITPY_WIFI_AIRLIFT) CFLAGS += -DCIRCUITPY_WIFI_AIRLIFT=$(CIRCUITPY_SOCKETPOOL_AIRLIFT) -CIRCUITPY_SOCKETPOOL_NATIVE ?= 0 +CIRCUITPY_SOCKETPOOL_NATIVE ?= $(CIRCUITPY_WIFI_NATIVE) CFLAGS += -DCIRCUITPY_SOCKETPOOL_NATIVE=$(CIRCUITPY_SOCKETPOOL_NATIVE) ifeq ($(CIRCUITPY_SOCKETPOOL_AIRLIFT)$(CIRCUITPY_SOCKETPOOL_NATIVE),11) @@ -563,6 +563,9 @@ ifeq ($(CIRCUITPY_SSL_AIRLIFT)$(CIRCUITPY_SSL_NATIVE),11) $(error "CIRCUITPY_SSL_AIRLIFT and CIRCUITPY_SSL_NATIVE cannot both be enabled") endif +CIRCUITPY_SSL ?= $(call enable-if-any,$(CIRCUITPY_SSL_AIRLIFT) $(CIRCUITPY_SSL_NATIVE)) +CFLAGS += -DCIRCUITPY_SSL=$(CIRCUITPY_SSL) + CIRCUITPY_SSL_MBEDTLS ?= 0 CFLAGS += -DCIRCUITPY_SSL_MBEDTLS=$(CIRCUITPY_SSL_MBEDTLS) diff --git a/shared-bindings/audiofreeverb/Freeverb.c b/shared-bindings/audiofreeverb/Freeverb.c index 12eb7ef70ab79..2e71b9a80bd52 100644 --- a/shared-bindings/audiofreeverb/Freeverb.c +++ b/shared-bindings/audiofreeverb/Freeverb.c @@ -88,12 +88,9 @@ static mp_obj_t audiofreeverb_freeverb_make_new(const mp_obj_type_t *type, size_ mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, 2, MP_QSTR_channel_count); mp_int_t sample_rate = mp_arg_validate_int_min(args[ARG_sample_rate].u_int, 1, MP_QSTR_sample_rate); if (args[ARG_samples_signed].u_bool != true) { - mp_raise_ValueError(MP_ERROR_TEXT("samples_signed must be true")); - } - mp_int_t bits_per_sample = args[ARG_bits_per_sample].u_int; - if (bits_per_sample != 16) { - mp_raise_ValueError(MP_ERROR_TEXT("bits_per_sample must be 16")); + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q must be %q"), MP_QSTR_samples_signed, MP_QSTR_true); } + mp_int_t bits_per_sample = mp_arg_validate_int(args[ARG_bits_per_sample].u_int, 16); audiofreeverb_freeverb_obj_t *self = mp_obj_malloc(audiofreeverb_freeverb_obj_t, &audiofreeverb_freeverb_type); common_hal_audiofreeverb_freeverb_construct(self, args[ARG_roomsize].u_obj, args[ARG_damp].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index 1513a7cf0944a..7bb05a0429de2 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -214,6 +214,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(busio_spi_configure_obj, 1, busio_spi_configure); static mp_obj_t busio_spi_obj_try_lock(mp_obj_t self_in) { busio_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); return mp_obj_new_bool(common_hal_busio_spi_try_lock(self)); } MP_DEFINE_CONST_FUN_OBJ_1(busio_spi_try_lock_obj, busio_spi_obj_try_lock); diff --git a/shared-bindings/ipaddress/IPv4Address.c b/shared-bindings/ipaddress/IPv4Address.c index 5aab02e9f5cce..178c54100f253 100644 --- a/shared-bindings/ipaddress/IPv4Address.c +++ b/shared-bindings/ipaddress/IPv4Address.c @@ -14,6 +14,7 @@ #include "py/objstr.h" #include "py/runtime.h" #include "shared-bindings/ipaddress/__init__.h" +#include "shared-bindings/ipaddress/IPv4Address.h" //| class IPv4Address: //| """Encapsulates an IPv4 address.""" diff --git a/shared-bindings/ipaddress/IPv4Address.h b/shared-bindings/ipaddress/IPv4Address.h index 4b3d6eb0d2bd7..a1a91dd7a77ae 100644 --- a/shared-bindings/ipaddress/IPv4Address.h +++ b/shared-bindings/ipaddress/IPv4Address.h @@ -10,6 +10,13 @@ extern const mp_obj_type_t ipaddress_ipv4address_type; -mp_obj_t common_hal_ipaddress_new_ipv4address(uint32_t value); +uint32_t ipv4_bytes_to_uint32(const uint8_t bytes[4]); +void ipv4_uint32_to_bytes(uint32_t ipv4, uint8_t bytes[4]); +mp_obj_t ipv4_bytes_to_str(const uint8_t ipv4[4]); +void ipaddress_ipv4address_to_bytes(ipaddress_ipv4address_obj_t *ipv4_address, uint8_t ipv4_bytes[4]); +bool ipaddress_parse_ipv4address(const char *ip_str, size_t len, uint32_t *ip_out); + void common_hal_ipaddress_ipv4address_construct(ipaddress_ipv4address_obj_t *self, uint8_t *buf, size_t len); mp_obj_t common_hal_ipaddress_ipv4address_get_packed(ipaddress_ipv4address_obj_t *self); +mp_obj_t common_hal_ipaddress_new_ipv4address(uint32_t value); +mp_obj_t common_hal_ipaddress_new_ipv4address_from_bytes(uint8_t bytes[4]); diff --git a/shared-bindings/ipaddress/__init__.c b/shared-bindings/ipaddress/__init__.c index 60bc6226ba1b6..d0bfa419bea25 100644 --- a/shared-bindings/ipaddress/__init__.c +++ b/shared-bindings/ipaddress/__init__.c @@ -6,7 +6,6 @@ #include "py/objexcept.h" #include "py/objstr.h" -#include "py/parsenum.h" #include "py/runtime.h" #include "shared-bindings/ipaddress/__init__.h" #include "shared-bindings/ipaddress/IPv4Address.h" @@ -17,46 +16,6 @@ //| """ //| //| - - -bool ipaddress_parse_ipv4address(const char *str_data, size_t str_len, uint32_t *ip_out) { - size_t period_count = 0; - size_t period_index[4] = {0, 0, 0, str_len}; - for (size_t i = 0; i < str_len; i++) { - if (str_data[i] == '.') { - if (period_count < 3) { - period_index[period_count] = i; - } - period_count++; - } - } - if (period_count > 3) { - return false; - } - - size_t last_period = 0; - if (ip_out != NULL) { - *ip_out = 0; - } - for (size_t i = 0; i < 4; i++) { - // Catch exceptions thrown by mp_parse_num_integer - nlr_buf_t nlr; - mp_obj_t octet; - if (nlr_push(&nlr) == 0) { - octet = mp_parse_num_integer((const char *)str_data + last_period, period_index[i] - last_period, 10, NULL); - nlr_pop(); - } else { - return false; - } - last_period = period_index[i] + 1; - if (ip_out != NULL) { - mp_int_t int_octet = MP_OBJ_SMALL_INT_VALUE(octet); - *ip_out |= int_octet << (i * 8); - } - } - return true; -} - //| def ip_address(obj: Union[int, str]) -> IPv4Address: //| """Return a corresponding IP address object or raise ValueError if not possible.""" //| ... diff --git a/shared-bindings/ipaddress/__init__.h b/shared-bindings/ipaddress/__init__.h index d9202b478d1fe..f7ccde7f3678a 100644 --- a/shared-bindings/ipaddress/__init__.h +++ b/shared-bindings/ipaddress/__init__.h @@ -5,9 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "shared-module/ipaddress/__init__.h" - -bool ipaddress_parse_ipv4address(const char *ip_str, size_t len, uint32_t *ip_out); - -mp_obj_t common_hal_ipaddress_new_ipv4address(uint32_t value); diff --git a/shared-bindings/socketpool/Socket.c b/shared-bindings/socketpool/Socket.c index 5dac9d150e8ff..80ed628a7eb6a 100644 --- a/shared-bindings/socketpool/Socket.c +++ b/shared-bindings/socketpool/Socket.c @@ -152,6 +152,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_listen_obj, socketpool_socket //| """Reads some bytes from a remote address. //| //| Returns a tuple containing +//| //| * the number of bytes received into the given buffer //| * a remote_address, which is a tuple of ip address and port number //| @@ -376,7 +377,7 @@ static mp_obj_t socketpool_socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_ socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_uint_t timeout_ms; if (timeout_in == mp_const_none) { - timeout_ms = -1; + timeout_ms = SOCKET_BLOCK_FOREVER; } else { #if MICROPY_PY_BUILTINS_FLOAT timeout_ms = 1000 * mp_obj_get_float(timeout_in); @@ -452,10 +453,10 @@ static mp_uint_t socket_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg if (request == MP_STREAM_POLL) { mp_uint_t flags = arg; ret = 0; - if ((flags & MP_STREAM_POLL_RD) && common_hal_socketpool_readable(self) > 0) { + if ((flags & MP_STREAM_POLL_RD) && common_hal_socketpool_socket_readable(self) > 0) { ret |= MP_STREAM_POLL_RD; } - if ((flags & MP_STREAM_POLL_WR) && common_hal_socketpool_writable(self)) { + if ((flags & MP_STREAM_POLL_WR) && common_hal_socketpool_socket_writable(self)) { ret |= MP_STREAM_POLL_WR; } } else { diff --git a/shared-bindings/socketpool/Socket.h b/shared-bindings/socketpool/Socket.h index a883ebd505463..3829bb9e7bbe3 100644 --- a/shared-bindings/socketpool/Socket.h +++ b/shared-bindings/socketpool/Socket.h @@ -8,10 +8,12 @@ #include "common-hal/socketpool/Socket.h" +#define SOCKET_BLOCK_FOREVER ((mp_uint_t)-1) + extern const mp_obj_type_t socketpool_socket_type; socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out); -size_t common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port); +int common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port); void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self); void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port); bool common_hal_socketpool_socket_get_closed(socketpool_socket_obj_t *self); @@ -21,21 +23,20 @@ mp_int_t common_hal_socketpool_socket_get_type(socketpool_socket_obj_t *self); bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *self, int backlog); mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len, mp_obj_t *peer_out); -mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len); +mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len); mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len); 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); void common_hal_socketpool_socket_settimeout(socketpool_socket_obj_t *self, uint32_t timeout_ms); int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int level, int optname, const void *value, size_t optlen); -bool common_hal_socketpool_readable(socketpool_socket_obj_t *self); -bool common_hal_socketpool_writable(socketpool_socket_obj_t *self); +bool common_hal_socketpool_socket_readable(socketpool_socket_obj_t *self); +bool common_hal_socketpool_socket_writable(socketpool_socket_obj_t *self); // Non-allocating versions for internal use. int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, socketpool_socket_obj_t *accepted); void socketpool_socket_close(socketpool_socket_obj_t *self); int socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len); -int socketpool_socket_recv_into(socketpool_socket_obj_t *self, - const uint8_t *buf, uint32_t len); +int socketpool_socket_recv_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len); // Moves self to sock without closing the real socket. self will think its closed afterwards. void socketpool_socket_move(socketpool_socket_obj_t *self, socketpool_socket_obj_t *sock); diff --git a/shared-bindings/socketpool/__init__.c b/shared-bindings/socketpool/__init__.c index e3e3f035ce725..75bb65de0e05e 100644 --- a/shared-bindings/socketpool/__init__.c +++ b/shared-bindings/socketpool/__init__.c @@ -12,7 +12,8 @@ #include "shared-bindings/socketpool/Socket.h" #include "shared-bindings/socketpool/SocketPool.h" -//| """ +//| """Network sockets managed in a pool +//| //| The `socketpool` module provides sockets through a pool. The pools themselves //| act like CPython's `socket` module. //| diff --git a/shared-bindings/ssl/SSLSocket.c b/shared-bindings/ssl/SSLSocket.c index 92440f9ea30f7..06e8e684775f0 100644 --- a/shared-bindings/ssl/SSLSocket.c +++ b/shared-bindings/ssl/SSLSocket.c @@ -191,10 +191,10 @@ static mp_obj_t ssl_sslsocket_send(mp_obj_t self_in, mp_obj_t buf_in) { } static MP_DEFINE_CONST_FUN_OBJ_2(ssl_sslsocket_send_obj, ssl_sslsocket_send); -// //| def setsockopt(self, level: int, optname: int, value: int | ReadableBuffer) -> None: -// //| """Sets socket options""" -// //| ... -// //| +//| def setsockopt(self, level: int, optname: int, value: int | ReadableBuffer) -> None: +//| """Sets socket options""" +//| ... +//| static mp_obj_t ssl_sslsocket_setsockopt(size_t n_args, const mp_obj_t *args) { ssl_sslsocket_obj_t *self = MP_OBJ_TO_PTR(args[0]); mp_obj_t level = args[1]; diff --git a/shared-bindings/ssl/__init__.c b/shared-bindings/ssl/__init__.c index 0d10090e9cb99..5d7384916a29b 100644 --- a/shared-bindings/ssl/__init__.c +++ b/shared-bindings/ssl/__init__.c @@ -11,7 +11,8 @@ #include "shared-bindings/ssl/__init__.h" #include "shared-bindings/ssl/SSLContext.h" -//| """ +//| """SSL/TLS contexts for wrapped sockets +//| //| The `ssl` module provides SSL contexts to wrap sockets in. //| //| |see_cpython_module| :mod:`cpython:ssl`. diff --git a/shared-bindings/util.c b/shared-bindings/util.c index 0bcaa2eef41e9..d1f73913d339a 100644 --- a/shared-bindings/util.c +++ b/shared-bindings/util.c @@ -15,6 +15,21 @@ void raise_deinited_error(void) { mp_raise_ValueError(MP_ERROR_TEXT("Object has been deinitialized and can no longer be used. Create a new object.")); } +// Use an mp_rom_map_elem_t table (usually a locals_dict_table) to help print an object. +void elem_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp_rom_map_elem_t *elems, size_t n_elems) { + const mp_obj_type_t *type = mp_obj_get_type(self_in); + mp_printf(print, "%q(", type->name); + for (size_t i = 0; i < n_elems; i++) { + if (i > 0) { + mp_print_str(print, ", "); + } + mp_printf(print, "%q=", MP_OBJ_QSTR_VALUE(elems[i].key)); + mp_obj_print_helper(print, mp_load_attr(self_in, MP_OBJ_QSTR_VALUE(elems[i].key)), PRINT_REPR); + } + mp_print_str(print, ")"); +} + +// Use an mp_arg_t table (usually used in a constructor) to help print an object. void properties_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp_arg_t *properties, size_t n_properties) { const mp_obj_type_t *type = mp_obj_get_type(self_in); mp_printf(print, "%q(", type->name); diff --git a/shared-bindings/util.h b/shared-bindings/util.h index d53e5c6e8da13..2393163217d74 100644 --- a/shared-bindings/util.h +++ b/shared-bindings/util.h @@ -10,6 +10,7 @@ #include "py/runtime.h" NORETURN void raise_deinited_error(void); +void elem_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp_rom_map_elem_t *elems, size_t n_elems); void properties_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp_arg_t *properties, size_t n_properties); void properties_construct_helper(mp_obj_t self_in, const mp_arg_t *args, const mp_arg_val_t *vals, size_t n_properties); bool path_exists(const char *path); diff --git a/shared-bindings/wifi/AuthMode.h b/shared-bindings/wifi/AuthMode.h index ba90648e9312b..b2b9efbbdd630 100644 --- a/shared-bindings/wifi/AuthMode.h +++ b/shared-bindings/wifi/AuthMode.h @@ -19,4 +19,11 @@ typedef enum { } wifi_authmode_t; extern const mp_obj_type_t wifi_authmode_type; + extern const cp_enum_obj_t authmode_OPEN_obj; +extern const cp_enum_obj_t authmode_WEP_obj; +extern const cp_enum_obj_t authmode_PSK_obj; +extern const cp_enum_obj_t authmode_WPA_obj; +extern const cp_enum_obj_t authmode_WPA2_obj; +extern const cp_enum_obj_t authmode_WPA3_obj; +extern const cp_enum_obj_t authmode_ENTERPRISE_obj; diff --git a/shared-bindings/wifi/Network.c b/shared-bindings/wifi/Network.c index 93a483930e8d2..79eed25b9b670 100644 --- a/shared-bindings/wifi/Network.c +++ b/shared-bindings/wifi/Network.c @@ -9,6 +9,7 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "shared-bindings/wifi/Network.h" +#include "shared-bindings/util.h" //| class Network: //| """A wifi network provided by a nearby access point.""" @@ -101,9 +102,16 @@ static const mp_rom_map_elem_t wifi_network_locals_dict_table[] = { static MP_DEFINE_CONST_DICT(wifi_network_locals_dict, wifi_network_locals_dict_table); +static void network_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + elem_print_helper(print, self_in, + wifi_network_locals_dict_table, MP_ARRAY_SIZE(wifi_network_locals_dict_table)); +} + MP_DEFINE_CONST_OBJ_TYPE( wifi_network_type, MP_QSTR_Network, MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, - locals_dict, &wifi_network_locals_dict + locals_dict, &wifi_network_locals_dict, + print, network_print ); diff --git a/shared-bindings/wifi/Radio.c b/shared-bindings/wifi/Radio.c index 3e196735ad9fb..d56de1c33163e 100644 --- a/shared-bindings/wifi/Radio.c +++ b/shared-bindings/wifi/Radio.c @@ -4,6 +4,8 @@ // // SPDX-License-Identifier: MIT +#include "shared-bindings/util.h" +#include "shared-bindings/ipaddress/IPv4Address.h" #include "shared-bindings/wifi/__init__.h" #include "shared-bindings/wifi/AuthMode.h" #include "shared-bindings/wifi/PowerManagement.h" @@ -14,8 +16,7 @@ #include "py/unicode.h" #include "py/runtime.h" #include "py/objproperty.h" - -#define MAC_ADDRESS_LENGTH 6 +#include "shared/runtime/context_manager_helpers.h" static bool hostname_valid(const char *ptr, size_t len) { #if 0 // validated by mp_arg_validate_length_range @@ -77,7 +78,7 @@ static void validate_hex_password(const uint8_t *buf, size_t len) { //| ... //| -#if CIRCUITPY_WIFI_AIRLIFT + //| def init_airlift( //| spi: busio.SPI, //| cs: digitalio.DigitalInOut, @@ -85,7 +86,8 @@ static void validate_hex_password(const uint8_t *buf, size_t len) { //| reset: digitalio.DigitalInOut, //| gpio0: Optional[digitalio.DigitalInOut] = None, //| ) -> None: -//| """Create an ESP32 SPI WiFi control object. +//| """Initialize the connection to the AirLift co-processori control object. +//| Only implemented on boards that support AirLift `wifi`. //| //| :param busio.SPI spi: The SPI bus to use //| :param digitalio.DigitalInOut cs: Chip select pin @@ -94,6 +96,7 @@ static void validate_hex_password(const uint8_t *buf, size_t len) { //| :param digitalio.DigitalInOut gpio0: Optional GPIO0 pin for boot mode control //| """ static mp_obj_t wifi_radio_init_airlift(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + #if CIRCUITPY_WIFI_AIRLIFT enum { ARG_spi, ARG_cs, ARG_ready, ARG_reset, ARG_gpio0, }; static const mp_arg_t allowed_args[] = { { MP_QSTR_spi, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -119,11 +122,54 @@ static mp_obj_t wifi_radio_init_airlift(size_t n_args, const mp_obj_t *pos_args, mp_arg_validate_type_or_none(args[ARG_gpio0].u_obj, &digitalio_digitalinout_type, MP_QSTR_gpio0); common_hal_wifi_radio_init_airlift(self, spi, cs, ready, reset, gpio0 == mp_const_none ? NULL : gpio0); - return mp_const_none; + #else + mp_raise_NotImplementedError(NULL); + #endif // CIRCUITPY_WIFI_AIRLIFT } static MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_init_airlift_obj, 4, wifi_radio_init_airlift); -#endif + +//| def deinit(self) -> None: +//| """Deinitialises the UART and releases any hardware resources for reuse. +//| Only implemented on boards that support AirLift `wifi`. +//| """ +//| ... +//| +static mp_obj_t wifi_radio_obj_deinit(mp_obj_t self_in) { + #if CIRCUITPY_WIFI_AIRLIFT + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_wifi_radio_deinit(self); + return mp_const_none; + #else + mp_raise_NotImplementedError(NULL); + #endif // CIRCUITPY_WIFI_AIRLIFT +} +static MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_deinit_obj, wifi_radio_obj_deinit); + +static void check_for_deinit(wifi_radio_obj_t *self) { + // Native wifi cannot be deinited, so this is a no-op. + // AirLift can be deinited. + #if CIRCUITPY_WIFI_AIRLIFT + if (common_hal_wifi_radio_deinited(self)) { + raise_deinited_error(); + } + #endif // CIRCUITPY_WIFI_AIRLIFT +} + +//| def __enter__(self) -> UART: +//| """No-op used by Context Managers. Only useful on boards that support AirLift `wifi`.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info. +//| Only useful on boards that support AirLift `wifi`.""" +//| """ +//| ... +//| +// Provided by context manager helper. //| version: str //| """A string described the version of the underlying WiFi software (read-only).""" @@ -139,12 +185,18 @@ MP_PROPERTY_GETTER(wifi_radio_version_obj, //| """``True`` when the wifi radio is enabled. //| If you set the value to ``False``, any open sockets will be closed. //| """ -static mp_obj_t wifi_radio_get_enabled(mp_obj_t self) { +static mp_obj_t wifi_radio_get_enabled(mp_obj_t self_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_wifi_radio_get_enabled(self)); } MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_enabled_obj, wifi_radio_get_enabled); -static mp_obj_t wifi_radio_set_enabled(mp_obj_t self, mp_obj_t value) { +static mp_obj_t wifi_radio_set_enabled(mp_obj_t self_in, mp_obj_t value) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + const bool enabled = mp_obj_is_true(value); common_hal_wifi_radio_set_enabled(self, enabled); @@ -162,11 +214,16 @@ MP_PROPERTY_GETSET(wifi_radio_enabled_obj, //| the changes would only be reflected once the interface restarts/reconnects.""" static mp_obj_t wifi_radio_get_hostname(mp_obj_t self_in) { wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return common_hal_wifi_radio_get_hostname(self); } MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_hostname_obj, wifi_radio_get_hostname); static mp_obj_t wifi_radio_set_hostname(mp_obj_t self_in, mp_obj_t hostname_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + mp_buffer_info_t hostname; mp_get_buffer_raise(hostname_in, &hostname, MP_BUFFER_READ); @@ -176,7 +233,6 @@ static mp_obj_t wifi_radio_set_hostname(mp_obj_t self_in, mp_obj_t hostname_in) mp_raise_ValueError(MP_ERROR_TEXT("invalid hostname")); } - wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); common_hal_wifi_radio_set_hostname(self, hostname.buf); return mp_const_none; @@ -197,12 +253,17 @@ MP_PROPERTY_GETSET(wifi_radio_hostname_obj, static mp_obj_t _wifi_radio_get_mac_address(mp_obj_t self_in) { wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_FROM_PTR(common_hal_wifi_radio_get_mac_address(self)); } MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_mac_address_obj, _wifi_radio_get_mac_address); #if CIRCUITPY_WIFI_RADIO_SETTABLE_MAC_ADDRESS static mp_obj_t wifi_radio_set_mac_address(mp_obj_t self_in, mp_obj_t mac_address_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + mp_buffer_info_t mac_address; mp_get_buffer_raise(mac_address_in, &mac_address, MP_BUFFER_READ); @@ -210,7 +271,6 @@ static mp_obj_t wifi_radio_set_mac_address(mp_obj_t self_in, mp_obj_t mac_addres mp_raise_ValueError(MP_ERROR_TEXT("Invalid MAC address")); } - wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); common_hal_wifi_radio_set_mac_address(self, mac_address.buf); return mp_const_none; @@ -231,13 +291,17 @@ MP_PROPERTY_GETTER(wifi_radio_mac_address_obj, //| """Wifi transmission power, in dBm.""" static mp_obj_t wifi_radio_get_tx_power(mp_obj_t self_in) { wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_float(common_hal_wifi_radio_get_tx_power(self)); } MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_tx_power_obj, wifi_radio_get_tx_power); static mp_obj_t wifi_radio_set_tx_power(mp_obj_t self_in, mp_obj_t tx_power_in) { - mp_float_t tx_power = mp_obj_get_float(tx_power_in); wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + mp_float_t tx_power = mp_obj_get_float(tx_power_in); common_hal_wifi_radio_set_tx_power(self, tx_power); return mp_const_none; } @@ -252,12 +316,16 @@ MP_PROPERTY_GETSET(wifi_radio_tx_power_obj, //| """ static mp_obj_t wifi_radio_get_power_management(mp_obj_t self_in) { wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return cp_enum_find(&wifi_power_management_type, common_hal_wifi_radio_get_power_management(self)); } MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_power_management_obj, wifi_radio_get_power_management); static mp_obj_t wifi_radio_set_power_management(mp_obj_t self_in, mp_obj_t power_management_in) { wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + wifi_power_management_t power_management = cp_enum_value(&wifi_power_management_type, power_management_in, MP_QSTR_power_management); if (power_management == POWER_MANAGEMENT_UNKNOWN) { @@ -281,12 +349,17 @@ MP_PROPERTY_GETSET(wifi_radio_power_management_obj, //| static mp_obj_t wifi_radio_get_mac_address_ap(mp_obj_t self_in) { wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return MP_OBJ_FROM_PTR(common_hal_wifi_radio_get_mac_address_ap(self)); } MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_mac_address_ap_obj, wifi_radio_get_mac_address_ap); #if CIRCUITPY_WIFI_RADIO_SETTABLE_MAC_ADDRESS static mp_obj_t wifi_radio_set_mac_address_ap(mp_obj_t self_in, mp_obj_t mac_address_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + mp_buffer_info_t mac_address; mp_get_buffer_raise(mac_address_in, &mac_address, MP_BUFFER_READ); @@ -294,7 +367,6 @@ static mp_obj_t wifi_radio_set_mac_address_ap(mp_obj_t self_in, mp_obj_t mac_add mp_raise_ValueError(MP_ERROR_TEXT("Invalid MAC address")); } - wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); common_hal_wifi_radio_set_mac_address_ap(self, mac_address.buf); return mp_const_none; @@ -318,7 +390,8 @@ MP_PROPERTY_GETTER(wifi_radio_mac_address_ap_obj, //| //| .. note:: //| -//| In the raspberrypi port (RP2040 CYW43), ``start_channel`` and ``stop_channel`` are ignored. +//| In the raspberrypi port (RP2xxx CYW43) and on AirLift, +//| ``start_channel`` and ``stop_channel`` are ignored. //| """ //| ... //| @@ -330,6 +403,8 @@ static mp_obj_t wifi_radio_start_scanning_networks(size_t n_args, const mp_obj_t }; wifi_radio_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + 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); @@ -354,6 +429,7 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_start_scanning_networks_obj, 1, wif //| static mp_obj_t wifi_radio_stop_scanning_networks(mp_obj_t self_in) { wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); common_hal_wifi_radio_stop_scanning_networks(self); @@ -365,7 +441,10 @@ static MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_scanning_networks_obj, wifi_rad //| """Starts a Station.""" //| ... //| -static mp_obj_t wifi_radio_start_station(mp_obj_t self) { +static mp_obj_t wifi_radio_start_station(mp_obj_t self_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_wifi_radio_start_station(self); return mp_const_none; } @@ -375,7 +454,10 @@ MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_start_station_obj, wifi_radio_start_station //| """Stops the Station.""" //| ... //| -static mp_obj_t wifi_radio_stop_station(mp_obj_t self) { +static mp_obj_t wifi_radio_stop_station(mp_obj_t self_in) { + wifi_radio_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_wifi_radio_stop_station(self); return mp_const_none; } @@ -405,7 +487,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_station_obj, wifi_radio_stop_station); //| `wifi.AuthMode.PSK`, and one or both of `wifi.AuthMode.WPA` and `wifi.AuthMode.WPA2`. //| On Pi Pico W, ``authmode`` is ignored; it is always ``(wifi.AuthMode.WPA2, wifi.AuthMode.PSK)`` //| with a non-empty password, or ``(wifi.AuthMode.OPEN)``, when no password is given. -//| On Pi Pico W, the AP can be started and stopped only once per reboot. +//| On Pi Pico W, the access point can be started and stopped only once per reboot. +//| On AirLift, access point functionality is not available. //| //| The length of ``password`` must be 8-63 characters if it is ASCII, //| or exactly 64 hexadecimal characters if it is the hex form of the 256-bit key. @@ -415,7 +498,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_station_obj, wifi_radio_stop_station); //| //| .. note:: //| -//| In the raspberrypi port (RP2040 CYW43), ``max_connections`` is ignored. +//| In the raspberrypi port (RP2xxx CYW43), ``max_connections`` is ignored. //| """ //| ... //| @@ -517,7 +600,13 @@ MP_PROPERTY_GETTER(wifi_radio_ap_active_obj, //| significantly because a full scan doesn't occur. //| //| If ``bssid`` is given and not None, the scan will start at the first channel or the one given and -//| connect to the AP with the given ``bssid`` and ``ssid``.""" +//| connect to the AP with the given ``bssid`` and ``ssid``. +//| +//| **Limitations**: On AirLift, ``channel`` is ignored. +//| On AirLift and on raspberrypi CYW43, ``bssid` is not implemented. +//| On AirLift and on raspberrypi CYW43, ``timeout`` is 8 seconds if set to ``None`` or 0. +//| +//| """ //| ... //| static mp_obj_t wifi_radio_connect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -534,7 +623,7 @@ static mp_obj_t wifi_radio_connect(size_t n_args, const mp_obj_t *pos_args, mp_m 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); - mp_float_t timeout = 0; + mp_float_t timeout = 0.0f; if (args[ARG_timeout].u_obj != mp_const_none) { timeout = mp_obj_get_float(args[ARG_timeout].u_obj); } @@ -556,8 +645,6 @@ static mp_obj_t wifi_radio_connect(size_t n_args, const mp_obj_t *pos_args, mp_m } } - #define MAC_ADDRESS_LENGTH 6 - mp_buffer_info_t bssid; bssid.len = 0; // Should probably make sure bssid is just bytes and not something else too @@ -569,15 +656,28 @@ static mp_obj_t wifi_radio_connect(size_t n_args, const mp_obj_t *pos_args, mp_m } wifi_radio_error_t error = common_hal_wifi_radio_connect(self, ssid.buf, ssid.len, password.buf, password.len, args[ARG_channel].u_int, timeout, bssid.buf, bssid.len); - if (error == WIFI_RADIO_ERROR_AUTH_FAIL) { - mp_raise_ConnectionError(MP_ERROR_TEXT("Authentication failure")); - } else if (error == WIFI_RADIO_ERROR_NO_AP_FOUND) { - mp_raise_ConnectionError(MP_ERROR_TEXT("No network with that ssid")); - } else if (error != WIFI_RADIO_ERROR_NONE) { - mp_raise_msg_varg(&mp_type_ConnectionError, MP_ERROR_TEXT("Unknown failure %d"), error); - } + switch (error) { + case WIFI_RADIO_ERROR_AUTH_FAIL: + mp_raise_ConnectionError(MP_ERROR_TEXT("Authentication failure")); - return mp_const_none; + case WIFI_RADIO_ERROR_NO_AP_FOUND: + mp_raise_ConnectionError(MP_ERROR_TEXT("No network with that ssid")); + + case WIFI_RADIO_ERROR_NO_RADIO: + mp_raise_ConnectionError(MP_ERROR_TEXT("Interface must be started")); + + case WIFI_RADIO_ERROR_CONNECTION_FAIL: + mp_raise_ConnectionError(MP_ERROR_TEXT("Failed")); + + case WIFI_RADIO_ERROR_UNSPECIFIED: + mp_raise_ConnectionError(MP_ERROR_TEXT("Interface must be started")); + + case WIFI_RADIO_ERROR_NONE: + return mp_const_none; + + default: + mp_raise_msg_varg(&mp_type_ConnectionError, MP_ERROR_TEXT("Unknown failure %d"), error); + } } static MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_connect_obj, 1, wifi_radio_connect); @@ -645,7 +745,7 @@ MP_PROPERTY_GETTER(wifi_radio_ipv4_subnet_ap_obj, //| //| .. note:: //| -//| In the raspberrypi port (RP2040 CYW43), the access point needs to be started before the IP v4 address can be set. +//| In the raspberrypi port (RP2xxx CYW43), the access point needs to be started before the IP v4 address can be set. //| """ //| ... //| @@ -655,14 +755,19 @@ static mp_obj_t wifi_radio_set_ipv4_address(size_t n_args, const mp_obj_t *pos_a { MP_QSTR_ipv4, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, }, { MP_QSTR_netmask, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, }, { MP_QSTR_gateway, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, }, - { MP_QSTR_ipv4_dns, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_ipv4_dns, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, }; wifi_radio_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); 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); - common_hal_wifi_radio_set_ipv4_address(self, args[ARG_ipv4].u_obj, args[ARG_netmask].u_obj, args[ARG_gateway].u_obj, args[ARG_ipv4_dns].u_obj); + mp_obj_t ipv4 = mp_arg_validate_type(args[ARG_ipv4].u_obj, &ipaddress_ipv4address_type, MP_QSTR_ipv4); + mp_obj_t netmask = mp_arg_validate_type(args[ARG_netmask].u_obj, &ipaddress_ipv4address_type, MP_QSTR_netmask); + mp_obj_t gateway = mp_arg_validate_type(args[ARG_gateway].u_obj, &ipaddress_ipv4address_type, MP_QSTR_gateway); + mp_obj_t ipv4_dns = mp_arg_validate_type_or_none(args[ARG_ipv4_dns].u_obj, &ipaddress_ipv4address_type, MP_QSTR_ipv4_dns); + + common_hal_wifi_radio_set_ipv4_address(self, ipv4, netmask, gateway, ipv4_dns); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_set_ipv4_address_obj, 1, wifi_radio_set_ipv4_address); @@ -759,8 +864,8 @@ static mp_obj_t wifi_radio_get_dns(mp_obj_t self) { } MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_dns_obj, wifi_radio_get_dns); -static mp_obj_t wifi_radio_set_dns(mp_obj_t self, mp_obj_t dns_addr) { - common_hal_wifi_radio_set_dns(self, dns_addr); +static mp_obj_t wifi_radio_set_dns(mp_obj_t self, mp_obj_t dns_addrs) { + common_hal_wifi_radio_set_dns(self, dns_addrs); return mp_const_none; } @@ -780,7 +885,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ap_info_obj, wifi_radio_get_ap_info); //| stations_ap: None //| """In AP mode, returns list of named tuples, each of which contains: //| mac: bytearray (read-only) -//| rssi: int (read-only, None on Raspberry Pi Pico W) +//| rssi: int (read-only, None on raspberrypi RP2xxxx CY43) //| ipv4_address: ipv4_address (read-only, None if station connected but no address assigned yet or self-assigned address)""" //| static mp_obj_t wifi_radio_get_stations_ap(mp_obj_t self) { @@ -859,6 +964,8 @@ MP_PROPERTY_GETTER(wifi_radio_ap_info_obj, //| **Limitations:** On Espressif, calling `ping()` multiple times rapidly //| exhausts available resources after several calls. Rather than failing at that point, `ping()` //| will wait two seconds for enough resources to be freed up before proceeding. +//| +//| On AirLift, the timeout is ignored, and a ttl (time to live) value of 250 is used. //| """ //| ... //| @@ -884,14 +991,11 @@ static mp_obj_t wifi_radio_ping(size_t n_args, const mp_obj_t *pos_args, mp_map_ return mp_const_none; } - return mp_obj_new_float(time_ms / 1000.0); + return mp_obj_new_float(time_ms / 1000.0f); } static MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_ping_obj, 1, wifi_radio_ping); static const mp_rom_map_elem_t wifi_radio_locals_dict_table[] = { - #if CIRCUITPY_WIFI_AIRLIFT - { MP_ROM_QSTR(MP_QSTR_init_airlift), MP_ROM_PTR(&wifi_radio_init_airlift_obj) }, - #endif { MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&wifi_radio_version_obj) }, { MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&wifi_radio_enabled_obj) }, @@ -939,6 +1043,12 @@ static const mp_rom_map_elem_t wifi_radio_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_dns), MP_ROM_PTR(&wifi_radio_dns_obj) }, { MP_ROM_QSTR(MP_QSTR_ping), MP_ROM_PTR(&wifi_radio_ping_obj) }, + + // Airlift-specific operations. + { MP_ROM_QSTR(MP_QSTR_init_airlift), MP_ROM_PTR(&wifi_radio_init_airlift_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&wifi_radio_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, }; static MP_DEFINE_CONST_DICT(wifi_radio_locals_dict, wifi_radio_locals_dict_table); diff --git a/shared-bindings/wifi/Radio.h b/shared-bindings/wifi/Radio.h index 8e471d08449d0..a6a9981bb0d2d 100644 --- a/shared-bindings/wifi/Radio.h +++ b/shared-bindings/wifi/Radio.h @@ -17,6 +17,8 @@ #include "py/objstr.h" #include "py/objnamedtuple.h" +#define MAC_ADDRESS_LENGTH (6) + extern const mp_obj_type_t wifi_radio_type; extern const mp_obj_namedtuple_type_t wifi_radio_station_type; @@ -61,9 +63,15 @@ typedef enum { WIFI_RADIO_NO_AP_FOUND_W_COMPATIBLE_SECURITY = 210, // collapsed to AUTH_FAIL WIFI_RADIO_NO_AP_FOUND_IN_AUTHMODE_THRESHOLD = 211, // collapsed to AUTH_FAIL WIFI_RADIO_NO_AP_FOUND_IN_RSSI_THRESHOLD = 212, + WIFI_RADIO_ERROR_NO_RADIO = 500, } wifi_radio_error_t; +#if CIRCUITPY_WIFI_AIRLIFT +extern bool common_hal_wifi_radio_deinited(wifi_radio_obj_t *self); +extern void common_hal_wifi_radio_mark_deinit(wifi_radio_obj_t *self); +extern void common_hal_wifi_radio_deinit(wifi_radio_obj_t *self); extern void common_hal_wifi_radio_init_airlift(wifi_radio_obj_t *self, busio_spi_obj_t *spi, digitalio_digitalinout_obj_t *cs, digitalio_digitalinout_obj_t *ready, digitalio_digitalinout_obj_t *reset, digitalio_digitalinout_obj_t *gpio0); +#endif extern mp_obj_t common_hal_wifi_radio_get_version(wifi_radio_obj_t *self); diff --git a/shared-bindings/wifi/__init__.c b/shared-bindings/wifi/__init__.c index 2a1c0097080db..1981144f8f403 100644 --- a/shared-bindings/wifi/__init__.c +++ b/shared-bindings/wifi/__init__.c @@ -11,9 +11,10 @@ #include "shared-bindings/wifi/Packet.h" #include "shared-bindings/wifi/Radio.h" -//| """ -//| The `wifi` module provides necessary low-level functionality for managing -//| wifi connections. Use `socketpool` for communicating over the network. +//| """Network communications over WiFi +//| +//| The `wifi` module and its classes manage WiFi connection to a network and the WiFi hardware. +//| Use `socketpool` and `ssl` for network communication. //| """ //| diff --git a/shared-bindings/wifi/__init__.h b/shared-bindings/wifi/__init__.h index a1a606e502c06..c9dc27a70ca23 100644 --- a/shared-bindings/wifi/__init__.h +++ b/shared-bindings/wifi/__init__.h @@ -6,6 +6,7 @@ #pragma once +#include "common-hal/wifi/__init__.h" #include "shared-bindings/wifi/Radio.h" extern wifi_radio_obj_t common_hal_wifi_radio_obj; diff --git a/shared-module/ipaddress/IPv4Address.c b/shared-module/ipaddress/IPv4Address.c index 3fa06e52eae2c..622a7dc2fff13 100644 --- a/shared-module/ipaddress/IPv4Address.c +++ b/shared-module/ipaddress/IPv4Address.c @@ -4,12 +4,33 @@ // // SPDX-License-Identifier: MIT +#include "py/nlr.h" #include "py/obj.h" +#include "py/parsenum.h" #include "shared-bindings/ipaddress/__init__.h" #include "shared-bindings/ipaddress/IPv4Address.h" +uint32_t ipv4_bytes_to_uint32(const uint8_t bytes[4]) { + return bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24; +} + +void ipv4_uint32_to_bytes(uint32_t ipv4, uint8_t bytes[4]) { + bytes[0] = ipv4; + bytes[1] = ipv4 >> 8; + bytes[2] = ipv4 >> 16; + bytes[3] = ipv4 >> 24; +} + +mp_obj_t ipv4_bytes_to_str(const uint8_t ipv4[4]) { + vstr_t vstr; + mp_print_t print; + vstr_init_print(&vstr, 16, &print); + mp_printf(&print, "%d.%d.%d.%d", ipv4[0], ipv4[1], ipv4[2], ipv4[3]); + return mp_obj_new_str_from_vstr(&vstr); +} + void common_hal_ipaddress_ipv4address_construct(ipaddress_ipv4address_obj_t *self, uint8_t *buf, size_t len) { self->ip_bytes = mp_obj_new_bytes(buf, len); } @@ -17,3 +38,58 @@ void common_hal_ipaddress_ipv4address_construct(ipaddress_ipv4address_obj_t *sel mp_obj_t common_hal_ipaddress_ipv4address_get_packed(ipaddress_ipv4address_obj_t *self) { return self->ip_bytes; } + +mp_obj_t common_hal_ipaddress_new_ipv4address(uint32_t value) { + ipaddress_ipv4address_obj_t *self = mp_obj_malloc(ipaddress_ipv4address_obj_t, &ipaddress_ipv4address_type); + common_hal_ipaddress_ipv4address_construct(self, (uint8_t *)&value, 4); + return MP_OBJ_FROM_PTR(self); +} + +mp_obj_t common_hal_ipaddress_new_ipv4address_from_bytes(uint8_t bytes[4]) { + return common_hal_ipaddress_new_ipv4address(ipv4_bytes_to_uint32(bytes)); +} + +void ipaddress_ipv4address_to_bytes(ipaddress_ipv4address_obj_t *ipv4_address, uint8_t ipv4_bytes[4]) { + mp_obj_t packed = common_hal_ipaddress_ipv4address_get_packed(ipv4_address); + size_t len; + const char *bytes = mp_obj_str_get_data(packed, &len); + memcpy(ipv4_bytes, bytes, 4); +} + +bool ipaddress_parse_ipv4address(const char *str_data, size_t str_len, uint32_t *ip_out) { + size_t period_count = 0; + size_t period_index[4] = {0, 0, 0, str_len}; + for (size_t i = 0; i < str_len; i++) { + if (str_data[i] == '.') { + if (period_count < 3) { + period_index[period_count] = i; + } + period_count++; + } + } + if (period_count > 3) { + return false; + } + + size_t last_period = 0; + if (ip_out != NULL) { + *ip_out = 0; + } + for (size_t i = 0; i < 4; i++) { + // Catch exceptions thrown by mp_parse_num_integer + nlr_buf_t nlr; + mp_obj_t octet; + if (nlr_push(&nlr) == 0) { + octet = mp_parse_num_integer((const char *)str_data + last_period, period_index[i] - last_period, 10, NULL); + nlr_pop(); + } else { + return false; + } + last_period = period_index[i] + 1; + if (ip_out != NULL) { + mp_int_t int_octet = MP_OBJ_SMALL_INT_VALUE(octet); + *ip_out |= int_octet << (i * 8); + } + } + return true; +} diff --git a/shared-module/ipaddress/__init__.c b/shared-module/ipaddress/__init__.c index ea04d04480794..f1e3c8ba3f28a 100644 --- a/shared-module/ipaddress/__init__.c +++ b/shared-module/ipaddress/__init__.c @@ -3,12 +3,3 @@ // SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT - -#include "shared-bindings/ipaddress/__init__.h" -#include "shared-bindings/ipaddress/IPv4Address.h" - -mp_obj_t common_hal_ipaddress_new_ipv4address(uint32_t value) { - ipaddress_ipv4address_obj_t *self = mp_obj_malloc(ipaddress_ipv4address_obj_t, &ipaddress_ipv4address_type); - common_hal_ipaddress_ipv4address_construct(self, (uint8_t *)&value, 4); - return MP_OBJ_FROM_PTR(self); -} diff --git a/supervisor/port_heap.h b/supervisor/port_heap.h index 3e6b5a660fa79..f41150dda5c60 100644 --- a/supervisor/port_heap.h +++ b/supervisor/port_heap.h @@ -21,11 +21,14 @@ void port_heap_init(void); void *port_malloc(size_t size, bool dma_capable); +// Call m_malloc_fail if failed. +void *port_malloc_check(size_t size, bool dma_capable); void *port_malloc_zero(size_t size, bool dma_capable); void port_free(void *ptr); void *port_realloc(void *ptr, size_t size, bool dma_capable); +void *port_realloc_check(void *ptr, size_t size, bool dma_capable); #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE // Check if a buffer pointer is in DMA-capable memory. DMA-capable memory is also accessible during diff --git a/supervisor/shared/port.c b/supervisor/shared/port.c index aabf003fb302d..33169cd5b39f2 100644 --- a/supervisor/shared/port.c +++ b/supervisor/shared/port.c @@ -43,6 +43,14 @@ MP_WEAK void *port_malloc(size_t size, bool dma_capable) { return block; } +void *port_malloc_check(size_t size, bool dma_capable) { + void *p = port_malloc(size, dma_capable); + if (!p) { + m_malloc_fail(size); + } + return p; +} + // Ensure allocated memory is zero. MP_WEAK void *port_malloc_zero(size_t size, bool dma_capable) { void *ptr = port_malloc(size, dma_capable); @@ -60,6 +68,14 @@ MP_WEAK void *port_realloc(void *ptr, size_t size, bool dma_capable) { return tlsf_realloc(heap, ptr, size); } +void *port_realloc_check(void *ptr, size_t size, bool dma_capable) { + void *p = port_realloc(ptr, size, dma_capable); + if (!p) { + m_malloc_fail(size); + } + return p; +} + static bool max_size_walker(void *ptr, size_t size, int used, void *user) { size_t *max_size = (size_t *)user; if (!used && *max_size < size) { From 9208d685a636f73a2498ef07949aaa90a7274ade Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 22 Jan 2026 16:59:56 -0500 Subject: [PATCH 3/4] UDP working both ways --- devices/airlift/common-hal/socketpool/Socket.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devices/airlift/common-hal/socketpool/Socket.c b/devices/airlift/common-hal/socketpool/Socket.c index 0389894e0787f..1bbaf67d4d6ba 100644 --- a/devices/airlift/common-hal/socketpool/Socket.c +++ b/devices/airlift/common-hal/socketpool/Socket.c @@ -542,7 +542,8 @@ mp_int_t common_hal_socketpool_socket_get_type(socketpool_socket_obj_t *self) { int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int level, int optname, const void *value, size_t optlen) { - mp_raise_NotImplementedError(NULL); // TODO + //// mp_raise_NotImplementedError(NULL); // TODO + return 0; } bool common_hal_socketpool_socket_readable(socketpool_socket_obj_t *self) { From 8c7f994abea7bd5865dbacf6d817935a43690797 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 24 Jan 2026 16:51:27 -0500 Subject: [PATCH 4/4] getting closer on HTTPS server --- .../airlift/common-hal/socketpool/Socket.c | 121 ++++++++++++++---- .../airlift/common-hal/socketpool/Socket.h | 2 +- devices/airlift/common-hal/ssl/SSLSocket.c | 1 - devices/airlift/common-hal/wifi/Radio.c | 3 +- shared-bindings/socketpool/Socket.c | 12 +- 5 files changed, 104 insertions(+), 35 deletions(-) diff --git a/devices/airlift/common-hal/socketpool/Socket.c b/devices/airlift/common-hal/socketpool/Socket.c index 1bbaf67d4d6ba..f5e7004b4ebad 100644 --- a/devices/airlift/common-hal/socketpool/Socket.c +++ b/devices/airlift/common-hal/socketpool/Socket.c @@ -165,7 +165,7 @@ static bool socketpool_socket_get_connection_info(socketpool_socket_obj_t *self, -static airlift_socket_status_t socketpool_socket_status(socketpool_socket_obj_t *self) { +static airlift_client_socket_status_t client_socket_status(socketpool_socket_obj_t *self) { const uint8_t *params[1] = { &self->num }; size_t param_lengths[1] = { 1 }; @@ -185,23 +185,16 @@ static airlift_socket_status_t socketpool_socket_status(socketpool_socket_obj_t return result; } -int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, socketpool_socket_obj_t *accepted) { - if (self->type != SOCK_STREAM) { - return -MP_EOPNOTSUPP; - } - - if (common_hal_socketpool_socket_get_closed(self)) { - return -MP_EBADF; - } - +static bool server_socket_status(socketpool_socket_obj_t *self) { const uint8_t *params[1] = { &self->num }; size_t param_lengths[1] = { 1 }; - uint8_t accept_socket_num; - uint8_t *responses[1] = { &accept_socket_num }; + uint8_t result; + uint8_t *responses[1] = { &result }; size_t response_lengths[1] = { 1 }; - size_t num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, AVAIL_DATA_TCP_CMD, + // GET_STATE_TCP_CMD is a misnomer. It only checks whether there's a tcpserver for the socket. + size_t num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, GET_STATE_TCP_CMD, params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), AIRLIFT_DEFAULT_TIMEOUT_MS); @@ -209,19 +202,71 @@ int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, if (num_responses == 0) { raise_failed(); } - if (accept_socket_num == NO_SOCKET) { - return -EBADF; + + return result == 1; +} + +int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, socketpool_socket_obj_t *accepted) { + if (self->type != SOCK_STREAM) { + return -MP_EOPNOTSUPP; + } + + if (!server_socket_status(self)) { + return -MP_ENOTCONN; } - return accept_socket_num; + const uint8_t *params[1] = { &self->num }; + size_t param_lengths[1] = { 1 }; + + uint16_t accept_socket_num; + uint8_t *responses[1] = { (uint8_t *)&accept_socket_num }; + size_t response_lengths[1] = { sizeof(accept_socket_num) }; + + const uint64_t start_time = supervisor_ticks_ms64(); + while (true) { + // When passed a server socket, AVAIL_DATA_TCP_CMD returns the socket number on which to read data. + // For client and UDP sockets, it returns the number of bytes available. (Quite a difference!) + size_t num_responses = wifi_radio_send_command_get_response(self->socketpool->radio, AVAIL_DATA_TCP_CMD, + params, param_lengths, LENGTHS_8, MP_ARRAY_SIZE(params), + responses, response_lengths, LENGTHS_8, MP_ARRAY_SIZE(responses), + AIRLIFT_DEFAULT_TIMEOUT_MS); + + if (num_responses == 0) { + raise_failed(); + } + + if (accept_socket_num != NO_SOCKET) { + return accept_socket_num; + } + + if (self->timeout_ms == 0) { + // Non-blocking raises a different exception than a timeout. + return -MP_EAGAIN; + } + if ((self->timeout_ms != SOCKET_BLOCK_FOREVER && + supervisor_ticks_ms64() - start_time >= self->timeout_ms) || + mp_hal_is_interrupted()) { + return -MP_ETIMEDOUT; + } + + RUN_BACKGROUND_TASKS; + // Give the AirLift some time to do work instead of asking again immediately. + mp_hal_delay_ms(50); + } } + socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out) { socketpool_socket_obj_t *accepted = mp_obj_malloc_with_finaliser(socketpool_socket_obj_t, NULL); socketpool_socket_reset(accepted); int ret = socketpool_socket_accept(self, peer_out, accepted); if (ret < 0) { + if (ret == -MP_ETIMEDOUT) { + // There is a specific subclass for timeouts. + mp_raise_msg(&mp_type_TimeoutError, NULL); + } + // Otherwise, raise a general OSError. Includes EAGAIN. mp_raise_OSError(-ret); } @@ -238,16 +283,27 @@ int common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, mp_raise_OSError(MP_EINVAL); } - // Validate the host name (which might a numeric IP string) to an IPv4 address first. + // Validate the host name (which might be a numeric IP string) to an IPv4 address first. uint8_t ipv4[IPV4_LENGTH]; if (!socketpool_gethostbyname_ipv4(self->socketpool, host, ipv4)) { // Could not resolve hostname. common_hal_socketpool_socketpool_raise_gaierror_noname(); } + const uint8_t zero_ipv4[IPV4_LENGTH] = { 0 }; + uint8_t self_ipv4[IPV4_LENGTH]; + ipv4_uint32_to_bytes(wifi_radio_get_ipv4_address(self->socketpool->radio), self_ipv4); + + // The bound host's IP must be this host: 0.0.0.0 or wifi.radio.ipv4_address. + if (memcmp(ipv4, zero_ipv4, IPV4_LENGTH) != 0 && + memcmp(ipv4, self_ipv4, IPV4_LENGTH) != 0) { + // Same as CPython. + mp_raise_OSError(99); // EADDRNOTAVAIL (sometimes 125!) + } self->bound = true; memcpy(self->hostname, host, hostlen); self->hostname_len = hostlen; + self->port = port; return 0; } @@ -256,9 +312,7 @@ void socketpool_socket_close(socketpool_socket_obj_t *self) { socketpool_socket_stop_client(self); } - if (self->server_started) { - // TODO: how to shut down server? - } + // Re server_started: there is no way to shut down a server. } void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self) { @@ -342,12 +396,13 @@ void socketpool_socket_start_server_mode(socketpool_socket_obj_t *self, airlift_ return; } + uint8_t zero_ipv4[IPV4_LENGTH] = { 0 }; uint8_t port_bytes[2]; be_uint16_to_uint8_bytes((uint16_t)self->port, port_bytes); uint8_t conn_mode = mode; - const uint8_t *params[5] = { self->hostname, port_bytes, &self->num, &conn_mode }; - size_t param_lengths[5] = { self->hostname_len, 4, 2, 1, 1 }; + const uint8_t *params[4] = { zero_ipv4, port_bytes, &self->num, &conn_mode }; + size_t param_lengths[4] = { 4, 2, 1, 1 }; uint8_t result; uint8_t *responses[1] = { &result }; @@ -371,11 +426,23 @@ void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, } bool common_hal_socketpool_socket_get_closed(socketpool_socket_obj_t *self) { - return socketpool_socket_status(self) == SOCKET_CLOSED; + if (self->client_started) { + return client_socket_status(self) == SOCKET_CLOSED; + } else if (self->server_started) { + return !server_socket_status(self); + } else { + return false; + } } bool common_hal_socketpool_socket_get_connected(socketpool_socket_obj_t *self) { - return socketpool_socket_status(self) == SOCKET_ESTABLISHED; + if (self->client_started) { + return client_socket_status(self) == SOCKET_ESTABLISHED; + } else if (self->server_started) { + return server_socket_status(self); + } else { + return false; + } } bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *self, int backlog) { @@ -447,8 +514,10 @@ int socketpool_socket_recv_into(socketpool_socket_obj_t *self, uint8_t *buf, uin mp_hal_is_interrupted()) { return 0; } + RUN_BACKGROUND_TASKS; - mp_hal_delay_ms(500); + // Give the AirLift some time to do work instead of asking again immediately. + mp_hal_delay_ms(50); } } @@ -542,7 +611,7 @@ mp_int_t common_hal_socketpool_socket_get_type(socketpool_socket_obj_t *self) { int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int level, int optname, const void *value, size_t optlen) { - //// mp_raise_NotImplementedError(NULL); // TODO + // TODO: not sure any of this can be implemented. return 0; } diff --git a/devices/airlift/common-hal/socketpool/Socket.h b/devices/airlift/common-hal/socketpool/Socket.h index af00ebb859b3d..b168d4baf33f6 100644 --- a/devices/airlift/common-hal/socketpool/Socket.h +++ b/devices/airlift/common-hal/socketpool/Socket.h @@ -23,7 +23,7 @@ typedef enum { SOCKET_CLOSING = 8, SOCKET_LAST_ACK = 9, SOCKET_TIME_WAIT = 10, -} airlift_socket_status_t; +} airlift_client_socket_status_t; typedef struct ssl_sslsocket_obj ssl_sslsocket_obj_t; diff --git a/devices/airlift/common-hal/ssl/SSLSocket.c b/devices/airlift/common-hal/ssl/SSLSocket.c index 6d721eb12cda3..7db4e052d9573 100644 --- a/devices/airlift/common-hal/ssl/SSLSocket.c +++ b/devices/airlift/common-hal/ssl/SSLSocket.c @@ -55,7 +55,6 @@ void common_hal_ssl_sslsocket_bind(ssl_sslsocket_obj_t *self, mp_obj_t addr_in) } void common_hal_ssl_sslsocket_close(ssl_sslsocket_obj_t *self) { - // TODO: MORE?? common_hal_socketpool_socket_close(self->socket); } diff --git a/devices/airlift/common-hal/wifi/Radio.c b/devices/airlift/common-hal/wifi/Radio.c index 19cecbfa26dd8..d1a20b61fa4f4 100644 --- a/devices/airlift/common-hal/wifi/Radio.c +++ b/devices/airlift/common-hal/wifi/Radio.c @@ -29,7 +29,7 @@ // Print out SPI traffic with AirLift. -#define DEBUG_AIRLIFT 1 +#define DEBUG_AIRLIFT 0 #if DEBUG_AIRLIFT static const char *command_name(uint8_t command) { @@ -420,7 +420,6 @@ size_t wifi_radio_wait_response_cmd(wifi_radio_obj_t *self, uint8_t cmd, response_lengths[i] = read_length; // Read and discard bytes that didn't fit in buffer. - // TODO report error? if (read_length > response_lengths[i]) { #if DEBUG_AIRLIFT PLAT_PRINTF(" Tuple[Socket, Tuple[str, int]]: //| """Accept a connection on a listening socket of type SOCK_STREAM, //| creating a new socket of type SOCK_STREAM. -//| Returns a tuple of (new_socket, remote_address)""" +//| Returns a tuple of ``(new_socket, remote_address)``, +//| where remote address is a tuple of ``(host, port)``. +//| """ //| static mp_obj_t _socketpool_socket_accept(mp_obj_t self_in) { socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -72,7 +74,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(socketpool_socket_accept_obj, _socketpool_socke //| def bind(self, address: Tuple[str, int]) -> None: //| """Bind a socket to an address //| -//| :param ~tuple address: tuple of (remote_address, remote_port)""" +//| :param ~tuple address: tuple of ``(host, port)``""" //| ... //| static mp_obj_t socketpool_socket_bind(mp_obj_t self_in, mp_obj_t addr_in) { @@ -110,7 +112,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(socketpool_socket_close_obj, _socketpool_socket //| def connect(self, address: Tuple[str, int]) -> None: //| """Connect a socket to a remote address //| -//| :param ~tuple address: tuple of (remote_address, remote_port)""" +//| :param ~tuple address: tuple of ``(host, port)``""" //| ... //| static mp_obj_t socketpool_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { @@ -154,7 +156,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_listen_obj, socketpool_socket //| Returns a tuple containing //| //| * the number of bytes received into the given buffer -//| * a remote_address, which is a tuple of ip address and port number +//| * a remote_address, which is a tuple of ``("ip_address", port)``. //| //| :param object buffer: buffer to read into""" //| ... @@ -289,7 +291,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_sendall_obj, _socketpool_sock //| Suits sockets of type SOCK_DGRAM //| //| :param ~bytes bytes: some bytes to send -//| :param ~tuple address: tuple of (remote_address, remote_port)""" +//| :param ~tuple address: tuple of ``(host, port)``""" //| ... //| static mp_obj_t socketpool_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) {