Skip to content

Commit 5c66e69

Browse files
committed
Merge pull request #864 from pguyot/w40/picow-ap-dhcpserver
Add DHCP server to Pico-W in AP mode Use a DHCP server that is bundled with Pico SDK (btstack) The DHCP server unfortunatley has no callback so we cannot implement the network driver callback yet. There still is no DNS server and no router. Also fix a bug where the default SSID name was atomvm-000000000000 because the MAC address wasn't set. Workaround consists in always enabling STA mode. These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 37d3ea9 + e29bf6f commit 5c66e69

File tree

3 files changed

+92
-5
lines changed

3 files changed

+92
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
- Added support for creations of binaries with unaligned strings
2323
- Added `-h` and `-v` flags to generic_unix AtomVM command
2424
- Removed support to ESP32 NVS from network module in order to make it generic. See also [UPDATING.md].
25-
- Added initial support for Pico-W: on-board LED, connection to wifi network.
25+
- Added initial support for Pico-W: on-board LED, Wifi (STA and AP modes).
2626

2727
### Changed
28+
2829
- Changed offset of atomvmlib and of program on Pico. See also [UPDATING.md].
2930

3031
### Fixed

src/platforms/rp2040/src/lib/CMakeLists.txt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,17 @@ if (NOT AVM_USE_32BIT_FLOAT)
6666
endif()
6767

6868
if (PICO_CYW43_SUPPORTED)
69-
target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC pico_cyw43_arch_lwip_threadsafe_background)
69+
set(BTSTACK_ROOT ${PICO_SDK_PATH}/lib/btstack)
70+
set(BTSTACK_3RD_PARTY_PATH ${BTSTACK_ROOT}/3rd-party)
71+
72+
add_library(pan_lwip_dhserver INTERFACE)
73+
target_sources(pan_lwip_dhserver INTERFACE
74+
${BTSTACK_3RD_PARTY_PATH}/lwip/dhcp-server/dhserver.c
75+
)
76+
target_include_directories(pan_lwip_dhserver INTERFACE
77+
${BTSTACK_3RD_PARTY_PATH}/lwip/dhcp-server
78+
)
79+
target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC pico_cyw43_arch_lwip_threadsafe_background INTERFACE pan_lwip_dhserver)
7080
target_link_options(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC "SHELL:-Wl,-u -Wl,networkregister_port_driver")
7181
endif()
7282

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

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#pragma GCC diagnostic ignored "-Wpedantic"
3232

3333
#include <cyw43.h>
34+
#include <dhserver.h>
3435
#include <pico/cyw43_arch.h>
3536

3637
#pragma GCC diagnostic pop
@@ -68,6 +69,7 @@ struct NetworkDriverData
6869
int link_status;
6970
int stas_count;
7071
uint8_t *stas_mac;
72+
struct dhcp_config *dhcp_config;
7173
};
7274

7375
// Callbacks do not allow for user data
@@ -212,7 +214,9 @@ static term start_sta(term sta_config, GlobalContext *global)
212214
static char *get_default_device_name()
213215
{
214216
uint8_t mac[6];
215-
int err = cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, mac);
217+
// Device name is used for AP mode. It seems the interface parameter is
218+
// ignored and both interfaces have the same MAC address.
219+
int err = cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_AP, mac);
216220
if (err) {
217221
return NULL;
218222
}
@@ -268,6 +272,64 @@ static void network_driver_cyw43_assoc_cb(bool assoc)
268272
driver_data->stas_count = nb_stas;
269273
}
270274

275+
static term setup_dhcp_server()
276+
{
277+
int max_stas;
278+
// Supposedly, max_stas doesn't change.
279+
cyw43_wifi_ap_get_max_stas(&cyw43_state, &max_stas);
280+
// max_stas is 10, but let's work for up to 253.
281+
// we do networking on a /24 and we reserve 0, 255 (broadcast) and our own address.
282+
if (max_stas > 253) {
283+
max_stas = 253;
284+
}
285+
286+
size_t dhcp_config_size = sizeof(struct dhcp_config) + max_stas * sizeof(struct dhcp_entry);
287+
driver_data->dhcp_config = malloc(dhcp_config_size);
288+
bzero(driver_data->dhcp_config, dhcp_config_size);
289+
driver_data->dhcp_config->num_entry = max_stas;
290+
driver_data->dhcp_config->entries = (dhcp_entry_t *) ((uint8_t *) driver_data->dhcp_config + sizeof(struct dhcp_config));
291+
uint32_t ip_addr4 = ntohl(ip4_addr_get_u32(netif_ip4_addr(&cyw43_state.netif[CYW43_ITF_AP])));
292+
driver_data->dhcp_config->addr[0] = ip_addr4 >> 24;
293+
driver_data->dhcp_config->addr[1] = (ip_addr4 >> 16) & 0xFF;
294+
driver_data->dhcp_config->addr[2] = (ip_addr4 >> 8) & 0xFF;
295+
driver_data->dhcp_config->addr[3] = ip_addr4 & 0xFF;
296+
297+
int self_last_ip_byte = ip_addr4 & 0xFF;
298+
int dhcp_client_addr = 0;
299+
300+
for (int i = 0; i < max_stas; i++) {
301+
driver_data->dhcp_config->entries[i].addr[0] = ip_addr4 >> 24;
302+
driver_data->dhcp_config->entries[i].addr[1] = (ip_addr4 >> 16) & 0xFF;
303+
driver_data->dhcp_config->entries[i].addr[2] = (ip_addr4 >> 8) & 0xFF;
304+
dhcp_client_addr++;
305+
if (dhcp_client_addr == self_last_ip_byte) {
306+
dhcp_client_addr++;
307+
}
308+
driver_data->dhcp_config->entries[i].addr[3] = dhcp_client_addr;
309+
driver_data->dhcp_config->entries[i].subnet[0] = 255;
310+
driver_data->dhcp_config->entries[i].subnet[1] = 255;
311+
driver_data->dhcp_config->entries[i].subnet[2] = 255;
312+
driver_data->dhcp_config->entries[i].subnet[3] = 0;
313+
driver_data->dhcp_config->entries[i].lease = 86400;
314+
}
315+
316+
// We don't have a DNS server yet but we can't route anything either.
317+
driver_data->dhcp_config->dns[0] = ip_addr4 >> 24;
318+
driver_data->dhcp_config->dns[1] = (ip_addr4 >> 16) & 0xFF;
319+
driver_data->dhcp_config->dns[2] = (ip_addr4 >> 8) & 0xFF;
320+
driver_data->dhcp_config->dns[3] = ip_addr4 & 0xFF;
321+
driver_data->dhcp_config->port = 67;
322+
323+
err_t err = dhserv_init(driver_data->dhcp_config);
324+
if (err) {
325+
free(driver_data->dhcp_config);
326+
driver_data->dhcp_config = NULL;
327+
return BADARG_ATOM;
328+
}
329+
330+
return OK_ATOM;
331+
}
332+
271333
static term start_ap(term ap_config, GlobalContext *global)
272334
{
273335
term ssid_term = interop_kv_get_value(ap_config, ssid_atom, global);
@@ -307,7 +369,9 @@ static term start_ap(term ap_config, GlobalContext *global)
307369
free(ssid);
308370
free(psk);
309371

310-
return OK_ATOM;
372+
// We need to start dhcp server after tcp/ip is setup on AP.
373+
// There can be a race condition here, but clients will retry resending DHCP Requests
374+
return setup_dhcp_server();
311375
}
312376

313377
static void network_driver_netif_status_cb(struct netif *netif)
@@ -341,12 +405,14 @@ static void start_network(Context *ctx, term pid, term ref, term config)
341405
if (driver_data == NULL) {
342406
driver_data = malloc(sizeof(struct NetworkDriverData));
343407
driver_data->stas_mac = NULL;
408+
driver_data->dhcp_config = NULL;
344409
}
345410
driver_data->global = ctx->global;
346411
driver_data->owner_process_id = term_to_local_process_id(pid);
347412
driver_data->ref_ticks = term_to_ref_ticks(ref);
348413
driver_data->link_status = CYW43_LINK_DOWN;
349414
free(driver_data->stas_mac);
415+
free(driver_data->dhcp_config);
350416
driver_data->stas_count = 0;
351417
driver_data->stas_mac = NULL;
352418

@@ -369,7 +435,9 @@ static void start_network(Context *ctx, term pid, term ref, term config)
369435
return;
370436
}
371437
} else {
372-
cyw43_arch_disable_sta_mode();
438+
// Always enable sta mode so the bus is initialized and we get a MAC
439+
// address.
440+
cyw43_arch_enable_sta_mode();
373441
}
374442

375443
if (ap_config) {
@@ -379,6 +447,10 @@ static void start_network(Context *ctx, term pid, term ref, term config)
379447
port_send_reply(ctx, pid, ref, error);
380448
return;
381449
}
450+
if (!sta_config) {
451+
// We can disable sta mode now.
452+
cyw43_arch_disable_sta_mode();
453+
}
382454
} else {
383455
cyw43_arch_disable_ap_mode();
384456
}
@@ -453,6 +525,10 @@ void network_driver_destroy(GlobalContext *global)
453525

454526
if (driver_data) {
455527
free(driver_data->stas_mac);
528+
if (driver_data->dhcp_config) {
529+
dhserv_free();
530+
}
531+
free(driver_data->dhcp_config);
456532
}
457533
free(driver_data);
458534
driver_data = NULL;

0 commit comments

Comments
 (0)