diff --git a/async/rust-toolchain.toml b/async/rust-toolchain.toml index 5f813cc7a05c100742c6799b30c74bf6fea9cae3..097836a7c9f1b6db58d7e65be3683f13e9510dab 100644 --- a/async/rust-toolchain.toml +++ b/async/rust-toolchain.toml @@ -1,9 +1,5 @@ -# Before upgrading check that everything is available on all tier1 targets here: -# https://rust-lang.github.io/rustup-components-history - -# 2023-04-15 has ICE building picow demo [toolchain] -channel = "nightly-2023-04-08" +channel = "nightly-2023-05-14" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", diff --git a/embassy/demos/common/src/server.rs b/embassy/demos/common/src/server.rs index 5c3d64f0e722795007d439c0437897140f35408f..1e25df941f74b02b4cc799067e4ebd103ea22e5e 100644 --- a/embassy/demos/common/src/server.rs +++ b/embassy/demos/common/src/server.rs @@ -18,6 +18,8 @@ use embassy_futures::join::join; use embedded_io::asynch; +use heapless::String; + use sunset::*; use sunset_embassy::SSHServer; @@ -43,7 +45,9 @@ macro_rules! singleton { // common entry point -pub async fn listener<D: Driver, S: Shell>(stack: &'static Stack<D>, config: &SSHConfig) -> ! { +pub async fn listener<D: Driver, S: Shell>(stack: &'static Stack<D>, + config: &SSHConfig, + init: S::Init) -> ! { // TODO: buffer size? // Does it help to be larger than ethernet MTU? // Should TX and RX be symmetrical? Or larger TX might fill ethernet @@ -62,7 +66,7 @@ pub async fn listener<D: Driver, S: Shell>(stack: &'static Stack<D>, config: &SS continue; } - let r = session::<S>(&mut socket, &config).await; + let r = session::<S>(&mut socket, &config, &init).await; if let Err(_e) = r { // warn!("Ended with error: {:?}", e); warn!("Ended with error"); @@ -71,12 +75,13 @@ pub async fn listener<D: Driver, S: Shell>(stack: &'static Stack<D>, config: &SS } /// Run a SSH session when a socket accepts a connection -async fn session<S: Shell>(socket: &mut TcpSocket<'_>, config: &SSHConfig) -> sunset::Result<()> { +async fn session<S: Shell>(socket: &mut TcpSocket<'_>, config: &SSHConfig, + init: &S::Init) -> sunset::Result<()> { // OK unwrap: has been accepted let src = socket.remote_endpoint().unwrap(); info!("Connection from {}:{}", src.addr, src.port); - let shell = S::default(); + let shell = S::new(init); let app = DemoServer::new(&shell, config)?; let app = Mutex::<NoopRawMutex, _>::new(app); @@ -129,8 +134,9 @@ impl<'a, S: Shell> ServBehaviour for DemoServer<'a, S> { Ok(&self.hostkeys) } - fn auth_unchallenged(&mut self, username: TextString) -> bool { + async fn auth_unchallenged(&mut self, username: TextString<'_>) -> bool { info!("Allowing auth for user {}", username.as_str().unwrap_or("bad")); + self.shell.authed(username.as_str().unwrap_or("")).await; true } @@ -167,10 +173,14 @@ impl<'a, S: Shell> ServBehaviour for DemoServer<'a, S> { } } -pub trait Shell : Default { +pub trait Shell { + type Init; + + fn new(init: &Self::Init) -> Self; + #[allow(unused_variables)] // TODO: eventually the compiler should add must_use automatically? - fn authed(&self, handle: ChanHandle) { + async fn authed(&self, username: &str) { info!("Authenticated") } diff --git a/embassy/demos/picow/Cargo.lock b/embassy/demos/picow/Cargo.lock index 6b497bd21937a10a91ca4e5abe734c6af2657de8..ada84716bc5f943de4d951b0f4e189406fd8f187 100644 --- a/embassy/demos/picow/Cargo.lock +++ b/embassy/demos/picow/Cargo.lock @@ -315,7 +315,7 @@ dependencies = [ [[package]] name = "cyw43" version = "0.1.0" -source = "git+https://github.com/embassy-rs/cyw43/?rev=c19de2984751ba6fa2972ee66cfa2a6310d5f0c1#c19de2984751ba6fa2972ee66cfa2a6310d5f0c1" +source = "git+https://github.com/embassy-rs/cyw43/?rev=6b5d9642d583bc034ee35b88c903545e7f423b4e#6b5d9642d583bc034ee35b88c903545e7f423b4e" dependencies = [ "atomic-polyfill 0.1.11", "cortex-m", @@ -333,10 +333,9 @@ dependencies = [ [[package]] name = "cyw43-pio" version = "0.1.0" -source = "git+https://github.com/embassy-rs/cyw43/?rev=c19de2984751ba6fa2972ee66cfa2a6310d5f0c1#c19de2984751ba6fa2972ee66cfa2a6310d5f0c1" +source = "git+https://github.com/embassy-rs/cyw43/?rev=6b5d9642d583bc034ee35b88c903545e7f423b4e#6b5d9642d583bc034ee35b88c903545e7f423b4e" dependencies = [ "cyw43", - "defmt", "embassy-rp", "pio", "pio-proc", @@ -692,6 +691,7 @@ name = "embassy-usb" version = "0.1.0" source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e" dependencies = [ + "defmt", "embassy-futures", "embassy-net-driver-channel", "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)", diff --git a/embassy/demos/picow/Cargo.toml b/embassy/demos/picow/Cargo.toml index d42d89fda87c31af4132ed6a5557bff56c4510ad..f3656168dce0ccf19f6b995c1f58fa984bfb5515 100644 --- a/embassy/demos/picow/Cargo.toml +++ b/embassy/demos/picow/Cargo.toml @@ -12,9 +12,11 @@ sunset = { path = "../../.." } sunset-sshwire-derive = { version = "0.1", path = "../../../sshwire-derive" } sunset-demo-embassy-common= { path = "../common", features = ["defmt"] } -cyw43 = { git = "https://github.com/embassy-rs/cyw43/", rev = "c19de2984751ba6fa2972ee66cfa2a6310d5f0c1", features = ["defmt"]} -cyw43-pio = { git = "https://github.com/embassy-rs/cyw43/", rev = "c19de2984751ba6fa2972ee66cfa2a6310d5f0c1" } -# cyw43 = { path = "/home/matt/3rd/rs/cyw43", features = ["defmt"]} +cyw43 = { git = "https://github.com/embassy-rs/cyw43/", rev = "6b5d9642d583bc034ee35b88c903545e7f423b4e", features = ["defmt"]} +cyw43-pio = { git = "https://github.com/embassy-rs/cyw43/", rev = "6b5d9642d583bc034ee35b88c903545e7f423b4e" } +# cyw43 = { path = "/home/matt/3rd/rs/cyw43", features = ["defmt"] } +# cyw43-pio = { path = "/home/matt/3rd/rs/cyw43/cyw43-pio" } + embassy-executor = { version = "0.2", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } embassy-time = { version = "0.1", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } @@ -51,7 +53,7 @@ rand = { version = "0.8", default-features = false, features = ["getrandom"] } sha2 = { version = "0.10", default-features = false } [features] -default = ["defmt", "sunset-demo-embassy-common/defmt"] +default = ["defmt", "sunset-demo-embassy-common/defmt", "embassy-usb/defmt"] defmt = [] # Use cyw43 firmware already on flash. This saves time when developing. diff --git a/embassy/demos/picow/rust-toolchain.toml b/embassy/demos/picow/rust-toolchain.toml index 5f813cc7a05c100742c6799b30c74bf6fea9cae3..097836a7c9f1b6db58d7e65be3683f13e9510dab 100644 --- a/embassy/demos/picow/rust-toolchain.toml +++ b/embassy/demos/picow/rust-toolchain.toml @@ -1,9 +1,5 @@ -# Before upgrading check that everything is available on all tier1 targets here: -# https://rust-lang.github.io/rustup-components-history - -# 2023-04-15 has ICE building picow demo [toolchain] -channel = "nightly-2023-04-08" +channel = "nightly-2023-05-14" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", diff --git a/embassy/demos/picow/src/flashconfig.rs b/embassy/demos/picow/src/flashconfig.rs index 0b3828c7d167f3e60557ebdde4f9b17c956dc11d..5391ace9fea9886aec6949d7a01dd7d362cb16d4 100644 --- a/embassy/demos/picow/src/flashconfig.rs +++ b/embassy/demos/picow/src/flashconfig.rs @@ -51,6 +51,10 @@ pub fn load_or_create(flash: &mut Flash<'_, FLASH, FLASH_SIZE>) -> Result<SSHCon Err(c) => info!("Existing config bad, making new"), } + create(flash) +} + +pub fn create(flash: &mut Flash<'_, FLASH, FLASH_SIZE>) -> Result<SSHConfig> { let c = SSHConfig::new()?; if let Err(e) = save(flash, &c) { warn!("Error writing config"); diff --git a/embassy/demos/picow/src/main.rs b/embassy/demos/picow/src/main.rs index 9413ae3510db58640dfed04a779b998c0d6e40ed..b99d07704a8b23994aa350eba27fef504558ec75 100644 --- a/embassy/demos/picow/src/main.rs +++ b/embassy/demos/picow/src/main.rs @@ -4,10 +4,26 @@ #![feature(async_fn_in_trait)] // #![allow(incomplete_features)] +#[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, info, warn, panic, error, trace}; + +use {defmt_rtt as _, panic_probe as _}; + use embassy_executor::Spawner; use embassy_net::Stack; -use embassy_rp::pio::PioPeripheral; -use {defmt_rtt as _, panic_probe as _}; +use embassy_futures::join::join; +use embassy_rp::{pio::PioPeripheral, interrupt}; +use embedded_io::{asynch, Io}; +use embedded_io::asynch::Write; + +use heapless::{String, Vec}; use static_cell::StaticCell; @@ -17,8 +33,7 @@ use embassy_sync::signal::Signal; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use sunset::*; -use sunset::prelude::*; -use sunset_embassy::SSHServer; +use sunset_embassy::{SSHServer, SunsetMutex}; pub(crate) use sunset_demo_embassy_common as demo_common; use crate::demo_common::singleton; @@ -26,10 +41,12 @@ use crate::demo_common::singleton; mod flashconfig; mod wifi; mod usbserial; -mod switchpipe; +mod takepipe; use demo_common::{SSHConfig, demo_menu, Shell}; +use takepipe::TakeBase; + const NUM_LISTENERS: usize = 4; // +1 for dhcp. referenced directly by wifi_stack() function pub(crate) const NUM_SOCKETS: usize = NUM_LISTENERS+1; @@ -45,80 +62,180 @@ async fn main(spawner: Spawner) { let mut flash = embassy_rp::flash::Flash::new(p.FLASH); - let config = flashconfig::load_or_create(&mut flash).unwrap(); + let config = if option_env!("RESET_FLASH").is_some() { + flashconfig::create(&mut flash).unwrap() + } else { + flashconfig::load_or_create(&mut flash).unwrap() + }; let ssh_config = &*singleton!( config ); + let usb_pipe = singleton!(takepipe::TakePipe::new()); + let usb_pipe = singleton!(usb_pipe.base()); + let usb_irq = interrupt::take!(USBCTRL_IRQ); + spawner.spawn(usb_serial_task(p.USB, usb_irq, usb_pipe)).unwrap(); + let (_, sm, _, _, _) = p.PIO0.split(); let wifi_net = ssh_config.wifi_net.as_str(); let wifi_pw = ssh_config.wifi_pw.as_ref().map(|p| p.as_str()); - // spawn the wifi stack - let stack = wifi::wifi_stack(&spawner, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, sm, + let (stack, wifi_control) = wifi::wifi_stack(&spawner, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, sm, 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 = switchpipe::SwitchPipe::new(); - let usb_pipe = usb_pipe.base(); - let r = usb_pipe.switch_read(); + let init = DemoShellInit { + usb_pipe, + wifi_control, + }; + let init = singleton!(init); for _ in 0..NUM_LISTENERS { - spawner.spawn(listener(&stack, &ssh_config)).unwrap(); + spawner.spawn(listener(&stack, &ssh_config, init)).unwrap(); } } // TODO: pool_size should be NUM_LISTENERS but needs a literal #[embassy_executor::task(pool_size = 4)] -async fn listener(stack: &'static Stack<cyw43::NetDriver<'static>>, config: &'static SSHConfig) -> ! { - demo_common::listener::<_, DemoShell>(stack, config).await +async fn listener(stack: &'static Stack<cyw43::NetDriver<'static>>, + config: &'static SSHConfig, + ctx: &'static DemoShellInit) -> ! { + demo_common::listener::<_, DemoShell>(stack, config, ctx).await +} + +struct DemoShellInit { + usb_pipe: &'static TakeBase<'static>, + wifi_control: &'static SunsetMutex<cyw43::Control<'static>>, } -#[derive(Default)] struct DemoShell { notify: Signal<NoopRawMutex, ChanHandle>, + ctx: &'static DemoShellInit, + + // Mutex is a bit of a bodge + username: SunsetMutex<String<20>>, +} + +impl DemoShell { + async fn menu<C>(&self, mut stdio: C) -> Result<()> + where C: asynch::Read + asynch::Write + Io<Error=sunset::Error> { + let mut menu_buf = [0u8; 64]; + let menu_out = demo_menu::BufOutput::default(); + + let mut menu = MenuRunner::new(&demo_menu::ROOT_MENU, &mut menu_buf, menu_out); + + // bodge + for c in "help\r\n".bytes() { + menu.input_byte(c); + } + menu.context.flush(&mut stdio).await?; + + loop { + let mut b = [0u8; 20]; + let lr = stdio.read(&mut b).await?; + if lr == 0 { + break + } + let b = &mut b[..lr]; + for c in b.iter() { + menu.input_byte(*c); + menu.context.flush(&mut stdio).await?; + } + } + Ok(()) + } + + async fn serial<C>(&self, mut stdio: C) -> Result<()> + where C: asynch::Read+asynch::Write+Clone+Io<Error=sunset::Error> { + + info!("serial top"); + + + let mut s2 = stdio.clone(); + + info!("take await"); + let (mut rx, mut tx) = self.ctx.usb_pipe.take().await; + info!("take done"); + let r = async { + let mut b = [0u8; 64]; + let mut btrans = Vec::<u8, 128>::new(); + loop { + let n = rx.read(&mut b).await?; + let b = &mut b[..n]; + btrans.clear(); + for c in b { + if *c == b'\n' { + // OK unwrap: btrans.len() = 2*b.len() + btrans.push(b'\r').unwrap(); + } + btrans.push(*c).unwrap(); + } + s2.write_all(&btrans).await?; + } + #[allow(unreachable_code)] + Ok::<(), sunset::Error>(()) + }; + let w = async { + let mut b = [0u8; 64]; + loop { + let n = stdio.read(&mut b).await?; + if n == 0 { + return Err(sunset::Error::ChannelEOF); + } + let b = &mut b[..n]; + for c in b.iter_mut() { + // input translate CR to LF + if *c == b'\r' { + *c = b'\n'; + } + } + tx.write_all(b).await?; + } + #[allow(unreachable_code)] + Ok::<(), sunset::Error>(()) + }; + + join(r, w).await; + info!("serial task completed"); + Ok(()) + } } impl Shell for DemoShell { + type Init = &'static DemoShellInit; + + fn new(ctx: &Self::Init) -> Self { + Self { + notify: Default::default(), + ctx, + username: SunsetMutex::new(String::new()), + } + } + fn open_shell(&self, handle: ChanHandle) { self.notify.signal(handle); } + async fn authed(&self, username: &str) { + let mut u = self.username.lock().await; + *u = username.try_into().unwrap_or(String::new()); + } + async fn run<'f, S: ServBehaviour>(&self, serv: &'f SSHServer<'f, S>) -> Result<()> { let session = async { // wait for a shell to start let chan_handle = self.notify.wait().await; - trace!("got handle"); - - let mut stdio = serv.stdio(chan_handle).await?; - - let mut menu_buf = [0u8; 64]; - let menu_out = demo_menu::BufOutput::default(); - - let mut menu = MenuRunner::new(&demo_menu::ROOT_MENU, &mut menu_buf, menu_out); + let stdio = serv.stdio(chan_handle).await?; - // bodge - for c in "help\r\n".bytes() { - menu.input_byte(c); - } - menu.context.flush(&mut stdio).await?; - - loop { - let mut b = [0u8; 20]; - let lr = stdio.read(&mut b).await?; - if lr == 0 { - break - } - let b = &mut b[..lr]; - for c in b.iter() { - menu.input_byte(*c); - menu.context.flush(&mut stdio).await?; - } + if *self.username.lock().await == "serial" { + self.serial(stdio).await + } else { + self.menu(stdio).await } - Ok(()) }; session.await @@ -133,11 +250,13 @@ async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { #[embassy_executor::task] async fn usb_serial_task(usb: embassy_rp::peripherals::USB, irq: embassy_rp::interrupt::USBCTRL_IRQ, + pipe: &'static TakeBase<'static>, ) -> ! { - // let mut p = embassy_sync::pipe::Pipe(64); + info!("usb serial"); + let (mut rx, mut tx) = pipe.split(); - // usbserial.usb_serial(usb, irq, tx, rx).await; + usbserial::usb_serial(usb, irq, &mut tx, &mut rx).await; todo!("shoudln't exit"); } diff --git a/embassy/demos/picow/src/takepipe.rs b/embassy/demos/picow/src/takepipe.rs new file mode 100644 index 0000000000000000000000000000000000000000..9be610b3a3efad3fdd5b2edbc19d4b3da5cee82c --- /dev/null +++ b/embassy/demos/picow/src/takepipe.rs @@ -0,0 +1,177 @@ +use embedded_io::{asynch, Io}; + +use embassy_sync::{pipe, mutex::{MutexGuard, Mutex}, signal::Signal}; +use embassy_sync::pipe::Pipe; +use embassy_futures::select::{select, Either}; + +use sunset_embassy::{SunsetMutex, SunsetRawMutex}; + +const SIZE: usize = 64; + +/// Allows a bidirectional pipe to be shared by many endpoints +/// +/// One end of the pipe is fixed (attached to eg a physical/virtual +/// uart), used with `.split()`. +/// +/// The other end can be used by many clients, one at a time. +/// When a subsequent client takes the pipe (with `.take()`), the existing +/// client loses the pipe and gets EOF. +/// +/// It works a bit like `screen -r -d`. +pub(crate) struct TakePipe { + fanout: Pipe<SunsetRawMutex, SIZE>, + fanin: Pipe<SunsetRawMutex, SIZE>, + wake: Signal<SunsetRawMutex, ()>, +} + +impl TakePipe { + pub fn new() -> Self { + Default::default() + } + + pub fn base(&self) -> TakeBase { + TakeBase { + shared_read: Mutex::new(self.fanout.reader()), + shared_write: Mutex::new(self.fanin.writer()), + pipe: self, + } + } +} + +impl Default for TakePipe { + fn default() -> Self { + Self { + fanout: Pipe::new(), + fanin: Pipe::new(), + wake: Signal::new(), + } + } +} + +pub(crate) struct TakeBase<'a> { + shared_read: Mutex<SunsetRawMutex, pipe::Reader<'a, SunsetRawMutex, SIZE>>, + shared_write: Mutex<SunsetRawMutex, pipe::Writer<'a, SunsetRawMutex, SIZE>>, + pipe: &'a TakePipe, +} + +impl<'a> TakeBase<'a> { + pub async fn take(&'a self) -> (TakeRead<'a>, TakeWrite<'a>) { + self.pipe.wake.signal(()); + let r = self.shared_read.lock().await; + let w = self.shared_write.lock().await; + // We could .clear() the pipes, but + // that wouldn't deal with data that has already progressed + // further along out the SSH channel etc. So we leave that + // for high levels to deal with if needed. + self.pipe.wake.reset(); + + let r = TakeRead { + pipe: self.pipe, + shared: Some(r), + }; + let w = TakeWrite { + pipe: self.pipe, + shared: Some(w), + }; + (r, w) + } + + pub fn split(&'a self) -> (TakeBaseRead<'a>, TakeBaseWrite<'a>) { + let r = TakeBaseRead { + pipe: self.pipe, + }; + let w = TakeBaseWrite { + pipe: self.pipe, + }; + (r, w) + } +} + +pub(crate) struct TakeBaseRead<'a> { + pipe: &'a TakePipe, +} + +pub(crate) struct TakeBaseWrite<'a> { + pipe: &'a TakePipe, +} + +impl<'a> asynch::Read for TakeBaseRead<'a> { + async fn read(&mut self, buf: &mut [u8]) -> sunset::Result<usize> { + Ok(self.pipe.fanin.read(buf).await) + } +} + +impl<'a> asynch::Write for TakeBaseWrite<'a> { + async fn write(&mut self, buf: &[u8]) -> sunset::Result<usize> { + Ok(self.pipe.fanout.write(buf).await) + } +} + +impl Io for TakeBaseRead<'_> { + type Error = sunset::Error; +} + +impl Io for TakeBaseWrite<'_> { + type Error = sunset::Error; +} + +pub(crate) struct TakeRead<'a> { + pipe: &'a TakePipe, + shared: Option<MutexGuard<'a, SunsetRawMutex, pipe::Reader<'a, SunsetRawMutex, SIZE>>>, +} + +impl asynch::Read for TakeRead<'_> { + + async fn read(&mut self, buf: &mut [u8]) -> sunset::Result<usize> { + let p = self.shared.as_ref().ok_or(sunset::Error::ChannelEOF)?; + + let r = select( + p.read(buf), + self.pipe.wake.wait(), + ); + + match r.await { + // read completed + Either::First(l) => Ok(l), + // lost the pipe + Either::Second(l) => { + self.shared = None; + Err(sunset::Error::ChannelEOF) + } + } + } +} + +impl Io for TakeRead<'_> { + type Error = sunset::Error; +} + +pub(crate) struct TakeWrite<'a> { + pipe: &'a TakePipe, + shared: Option<MutexGuard<'a, SunsetRawMutex, pipe::Writer<'a, SunsetRawMutex, SIZE>>>, +} + +impl asynch::Write for TakeWrite<'_> { + async fn write(&mut self, buf: &[u8]) -> sunset::Result<usize> { + let p = self.shared.as_ref().ok_or(sunset::Error::ChannelEOF)?; + + let r = select( + p.write(buf), + self.pipe.wake.wait(), + ); + + match r.await { + // write completed + Either::First(l) => Ok(l), + // lost the pipe + Either::Second(l) => { + self.shared = None; + Err(sunset::Error::ChannelEOF) + } + } + } +} + +impl Io for TakeWrite<'_> { + type Error = sunset::Error; +} diff --git a/embassy/demos/picow/src/usbserial.rs b/embassy/demos/picow/src/usbserial.rs new file mode 100644 index 0000000000000000000000000000000000000000..91d111d537b1e2b306a263ef93ac8f099d4a822d --- /dev/null +++ b/embassy/demos/picow/src/usbserial.rs @@ -0,0 +1,112 @@ + +#[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, info, warn, panic, error, trace}; + +use embassy_usb::{Builder}; +use embassy_rp::usb::Instance; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_futures::join::join; + +use embedded_io::asynch; + +use sunset::*; + +pub async fn usb_serial(usb: embassy_rp::peripherals::USB, + irq: embassy_rp::interrupt::USBCTRL_IRQ, + tx: &mut impl asynch::Write, + rx: &mut impl asynch::Read, + ) { + + info!("usb_serial top"); + + let driver = embassy_rp::usb::Driver::new(usb, irq); + + let mut config = embassy_usb::Config::new(0xf055, 0x6053); + config.manufacturer = Some("Sunset SSH"); + config.product = Some("picow demo"); + config.serial_number = Some("4"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for windows 7 compatiblity. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + ); + + let cdc = CdcAcmClass::new(&mut builder, &mut state, 64); + let (mut cdc_tx, mut cdc_rx) = cdc.split(); + + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + struct IoDone; + + let io = async { + loop { + info!("usb waiting"); + cdc_rx.wait_connection().await; + info!("Connected"); + + let io_tx = async { + let mut b = [0u8; 64]; + loop { + let n = cdc_rx.read_packet(&mut b).await .map_err(|_| IoDone)?; + let b = &b[..n]; + tx.write_all(b).await.map_err(|_| IoDone)?; + } + #[allow(unreachable_code)] + Ok::<_, IoDone>(()) + }; + + let io_rx = async { + let mut b = [0u8; 64]; + loop { + let n = rx.read(&mut b).await.map_err(|_| IoDone)?; + if n == 0 { + return Err(IoDone); + } + let b = &b[..n]; + cdc_tx.write_packet(b).await.map_err(|_| IoDone)?; + } + #[allow(unreachable_code)] + Ok::<_, IoDone>(()) + }; + + join(io_rx, io_tx).await; + info!("Disconnected"); + } + }; + + info!("usb join"); + join(usb_fut, io).await; + +} diff --git a/embassy/demos/picow/src/wifi.rs b/embassy/demos/picow/src/wifi.rs index e01c53739907c710dd44a247b81ce3f2674454cf..180bab7837e644912f97f7d1cc1bcba5a79c910c 100644 --- a/embassy/demos/picow/src/wifi.rs +++ b/embassy/demos/picow/src/wifi.rs @@ -2,6 +2,16 @@ // 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, info, warn, panic, error, trace}; + use embassy_rp::gpio::{Level, Output}; use embassy_rp::pio::{PioStateMachineInstance, Sm0, Pio0}; use embassy_rp::peripherals::*; @@ -33,7 +43,7 @@ pub(crate) async fn wifi_stack(spawner: &Spawner, p23: PIN_23, p24: PIN_24, p25: PIN_25, p29: PIN_29, dma: DMA_CH0, sm: PioStateMachineInstance<Pio0, Sm0>, wifi_net: &str, wpa_password: Option<&str>, - ) -> embassy_net::Stack<cyw43::NetDriver<'static>> + ) -> (embassy_net::Stack<cyw43::NetDriver<'static>>, cyw43::Control<'static>) { let (fw, clm) = get_fw(); @@ -47,12 +57,15 @@ pub(crate) async fn wifi_stack(spawner: &Spawner, spawner.spawn(wifi_task(runner)).unwrap(); control.init(clm).await; - // the default is PowerSave - control.set_power_management(cyw43::PowerManagementMode::Performance).await; + // the default is PowerSave. None is fastest. + // control.set_power_management(cyw43::PowerManagementMode::None).await; + // control.set_power_management(cyw43::PowerManagementMode::Performance).await; if let Some(pw) = wpa_password { + info!("wifi net {} pw {}", wifi_net, pw); control.join_wpa2(wifi_net, pw).await; } else { + info!("wifi net {} open", wifi_net); control.join_open(wifi_net).await; } @@ -61,12 +74,13 @@ pub(crate) async fn wifi_stack(spawner: &Spawner, let seed = OsRng.next_u64(); // Init network stack - Stack::new( + let stack = Stack::new( net_device, config, singleton!(StackResources::<{crate::NUM_SOCKETS}>::new()), seed - ) + ); + (stack, control) } fn get_fw() -> (&'static [u8], &'static [u8]) { diff --git a/embassy/demos/std/Cargo.lock b/embassy/demos/std/Cargo.lock index cbbcd4053855eca2396b71dd8bf117e80314724d..af0c5f15edf394e569915291afe16d1471c89263 100644 --- a/embassy/demos/std/Cargo.lock +++ b/embassy/demos/std/Cargo.lock @@ -766,17 +766,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" -[[package]] -name = "no-panic" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3ff7cb37ce690ee3b1bab16e77f6eae6320d91a9770ec1497f58a88c1f35c9" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "no-std-net" version = "0.5.0" @@ -1066,7 +1055,6 @@ dependencies = [ "heapless", "hmac", "log", - "no-panic", "poly1305", "pretty-hex", "rand_core", diff --git a/embassy/demos/std/rust-toolchain.toml b/embassy/demos/std/rust-toolchain.toml index 5f813cc7a05c100742c6799b30c74bf6fea9cae3..097836a7c9f1b6db58d7e65be3683f13e9510dab 100644 --- a/embassy/demos/std/rust-toolchain.toml +++ b/embassy/demos/std/rust-toolchain.toml @@ -1,9 +1,5 @@ -# Before upgrading check that everything is available on all tier1 targets here: -# https://rust-lang.github.io/rustup-components-history - -# 2023-04-15 has ICE building picow demo [toolchain] -channel = "nightly-2023-04-08" +channel = "nightly-2023-05-14" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", diff --git a/embassy/demos/std/src/main.rs b/embassy/demos/std/src/main.rs index 43a5f5f882d264307b3eddc46bfa34b0c81c78ed..4cd28b45d30f4153a650e5701fa841e2cc5a0ad4 100644 --- a/embassy/demos/std/src/main.rs +++ b/embassy/demos/std/src/main.rs @@ -76,6 +76,12 @@ struct DemoShell { } impl Shell for DemoShell { + type Init = (); + + fn new(init: &Self::Init) -> Self { + Default::default() + } + fn open_shell(&self, handle: ChanHandle) { self.notify.signal(handle); } @@ -123,7 +129,7 @@ impl Shell for DemoShell { #[embassy_executor::task(pool_size = 4)] async fn listener(stack: &'static Stack<TunTapDevice>, config: &'static SSHConfig) -> ! { - demo_common::listener::<_, DemoShell>(stack, config).await + demo_common::listener::<_, DemoShell>(stack, config, ()).await } diff --git a/embassy/rust-toolchain.toml b/embassy/rust-toolchain.toml index 5f813cc7a05c100742c6799b30c74bf6fea9cae3..097836a7c9f1b6db58d7e65be3683f13e9510dab 100644 --- a/embassy/rust-toolchain.toml +++ b/embassy/rust-toolchain.toml @@ -1,9 +1,5 @@ -# Before upgrading check that everything is available on all tier1 targets here: -# https://rust-lang.github.io/rustup-components-history - -# 2023-04-15 has ICE building picow demo [toolchain] -channel = "nightly-2023-04-08" +channel = "nightly-2023-05-14" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi",