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)
212214static 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+
271333static 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
313377static 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