Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 37 additions & 28 deletions ports/zephyr-cp/common-hal/wifi/Network.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,35 +34,44 @@ mp_obj_t common_hal_wifi_network_get_country(wifi_network_obj_t *self) {
}

mp_obj_t common_hal_wifi_network_get_authmode(wifi_network_obj_t *self) {
// Translate Zephyr's wifi_security_type. An empty list would read as an
// open network to any caller checking for AUTHMODE_OPEN.
uint32_t authmode_mask = 0;
// switch (self->record.authmode) {
// case WIFI_AUTH_OPEN:
// authmode_mask = AUTHMODE_OPEN;
// break;
// case WIFI_AUTH_WEP:
// authmode_mask = AUTHMODE_WEP;
// break;
// case WIFI_AUTH_WPA_PSK:
// authmode_mask = AUTHMODE_WPA | AUTHMODE_PSK;
// break;
// case WIFI_AUTH_WPA2_PSK:
// authmode_mask = AUTHMODE_WPA2 | AUTHMODE_PSK;
// break;
// case WIFI_AUTH_WPA_WPA2_PSK:
// authmode_mask = AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_PSK;
// break;
// case WIFI_AUTH_WPA2_ENTERPRISE:
// authmode_mask = AUTHMODE_WPA2 | AUTHMODE_ENTERPRISE;
// break;
// case WIFI_AUTH_WPA3_PSK:
// authmode_mask = AUTHMODE_WPA3 | AUTHMODE_PSK;
// break;
// case WIFI_AUTH_WPA2_WPA3_PSK:
// authmode_mask = AUTHMODE_WPA2 | AUTHMODE_WPA3 | AUTHMODE_PSK;
// break;
// default:
// break;
// }
switch (self->scan_result.security) {
case WIFI_SECURITY_TYPE_NONE:
authmode_mask = AUTHMODE_OPEN;
break;
case WIFI_SECURITY_TYPE_WEP:
authmode_mask = AUTHMODE_WEP;
break;
case WIFI_SECURITY_TYPE_WPA_PSK:
authmode_mask = AUTHMODE_WPA | AUTHMODE_PSK;
break;
case WIFI_SECURITY_TYPE_PSK:
case WIFI_SECURITY_TYPE_PSK_SHA256:
authmode_mask = AUTHMODE_WPA2 | AUTHMODE_PSK;
break;
case WIFI_SECURITY_TYPE_SAE: // == WIFI_SECURITY_TYPE_SAE_HNP (alias)
case WIFI_SECURITY_TYPE_SAE_H2E:
case WIFI_SECURITY_TYPE_SAE_AUTO:
case WIFI_SECURITY_TYPE_SAE_EXT_KEY:
case WIFI_SECURITY_TYPE_FT_SAE:
authmode_mask = AUTHMODE_WPA3 | AUTHMODE_PSK;
break;
case WIFI_SECURITY_TYPE_WPA_AUTO_PERSONAL:
authmode_mask = AUTHMODE_WPA | AUTHMODE_WPA2 | AUTHMODE_WPA3 | AUTHMODE_PSK;
break;
case WIFI_SECURITY_TYPE_EAP: // == WIFI_SECURITY_TYPE_EAP_TLS (alias)
case WIFI_SECURITY_TYPE_EAP_PEAP_MSCHAPV2:
case WIFI_SECURITY_TYPE_EAP_PEAP_GTC:
case WIFI_SECURITY_TYPE_EAP_TTLS_MSCHAPV2:
case WIFI_SECURITY_TYPE_EAP_PEAP_TLS:
case WIFI_SECURITY_TYPE_FT_EAP:
authmode_mask = AUTHMODE_WPA2 | AUTHMODE_ENTERPRISE;
break;
default:
break;
}
mp_obj_t authmode_list = mp_obj_new_list(0, NULL);
if (authmode_mask != 0) {
for (uint8_t i = 0; i < 32; i++) {
Expand Down
260 changes: 218 additions & 42 deletions ports/zephyr-cp/common-hal/wifi/Radio.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/dhcpv4.h>
// dns_resolve_get_default() for radio.ipv4_dns.
#include <zephyr/net/dns_resolve.h>
#include <zephyr/net/hostname.h>
#include <zephyr/net/wifi.h>
#include <zephyr/net/wifi_mgmt.h>
Expand Down Expand Up @@ -121,8 +124,13 @@ void common_hal_wifi_radio_set_hostname(wifi_radio_obj_t *self, const char *host
}

mp_obj_t common_hal_wifi_radio_get_mac_address(wifi_radio_obj_t *self) {
uint8_t mac[MAC_ADDRESS_LENGTH];
// esp_wifi_get_mac(ESP_IF_WIFI_STA, mac);
uint8_t mac[MAC_ADDRESS_LENGTH] = { 0 };
if (self->sta_netif != NULL) {
struct net_linkaddr *addr = net_if_get_link_addr(self->sta_netif);
if (addr != NULL && addr->len >= MAC_ADDRESS_LENGTH) {
memcpy(mac, addr->addr, MAC_ADDRESS_LENGTH);
}
}
return mp_obj_new_bytes(mac, MAC_ADDRESS_LENGTH);
}

Expand Down Expand Up @@ -457,12 +465,132 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t
// // We're connected, allow us to retry if we get disconnected.
// self->retries_left = self->starting_retries;
// }

struct wifi_connect_req_params params = { 0 };

params.ssid = ssid;
params.ssid_length = ssid_len;
params.band = WIFI_FREQ_BAND_2_4_GHZ;
params.channel = channel == 0 ? WIFI_CHANNEL_ANY : channel;
Comment thread
tannewt marked this conversation as resolved.
params.mfp = WIFI_MFP_OPTIONAL;
params.timeout = SYS_FOREVER_MS;

if (password_len > 0) {
params.psk = password;
params.psk_length = password_len;
// WPA_AUTO_PERSONAL lets the driver settle on WPA2 or WPA3 with the AP
// rather than us guessing: it maps to WPA3-transition, which an AP in
// either mode accepts. WIFI_SECURITY_TYPE_UNKNOWN is not an option, the
// driver rejects it with -ENOTSUP.
params.security = WIFI_SECURITY_TYPE_WPA_AUTO_PERSONAL;
} else {
params.security = WIFI_SECURITY_TYPE_NONE;
}

if (bssid_len == WIFI_MAC_ADDR_LEN) {
memcpy(params.bssid, bssid, WIFI_MAC_ADDR_LEN);
}

// Already associated to the network being asked for: leave the link alone.
// supervisor_start_web_workflow() calls connect() on every invocation, so
// tearing the association down here would churn the link continuously.
if (self->connected &&
ssid_len == self->current_ssid_len &&
memcmp(ssid, self->current_ssid, ssid_len) == 0) {
return WIFI_RADIO_ERROR_NONE;
}

// Switching networks. Connecting while associated returns -EALREADY and the
// failure path takes the interface down, so disconnect first.
if (self->connected) {
// A failure here is tolerated on purpose: if the interface really is
// unusable, the connect below returns a proper error to the caller.
(void)net_mgmt(NET_REQUEST_WIFI_DISCONNECT, self->sta_netif, NULL, 0);
// Give the controller a moment to tear the association down.
for (int i = 0; i < 40 && self->connected; i++) {
RUN_BACKGROUND_TASKS;
k_msleep(50);
}
self->connected = false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Background task here?

}

self->connected = false;
self->last_connect_status = -1;
self->last_disconnect_reason = 0;
k_sem_reset(&self->connect_sem);

int res = net_mgmt(NET_REQUEST_WIFI_CONNECT, self->sta_netif, &params, sizeof(params));
if (res == -EALREADY) {
// Record the SSID as the success path does, so the early return above
// matches on a later connect() to the same network.
self->connected = true;
self->current_ssid_len = MIN(ssid_len, sizeof(self->current_ssid));
memcpy(self->current_ssid, ssid, self->current_ssid_len);
return WIFI_RADIO_ERROR_NONE;
}
if (res < 0) {
return WIFI_RADIO_ERROR_UNSPECIFIED;
}

// Wait for NET_EVENT_WIFI_CONNECT_RESULT (or a DISCONNECT_RESULT standing
// in for a failed attempt), staying responsive to ctrl-C.
mp_float_t timeout_s = timeout <= 0 ? (mp_float_t)10 : timeout;
int64_t deadline = k_uptime_get() + (int64_t)(timeout_s * 1000);
bool signalled = false;
while (k_uptime_get() < deadline) {
RUN_BACKGROUND_TASKS;
if (k_sem_take(&self->connect_sem, K_MSEC(50)) == 0) {
signalled = true;
break;
}
Comment thread
tannewt marked this conversation as resolved.
if (mp_hal_is_interrupted()) {
return WIFI_RADIO_ERROR_UNSPECIFIED;
}
}

if (!signalled) {
return WIFI_RADIO_ERROR_HANDSHAKE_TIMEOUT;
}
if (!self->connected) {
switch (self->last_connect_status) {
case WIFI_STATUS_CONN_WRONG_PASSWORD:
return WIFI_RADIO_ERROR_AUTH_FAIL;
case WIFI_STATUS_CONN_AP_NOT_FOUND:
return WIFI_RADIO_ERROR_NO_AP_FOUND;
case WIFI_STATUS_CONN_TIMEOUT:
return WIFI_RADIO_ERROR_HANDSHAKE_TIMEOUT;
default:
return WIFI_RADIO_ERROR_CONNECTION_FAIL;
}
}

// Remember which network this association is for, so a later connect() for
// the same SSID can return without disturbing it.
self->current_ssid_len = MIN(ssid_len, sizeof(self->current_ssid));
memcpy(self->current_ssid, ssid, self->current_ssid_len);

// Associated. Ask for an address; the AP side of DHCP can take a moment.
#if defined(CONFIG_NET_DHCPV4)
net_dhcpv4_start(self->sta_netif);
int64_t ip_deadline = k_uptime_get() + 15000;
while (k_uptime_get() < ip_deadline) {
if (net_if_ipv4_get_global_addr(self->sta_netif, NET_ADDR_PREFERRED) != NULL) {
break;
}
Comment thread
tannewt marked this conversation as resolved.
if (mp_hal_is_interrupted()) {
break;
}
RUN_BACKGROUND_TASKS;
k_msleep(50);
}
#endif

return WIFI_RADIO_ERROR_NONE;
}

bool common_hal_wifi_radio_get_connected(wifi_radio_obj_t *self) {
// return self->sta_mode && esp_netif_is_netif_up(self->netif);
return false;
return self->connected && self->sta_netif != NULL &&
net_if_is_up(self->sta_netif);
}

mp_obj_t common_hal_wifi_radio_get_ap_info(wifi_radio_obj_t *self) {
Expand Down Expand Up @@ -500,11 +628,23 @@ mp_obj_t common_hal_wifi_radio_get_ap_info(wifi_radio_obj_t *self) {
}

mp_obj_t common_hal_wifi_radio_get_ipv4_gateway(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
if (self->sta_netif == NULL || !net_if_is_up(self->sta_netif)) {
return mp_const_none;
}
// net_if_ip.ipv4 only exists with CONFIG_NET_IPV4, and this file builds for
// every Wi-Fi board in the port, not just ones that enable it.
#if defined(CONFIG_NET_IPV4)
const struct net_if_config *cfg = net_if_get_config(self->sta_netif);
if (cfg == NULL || cfg->ip.ipv4 == NULL) {
return mp_const_none;
}
if (cfg->ip.ipv4->gw.s_addr == 0) {
return mp_const_none;
}
return common_hal_ipaddress_new_ipv4address(cfg->ip.ipv4->gw.s_addr);
#else
return mp_const_none;
// }
// esp_netif_get_ip_info(self->netif, &self->ip_info);
// return common_hal_ipaddress_new_ipv4address(self->ip_info.gw.addr);
#endif
}

mp_obj_t common_hal_wifi_radio_get_ipv4_gateway_ap(wifi_radio_obj_t *self) {
Expand All @@ -516,11 +656,24 @@ mp_obj_t common_hal_wifi_radio_get_ipv4_gateway_ap(wifi_radio_obj_t *self) {
}

mp_obj_t common_hal_wifi_radio_get_ipv4_subnet(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
if (self->sta_netif == NULL || !net_if_is_up(self->sta_netif)) {
return mp_const_none;
}
// See get_ipv4_gateway: net_if_ip.ipv4 needs CONFIG_NET_IPV4.
#if defined(CONFIG_NET_IPV4)
struct net_if_ipv4 *ipv4 = self->sta_netif->config.ip.ipv4;
if (ipv4 == NULL) {
return mp_const_none;
}
for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) {
if (ipv4->unicast[i].ipv4.is_used &&
ipv4->unicast[i].ipv4.addr_state == NET_ADDR_PREFERRED) {
return common_hal_ipaddress_new_ipv4address(
ipv4->unicast[i].netmask.s_addr);
}
}
#endif
return mp_const_none;
// }
// esp_netif_get_ip_info(self->netif, &self->ip_info);
// return common_hal_ipaddress_new_ipv4address(self->ip_info.netmask.addr);
}

mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self) {
Expand Down Expand Up @@ -563,31 +716,48 @@ mp_obj_t common_hal_wifi_radio_get_ipv4_subnet_ap(wifi_radio_obj_t *self) {
// }

mp_obj_t common_hal_wifi_radio_get_addresses(wifi_radio_obj_t *self) {
// return common_hal_wifi_radio_get_addresses_netif(self, self->netif);
return mp_const_none;
// shared-bindings documents this as Sequence[str], empty when not
// connected, so format as a dotted-quad string rather than returning an
// IPv4Address object. Same address as wifi_radio_get_ipv4_address().
uint32_t ipv4_address = wifi_radio_get_ipv4_address(self);
if (ipv4_address == 0) {
return mp_const_empty_tuple;
}
uint8_t *octets = (uint8_t *)&ipv4_address;
char buf[16];
snprintf(buf, sizeof(buf), "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]);
mp_obj_t args[] = { mp_obj_new_str(buf, strlen(buf)) };
return mp_obj_new_tuple(MP_ARRAY_SIZE(args), args);
}

mp_obj_t common_hal_wifi_radio_get_addresses_ap(wifi_radio_obj_t *self) {
// return common_hal_wifi_radio_get_addresses_netif(self, self->ap_netif);
return mp_const_none;
// AP mode is unimplemented here, but mp_const_none is still the wrong type
// for the Sequence[str] contract.
Comment thread
tannewt marked this conversation as resolved.
return mp_const_empty_tuple;
}

uint32_t wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
// return 0;
// }
// esp_netif_get_ip_info(self->netif, &self->ip_info);
// return self->ip_info.ip.addr;
return 0;
// Raw uint32_t sibling of common_hal_wifi_radio_get_ipv4_address(),
// used internally by supervisor/shared/web_workflow/web_workflow.c.
if (self->sta_netif == NULL || !net_if_is_up(self->sta_netif)) {
return 0;
}
struct in_addr *addr = net_if_ipv4_get_global_addr(self->sta_netif, NET_ADDR_PREFERRED);
if (addr == NULL) {
return 0;
}
return addr->s_addr;
}

mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
// return mp_const_none;
// }
// esp_netif_get_ip_info(self->netif, &self->ip_info);
// return common_hal_ipaddress_new_ipv4address(self->ip_info.ip.addr);
return mp_const_none;
if (self->sta_netif == NULL || !net_if_is_up(self->sta_netif)) {
return mp_const_none;
}
struct in_addr *addr = net_if_ipv4_get_global_addr(self->sta_netif, NET_ADDR_PREFERRED);
if (addr == NULL) {
return mp_const_none;
}
return common_hal_ipaddress_new_ipv4address(addr->s_addr);
}

mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self) {
Expand All @@ -600,20 +770,26 @@ mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self) {
}

mp_obj_t common_hal_wifi_radio_get_ipv4_dns(wifi_radio_obj_t *self) {
// if (!esp_netif_is_netif_up(self->netif)) {
// return mp_const_none;
// }

// esp_netif_get_dns_info(self->netif, ESP_NETIF_DNS_MAIN, &self->dns_info);

// if (self->dns_info.ip.type != ESP_IPADDR_TYPE_V4) {
// return mp_const_none;
// }
// // dns_info is of type esp_netif_dns_info_t, which is just ever so slightly
// // different than esp_netif_ip_info_t used for
// // common_hal_wifi_radio_get_ipv4_address (includes both ipv4 and 6),
// // so some extra jumping is required to get to the actual address
// return common_hal_ipaddress_new_ipv4address(self->dns_info.ip.u_addr.ip4.addr);
// Zephyr keeps resolver state in the DNS resolve context rather than on
// the interface, so read it there.
#if defined(CONFIG_DNS_RESOLVER)
if (self->sta_netif == NULL || !net_if_is_up(self->sta_netif)) {
return mp_const_none;
}
struct dns_resolve_context *ctx = dns_resolve_get_default();
if (ctx == NULL) {
return mp_const_none;
}
for (int i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS; i++) {
if (ctx->servers[i].dns_server.sa_family == AF_INET) {
struct sockaddr_in *addr =
(struct sockaddr_in *)&ctx->servers[i].dns_server;
if (addr->sin_addr.s_addr != 0) {
return common_hal_ipaddress_new_ipv4address(addr->sin_addr.s_addr);
}
}
}
#endif
return mp_const_none;
}

Expand Down
Loading
Loading