Skip to content

Commit

Permalink
Add pico_w network driver support for setting dhcp_hostname
Browse files Browse the repository at this point in the history
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 <winford@object.stream>
  • Loading branch information
UncleGrumpy committed May 29, 2024
1 parent 571c904 commit 2cfae05
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 18 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
140 changes: 122 additions & 18 deletions src/platforms/rp2040/src/lib/networkdriver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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;
};

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 2cfae05

Please sign in to comment.