From 2cfae058eb4a78b34e0663f7a0da7bfb14f847a9 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 | 6 + src/platforms/rp2040/src/lib/networkdriver.c | 140 ++++++++++++++++--- 2 files changed, 128 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc5bbe009..5f3805c44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.3] - Unreleased + +### Added +- Added support to Pico-W network driver for setting `dhcp_hostname` +(see issue 1094)[https://github.com/atomvm/AtomVM/issues/1094]. + ## [0.6.2] - 25-05-2024 ### Added diff --git a/src/platforms/rp2040/src/lib/networkdriver.c b/src/platforms/rp2040/src/lib/networkdriver.c index d4a5d9171..a30a53d19 100644 --- a/src/platforms/rp2040/src/lib/networkdriver.c +++ b/src/platforms/rp2040/src/lib/networkdriver.c @@ -40,11 +40,13 @@ #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_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"); @@ -79,6 +81,8 @@ struct NetworkDriverData int stas_count; uint8_t *stas_mac; struct dhcp_config *dhcp_config; + char *hostname; + char *ap_hostname; queue_t queue; }; @@ -211,10 +215,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 @@ -235,8 +259,54 @@ static term start_sta(term sta_config, GlobalContext *global) return BADARG_ATOM; } } + char *hostname = driver_data->hostname; + if (term_is_invalid_term(hostname_term)) { + size_t buf_size = DEFAULT_HOSTNAME_SIZE; + hostname = malloc(buf_size); + if (IS_NULL_PTR(hostname)) { + free(ssid); + free(psk); + return OUT_OF_MEMORY_ATOM; + } else { + term error = get_default_device_name(hostname, global); + if (error != OK_ATOM) { + free(ssid); + free(psk); + free(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; + } + hostname = malloc(strlen(buf) + 1); + if (IS_NULL_PTR(hostname)) { + free(ssid); + free(psk); + free(buf); + return OUT_OF_MEMORY_ATOM; + } + memcpy(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], 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 @@ -253,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; @@ -380,6 +433,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 hostname_term = interop_kv_get_value(ap_config, dhcp_hostname_atom, global); // // Check parameters @@ -390,7 +444,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; @@ -417,6 +471,50 @@ static term start_ap(term ap_config, GlobalContext *global) } } + char *ap_hostname = driver_data->ap_hostname; + if (term_is_invalid_term(hostname_term)) { + ap_hostname = malloc(DEFAULT_HOSTNAME_SIZE); + if (IS_NULL_PTR(ap_hostname)) { + free(ssid); + free(psk); + return OUT_OF_MEMORY_ATOM; + } else { + term error = get_default_device_name(ap_hostname, global); + if (error != OK_ATOM) { + free(ssid); + free(psk); + free(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 (!ok || IS_NULL_PTR(buf)) { + free(ssid); + free(psk); + return BADARG_ATOM; + } + ap_hostname = malloc(strlen(buf) + 1); + if (IS_NULL_PTR(ap_hostname)) { + free(ssid); + free(psk); + free(buf); + return OUT_OF_MEMORY_ATOM; + } + memcpy(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); @@ -703,6 +801,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) {