From 3d02cc43d9b85e0d2abaa37cce097033cbd8a584 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Tue, 20 Jun 2023 23:29:41 +0800
Subject: [PATCH] Add w5500-evb-pico support

A few other bits of tidying too
---
 Cargo.lock                           | 14 +++-
 Cargo.toml                           |  3 +-
 embassy/demos/common/src/config.rs   | 55 ++++++++++------
 embassy/demos/picow/Cargo.toml       | 17 +++--
 embassy/demos/picow/src/main.rs      | 79 +++++++++++++----------
 embassy/demos/picow/src/picowmenu.rs | 28 ++++++++
 embassy/demos/picow/src/w5500.rs     | 95 ++++++++++++++++++++++++++++
 embassy/demos/picow/src/wifi.rs      | 37 +++++++----
 8 files changed, 254 insertions(+), 74 deletions(-)
 create mode 100644 embassy/demos/picow/src/w5500.rs

diff --git a/Cargo.lock b/Cargo.lock
index a9a0c8e..843b27c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -931,6 +931,18 @@ dependencies = [
  "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy/?rev=ae83e6f5367197feb8361b9a28adbdedbe37e0c5)",
 ]
 
+[[package]]
+name = "embassy-net-w5500"
+version = "0.1.0"
+source = "git+https://github.com/embassy-rs/embassy/?rev=ae83e6f5367197feb8361b9a28adbdedbe37e0c5#ae83e6f5367197feb8361b9a28adbdedbe37e0c5"
+dependencies = [
+ "embassy-futures",
+ "embassy-net-driver-channel",
+ "embassy-time",
+ "embedded-hal 1.0.0-alpha.10",
+ "embedded-hal-async",
+]
+
 [[package]]
 name = "embassy-rp"
 version = "0.1.0"
@@ -1010,7 +1022,6 @@ name = "embassy-usb"
 version = "0.1.0"
 source = "git+https://github.com/embassy-rs/embassy/?rev=ae83e6f5367197feb8361b9a28adbdedbe37e0c5#ae83e6f5367197feb8361b9a28adbdedbe37e0c5"
 dependencies = [
- "defmt",
  "embassy-futures",
  "embassy-net-driver-channel",
  "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy/?rev=ae83e6f5367197feb8361b9a28adbdedbe37e0c5)",
@@ -2736,6 +2747,7 @@ dependencies = [
  "embassy-futures",
  "embassy-net",
  "embassy-net-driver",
+ "embassy-net-w5500",
  "embassy-rp",
  "embassy-sync 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "embassy-time",
diff --git a/Cargo.toml b/Cargo.toml
index dc0e73c..16907ce 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.2.0-alpha"
 edition = "2021"
 description = "A SSH library suitable for embedded and larger programs"
 repository = "https://github.com/mkj/sunset"
-categories = ["network-programming", "no-std"]
+categories = ["network-programming", "embedded", "no-std"]
 license = "MPL-2.0"
 keywords = ["ssh"]
 
@@ -99,6 +99,7 @@ embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "ae8
 
 cyw43 = { git = "https://github.com/embassy-rs/embassy/", rev = "ae83e6f5367197feb8361b9a28adbdedbe37e0c5" }
 cyw43-pio = { git = "https://github.com/embassy-rs/embassy/", rev = "ae83e6f5367197feb8361b9a28adbdedbe37e0c5" }
+embassy-net-w5500 = { git = "https://github.com/embassy-rs/embassy/", rev = "ae83e6f5367197feb8361b9a28adbdedbe37e0c5" }
 
 bcrypt = { version = "0.14", git = "https://github.com/mkj/rust-bcrypt", branch = "noalloc" }
 
diff --git a/embassy/demos/common/src/config.rs b/embassy/demos/common/src/config.rs
index 3378b31..1585ab9 100644
--- a/embassy/demos/common/src/config.rs
+++ b/embassy/demos/common/src/config.rs
@@ -50,15 +50,27 @@ pub struct SSHConfig {
     pub wifi_net: String<32>,
     /// WPA2 passphrase. None is Open network.
     pub wifi_pw: Option<String<63>>,
+
+    pub mac: [u8; 6],
+}
+
+fn random_mac() -> Result<[u8; 6]> {
+    let mut mac = [0u8; 6];
+    sunset::random::fill_random(&mut mac)?;
+    // unicast, locally administered
+    mac[0] = (mac[0] & 0xfc) | 0x02;
+    Ok(mac)
 }
 
+
 impl SSHConfig {
     /// Bump this when the format changes
-    pub const CURRENT_VERSION: u8 = 4;
+    pub const CURRENT_VERSION: u8 = 5;
     /// A buffer this large will fit any SSHConfig.
     // It can be updated by looking at
-    // `cargo test -- roundtrip_config --show-output`
-    pub const BUF_SIZE: usize = 443;
+    // `cargo test -- roundtrip_config`
+    // in the demos/common directory
+    pub const BUF_SIZE: usize = 449;
 
     /// Creates a new config with default parameters.
     ///
@@ -68,6 +80,7 @@ impl SSHConfig {
 
         let wifi_net = option_env!("WIFI_NET").unwrap_or("guest").into();
         let wifi_pw = option_env!("WIFI_PW").map(|p| p.into());
+        let mac = random_mac()?;
         Ok(SSHConfig {
             hostkey,
             console_pw: None,
@@ -77,6 +90,7 @@ impl SSHConfig {
             admin_keys: Default::default(),
             wifi_net,
             wifi_pw,
+            mac,
         })
     }
 
@@ -143,28 +157,25 @@ impl SSHEncode for SSHConfig {
         info!("enc si");
         enc_signkey(&self.hostkey, s)?;
 
-        info!("enc pw");
         enc_option(&self.console_pw, s)?;
 
         for k in self.console_keys.iter() {
-            info!("enc k");
             enc_option(k, s)?;
         }
 
         self.console_noauth.enc(s)?;
 
-        info!("enc ad");
         enc_option(&self.admin_pw, s)?;
 
         for k in self.admin_keys.iter() {
-            info!("enc ke");
             enc_option(k, s)?;
         }
 
-        info!("enc net");
         self.wifi_net.as_str().enc(s)?;
-        info!("enc netpw");
         enc_option(&self.wifi_pw, s)?;
+
+        self.mac.enc(s)?;
+
         Ok(())
     }
 }
@@ -174,34 +185,29 @@ impl<'de> SSHDecode<'de> for SSHConfig {
     where
         S: SSHSource<'de>,
     {
-        info!("dec si");
         let hostkey = dec_signkey(s)?;
 
-        info!("dec pw");
         let console_pw = dec_option(s)?;
 
         let mut console_keys = [None, None, None];
         for k in console_keys.iter_mut() {
-            info!("dec k");
             *k = dec_option(s)?;
         }
 
         let console_noauth = SSHDecode::dec(s)?;
 
-        info!("dec ad");
         let admin_pw = dec_option(s)?;
 
         let mut admin_keys = [None, None, None];
         for k in admin_keys.iter_mut() {
-            info!("dec adk");
             *k = dec_option(s)?;
         }
 
-        info!("dec wn");
         let wifi_net = SSHDecode::dec(s)?;
-        info!("dec wp");
         let wifi_pw = dec_option(s)?;
 
+        let mac = SSHDecode::dec(s)?;
+
         Ok(Self {
             hostkey,
             console_pw,
@@ -211,6 +217,7 @@ impl<'de> SSHDecode<'de> for SSHConfig {
             admin_keys,
             wifi_net,
             wifi_pw,
+            mac,
         })
     }
 }
@@ -289,12 +296,12 @@ mod tests {
         let mut buf = [0u8; 1000];
         let l = sshwire::write_ssh(&mut buf, &c1).unwrap();
         let v = &buf[..l];
-        let c2: SSHConfig = sshwire::read_ssh(&buf, None).unwrap();
+        let c2: SSHConfig = sshwire::read_ssh(v, None).unwrap();
         assert_eq!(c1, c2);
 
         // All the fruit, to check BUF_SIZE.
         // Variable length fields are all max size.
-        let mut c1 = SSHConfig {
+        let c1 = SSHConfig {
             hostkey: c1.hostkey,
             console_pw: Some(PwHash::new("zong").unwrap()),
             console_keys: [
@@ -313,13 +320,21 @@ mod tests {
             wifi_pw: Some(
                 core::str::from_utf8([b'f'; 63].as_slice()).unwrap().into(),
             ),
+            mac: [6,2,3,4,5,6],
         };
 
-        let mut buf = [0u8; SSHConfig::BUF_SIZE];
+        // test once to determine size to print
+        let mut buf = [0u8; 3000];
         let l = sshwire::write_ssh(&mut buf, &c1).unwrap();
+        let size_msg = format!("BUF_SIZE must be at least {}", l);
+        println!("{size_msg}");
+
+        // now test for real
+        let mut buf = [0u8; SSHConfig::BUF_SIZE];
+        let l = sshwire::write_ssh(&mut buf, &c1).expect(&size_msg);
         println!("BUF_SIZE must be at least {}", l);
         let v = &buf[..l];
-        let c2: SSHConfig = sshwire::read_ssh(&buf, None).unwrap();
+        let c2: SSHConfig = sshwire::read_ssh(v, None).unwrap();
         assert_eq!(c1, c2);
     }
 }
diff --git a/embassy/demos/picow/Cargo.toml b/embassy/demos/picow/Cargo.toml
index a20b656..52b9fac 100644
--- a/embassy/demos/picow/Cargo.toml
+++ b/embassy/demos/picow/Cargo.toml
@@ -9,11 +9,13 @@ sunset = { path = "../../.." }
 sunset-sshwire-derive = { version = "0.1", path = "../../../sshwire-derive" }
 sunset-demo-embassy-common= { path = "../common" }
 
-cyw43 = { version = "0.1.0", features = ["defmt"]}
-cyw43-pio = "0.1.0"
+cyw43 = { version = "0.1.0", optional = true, features = ["defmt"] }
+cyw43-pio = { version = "0.1.0", optional = true }
 # cyw43 = { path = "/home/matt/3rd/rs/cyw43", features = ["defmt"] }
 # cyw43-pio = { path = "/home/matt/3rd/rs/cyw43/cyw43-pio" }
 
+embassy-net-w5500 = { version = "0.1.0", optional = true }
+
 embassy-executor = { version = "0.2",  features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m", "nightly"] }
 embassy-time = { version = "0.1",  features = ["defmt", "defmt-timestamp-uptime"] }
 embassy-rp = { version = "0.1.0",  features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] }
@@ -25,7 +27,7 @@ embassy-sync = { version = "0.2.0" }
 embassy-futures = { version = "0.1.0" }
 embassy-usb = { version = "0.1.0" }
 atomic-polyfill = "0.1.5"
-static_cell = "1.0"
+static_cell = { version = "1.0", features = [ "nightly" ] }
 
 defmt = { version  = "0.3", optional = true }
 defmt-rtt = "0.3"
@@ -60,13 +62,18 @@ sha2 = { version = "0.10", default-features = false }
 smoltcp = { version = "0.9", default-features = false }
 
 [features]
-default = ["defmt", "sunset-demo-embassy-common/defmt", "embassy-usb/defmt"]
+default = ["cyw43", "defmt", "sunset-demo-embassy-common/defmt" ]
 defmt = ["dep:defmt", "sunset/defmt", "sunset-embassy/defmt", "smoltcp/defmt"]
 
+# for pico w board
+cyw43 = ["dep:cyw43", "dep:cyw43-pio"]
+# for wiznet w5500-evb-pico board
+w5500 = ["dep:embassy-net-w5500"]
+
 # Use cyw43 firmware already on flash. This saves time when developing.
 # probe-rs-cli download firmware/43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
 # probe-rs-cli download firmware/43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
 romfw = []
 
-# Default console is serial
+# Set default console to serial
 serial1 = []
diff --git a/embassy/demos/picow/src/main.rs b/embassy/demos/picow/src/main.rs
index 55515b5..16d292a 100644
--- a/embassy/demos/picow/src/main.rs
+++ b/embassy/demos/picow/src/main.rs
@@ -41,8 +41,14 @@ mod picowmenu;
 mod serial;
 mod takepipe;
 mod usbserial;
+#[cfg(feature = "w5500")]
+mod w5500;
+#[cfg(feature = "cyw43")]
 mod wifi;
 
+#[cfg(not(any(feature = "cyw43", feature = "w5500")))]
+compile_error!("No network device selected. Use cyw43 or w5500 feature");
+
 use demo_common::{SSHConfig, Shell};
 
 use takepipe::TakePipe;
@@ -53,7 +59,7 @@ pub(crate) const NUM_SOCKETS: usize = NUM_LISTENERS + 1;
 
 #[embassy_executor::main]
 async fn main(spawner: Spawner) {
-    info!("Hello World!");
+    info!("Welcome to Sunset SSH");
 
     let mut p = embassy_rp::init(Default::default());
 
@@ -72,20 +78,6 @@ async fn main(spawner: Spawner) {
 
     let config = &*singleton!(SunsetMutex::new(config));
 
-    let (wifi_net, wifi_pw) = {
-        let c = config.lock().await;
-        (c.wifi_net.clone(), c.wifi_pw.clone())
-    };
-    // spawn the wifi stack
-    let (stack, wifi_control) = wifi::wifi_stack(
-        &spawner, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, p.PIO0,
-        wifi_net, wifi_pw,
-    )
-    .await;
-    let stack = &*singleton!(stack);
-    let wifi_control = singleton!(SunsetMutex::new(wifi_control));
-    spawner.spawn(net_task(&stack)).unwrap();
-
     let usb_pipe = {
         let p = singleton!(takepipe::TakePipeStorage::new());
         singleton!(p.pipe())
@@ -110,27 +102,44 @@ async fn main(spawner: Spawner) {
         embassy_rp::watchdog::Watchdog::new(p.WATCHDOG)
     ));
 
-    let state = GlobalState {
-        usb_pipe,
-        serial1_pipe,
-
-        _wifi_control: wifi_control,
-        config,
-        flash,
-        watchdog,
-    };
+    let state = GlobalState { usb_pipe, serial1_pipe, config, flash, watchdog };
     let state = singleton!(state);
 
     spawner.spawn(usbserial::task(p.USB, state)).unwrap();
 
-    for _ in 0..NUM_LISTENERS {
-        spawner.spawn(listener(&stack, config, state)).unwrap();
+    // spawn the wifi stack
+    #[cfg(feature = "cyw43")]
+    {
+        let stack = wifi::wifi_stack(
+            &spawner, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, p.PIO0,
+            config,
+        )
+        .await;
+
+        for _ in 0..NUM_LISTENERS {
+            spawner.spawn(cyw43_listener(&stack, config, state)).unwrap();
+        }
+    }
+
+    // spawn the ethernet stack
+    #[cfg(feature = "w5500")]
+    {
+        let stack = w5500::w5500_stack(
+            &spawner, p.PIN_16, p.PIN_17, p.PIN_18, p.PIN_19, p.PIN_20, p.PIN_21,
+            p.DMA_CH0, p.DMA_CH1, p.SPI0, config,
+        )
+        .await;
+
+        for _ in 0..NUM_LISTENERS {
+            spawner.spawn(w5500_listener(&stack, config, state)).unwrap();
+        }
     }
 }
 
 // TODO: pool_size should be NUM_LISTENERS but needs a literal
+#[cfg(feature = "cyw43")]
 #[embassy_executor::task(pool_size = 4)]
-async fn listener(
+async fn cyw43_listener(
     stack: &'static Stack<cyw43::NetDriver<'static>>,
     config: &'static SunsetMutex<SSHConfig>,
     global: &'static GlobalState,
@@ -138,12 +147,21 @@ async fn listener(
     demo_common::listener::<_, DemoShell>(stack, config, global).await
 }
 
+#[cfg(feature = "w5500")]
+#[embassy_executor::task(pool_size = 4)]
+async fn w5500_listener(
+    stack: &'static Stack<embassy_net_w5500::Device<'static>>,
+    config: &'static SunsetMutex<SSHConfig>,
+    global: &'static GlobalState,
+) -> ! {
+    demo_common::listener::<_, DemoShell>(stack, config, global).await
+}
+
 pub(crate) struct GlobalState {
     // If taking multiple mutexes, lock in the order below avoid inversion.
     pub usb_pipe: &'static TakePipe<'static>,
     pub serial1_pipe: &'static TakePipe<'static>,
 
-    pub _wifi_control: &'static SunsetMutex<cyw43::Control<'static>>,
     pub config: &'static SunsetMutex<SSHConfig>,
     pub flash: &'static SunsetMutex<
         embassy_rp::flash::Flash<'static, FLASH, { flashconfig::FLASH_SIZE }>,
@@ -312,8 +330,3 @@ impl Shell for DemoShell {
         session.await
     }
 }
-
-#[embassy_executor::task]
-async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! {
-    stack.run().await
-}
diff --git a/embassy/demos/picow/src/picowmenu.rs b/embassy/demos/picow/src/picowmenu.rs
index 5ac58f0..f54296a 100644
--- a/embassy/demos/picow/src/picowmenu.rs
+++ b/embassy/demos/picow/src/picowmenu.rs
@@ -174,6 +174,7 @@ pub(crate) const SETUP_MENU: Menu<MenuCtx> = Menu {
         // &GPIO_ITEM,
         &SERIAL_ITEM,
         &WIFI_ITEM,
+        &NET_ITEM,
         &Item {
             command: "reset",
             help: Some("Reset picow. Will log out."),
@@ -390,6 +391,26 @@ const WIFI_ITEM: Item<MenuCtx> = Item {
     help: None,
 };
 
+const NET_ITEM: Item<MenuCtx> = Item {
+    command: "net",
+    item_type: ItemType::Menu(&Menu {
+        label: "net",
+        items: &[
+            &Item {
+                command: "info",
+                item_type: ItemType::Callback {
+                    parameters: &[],
+                    function: do_net_info,
+                },
+                help: None,
+            },
+        ],
+        entry: None,
+        exit: None,
+    }),
+    help: None,
+};
+
 // const _GPIO_ITEM: Item<MenuCtx> = Item {
 //     command: "gpio",
 //     item_type: ItemType::Menu(&Menu {
@@ -669,6 +690,13 @@ fn do_wifi_open(_item: &Item<MenuCtx>, args: &[&str], context: &mut MenuCtx) {
     wifi_entry(context);
 }
 
+fn do_net_info(_item: &Item<MenuCtx>, _args: &[&str], context: &mut MenuCtx) {
+    context.with_config(|c, out| {
+        let _ = write!(out, "wired mac {:x?} ", c.mac);
+    });
+}
+
+
 // Returns an error on EOF etc.
 pub(crate) async fn request_pw<E>(
     tx: &mut impl asynch::Write<Error = E>,
diff --git a/embassy/demos/picow/src/w5500.rs b/embassy/demos/picow/src/w5500.rs
new file mode 100644
index 0000000..dc9600d
--- /dev/null
+++ b/embassy/demos/picow/src/w5500.rs
@@ -0,0 +1,95 @@
+// Modified from https://github.com/embassy-rs/cyw43/
+// Copyright (c) 2019-2022 Embassy project contributors
+// MIT or Apache-2.0 license
+
+#[allow(unused_imports)]
+#[cfg(not(feature = "defmt"))]
+pub use log::{debug, error, info, log, trace, warn};
+
+#[allow(unused_imports)]
+#[cfg(feature = "defmt")]
+pub use defmt::{debug, error, info, panic, trace, warn};
+
+use embassy_executor::Spawner;
+use embassy_net::{Stack, StackResources};
+use embassy_rp::gpio::{Input, Level, Output, Pull};
+use embassy_rp::peripherals::*;
+use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
+use embedded_hal_async::spi::ExclusiveDevice;
+
+use embassy_net_w5500::*;
+
+use static_cell::make_static;
+
+use rand::rngs::OsRng;
+use rand::RngCore;
+
+use crate::{SSHConfig, SunsetMutex};
+
+#[embassy_executor::task]
+async fn ethernet_task(
+    runner: Runner<
+        'static,
+        ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>>,
+        Input<'static, PIN_21>,
+        Output<'static, PIN_20>,
+    >,
+) -> ! {
+    runner.run().await
+}
+
+pub(crate) async fn w5500_stack(
+    spawner: &Spawner,
+    p16: PIN_16,
+    p17: PIN_17,
+    p18: PIN_18,
+    p19: PIN_19,
+    p20: PIN_20,
+    p21: PIN_21,
+    dma0: DMA_CH0,
+    dma1: DMA_CH1,
+    spi0: SPI0,
+    config: &'static SunsetMutex<SSHConfig>,
+) -> &'static embassy_net::Stack<embassy_net_w5500::Device<'static>> {
+    let mut spi_cfg = SpiConfig::default();
+    spi_cfg.frequency = 50_000_000;
+    let (miso, mosi, clk) = (p16, p19, p18);
+    let spi = Spi::new(spi0, clk, mosi, miso, dma0, dma1, spi_cfg);
+    let cs = Output::new(p17, Level::High);
+    let w5500_int = Input::new(p21, Pull::Up);
+    let w5500_reset = Output::new(p20, Level::High);
+
+    let mac_addr = config.lock().await.mac;
+    // 
+    let state = make_static!(State::<8, 8>::new());
+    let (device, runner) = embassy_net_w5500::new(
+        mac_addr,
+        state,
+        ExclusiveDevice::new(spi, cs),
+        w5500_int,
+        w5500_reset,
+    )
+    .await;
+    spawner.spawn(ethernet_task(runner)).unwrap();
+
+    // Generate random seed
+    let seed = OsRng.next_u64();
+
+    // Init network stack
+    let stack = &*make_static!(Stack::new(
+        device,
+        embassy_net::Config::dhcpv4(Default::default()),
+        make_static!(StackResources::<{ crate::NUM_SOCKETS }>::new()),
+        seed
+    ));
+
+    // Launch network task
+    spawner.spawn(net_task(&stack)).unwrap();
+
+    stack
+}
+
+#[embassy_executor::task]
+async fn net_task(stack: &'static Stack<Device<'static>>) -> ! {
+    stack.run().await
+}
diff --git a/embassy/demos/picow/src/wifi.rs b/embassy/demos/picow/src/wifi.rs
index 850a629..1a02d2e 100644
--- a/embassy/demos/picow/src/wifi.rs
+++ b/embassy/demos/picow/src/wifi.rs
@@ -1,4 +1,5 @@
-// Modified from https://github.com/embassy-rs/cyw43/
+// Modified from embassy 
+// examples/rp/src/bin/wifi_tcp_server.rs
 // Copyright (c) 2019-2022 Embassy project contributors
 // MIT or Apache-2.0 license
 
@@ -21,12 +22,11 @@ use embassy_net::{Stack, StackResources};
 use cyw43_pio::PioSpi;
 
 use static_cell::StaticCell;
-use heapless::String;
-
 use rand::rngs::OsRng;
 use rand::RngCore;
 
 use crate::demo_common::singleton;
+use crate::{SunsetMutex, SSHConfig};
 
 #[embassy_executor::task]
 async fn wifi_task(
@@ -43,10 +43,10 @@ async fn wifi_task(
 pub(crate) async fn wifi_stack(spawner: &Spawner,
     p23: PIN_23, p24: PIN_24, p25: PIN_25, p29: PIN_29, dma: DMA_CH0,
     pio0: PIO0,
-    wifi_net: String<32>, wpa_password: Option<String<63>>,
-
-    ) -> (embassy_net::Stack<cyw43::NetDriver<'static>>, cyw43::Control<'static>)
+    config: &'static SunsetMutex<SSHConfig>,
+    ) -> &'static embassy_net::Stack<cyw43::NetDriver<'static>>
     {
+    // TODO: return `control` once it can do something useful
 
     let (fw, clm) = get_fw();
 
@@ -64,10 +64,15 @@ pub(crate) async fn wifi_stack(spawner: &Spawner,
     // control.set_power_management(cyw43::PowerManagementMode::None).await;
     // control.set_power_management(cyw43::PowerManagementMode::Performance).await;
 
+    let (wifi_net, wifi_pw) = {
+        let c = config.lock().await;
+        (c.wifi_net.clone(), c.wifi_pw.clone())
+    };
+
     // TODO: this should move out of the critical path, run in the bg.
     // just return control before joining.
     for _ in 0..2 {
-        let status = if let Some(ref pw) = wpa_password {
+        let status = if let Some(ref pw) = wifi_pw {
             info!("wifi net {} wpa2", wifi_net);
             control.join_wpa2(&wifi_net, &pw).await
         } else {
@@ -81,11 +86,6 @@ pub(crate) async fn wifi_stack(spawner: &Spawner,
         }
     }
 
-    // if let Err(e) = status {
-    //     // wait forever
-    //     let () = futures::future::pending().await;
-    // }
-
     let config = embassy_net::Config::dhcpv4(Default::default());
 
     let seed = OsRng.next_u64();
@@ -97,11 +97,20 @@ pub(crate) async fn wifi_stack(spawner: &Spawner,
         singleton!(StackResources::<{crate::NUM_SOCKETS}>::new()),
         seed
     );
-    (stack, control)
+
+    let stack = &*singleton!(stack);
+    spawner.spawn(net_task(&stack)).unwrap();
+
+    stack
+}
+
+#[embassy_executor::task]
+async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! {
+    stack.run().await
 }
 
+// Get the WiFi firmware and Country Locale Matrix (CLM) blobs.
 fn get_fw() -> (&'static [u8], &'static [u8]) {
-    // Include the WiFi firmware and Country Locale Matrix (CLM) blobs.
     #[cfg(not(feature = "romfw"))]
     let (fw, clm) = (
         include_bytes!("../firmware/43439A0.bin"),
-- 
GitLab