Skip to content

Commit 8341a72

Browse files
committed
Set inique dhcp_hostname, or configured name on pico_w
Correctly set `dhcp_hostname` from `sta_config_options()` and/or `ap_config_options()`, or, if undefined, sets a unique 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, and cause unpredictable problems for some routers. Fixes #1094 on release-0.6 branch IMPORTANT: DO NOT FORWARD this commit to main branch. Breaking changes for rp2 (formerly rp2040) platform necessitate a seaprate PR to fix this issue for release-0.6, a fix has already been submitted to main branch (PR #1173). Signed-off-by: Winford <[email protected]>
1 parent 42d2944 commit 8341a72

File tree

2 files changed

+124
-30
lines changed

2 files changed

+124
-30
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ with nodejs and emscripten)
1616
- Added `uart:read/2` with a timeout parameter.
1717
- Missing `erlang:is_function/2` BIF
1818
- Added `erlang:is_record/2`
19+
- Added ability to set per-interface `dhcp_hostname` on Pico W if present in config.
1920

2021
### Fixed
2122

@@ -67,6 +68,9 @@ memory error
6768
- Fixed nif_atomvm_posix_read GC bug
6869
- Fixed `erlang:is_number/1` function, now returns true also for floats
6970
- Fixed unlink protocol and add support for `link/1` on ports
71+
- Correctly set Pico-W unique dhcp hostname when using the default, previously all rp2040 devices
72+
used the same "PicoW" dhcp hostname, causing collisions when multiple rp2040 are on the same
73+
network. (See issue #1094)
7074

7175
### Changed
7276

src/platforms/rp2040/src/lib/networkdriver.c

Lines changed: 120 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,20 @@
3535
#include <hardware/rtc.h>
3636
#include <lwip/apps/sntp.h>
3737
#include <pico/cyw43_arch.h>
38+
#include <string.h>
3839

3940
#pragma GCC diagnostic pop
4041

4142
#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE)
43+
#define DEFAULT_HOSTNAME_FMT "atomvm-%02x%02x%02x%02x%02x%02x"
44+
#define DEFAULT_HOSTNAME_SIZE (strlen("atomvm-") + 12 + 1)
4245

4346
static const char *const ap_atom = ATOM_STR("\x2", "ap");
4447
static const char *const ap_channel_atom = ATOM_STR("\xA", "ap_channel");
4548
static const char *const ap_sta_connected_atom = ATOM_STR("\x10", "ap_sta_connected");
4649
static const char *const ap_sta_disconnected_atom = ATOM_STR("\x13", "ap_sta_disconnected");
4750
static const char *const ap_started_atom = ATOM_STR("\xA", "ap_started");
51+
static const char *const dhcp_hostname_atom = ATOM_STR("\xD", "dhcp_hostname");
4852
static const char *const host_atom = ATOM_STR("\x4", "host");
4953
static const char *const psk_atom = ATOM_STR("\x3", "psk");
5054
static const char *const sntp_atom = ATOM_STR("\x4", "sntp");
@@ -79,6 +83,8 @@ struct NetworkDriverData
7983
int stas_count;
8084
uint8_t *stas_mac;
8185
struct dhcp_config *dhcp_config;
86+
char *hostname;
87+
char *ap_hostname;
8288
queue_t queue;
8389
};
8490

@@ -100,6 +106,14 @@ struct NetworkDriverEvent
100106
};
101107
};
102108

109+
enum DriverErrorCodeType
110+
{
111+
DriverOK,
112+
DriverBADARG,
113+
DriverMACError,
114+
DriverOOM
115+
};
116+
103117
// Callbacks do not allow for user data
104118
// netif->state is actually pointing to &cyw43_state
105119
static struct NetworkDriverData *driver_data;
@@ -120,6 +134,26 @@ static term tuple_from_addr(Heap *heap, uint32_t addr)
120134
return port_heap_create_tuple_n(heap, 4, terms);
121135
}
122136

137+
static term error_code_to_term(int error, GlobalContext *global)
138+
{
139+
switch (error) {
140+
case DriverOK:
141+
return OK_ATOM;
142+
break;
143+
case DriverBADARG:
144+
return BADARG_ATOM;
145+
break;
146+
case DriverMACError:
147+
return globalcontext_make_atom(global, ATOM_STR("\x10", "device_mac_error"));
148+
break;
149+
case DriverOOM:
150+
return OUT_OF_MEMORY_ATOM;
151+
break;
152+
default:
153+
return BADARG_ATOM;
154+
}
155+
}
156+
123157
static void send_term(Heap *heap, term t)
124158
{
125159
term ref = term_from_ref_ticks(driver_data->ref_ticks, heap);
@@ -211,15 +245,62 @@ static void send_sntp_sync(struct timeval *tv)
211245
END_WITH_STACK_HEAP(heap, driver_data->global);
212246
}
213247

248+
static enum DriverErrorCodeType write_default_device_name(size_t size, char **out)
249+
{
250+
uint8_t mac[6];
251+
// Device name is used for AP mode ssid (if undefined), and for the
252+
// default dhcp_hostname on both interfaces. It seems the interface
253+
// parameter is ignored and both interfaces have the same MAC address.
254+
int err = cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, mac);
255+
if (UNLIKELY(err)) {
256+
return DriverMACError;
257+
}
258+
*out = malloc(size);
259+
if (IS_NULL_PTR(out)) {
260+
return DriverOOM;
261+
}
262+
snprintf(*out, size,
263+
DEFAULT_HOSTNAME_FMT, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
264+
//*out = strndup(buf, size);
265+
//free(buf);
266+
return DriverOK;
267+
}
268+
269+
static enum DriverErrorCodeType set_interface_dhcp_name(term dhcp_name, char **out)
270+
{
271+
if (term_is_invalid_term(dhcp_name)) {
272+
enum DriverErrorCodeType ok_ret = write_default_device_name(DEFAULT_HOSTNAME_SIZE, out);
273+
if (UNLIKELY(ok_ret != DriverOK)) {
274+
free(out);
275+
cyw43_arch_disable_sta_mode();
276+
return ok_ret;
277+
}
278+
} else {
279+
int ok = 0;
280+
*out = interop_term_to_string(dhcp_name, &ok);
281+
if (!ok || IS_NULL_PTR(out)) {
282+
if (out != NULL) {
283+
free(out);
284+
cyw43_arch_disable_sta_mode();
285+
return DriverBADARG;
286+
}
287+
cyw43_arch_disable_sta_mode();
288+
return DriverOOM;
289+
}
290+
}
291+
return DriverOK;
292+
}
293+
214294
static term start_sta(term sta_config, GlobalContext *global)
215295
{
216296
term ssid_term = interop_kv_get_value(sta_config, ssid_atom, global);
217297
term pass_term = interop_kv_get_value(sta_config, psk_atom, global);
298+
term hostname_term = interop_kv_get_value(sta_config, dhcp_hostname_atom, global);
218299

219300
//
220301
// Check parameters
221302
//
222-
if (term_is_invalid_term(ssid_term)) {
303+
if (UNLIKELY(term_is_invalid_term(ssid_term))) {
223304
return BADARG_ATOM;
224305
}
225306
int ok = 0;
@@ -230,13 +311,21 @@ static term start_sta(term sta_config, GlobalContext *global)
230311
char *psk = NULL;
231312
if (!term_is_invalid_term(pass_term)) {
232313
psk = interop_term_to_string(pass_term, &ok);
233-
if (!ok) {
314+
if (UNLIKELY(!ok)) {
234315
free(ssid);
235316
return BADARG_ATOM;
236317
}
237318
}
238319

239-
cyw43_arch_enable_sta_mode();
320+
enum DriverErrorCodeType ret = set_interface_dhcp_name(hostname_term, &driver_data->hostname);
321+
if (UNLIKELY(ret != DriverOK)) {
322+
free(ssid);
323+
free(psk);
324+
return error_code_to_term(ret, global);
325+
}
326+
327+
netif_set_hostname(&cyw43_state.netif[CYW43_ITF_STA], driver_data->hostname);
328+
240329
uint32_t auth = (psk == NULL) ? CYW43_AUTH_OPEN : CYW43_AUTH_WPA2_MIXED_PSK;
241330
int result = cyw43_arch_wifi_connect_async(ssid, psk, auth);
242331
// We need to set the callback after calling connect async because it's
@@ -247,32 +336,14 @@ static term start_sta(term sta_config, GlobalContext *global)
247336
free(ssid);
248337
free(psk);
249338
if (result != 0) {
339+
free(driver_data->hostname);
340+
cyw43_arch_disable_sta_mode();
250341
return BADARG_ATOM;
251342
}
252343

253344
return OK_ATOM;
254345
}
255346

256-
static char *get_default_device_name()
257-
{
258-
uint8_t mac[6];
259-
// Device name is used for AP mode. It seems the interface parameter is
260-
// ignored and both interfaces have the same MAC address.
261-
int err = cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_AP, mac);
262-
if (err) {
263-
return NULL;
264-
}
265-
266-
size_t buf_size = strlen("atomvm-") + 12 + 1;
267-
char *buf = malloc(buf_size);
268-
if (IS_NULL_PTR(buf)) {
269-
return NULL;
270-
}
271-
snprintf(buf, buf_size,
272-
"atomvm-%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
273-
return buf;
274-
}
275-
276347
static void network_driver_do_cyw43_assoc(GlobalContext *glb)
277348
{
278349
int max_stas;
@@ -384,13 +455,18 @@ static term start_ap(term ap_config, GlobalContext *global)
384455
term ssid_term = interop_kv_get_value(ap_config, ssid_atom, global);
385456
term pass_term = interop_kv_get_value(ap_config, psk_atom, global);
386457
term channel_term = interop_kv_get_value(ap_config, ap_channel_atom, global);
458+
term hostname_term = interop_kv_get_value(ap_config, dhcp_hostname_atom, global);
387459

388460
//
389461
// Check parameters
390462
//
391463
char *ssid = NULL;
392464
if (term_is_invalid_term(ssid_term)) {
393-
ssid = get_default_device_name();
465+
enum DriverErrorCodeType ret = write_default_device_name(DEFAULT_HOSTNAME_SIZE, &ssid);
466+
if (UNLIKELY(ret != DriverOK)) {
467+
free(ssid);
468+
return error_code_to_term(ret, global);
469+
}
394470
} else {
395471
int ok = 0;
396472
ssid = interop_term_to_string(ssid_term, &ok);
@@ -402,11 +478,11 @@ static term start_ap(term ap_config, GlobalContext *global)
402478
if (!term_is_invalid_term(pass_term)) {
403479
int ok = 0;
404480
psk = interop_term_to_string(pass_term, &ok);
405-
if (strlen(psk) < 8) {
481+
if (UNLIKELY(strlen(psk) < 8)) {
406482
free(ssid);
407483
return BADARG_ATOM;
408484
}
409-
if (!ok) {
485+
if (UNLIKELY(!ok)) {
410486
free(ssid);
411487
return BADARG_ATOM;
412488
}
@@ -419,9 +495,18 @@ static term start_ap(term ap_config, GlobalContext *global)
419495
}
420496
}
421497

498+
enum DriverErrorCodeType ret = set_interface_dhcp_name(hostname_term, &driver_data->ap_hostname);
499+
if (UNLIKELY(ret != DriverOK)) {
500+
free(ssid);
501+
free(psk);
502+
return error_code_to_term(ret, global);
503+
}
504+
422505
uint32_t auth = (psk == NULL) ? CYW43_AUTH_OPEN : CYW43_AUTH_WPA2_AES_PSK;
423506
cyw43_state.assoc_cb = network_driver_cyw43_assoc_cb;
424507
cyw43_arch_enable_ap_mode(ssid, psk, auth);
508+
// Set hostname after enabling AP mode otherwise hostname will revert to "PicoW"
509+
netif_set_hostname(&cyw43_state.netif[CYW43_ITF_AP], driver_data->ap_hostname);
425510
send_ap_started(global);
426511
free(ssid);
427512
free(psk);
@@ -574,17 +659,18 @@ static void start_network(Context *ctx, term pid, term ref, term config)
574659
return;
575660
}
576661

662+
// Always enable sta mode so the bus is initialized and we get a MAC
663+
// address. This is done before configuring the interface because the
664+
// MAC is added to the default hostname, and default ssid in ap mode.
665+
// (i.e. atomvm-0123456789ab)
666+
cyw43_arch_enable_sta_mode();
577667
if (!term_is_invalid_term(sta_config)) {
578668
term result_atom = start_sta(sta_config, ctx->global);
579669
if (result_atom != OK_ATOM) {
580670
term error = port_create_error_tuple(ctx, result_atom);
581671
port_send_reply(ctx, pid, ref, error);
582672
return;
583673
}
584-
} else {
585-
// Always enable sta mode so the bus is initialized and we get a MAC
586-
// address.
587-
cyw43_arch_enable_sta_mode();
588674
}
589675

590676
if (!term_is_invalid_term(ap_config)) {
@@ -705,6 +791,10 @@ void network_driver_init(GlobalContext *global)
705791
void network_driver_destroy(GlobalContext *global)
706792
{
707793
if (driver_data) {
794+
free(driver_data->hostname);
795+
if (driver_data->ap_hostname) {
796+
free(driver_data->ap_hostname);
797+
}
708798
free(driver_data->sntp_hostname);
709799
free(driver_data->stas_mac);
710800
if (driver_data->dhcp_config) {

0 commit comments

Comments
 (0)