From b9c1e4c20555a963ffa173781cb26ffe7fadd446 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Fri, 21 Oct 2022 20:51:42 +0200 Subject: [PATCH] drivers/ninaw10: Connect to WiFi asynchronously. Before this patch, WiFi connection was blocking, and could raise exceptions if the connection failed for any reason (including timeouts). This doesn't match the behavior of other WiFi modules, which connect asynchronously, and requires handling of exceptions on connect. This change makes `connect()` work asynchronously by scheduling code to poll connection status, and handle reconnects (if needed), and return immediately without blocking. --- drivers/ninaw10/nina_wifi_drv.c | 37 ++++++--------------------- drivers/ninaw10/nina_wifi_drv.h | 26 +++++++++++++++++++ extmod/network_ninaw10.c | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 29 deletions(-) diff --git a/drivers/ninaw10/nina_wifi_drv.c b/drivers/ninaw10/nina_wifi_drv.c index 7a1167b2c9..22ad0aae70 100644 --- a/drivers/ninaw10/nina_wifi_drv.c +++ b/drivers/ninaw10/nina_wifi_drv.c @@ -108,6 +108,7 @@ typedef enum { // Disonnect/status commands. NINA_CMD_DISCONNECT = 0x30, NINA_CMD_CONN_STATUS = 0x20, + NINA_CMD_CONN_REASON = 0x1F, // Interface config commands. NINA_CMD_SET_IF_CONFIG = 0x14, @@ -173,19 +174,6 @@ typedef enum { NINA_CMD_CMD_DOWNLOAD_OTA = 0x67, } nina_cmd_t; -typedef enum { - NINA_STATUS_IDLE = 0, - NINA_STATUS_NO_SSID_AVAIL, - NINA_STATUS_SCAN_COMPLETED, - NINA_STATUS_CONNECTED, - NINA_STATUS_CONNECT_FAILED, - NINA_STATUS_CONNECTION_LOST, - NINA_STATUS_DISCONNECTED, - NINA_STATUS_AP_LISTENING, - NINA_STATUS_AP_CONNECTED, - NINA_STATUS_AP_FAILED -} nina_status_t; - typedef enum { SOCKET_STATE_CLOSED = 0, SOCKET_STATE_LISTEN, @@ -364,13 +352,15 @@ int nina_deinit(void) { return nina_bsp_deinit(); } -static int nina_connection_status() { +int nina_connection_status(void) { return nina_send_command_read_ack(NINA_CMD_CONN_STATUS, 0, ARG_8BITS, NULL); } -int nina_connect(const char *ssid, uint8_t security, const char *key, uint16_t channel) { - uint8_t status = NINA_STATUS_CONNECT_FAILED; +int nina_connection_reason(void) { + return nina_send_command_read_ack(NINA_CMD_CONN_REASON, 0, ARG_8BITS, NULL); +} +int nina_connect(const char *ssid, uint8_t security, const char *key, uint16_t channel) { if (key == NULL && security != NINA_SEC_OPEN) { return -1; } @@ -398,18 +388,7 @@ int nina_connect(const char *ssid, uint8_t security, const char *key, uint16_t c return -1; } - for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(10)) { - status = nina_connection_status(); - if ((status != NINA_STATUS_IDLE) && (status != NINA_STATUS_NO_SSID_AVAIL) && (status != NINA_STATUS_SCAN_COMPLETED)) { - break; - } - - if ((mp_hal_ticks_ms() - start) >= NINA_CONNECT_TIMEOUT) { - break; - } - } - - return (status == NINA_STATUS_CONNECTED) ? 0 : -1; + return 0; } int nina_start_ap(const char *ssid, uint8_t security, const char *key, uint16_t channel) { @@ -439,7 +418,7 @@ int nina_start_ap(const char *ssid, uint8_t security, const char *key, uint16_t for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(10)) { status = nina_connection_status(); - if ((status != NINA_STATUS_IDLE) && (status != NINA_STATUS_NO_SSID_AVAIL) && (status != NINA_STATUS_SCAN_COMPLETED)) { + if ((status != NINA_STATUS_IDLE) && (status != NINA_STATUS_NO_SSID_AVAIL)) { break; } diff --git a/drivers/ninaw10/nina_wifi_drv.h b/drivers/ninaw10/nina_wifi_drv.h index 22163c7286..4b09ac8bd2 100644 --- a/drivers/ninaw10/nina_wifi_drv.h +++ b/drivers/ninaw10/nina_wifi_drv.h @@ -46,6 +46,17 @@ #define NINA_FW_VER_MINOR_OFFS (2) #define NINA_FW_VER_PATCH_OFFS (4) +#define NINA_ESP_REASON_AUTH_EXPIRE (2) +#define NINA_ESP_REASON_ASSOC_EXPIRE (4) +#define NINA_ESP_REASON_NOT_AUTHED (6) +#define NINA_ESP_REASON_4WAY_HANDSHAKE_TIMEOUT (15) +#define NINA_ESP_REASON_BEACON_TIMEOUT (200) +#define NINA_ESP_REASON_NO_AP_FOUND (201) +#define NINA_ESP_REASON_AUTH_FAIL (202) +#define NINA_ESP_REASON_ASSOC_FAIL (203) +#define NINA_ESP_REASON_HANDSHAKE_TIMEOUT (204) +#define NINA_ESP_REASON_CONNECTION_FAIL (205) + typedef enum { NINA_SEC_INVALID = 0, NINA_SEC_OPEN, @@ -59,6 +70,19 @@ typedef enum { NINA_SOCKET_TYPE_RAW = 3, } nina_socket_type_t; +typedef enum { + NINA_STATUS_IDLE = 0, + NINA_STATUS_NO_SSID_AVAIL = 1, + NINA_STATUS_SCAN_COMPLETED = 2, + NINA_STATUS_CONNECTED = 3, + NINA_STATUS_CONNECT_FAILED = 4, + NINA_STATUS_CONNECTION_LOST = 5, + NINA_STATUS_DISCONNECTED = 6, + NINA_STATUS_AP_LISTENING = 7, + NINA_STATUS_AP_CONNECTED = 8, + NINA_STATUS_AP_FAILED = 9 +} nina_status_t; + typedef struct { uint8_t ip_addr[NINA_IPV4_ADDR_LEN]; uint8_t subnet_addr[NINA_IPV4_ADDR_LEN]; @@ -85,6 +109,8 @@ typedef int (*nina_scan_callback_t)(nina_scan_result_t *, void *); int nina_init(void); int nina_deinit(void); +int nina_connection_status(void); +int nina_connection_reason(void); int nina_connect(const char *ssid, uint8_t security, const char *key, uint16_t channel); int nina_start_ap(const char *ssid, uint8_t security, const char *key, uint16_t channel); int nina_disconnect(void); diff --git a/extmod/network_ninaw10.c b/extmod/network_ninaw10.c index f4cc0b2224..0852deabe9 100644 --- a/extmod/network_ninaw10.c +++ b/extmod/network_ninaw10.c @@ -51,6 +51,9 @@ typedef struct _nina_obj_t { mp_obj_base_t base; bool active; uint32_t itf; + mp_uint_t security; + char ssid[NINA_MAX_SSID_LEN + 1]; + char key[NINA_MAX_WPA_LEN + 1]; } nina_obj_t; // For auto-binding UDP sockets @@ -78,6 +81,7 @@ const mod_network_nic_type_t mod_network_nic_type_nina; static nina_obj_t network_nina_wl_sta = {{(mp_obj_type_t *)&mod_network_nic_type_nina}, false, MOD_NETWORK_STA_IF}; static nina_obj_t network_nina_wl_ap = {{(mp_obj_type_t *)&mod_network_nic_type_nina}, false, MOD_NETWORK_AP_IF}; static mp_sched_node_t mp_wifi_sockpoll_node; +static mp_sched_node_t mp_wifi_connpoll_node; STATIC void network_ninaw10_poll_sockets(mp_sched_node_t *node) { (void)node; @@ -99,6 +103,40 @@ STATIC void network_ninaw10_poll_sockets(mp_sched_node_t *node) { } } +STATIC void network_ninaw10_poll_connect(mp_sched_node_t *node) { + nina_obj_t *self = &network_nina_wl_sta; + + int status = nina_connection_status(); + if (status == NINA_STATUS_CONNECTED) { + // Connected to AP, nothing else to do. + return; + } + + if (status != NINA_STATUS_NO_SSID_AVAIL) { + // If not connected, and no connection in progress, the connection attempt has failed. + // Read the ESP failure reason, reconnect and reschedule the connection polling code. + int reason = nina_connection_reason(); + if (reason == NINA_ESP_REASON_AUTH_EXPIRE || + reason == NINA_ESP_REASON_ASSOC_EXPIRE || + reason == NINA_ESP_REASON_NOT_AUTHED || + reason == NINA_ESP_REASON_4WAY_HANDSHAKE_TIMEOUT || + reason >= NINA_ESP_REASON_BEACON_TIMEOUT) { + debug_printf(&mp_plat_print, "poll_connect() status: %d reason %d\n", status, reason); + if (nina_connect(self->ssid, self->security, self->key, 0) != 0) { + mp_raise_msg_varg(&mp_type_OSError, + MP_ERROR_TEXT("could not connect to ssid=%s, sec=%d, key=%s\n"), + self->ssid, self->security, self->key); + } + } else { + // Will not attempt to reconnect if there's another error code set. + return; + } + } + + // Reschedule the connection polling code. + mp_sched_schedule_node(&mp_wifi_connpoll_node, network_ninaw10_poll_connect); +} + STATIC mp_obj_t network_ninaw10_timer_callback(mp_obj_t none_in) { if (MP_STATE_PORT(mp_wifi_sockpoll_list) != MP_OBJ_NULL && MP_STATE_PORT(mp_wifi_sockpoll_list)->len) { mp_sched_schedule_node(&mp_wifi_sockpoll_node, network_ninaw10_poll_sockets); @@ -240,6 +278,12 @@ STATIC mp_obj_t network_ninaw10_connect(mp_uint_t n_args, const mp_obj_t *pos_ar mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("could not connect to ssid=%s, sec=%d, key=%s\n"), ssid, security, key); } + + // Save connection info to re-connect if needed. + self->security = security; + strncpy(self->key, key, NINA_MAX_WPA_LEN); + strncpy(self->ssid, ssid, NINA_MAX_SSID_LEN); + mp_sched_schedule_node(&mp_wifi_connpoll_node, network_ninaw10_poll_connect); } else { mp_uint_t channel = args[ARG_channel].u_int; @@ -252,6 +296,7 @@ STATIC mp_obj_t network_ninaw10_connect(mp_uint_t n_args, const mp_obj_t *pos_ar mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("failed to start in AP mode")); } } + return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(network_ninaw10_connect_obj, 1, network_ninaw10_connect);