diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c1b1793e..55ae94261 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.6.5] - 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.4] - 2024-08-18 ### Added diff --git a/src/platforms/rp2040/src/lib/networkdriver.c b/src/platforms/rp2040/src/lib/networkdriver.c index 240d66528..7a2154bfd 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,55 @@ 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 (UNLIKELY(!ok)) { + free(ssid); + free(psk); + free(buf); + return BADARG_ATOM; + } + driver_data->hostname = strdup(buf); + // Newlib implementation of strdup might return NULL + // See: https://www.sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=newlib/libc/string/strdup_r.c;h=14c80f73f18c1b91454adadc626e415f05141da5;hb=HEAD + if (IS_NULL_PTR(driver_data->hostname)) { + free(ssid); + free(psk); + free(buf); + return OUT_OF_MEMORY_ATOM; + } + 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 +325,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 +436,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 +447,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 +481,50 @@ 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)) { + free(ssid); + free(psk); + free(buf); + return BADARG_ATOM; + } + driver_data->ap_hostname = strdup(buf); + // Newlib implementation of strdup might return NULL + if (IS_NULL_PTR(driver_data->ap_hostname)) { + free(ssid); + free(psk); + free(buf); + return OUT_OF_MEMORY_ATOM; + } + 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,8 @@ void network_driver_init(GlobalContext *global) void network_driver_destroy(GlobalContext *global) { if (driver_data) { + free(driver_data->hostname); + free(driver_data->ap_hostname); free(driver_data->sntp_hostname); free(driver_data->stas_mac); if (driver_data->dhcp_config) {