Skip to content

Commit e76ab09

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 <winford@object.stream>
1 parent 42d2944 commit e76ab09

File tree

2 files changed

+122
-30
lines changed

2 files changed

+122
-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: 118 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,60 @@ 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+
return DriverOK;
265+
}
266+
267+
static enum DriverErrorCodeType set_interface_dhcp_name(term dhcp_name, char **out)
268+
{
269+
if (term_is_invalid_term(dhcp_name)) {
270+
enum DriverErrorCodeType ok_ret = write_default_device_name(DEFAULT_HOSTNAME_SIZE, out);
271+
if (UNLIKELY(ok_ret != DriverOK)) {
272+
free(out);
273+
cyw43_arch_disable_sta_mode();
274+
return ok_ret;
275+
}
276+
} else {
277+
int ok = 0;
278+
*out = interop_term_to_string(dhcp_name, &ok);
279+
if (!ok || IS_NULL_PTR(out)) {
280+
if (out != NULL) {
281+
free(out);
282+
cyw43_arch_disable_sta_mode();
283+
return DriverBADARG;
284+
}
285+
cyw43_arch_disable_sta_mode();
286+
return DriverOOM;
287+
}
288+
}
289+
return DriverOK;
290+
}
291+
214292
static term start_sta(term sta_config, GlobalContext *global)
215293
{
216294
term ssid_term = interop_kv_get_value(sta_config, ssid_atom, global);
217295
term pass_term = interop_kv_get_value(sta_config, psk_atom, global);
296+
term hostname_term = interop_kv_get_value(sta_config, dhcp_hostname_atom, global);
218297

219298
//
220299
// Check parameters
221300
//
222-
if (term_is_invalid_term(ssid_term)) {
301+
if (UNLIKELY(term_is_invalid_term(ssid_term))) {
223302
return BADARG_ATOM;
224303
}
225304
int ok = 0;
@@ -230,13 +309,21 @@ static term start_sta(term sta_config, GlobalContext *global)
230309
char *psk = NULL;
231310
if (!term_is_invalid_term(pass_term)) {
232311
psk = interop_term_to_string(pass_term, &ok);
233-
if (!ok) {
312+
if (UNLIKELY(!ok)) {
234313
free(ssid);
235314
return BADARG_ATOM;
236315
}
237316
}
238317

239-
cyw43_arch_enable_sta_mode();
318+
enum DriverErrorCodeType ret = set_interface_dhcp_name(hostname_term, &driver_data->hostname);
319+
if (UNLIKELY(ret != DriverOK)) {
320+
free(ssid);
321+
free(psk);
322+
return error_code_to_term(ret, global);
323+
}
324+
325+
netif_set_hostname(&cyw43_state.netif[CYW43_ITF_STA], driver_data->hostname);
326+
240327
uint32_t auth = (psk == NULL) ? CYW43_AUTH_OPEN : CYW43_AUTH_WPA2_MIXED_PSK;
241328
int result = cyw43_arch_wifi_connect_async(ssid, psk, auth);
242329
// We need to set the callback after calling connect async because it's
@@ -247,32 +334,14 @@ static term start_sta(term sta_config, GlobalContext *global)
247334
free(ssid);
248335
free(psk);
249336
if (result != 0) {
337+
free(driver_data->hostname);
338+
cyw43_arch_disable_sta_mode();
250339
return BADARG_ATOM;
251340
}
252341

253342
return OK_ATOM;
254343
}
255344

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-
276345
static void network_driver_do_cyw43_assoc(GlobalContext *glb)
277346
{
278347
int max_stas;
@@ -384,13 +453,18 @@ static term start_ap(term ap_config, GlobalContext *global)
384453
term ssid_term = interop_kv_get_value(ap_config, ssid_atom, global);
385454
term pass_term = interop_kv_get_value(ap_config, psk_atom, global);
386455
term channel_term = interop_kv_get_value(ap_config, ap_channel_atom, global);
456+
term hostname_term = interop_kv_get_value(ap_config, dhcp_hostname_atom, global);
387457

388458
//
389459
// Check parameters
390460
//
391461
char *ssid = NULL;
392462
if (term_is_invalid_term(ssid_term)) {
393-
ssid = get_default_device_name();
463+
enum DriverErrorCodeType ret = write_default_device_name(DEFAULT_HOSTNAME_SIZE, &ssid);
464+
if (UNLIKELY(ret != DriverOK)) {
465+
free(ssid);
466+
return error_code_to_term(ret, global);
467+
}
394468
} else {
395469
int ok = 0;
396470
ssid = interop_term_to_string(ssid_term, &ok);
@@ -402,11 +476,11 @@ static term start_ap(term ap_config, GlobalContext *global)
402476
if (!term_is_invalid_term(pass_term)) {
403477
int ok = 0;
404478
psk = interop_term_to_string(pass_term, &ok);
405-
if (strlen(psk) < 8) {
479+
if (UNLIKELY(strlen(psk) < 8)) {
406480
free(ssid);
407481
return BADARG_ATOM;
408482
}
409-
if (!ok) {
483+
if (UNLIKELY(!ok)) {
410484
free(ssid);
411485
return BADARG_ATOM;
412486
}
@@ -419,9 +493,18 @@ static term start_ap(term ap_config, GlobalContext *global)
419493
}
420494
}
421495

496+
enum DriverErrorCodeType ret = set_interface_dhcp_name(hostname_term, &driver_data->ap_hostname);
497+
if (UNLIKELY(ret != DriverOK)) {
498+
free(ssid);
499+
free(psk);
500+
return error_code_to_term(ret, global);
501+
}
502+
422503
uint32_t auth = (psk == NULL) ? CYW43_AUTH_OPEN : CYW43_AUTH_WPA2_AES_PSK;
423504
cyw43_state.assoc_cb = network_driver_cyw43_assoc_cb;
424505
cyw43_arch_enable_ap_mode(ssid, psk, auth);
506+
// Set hostname after enabling AP mode otherwise hostname will revert to "PicoW"
507+
netif_set_hostname(&cyw43_state.netif[CYW43_ITF_AP], driver_data->ap_hostname);
425508
send_ap_started(global);
426509
free(ssid);
427510
free(psk);
@@ -574,17 +657,18 @@ static void start_network(Context *ctx, term pid, term ref, term config)
574657
return;
575658
}
576659

660+
// Always enable sta mode so the bus is initialized and we get a MAC
661+
// address. This is done before configuring the interface because the
662+
// MAC is added to the default hostname, and default ssid in ap mode.
663+
// (i.e. atomvm-0123456789ab)
664+
cyw43_arch_enable_sta_mode();
577665
if (!term_is_invalid_term(sta_config)) {
578666
term result_atom = start_sta(sta_config, ctx->global);
579667
if (result_atom != OK_ATOM) {
580668
term error = port_create_error_tuple(ctx, result_atom);
581669
port_send_reply(ctx, pid, ref, error);
582670
return;
583671
}
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();
588672
}
589673

590674
if (!term_is_invalid_term(ap_config)) {
@@ -705,6 +789,10 @@ void network_driver_init(GlobalContext *global)
705789
void network_driver_destroy(GlobalContext *global)
706790
{
707791
if (driver_data) {
792+
free(driver_data->hostname);
793+
if (driver_data->ap_hostname) {
794+
free(driver_data->ap_hostname);
795+
}
708796
free(driver_data->sntp_hostname);
709797
free(driver_data->stas_mac);
710798
if (driver_data->dhcp_config) {

0 commit comments

Comments
 (0)