forked from micropython/micropython
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
zephyr-cp/wifi: implement station connect, IPv4 getters and real authmode #11228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
8267c84
zephyr-cp/wifi: implement station connect
mikeysklar 5be3e58
zephyr-cp/wifi: per-AP security selection, real authmode, IPv4 getters
mikeysklar 7596900
zephyr-cp/wifi: fix the "ip" field in version.json
mikeysklar 0613913
zephyr-cp/wifi: fix wifi.radio.addresses
mikeysklar 689420c
zephyr-cp/wifi: expose raw MAC getter, guard net_if_ip.ipv4 access
mikeysklar 090f819
zephyr-cp/wifi: use WPA_AUTO_PERSONAL and drop the scan cache
mikeysklar 6429231
zephyr-cp/wifi: leave the scan result handler alone
mikeysklar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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> | ||
|
|
@@ -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); | ||
| } | ||
|
|
||
|
|
@@ -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; | ||
| 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; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, ¶ms, 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; | ||
| } | ||
|
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; | ||
| } | ||
|
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) { | ||
|
|
@@ -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) { | ||
|
|
@@ -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) { | ||
|
|
@@ -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. | ||
|
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) { | ||
|
|
@@ -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; | ||
| } | ||
|
|
||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.