diff --git a/devices/airlift/common-hal/socketpool/Socket.c b/devices/airlift/common-hal/socketpool/Socket.c new file mode 100644 index 0000000000000..f5e7004b4ebad --- /dev/null +++ b/devices/airlift/common-hal/socketpool/Socket.c @@ -0,0 +1,639 @@ +// 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" +///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 +// #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 socket_user_reset(void) { +} + +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) { +} + +// 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); + } +} + +// // 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) { + + sock->proto = IPPROTO_IP; + + // 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) { + + 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) { + 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_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 }; + + 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; +} + +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 result; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { 1 }; + + // 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); + + if (num_responses == 0) { + raise_failed(); + } + + 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; + } + + 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); + } + + return accepted; +} + +int common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, + const char *host, size_t hostlen, uint32_t port) { + 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 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; +} + +void socketpool_socket_close(socketpool_socket_obj_t *self) { + if (self->client_started) { + socketpool_socket_stop_client(self); + } + + // Re server_started: there is no way to shut down a 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 { + // // Otherwise, stop the current client, so we can restart it with a different address. + // socketpool_socket_stop_client(self); + // } + // } + + 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 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 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[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 }; + 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) { + 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) { + 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) { + 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) { + 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) { + 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, 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; + // Give the AirLift some time to do work instead of asking again immediately. + mp_hal_delay_ms(50); + } +} + +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) { + 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) { + 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) { + 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) { + // 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; +} + + +int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int level, int optname, const void *value, size_t optlen) { + // TODO: not sure any of this can be implemented. + 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_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 = NO_SOCKET; +} + +void socketpool_socket_reset(socketpool_socket_obj_t *self) { + 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 new file mode 100644 index 0000000000000..b168d4baf33f6 --- /dev/null +++ b/devices/airlift/common-hal/socketpool/Socket.h @@ -0,0 +1,72 @@ +// 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" +#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_client_socket_status_t; + +typedef struct ssl_sslsocket_obj ssl_sslsocket_obj_t; + +typedef struct { + mp_obj_base_t base; + socketpool_socketpool_obj_t *socketpool; + ssl_sslsocket_obj_t *ssl_socket; + 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 new file mode 100644 index 0000000000000..f737e4eea4cc2 --- /dev/null +++ b/devices/airlift/common-hal/socketpool/SocketPool.c @@ -0,0 +1,93 @@ +// 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/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")); + } + // Not really needed, but more convenient. + self->radio = radio; +} + +// common_hal_socketpool_socket is in socketpool/Socket.c to centralize open socket tracking. + +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) { + 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 new file mode 100644 index 0000000000000..04d2fe0854a98 --- /dev/null +++ b/devices/airlift/common-hal/socketpool/SocketPool.h @@ -0,0 +1,17 @@ +// 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/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 new file mode 100644 index 0000000000000..ac260523b3160 --- /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..bdbe682b41b59 --- /dev/null +++ b/devices/airlift/common-hal/socketpool/__init__.h @@ -0,0 +1,7 @@ +// 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 diff --git a/devices/airlift/common-hal/ssl/SSLContext.c b/devices/airlift/common-hal/ssl/SSLContext.c new file mode 100644 index 0000000000000..17b5f63b2e464 --- /dev/null +++ b/devices/airlift/common-hal/ssl/SSLContext.c @@ -0,0 +1,38 @@ +// 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) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_ssl_sslcontext_set_default_verify_paths(ssl_sslcontext_obj_t *self) { + // 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 true; +} + +void common_hal_ssl_sslcontext_set_check_hostname(ssl_sslcontext_obj_t *self, bool 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) { + mp_raise_NotImplementedError(NULL); +} diff --git a/devices/airlift/common-hal/ssl/SSLContext.h b/devices/airlift/common-hal/ssl/SSLContext.h new file mode 100644 index 0000000000000..99243b6729004 --- /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..7db4e052d9573 --- /dev/null +++ b/devices/airlift/common-hal/ssl/SSLSocket.c @@ -0,0 +1,132 @@ +// 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/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" + +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) { + + 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 *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) { + 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) { + 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) { + 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) { + common_hal_socketpool_socket_close(self->socket); +} + +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); + + 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); + + 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 common_hal_socketpool_socket_get_closed(self->socket); +} + +bool common_hal_ssl_sslsocket_get_connected(ssl_sslsocket_obj_t *self) { + return common_hal_socketpool_socket_get_connected(self->socket); +} + +void common_hal_ssl_sslsocket_listen(ssl_sslsocket_obj_t *self, int backlog) { + mp_raise_NotImplementedError(NULL); +} + +mp_obj_t common_hal_ssl_sslsocket_accept(ssl_sslsocket_obj_t *self) { + 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) { + 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) { + 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); +} + +bool common_hal_ssl_sslsocket_readable(ssl_sslsocket_obj_t *self) { + return common_hal_socketpool_socket_readable(self->socket); +} + +bool common_hal_ssl_sslsocket_writable(ssl_sslsocket_obj_t *self) { + 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 new file mode 100644 index 0000000000000..918a82ab9a1d5 --- /dev/null +++ b/devices/airlift/common-hal/ssl/SSLSocket.h @@ -0,0 +1,19 @@ +// 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" +#include "shared-bindings/socketpool/Socket.h" + +typedef struct ssl_sslsocket_obj { + mp_obj_base_t base; + socketpool_socket_obj_t *socket; + char hostname[MAX_HOSTNAME_LENGTH + 1]; + bool closed; + 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 new file mode 100644 index 0000000000000..c4b99294a39cd --- /dev/null +++ b/devices/airlift/common-hal/ssl/__init__.c @@ -0,0 +1,17 @@ +// 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) { +} 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..a683b1821dec3 --- /dev/null +++ b/devices/airlift/common-hal/wifi/Monitor.c @@ -0,0 +1,59 @@ +// 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; + +void common_hal_wifi_monitor_construct(wifi_monitor_obj_t *self, uint8_t channel, size_t queue) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_wifi_monitor_deinited(void) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_wifi_monitor_deinit(wifi_monitor_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_wifi_monitor_set_channel(wifi_monitor_obj_t *self, uint8_t channel) { + mp_raise_NotImplementedError(NULL); +} + +mp_obj_t common_hal_wifi_monitor_get_channel(wifi_monitor_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +mp_obj_t common_hal_wifi_monitor_get_queue(wifi_monitor_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +mp_obj_t common_hal_wifi_monitor_get_lost(wifi_monitor_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +mp_obj_t common_hal_wifi_monitor_get_queued(wifi_monitor_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +mp_obj_t common_hal_wifi_monitor_get_packet(wifi_monitor_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} diff --git a/devices/airlift/common-hal/wifi/Monitor.h b/devices/airlift/common-hal/wifi/Monitor.h new file mode 100644 index 0000000000000..080aa8e6071fa --- /dev/null +++ b/devices/airlift/common-hal/wifi/Monitor.h @@ -0,0 +1,13 @@ +// 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; +} 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..3adf9c4ca0292 --- /dev/null +++ b/devices/airlift/common-hal/wifi/Network.c @@ -0,0 +1,119 @@ +// 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" + +// 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); +} + +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 self->bssid; +} + +mp_obj_t common_hal_wifi_network_get_rssi(wifi_network_obj_t *self) { + return self->rssi; +} + +mp_obj_t common_hal_wifi_network_get_channel(wifi_network_obj_t *self) { + return self->channel; +} + +mp_obj_t common_hal_wifi_network_get_country(wifi_network_obj_t *self) { + return self->country; +} + +mp_obj_t common_hal_wifi_network_get_authmode(wifi_network_obj_t *self) { + return self->authmode; +} diff --git a/devices/airlift/common-hal/wifi/Network.h b/devices/airlift/common-hal/wifi/Network.h new file mode 100644 index 0000000000000..83fceefb5b367 --- /dev/null +++ b/devices/airlift/common-hal/wifi/Network.h @@ -0,0 +1,21 @@ +// 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; + 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 new file mode 100644 index 0000000000000..d1a20b61fa4f4 --- /dev/null +++ b/devices/airlift/common-hal/wifi/Radio.c @@ -0,0 +1,1238 @@ +// 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 + +#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/busio/SPI.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/ipaddress/IPv4Address.h" +#include "shared-bindings/ipaddress/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/time/__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" + + +// Print out SPI traffic with AirLift. +#define DEBUG_AIRLIFT 0 + +#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_dio, true); + common_hal_busio_spi_unlock(self->spi); + } +} + +// 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_ms) { + if (common_hal_digitalio_digitalinout_get_value(self->ready_dio) == value) { + return; + } + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + break; + } + } + + 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 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 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_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; +} + +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")); + } +} + +// AirLift communication routines, some of which are also called from other classes. + +// 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")); + } +} + +// 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")); + } +} + +// 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); +} + +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 +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) { + spi_end_transaction(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("Error response to AirLift command")); + } + if (r == desired) { + return; + } + mp_hal_delay_ms(10); + } + spi_end_transaction(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("timeout waiting for byte")); +} + +// Check that next byte matches expected value. +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) { + 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_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_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(" 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 + +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) { + check_for_enabled(self); + + 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 }; + + 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; + } +} + + +bool common_hal_wifi_radio_get_enabled(wifi_radio_obj_t *self) { + return self->enabled; +} + +void common_hal_wifi_radio_set_enabled(wifi_radio_obj_t *self, bool enabled) { + if (enabled != self->enabled) { + if (enabled) { + // Do a fresh reset to disable anything going on now. + } + self->enabled = enabled; + } +} + +mp_obj_t common_hal_wifi_radio_get_hostname(wifi_radio_obj_t *self) { + 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_lengths[1] = { strlen(hostname) }; + + uint8_t result = 0; + uint8_t *responses[1] = { &result }; + size_t response_lengths[1] = { 1 }; + + 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) { + check_for_enabled(self); + + uint8_t mac[MAC_ADDRESS_LENGTH]; + if (wifi_radio_get_mac_address(self, mac)) { + return mp_obj_new_bytes(mac, MAC_ADDRESS_LENGTH); + } else { + return mp_const_none; + } +} + +void common_hal_wifi_radio_set_mac_address(wifi_radio_obj_t *self, const uint8_t *mac) { + mp_raise_NotImplementedError(NULL); +} + +mp_float_t common_hal_wifi_radio_get_tx_power(wifi_radio_obj_t *self) { + return 20.0f; +} + +void common_hal_wifi_radio_set_tx_power(wifi_radio_obj_t *self, const mp_float_t tx_power) { + mp_raise_NotImplementedError(NULL); +} + +wifi_power_management_t common_hal_wifi_radio_get_power_management(wifi_radio_obj_t *self) { + 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) { + 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) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_wifi_radio_set_mac_address_ap(wifi_radio_obj_t *self, const uint8_t *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) { + 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) { + // Nothing to do: the scan is done all at once. +} + +void common_hal_wifi_radio_start_station(wifi_radio_obj_t *self) { + common_hal_wifi_radio_set_enabled(self, true); +} + +void common_hal_wifi_radio_stop_station(wifi_radio_obj_t *self) { + 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) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_wifi_radio_get_ap_active(wifi_radio_obj_t *self) { + return mp_const_false; +} + +void common_hal_wifi_radio_stop_ap(wifi_radio_obj_t *self) { + // 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) { + 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 (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 wifi_radio_get_connection_status(self) == WL_CONNECTED; +} + +mp_obj_t common_hal_wifi_radio_get_ap_info(wifi_radio_obj_t *self) { + 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) { + 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) { + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_ipv4_subnet(wifi_radio_obj_t *self) { + 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) { + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_addresses(wifi_radio_obj_t *self) { + 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 mp_const_empty_tuple; +} + +uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) { + 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) { + 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) { + return mp_const_none; +} + +mp_obj_t common_hal_wifi_radio_get_ipv4_dns(wifi_radio_obj_t *self) { + 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) { + 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 (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) { + // not controllable +} + +void common_hal_wifi_radio_start_dhcp_server(wifi_radio_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_wifi_radio_stop_dhcp_server(wifi_radio_obj_t *self) { + 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) { + check_for_enabled(self); + + uint8_t valid_params_arg = 0; + + uint8_t ipv4_bytes[IPV4_LENGTH]; + ipaddress_ipv4address_to_bytes(ipv4, ipv4_bytes); + + uint8_t netmask_bytes[IPV4_LENGTH]; + ipaddress_ipv4address_to_bytes(netmask, netmask_bytes); + + uint8_t gateway_bytes[IPV4_LENGTH]; + ipaddress_ipv4address_to_bytes(gateway, gateway_bytes); + + // 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 }; + + 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")); + } + + if (ipv4_dns != mp_const_none) { + 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) { + 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) { + 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) { + gc_collect_ptr(self); +} + +mp_obj_t common_hal_wifi_radio_get_dns(wifi_radio_obj_t *self) { + check_for_enabled(self); + + mp_obj_t dns[2]; + size_t count; + + wifi_radio_get_dns_config(self, dns, &count); + + // Convert the returned IPv4Address objects to strings. + // Return a tuple of those strings. + + 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) { + 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 new file mode 100644 index 0000000000000..dee1d61249ccd --- /dev/null +++ b/devices/airlift/common-hal/wifi/Radio.h @@ -0,0 +1,213 @@ +// 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" + +// Forward declarations due to mutually referenced typedef struct definitions. +typedef struct wifi_scannednetworks_obj_t wifi_scannednetworks_obj_t; + +// Connection modes +typedef enum { + AIRLIFT_TCP_MODE = 0, + AIRLIFT_UDP_MODE = 1, + AIRLIFT_TLS_MODE = 2, +} airlift_conn_mode_t; + +typedef struct wifi_radio_obj_t { + mp_obj_base_t base; + wifi_scannednetworks_obj_t *current_scan; + uint32_t ping_elapsed_time; + 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_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 + +// Command opcodes +#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. +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_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_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 new file mode 100644 index 0000000000000..cdf86ce32304c --- /dev/null +++ b/devices/airlift/common-hal/wifi/ScannedNetworks.c @@ -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 + +#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/AuthMode.h" +#include "shared-bindings/wifi/Network.h" +#include "shared-bindings/wifi/Radio.h" +#include "shared-bindings/wifi/ScannedNetworks.h" + +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; + } + + 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); +} + +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) { +} diff --git a/devices/airlift/common-hal/wifi/ScannedNetworks.h b/devices/airlift/common-hal/wifi/ScannedNetworks.h new file mode 100644 index 0000000000000..81ea5f03b3079 --- /dev/null +++ b/devices/airlift/common-hal/wifi/ScannedNetworks.h @@ -0,0 +1,28 @@ +// 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" +#include "py/objtuple.h" +#include "shared-bindings/wifi/Radio.h" + + +typedef struct wifi_scannednetworks_obj_t { + mp_obj_base_t base; + mp_obj_tuple_t *networks; + size_t next_network_index; +} wifi_scannednetworks_obj_t; + + +// Forward declaration due to mutually referenced typedef struct definitions. +typedef struct wifi_radio_obj_t wifi_radio_obj_t; + +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 new file mode 100644 index 0000000000000..011c78b5b8b0b --- /dev/null +++ b/devices/airlift/common-hal/wifi/__init__.c @@ -0,0 +1,49 @@ +// 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 "py/runtime.h" + +#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 *radio = &common_hal_wifi_radio_obj; + + // if (wifi_inited) { + // if (user_initiated && !wifi_user_initiated) { + // common_hal_wifi_radio_set_enabled(radio, true); + // } + // return; + // } + // wifi_inited = true; + // wifi_user_initiated = user_initiated; + + radio->base.type = &wifi_radio_type; + common_hal_wifi_radio_mark_deinit(radio); +} + +void wifi_user_reset(void) { + // if (wifi_user_initiated) { + // wifi_reset(); + // wifi_user_initiated = false; + // } +} + +void wifi_reset(void) { + // if (!wifi_inited) { + // return; + // } +} + +void common_hal_wifi_gc_collect(void) { + common_hal_wifi_radio_gc_collect(&common_hal_wifi_radio_obj); +} diff --git a/devices/airlift/common-hal/wifi/__init__.h b/devices/airlift/common-hal/wifi/__init__.h new file mode 100644 index 0000000000000..2cfdcb54b3197 --- /dev/null +++ b/devices/airlift/common-hal/wifi/__init__.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" + +struct sockaddr_storage; + +void wifi_reset(void); 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_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_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_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 83333f8f6a550..640b18ec1bfd7 160000 --- a/frozen/Adafruit_CircuitPython_LIS3DH +++ b/frozen/Adafruit_CircuitPython_LIS3DH @@ -1 +1 @@ -Subproject commit 83333f8f6a5501f9cbee215ebc791cd2fb67bd7d +Subproject commit 640b18ec1bfd71e0a70f7ff3b8784043cd2d2671 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 059c805bd1f91..067b1b80dfd42 160000 --- a/frozen/Adafruit_CircuitPython_PortalBase +++ b/frozen/Adafruit_CircuitPython_PortalBase @@ -1 +1 @@ -Subproject commit 059c805bd1f9157495d17739c8082f9a2042bdbf +Subproject commit 067b1b80dfd42fe1b62416eef2790b7d99d46206 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_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/locale/circuitpython.pot b/locale/circuitpython.pot index bcc7d1dd1ca9d..2cb1dd4c8ffca 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -172,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 "" @@ -980,6 +984,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 "" @@ -1216,7 +1224,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 "" @@ -2681,10 +2689,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 @@ -4127,10 +4131,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/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/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/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..f34f9b8b4058e 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 @@ -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 dc5311bb8adc8..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,6 +73,10 @@ static void set_mode_ap(wifi_radio_obj_t *self, bool state) { self->ap_mode = state; } +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) { return self->started; } @@ -623,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/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index a967b8a4f15f5..ce36c83a14f21 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/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 3c22c3548d191..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]) @@ -52,6 +50,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; } @@ -476,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/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..1804348f292ae 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 @@ -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 56c836cb3a490..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 @@ -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,25 @@ 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 \ + wifi/AuthMode.c \ + wifi/Packet.c \ + wifi/PowerManagement.c +endif + ifeq ($(CIRCUITPY_SAFEMODE_PY),1) SRC_BINDINGS_ENUMS += \ supervisor/SafeModeReason.c @@ -809,18 +846,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..c71598052a869 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,16 +534,36 @@ 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 ?= $(CIRCUITPY_WIFI_NATIVE) +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_NATIVE ?= 0 +CFLAGS += -DCIRCUITPY_SSL_NATIVE=$(CIRCUITPY_SSL_NATIVE) -CIRCUITPY_SSL ?= $(CIRCUITPY_WIFI) +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 @@ -718,12 +738,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/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 24c02a16e634b..6e60895ac3620 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..8a5ef84870b6a 100644 --- a/shared-bindings/socketpool/Socket.c +++ b/shared-bindings/socketpool/Socket.c @@ -57,7 +57,9 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socketpool_socket___exit___obj, 4, 4, //| def accept(self) -> 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) { @@ -152,8 +154,9 @@ 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 +//| * a remote_address, which is a tuple of ``("ip_address", port)``. //| //| :param object buffer: buffer to read into""" //| ... @@ -288,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) { @@ -376,7 +379,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 +455,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 9a7d445fd0473..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,16 +78,125 @@ static void validate_hex_password(const uint8_t *buf, size_t len) { //| ... //| + +//| def init_airlift( +//| spi: busio.SPI, +//| cs: digitalio.DigitalInOut, +//| ready: digitalio.DigitalInOut, +//| reset: digitalio.DigitalInOut, +//| gpio0: Optional[digitalio.DigitalInOut] = None, +//| ) -> None: +//| """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 +//| :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) { + #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 }, + { 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; + #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); + +//| 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).""" +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. //| """ -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); @@ -104,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); @@ -118,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; @@ -139,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); @@ -152,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; @@ -173,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; } @@ -194,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) { @@ -223,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); @@ -236,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; @@ -260,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. //| """ //| ... //| @@ -272,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); @@ -296,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); @@ -307,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; } @@ -317,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; } @@ -347,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. @@ -357,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. //| """ //| ... //| @@ -459,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) { @@ -476,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); } @@ -498,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 @@ -511,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); @@ -587,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. //| """ //| ... //| @@ -597,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); @@ -701,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; } @@ -722,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) { @@ -801,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. //| """ //| ... //| @@ -826,11 +991,12 @@ 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[] = { + { 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) }, @@ -877,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 d3c12344b8706..a6a9981bb0d2d 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" @@ -15,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; @@ -59,16 +63,24 @@ 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); + 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); 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) {