From 7d3972bb938df35099b39a1bba7e591c383b145b Mon Sep 17 00:00:00 2001 From: Winford Date: Tue, 28 May 2024 02:32:54 -0700 Subject: [PATCH] Add pico_w network driver support for setting dhcp_hostname Adds support for setting `dhcp_hostname` from `sta_config_options()` and/or `ap_config_options()`, or, if undefined, sets the hostname to the default device name of `atomvm-${DEVICE_MAC}`. Formerly all pico_w devices tried to register to an access point with the factory default `PicoW` hostname, which would make all subsequent pico devices to connect to an access point, after the first one, unreachable by hostname. Closes #1094 Signed-off-by: Winford --- CHANGELOG.md | 2 + src/platforms/rp2040/src/lib/networkdriver.c | 141 ++++++++++++++++--- 2 files changed, 125 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 528fbaa96..befb12650 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support for `erlang:apply/2` - Support for `lists:keystore/4` - Support for `erlang:size/1` bif +- Added support to Pico-W network driver for setting `dhcp_hostname` +(see issue 1094)[https://github.com/atomvm/AtomVM/issues/1094]. ### Changed diff --git a/src/platforms/rp2040/src/lib/networkdriver.c b/src/platforms/rp2040/src/lib/networkdriver.c index 240d66528..2f23d21e0 100644 --- a/src/platforms/rp2040/src/lib/networkdriver.c +++ b/src/platforms/rp2040/src/lib/networkdriver.c @@ -40,12 +40,14 @@ #define PORT_REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE) #define DEFAULT_HOSTNAME_SIZE (strlen("atomvm-") + 12 + 1) +#define MAX_HOSTNAME_SIZE (32 + 1) static const char *const ap_atom = ATOM_STR("\x2", "ap"); static const char *const ap_channel_atom = ATOM_STR("\xA", "ap_channel"); static const char *const ap_sta_connected_atom = ATOM_STR("\x10", "ap_sta_connected"); static const char *const ap_sta_disconnected_atom = ATOM_STR("\x13", "ap_sta_disconnected"); static const char *const ap_started_atom = ATOM_STR("\xA", "ap_started"); +static const char *const dhcp_hostname_atom = ATOM_STR("\xD", "dhcp_hostname"); static const char *const host_atom = ATOM_STR("\x4", "host"); static const char *const psk_atom = ATOM_STR("\x3", "psk"); static const char *const sntp_atom = ATOM_STR("\x4", "sntp"); @@ -80,6 +82,8 @@ struct NetworkDriverData int stas_count; uint8_t *stas_mac; struct dhcp_config *dhcp_config; + char *hostname; + char *ap_hostname; queue_t queue; }; @@ -212,10 +216,30 @@ static void send_sntp_sync(struct timeval *tv) END_WITH_STACK_HEAP(heap, driver_data->global); } +// param should be pointer to malloc'd destination to copy device hostname +// return ok atom or error as atom +static term get_default_device_name(char *name, GlobalContext *global) +{ + uint8_t mac[6]; + // Device name is used for AP mode ssid (if undefined), and for the + // default dhcp_hostname on both interfaces. It seems the interface + // parameter is ignored and both interfaces have the same MAC address. + int err = cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_AP, mac); + if (err) { + return globalcontext_make_atom(global, ATOM_STR("\x10", "device_mac_error")); + } + + size_t buf_size = DEFAULT_HOSTNAME_SIZE; + snprintf(name, buf_size, + "atomvm-%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return OK_ATOM; +} + static term start_sta(term sta_config, GlobalContext *global) { term ssid_term = interop_kv_get_value(sta_config, ssid_atom, global); term pass_term = interop_kv_get_value(sta_config, psk_atom, global); + term hostname_term = interop_kv_get_value(sta_config, dhcp_hostname_atom, global); // // Check parameters @@ -236,8 +260,53 @@ static term start_sta(term sta_config, GlobalContext *global) return BADARG_ATOM; } } + if (term_is_invalid_term(hostname_term)) { + size_t buf_size = DEFAULT_HOSTNAME_SIZE; + driver_data->hostname = malloc(buf_size); + if (IS_NULL_PTR(driver_data->hostname)) { + free(ssid); + free(psk); + return OUT_OF_MEMORY_ATOM; + } else { + term error = get_default_device_name(driver_data->hostname, global); + if (error != OK_ATOM) { + free(ssid); + free(psk); + free(driver_data->hostname); + return error; + } + } + } else { + int ok = 0; + char *buf = malloc(MAX_HOSTNAME_SIZE); + if (IS_NULL_PTR(buf)) { + free(ssid); + free(psk); + return OUT_OF_MEMORY_ATOM; + } else { + buf = interop_term_to_string(hostname_term, &ok); + } + if (!ok || IS_NULL_PTR(buf)) { + free(ssid); + free(psk); + return BADARG_ATOM; + } + driver_data->hostname = malloc(strlen(buf) + 1); + if (IS_NULL_PTR(driver_data->hostname)) { + free(ssid); + free(psk); + free(buf); + return OUT_OF_MEMORY_ATOM; + } + memcpy(driver_data->hostname, buf, strlen(buf) + 1); + free(buf); + } cyw43_arch_enable_sta_mode(); + + // hostname must be set after enabling sta mode, or it will be overwritten to factory "PicoW". + netif_set_hostname(&cyw43_state.netif[CYW43_ITF_STA], driver_data->hostname); + uint32_t auth = (psk == NULL) ? CYW43_AUTH_OPEN : CYW43_AUTH_WPA2_MIXED_PSK; int result = cyw43_arch_wifi_connect_async(ssid, psk, auth); // We need to set the callback after calling connect async because it's @@ -254,23 +323,6 @@ static term start_sta(term sta_config, GlobalContext *global) return OK_ATOM; } -// param should be pointer to malloc'd destination to copy device hostname -// return ok atom or error as atom -static term get_default_device_name(char *name) -{ - uint8_t mac[6]; - // Device name is used for AP mode ssid (if undefined), and for the - // default dhcp_hostname on both interfaces. It seems the interface - // parameter is ignored and both interfaces have the same MAC address. - int err = cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_AP, mac); - if (err) { - return globalcontext_make_atom(global, ATOM_STR("\x10", "device_mac_error")); - } - - snprintf(name, buf_size, - "atomvm-%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return OK_ATOM; - static void network_driver_do_cyw43_assoc(GlobalContext *glb) { int max_stas; @@ -382,6 +434,7 @@ static term start_ap(term ap_config, GlobalContext *global) term ssid_term = interop_kv_get_value(ap_config, ssid_atom, global); term pass_term = interop_kv_get_value(ap_config, psk_atom, global); term channel_term = interop_kv_get_value(ap_config, ap_channel_atom, global); + term hostname_term = interop_kv_get_value(ap_config, dhcp_hostname_atom, global); // // Check parameters @@ -392,7 +445,7 @@ static term start_ap(term ap_config, GlobalContext *global) if (IS_NULL_PTR(ssid)) { return OUT_OF_MEMORY_ATOM; } - term error = get_default_device_name(ssid); + term error = get_default_device_name(ssid, global); if (error != OK_ATOM) { free(ssid); return error; @@ -426,6 +479,52 @@ static term start_ap(term ap_config, GlobalContext *global) } } + if (term_is_invalid_term(hostname_term)) { + driver_data->ap_hostname = malloc(DEFAULT_HOSTNAME_SIZE); + if (IS_NULL_PTR(driver_data->ap_hostname)) { + free(ssid); + free(psk); + return OUT_OF_MEMORY_ATOM; + } else { + term error = get_default_device_name(driver_data->ap_hostname, global); + if (error != OK_ATOM) { + free(ssid); + free(psk); + free(driver_data->ap_hostname); + return error; + } + } + } else { + int ok = 0; + char *buf = malloc(MAX_HOSTNAME_SIZE); + if (IS_NULL_PTR(buf)) { + free(ssid); + free(psk); + return OUT_OF_MEMORY_ATOM; + } else { + buf = interop_term_to_string(hostname_term, &ok); + } + if (UNLIKELY(!ok || buf == NULL)) { + free(ssid); + free(psk); + if (buf != NULL) { + free(buf); + } + return BADARG_ATOM; + } + driver_data->ap_hostname = malloc(strlen(buf) + 1); + if (IS_NULL_PTR(driver_data->ap_hostname)) { + free(ssid); + free(psk); + free(buf); + return OUT_OF_MEMORY_ATOM; + } + memcpy(driver_data->ap_hostname, buf, strlen(buf) + 1); + free(buf); + } + + netif_set_hostname(&cyw43_state.netif[CYW43_ITF_AP], driver_data->ap_hostname); + uint32_t auth = (psk == NULL) ? CYW43_AUTH_OPEN : CYW43_AUTH_WPA2_AES_PSK; cyw43_state.assoc_cb = network_driver_cyw43_assoc_cb; cyw43_arch_enable_ap_mode(ssid, psk, auth); @@ -712,6 +811,12 @@ void network_driver_init(GlobalContext *global) void network_driver_destroy(GlobalContext *global) { if (driver_data) { + if (driver_data->hostname) { + free(driver_data->hostname); + } + if (driver_data->ap_hostname) { + free(driver_data->ap_hostname); + } free(driver_data->sntp_hostname); free(driver_data->stas_mac); if (driver_data->dhcp_config) {