diff --git a/Cargo.lock b/Cargo.lock index 4b5279289df6675a9bafe867109c4ca545d16c15..d5f837259f10a0f550c6d04c148976b1f56d3efd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2854,6 +2854,7 @@ dependencies = [ "sunset-demo-embassy-common", "sunset-embassy", "sunset-sshwire-derive", + "usbd-hid", ] [[package]] diff --git a/embassy/demos/picow/Cargo.toml b/embassy/demos/picow/Cargo.toml index 32406027ab3bfebb7bc56f22c074ef008dbf07f4..1335c7e94c30076ee0eb351ca62235d1863705cb 100644 --- a/embassy/demos/picow/Cargo.toml +++ b/embassy/demos/picow/Cargo.toml @@ -61,6 +61,8 @@ sha2 = { version = "0.10", default-features = false } # for defmt feature smoltcp = { version = "0.10", default-features = false } +usbd-hid = "0.6" + [features] default = ["cyw43", "defmt", "sunset-demo-embassy-common/defmt" ] defmt = ["dep:defmt", "sunset/defmt", "sunset-embassy/defmt", "smoltcp/defmt"] diff --git a/embassy/demos/picow/src/keyboard.rs b/embassy/demos/picow/src/keyboard.rs new file mode 100644 index 0000000000000000000000000000000000000000..6e11df943fc91a75501c6cbdc36d2ca24b15fee1 --- /dev/null +++ b/embassy/demos/picow/src/keyboard.rs @@ -0,0 +1,52 @@ +#[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_futures::join::join; +use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler}; +use embassy_usb::control::OutResponse; +use embassy_usb_driver::Driver; + +use crate::*; + +pub(crate) async fn run<'a, D: Driver<'a>>( + _global: &'static GlobalState, + hid: HidReaderWriter<'a, D, 1, 8>, +) -> ! { + let (reader, _writer) = hid.split(); + let keyb_fut = async { + loop { + todo!(); + } + }; + + let handler = Handler; + let control_fut = reader.run(false, &handler); + + join(keyb_fut, control_fut).await; + unreachable!() +} + + +struct Handler; + +impl RequestHandler for Handler { + fn get_report(&self, _id: ReportId, _buf: &mut [u8]) -> Option<usize> { + None + } + + fn set_report(&self, _id: ReportId, _data: &[u8]) -> OutResponse { + OutResponse::Accepted + } + + fn set_idle_ms(&self, _id: Option<ReportId>, _dur: u32) { + } + + fn get_idle_ms(&self, _id: Option<ReportId>) -> Option<u32> { + None + } +} diff --git a/embassy/demos/picow/src/main.rs b/embassy/demos/picow/src/main.rs index b657ac3bef523877f016c0963fdb833ab33d11ae..2398d927ff891bed821d734716f35b400fb80927 100644 --- a/embassy/demos/picow/src/main.rs +++ b/embassy/demos/picow/src/main.rs @@ -37,7 +37,8 @@ mod flashconfig; mod picowmenu; mod serial; mod takepipe; -mod usbserial; +mod usb; +mod keyboard; #[cfg(feature = "w5500")] mod w5500; #[cfg(feature = "cyw43")] @@ -147,7 +148,7 @@ async fn main(spawner: Spawner) { } // USB task requires `state` - spawner.spawn(usbserial::task(p.USB, state)).unwrap(); + spawner.spawn(usb::task(p.USB, state)).unwrap(); } // TODO: pool_size should be NUM_LISTENERS but needs a literal diff --git a/embassy/demos/picow/src/usbserial.rs b/embassy/demos/picow/src/usb.rs similarity index 64% rename from embassy/demos/picow/src/usbserial.rs rename to embassy/demos/picow/src/usb.rs index 86bdad728e303590436047dec0b3669b79be5f3c..e158ce77dcd1ffac4aed6214fe858b7433f606e0 100644 --- a/embassy/demos/picow/src/usbserial.rs +++ b/embassy/demos/picow/src/usb.rs @@ -6,13 +6,15 @@ pub use log::{debug, error, info, log, trace, warn}; #[cfg(feature = "defmt")] pub use defmt::{debug, error, info, panic, trace, warn}; -use embassy_futures::join::{join, join3}; -use embassy_rp::usb::{InterruptHandler}; +use embassy_futures::join::{join, join4}; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::USB; -use embassy_usb::class::cdc_acm::{self, CdcAcmClass, State}; +use embassy_rp::usb::InterruptHandler; +use embassy_usb::class::cdc_acm::{self, CdcAcmClass}; +use embassy_usb::class::hid::{self, HidReaderWriter}; use embassy_usb::Builder; use embassy_usb_driver::Driver; +use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use embedded_io_async::{Read, Write, BufRead, ErrorType}; @@ -29,8 +31,7 @@ bind_interrupts!(struct Irqs { pub(crate) async fn task( usb: embassy_rp::peripherals::USB, global: &'static GlobalState, -) -> ! -{ +) -> ! { let driver = embassy_rp::usb::Driver::new(usb, Irqs); let mut config = embassy_usb::Config::new(0xf055, 0x6053); @@ -55,8 +56,9 @@ pub(crate) async fn task( let mut control_buf = [0; 64]; // lives longer than builder - let mut usb_state0 = State::new(); - let mut usb_state2 = State::new(); + let mut usb_state0 = cdc_acm::State::new(); + let mut usb_state2 = cdc_acm::State::new(); + let mut usb_state4 = hid::State::new(); let mut builder = Builder::new( driver, @@ -69,75 +71,97 @@ pub(crate) async fn task( // if00 let cdc0 = CdcAcmClass::new(&mut builder, &mut usb_state0, 64); - let (mut cdc0_tx, mut cdc0_rx) = cdc0.split(); // if02 let cdc2 = CdcAcmClass::new(&mut builder, &mut usb_state2, 64); - let (mut cdc2_tx, mut cdc2_rx) = cdc2.split(); - let mut usb = builder.build(); + let hid_config = embassy_usb::class::hid::Config { + report_descriptor: KeyboardReport::desc(), + request_handler: None, + poll_ms: 20, + max_packet_size: 64, + }; + let hid = + HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut usb_state4, hid_config); + let mut usb = builder.build(); // Run the USB device. let usb_fut = usb.run(); // console via SSH on if00 - let io0 = async { - let (mut chan_rx, mut chan_tx) = global.usb_pipe.split(); - let chan_rx = &mut chan_rx; - let chan_tx = &mut chan_tx; - loop { - info!("USB waiting"); - cdc0_rx.wait_connection().await; - info!("USB connected"); - let mut cdc0_tx = CDCWrite::new(&mut cdc0_tx); - let mut cdc0_rx = CDCRead::new(&mut cdc0_rx); - - let io_tx = io_buf_copy(&mut cdc0_rx, chan_tx); - let io_rx = io_copy::<64, _, _>(chan_rx, &mut cdc0_tx); - - let _ = join(io_rx, io_tx).await; - info!("USB disconnected"); - } - }; + let io0_run = console_if00_run(&global, cdc0); // Admin menu on if02 - let setup = async { - 'usb: loop { - cdc2_rx.wait_connection().await; - let mut cdc2_tx = CDCWrite::new(&mut cdc2_tx); - let mut cdc2_rx = CDCRead::new(&mut cdc2_rx); - - // wait for a keystroke before writing anything. - let mut c = [0u8]; - let _ = cdc2_rx.read_exact(&mut c).await; - - let p = { - let c = global.config.lock().await; - c.admin_pw.clone() - }; - - if let Some(p) = p { - 'pw: loop { - match request_pw(&mut cdc2_tx, &mut cdc2_rx).await { - Ok(pw) => { - if p.check(&pw) { - let _ = cdc2_tx.write_all(b"Good\r\n").await; - break 'pw - } + let io2_run = menu_if02_run(&global, cdc2); + + // keyboard + let hid_run = keyboard::run(&global, hid); + + join4(usb_fut, io0_run, io2_run, hid_run).await; + unreachable!() +} + +async fn console_if00_run<'a, D: Driver<'a>>( + global: &'static GlobalState, + cdc: CdcAcmClass<'a, D>, +) -> ! { + let (mut cdc_tx, mut cdc_rx) = cdc.split(); + let (mut chan_rx, mut chan_tx) = global.usb_pipe.split(); + let chan_rx = &mut chan_rx; + let chan_tx = &mut chan_tx; + loop { + info!("USB waiting"); + cdc_rx.wait_connection().await; + info!("USB connected"); + let mut cdc_tx = CDCWrite::new(&mut cdc_tx); + let mut cdc_rx = CDCRead::new(&mut cdc_rx); + + let io_tx = io_buf_copy(&mut cdc_rx, chan_tx); + let io_rx = io_copy::<64, _, _>(chan_rx, &mut cdc_tx); + + let _ = join(io_rx, io_tx).await; + info!("USB disconnected"); + } +} + +async fn menu_if02_run<'a, D: Driver<'a>>( + global: &'static GlobalState, + cdc: CdcAcmClass<'a, D>, +) -> ! { + let (mut cdc_tx, mut cdc_rx) = cdc.split(); + 'usb: loop { + cdc_rx.wait_connection().await; + let mut cdc_tx = CDCWrite::new(&mut cdc_tx); + let mut cdc_rx = CDCRead::new(&mut cdc_rx); + + // wait for a keystroke before writing anything. + let mut c = [0u8]; + let _ = cdc_rx.read_exact(&mut c).await; + + let p = { + let c = global.config.lock().await; + c.admin_pw.clone() + }; + + if let Some(p) = p { + 'pw: loop { + match request_pw(&mut cdc_tx, &mut cdc_rx).await { + Ok(pw) => { + if p.check(&pw) { + let _ = cdc_tx.write_all(b"Good\r\n").await; + break 'pw; } - Err(_) => continue 'usb } + Err(_) => continue 'usb, } } - - let _ = menu(&mut cdc2_rx, &mut cdc2_tx, true, global).await; } - }; - join3(usb_fut, io0, setup).await; - unreachable!() + let _ = menu(&mut cdc_rx, &mut cdc_tx, true, global).await; + } } +// TODO: this could be merged into embassy? pub struct CDCRead<'a, 'p, D: Driver<'a>> { cdc: &'p mut cdc_acm::Receiver<'a, D>, // sufficient for max packet @@ -164,14 +188,14 @@ impl<'a, D: Driver<'a>> Read for CDCRead<'a, '_, D> { .read_packet(ret) .await .map_err(|_| sunset::Error::ChannelEOF)?; - return Ok(n) + return Ok(n); } let b = self.fill_buf().await?; let n = ret.len().min(b.len()); (&mut ret[..n]).copy_from_slice(&b[..n]); self.consume(n); - return Ok(n) + return Ok(n); } }