diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..3501136 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +style_edition = "2024" diff --git a/prek.toml b/prek.toml new file mode 100644 index 0000000..37c38d6 --- /dev/null +++ b/prek.toml @@ -0,0 +1,19 @@ +[[repos]] +repo = "local" +hooks = [ + { + id = "cargo-fmt", + name = "cargo-fmt", + language = "system", + entry = "cargo fmt --", + types = ["rust"], + }, + { + id = "cargo-clippy", + name = "cargo-clippy", + language = "system", + entry = "cargo clippy -- -Dwarnings", + types = ["rust"], + pass_filenames = false, + }, +] diff --git a/src/keyboard.rs b/src/keyboard.rs index 6a249b0..9fbced5 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,18 +1,8 @@ -use core::sync::atomic::Ordering; - +use crate::usb::{KeyboardAction, KeyboardEvent}; use defmt::*; use embassy_rp::gpio::Input; -use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex}; -use embassy_time::{Duration, Timer}; -use embassy_usb::{ - class::hid::{HidProtocolMode, HidWriter}, - driver::Driver, -}; -use usbd_hid::descriptor::KeyboardReport; - -use crate::HID_PROTOCOL_MODE; - -const WRITE_N: usize = 8; +use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::channel::Sender; pub struct Key<'a> { button: Input<'a>, @@ -20,60 +10,32 @@ pub struct Key<'a> { } impl Key<'_> { - pub async fn process<'d, D: Driver<'d>>( + pub async fn process<'ch, M, const N: usize>( &mut self, - mut writer: Mutex>, - ) { + sender: Sender<'ch, M, KeyboardEvent, N>, + ) where + M: RawMutex, + { loop { self.button.wait_for_high().await; info!("Button {} pressed", self.value); - if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { - match writer.get_mut().write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { - Ok(()) => {} - Err(e) => warn!("Failed to send boot report: {:?}", e), - }; - } else { - // Create a report with the A key pressed. (no shift modifier) - let report = KeyboardReport { - keycodes: [4, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - // Send the report. - match writer.get_mut().write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; - } - - // Debounce - Timer::after(Duration::from_millis(50)).await; + sender + .send(KeyboardEvent { + key: self.value, + action: KeyboardAction::Press, + }) + .await; self.button.wait_for_low().await; info!("Button {} unpressed", self.value); - if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { - match writer.get_mut().write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { - Ok(()) => {} - Err(e) => warn!("Failed to send boot report: {:?}", e), - }; - } else { - let report = KeyboardReport { - keycodes: [0, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - match writer.get_mut().write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; - } - - // Debounce - Timer::after(Duration::from_millis(50)).await; + sender + .send(KeyboardEvent { + key: self.value, + action: KeyboardAction::Depress, + }) + .await; } } } @@ -85,8 +47,7 @@ impl Key<'_> { } } -pub struct Keyboard<'d, D: Driver<'d>, const KEY_N: usize> { - writer: Mutex>, +pub struct Keyboard<'d, const KEY_N: usize> { keys: [Option>; KEY_N], num_keys: usize, } @@ -95,10 +56,9 @@ pub enum KeyboardError { MaxKeys, } -impl<'d, D: Driver<'d>, const KEY_N: usize> Keyboard<'d, D, KEY_N> { - pub fn new(writer: HidWriter<'d, D, WRITE_N>) -> Self { +impl<'d, const KEY_N: usize> Keyboard<'d, KEY_N> { + pub fn new() -> Self { Self { - writer: Mutex::new(writer), keys: [const { None }; KEY_N], num_keys: 0, } @@ -116,8 +76,11 @@ impl<'d, D: Driver<'d>, const KEY_N: usize> Keyboard<'d, D, KEY_N> { Ok(()) } - pub async fn process(mut self) { + pub async fn process<'ch, M, const N: usize>(mut self, sender: Sender<'ch, M, KeyboardEvent, N>) + where + M: RawMutex, + { // TODO: Make this a loop that is concatinated together - self.keys[0].as_mut().unwrap().process(self.writer).await; + self.keys[0].as_mut().unwrap().process(sender).await; } } diff --git a/src/main.rs b/src/main.rs index e291baf..e96bccd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,10 +3,10 @@ mod heartbeat; mod keyboard; +mod usb; use crate::heartbeat::heartbeat; use crate::keyboard::Keyboard; -use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; @@ -14,12 +14,8 @@ use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; -use embassy_usb::class::hid::{ - HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, -}; -use embassy_usb::control::OutResponse; -use embassy_usb::{Builder, Config, Handler}; -use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; use {defmt_rtt as _, panic_probe as _}; @@ -27,59 +23,16 @@ bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; }); -static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); - #[embassy_executor::main] async fn main(_spawner: Spawner) { + let keyboard_events: Channel = Channel::new(); + let p = embassy_rp::init(Default::default()); + // Create the driver, from the HAL. let driver = Driver::new(p.USB, Irqs); - - // Create embassy-usb Config - let mut config = Config::new(0xc0de, 0xcafe); - config.manufacturer = Some("Nicholas Mello"); - config.product = Some("Pi Pico Arcade Keyboard"); - config.serial_number = Some("00000001"); - config.max_power = 100; - config.max_packet_size_0 = 64; - config.composite_with_iads = false; - config.device_class = 0; - config.device_sub_class = 0; - config.device_protocol = 0; - - // Create embassy-usb DeviceBuilder using the driver and config. - // It needs some buffers for building the descriptors. - let mut config_descriptor = [0; 256]; - let mut bos_descriptor = [0; 256]; - // You can also add a Microsoft OS descriptor. - let mut msos_descriptor = [0; 256]; - let mut control_buf = [0; 64]; - let mut request_handler = MyRequestHandler {}; - let mut device_handler = MyDeviceHandler::new(); - - let mut state = State::new(); - - let mut builder = Builder::new( - driver, - config, - &mut config_descriptor, - &mut bos_descriptor, - &mut msos_descriptor, - &mut control_buf, - ); - - builder.handler(&mut device_handler); - - // Create classes on the builder. - let config = embassy_usb::class::hid::Config { - report_descriptor: KeyboardReport::desc(), - request_handler: None, - poll_ms: 60, - max_packet_size: 64, - hid_subclass: HidSubclass::Boot, - hid_boot_protocol: HidBootProtocol::Keyboard, - }; - let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); + let mut usb_buf = usb::UsbKeyboardBuf::new(); + let usbkey = usb::UsbKeyboard::create_usb(&mut usb_buf, driver); // Set up the signal pin that will be used to trigger the keyboard. let mut signal_pin = Input::new(p.PIN_16, Pull::None); @@ -87,9 +40,7 @@ async fn main(_spawner: Spawner) { // Enable the schmitt trigger to slightly debounce. signal_pin.set_schmitt(true); - let (reader, writer) = hid.split(); - - let mut keyboard = Keyboard::<_, 1>::new(writer); + let mut keyboard = Keyboard::<1>::new(); match keyboard.add_key(signal_pin, 'a') { Ok(()) => info!("Key 'a' registered"), Err(_) => { @@ -97,95 +48,11 @@ async fn main(_spawner: Spawner) { } } - let out_fut = async { - reader.run(false, &mut request_handler).await; - }; - - let mut usb = builder.build(); let led = Output::new(p.PIN_25, Level::Low); join( - join(usb.run(), heartbeat(led)), - join(keyboard.process(), out_fut), + join(usbkey.run(keyboard_events.receiver()), heartbeat(led)), + keyboard.process(keyboard_events.sender()), ) .await; } - -struct MyRequestHandler {} - -impl RequestHandler for MyRequestHandler { - fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option { - info!("Get report for {:?}", id); - None - } - - fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse { - info!("Set report for {:?}: {=[u8]}", id, data); - OutResponse::Accepted - } - - fn get_protocol(&self) -> HidProtocolMode { - let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); - info!("The current HID protocol mode is: {}", protocol); - protocol - } - - fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { - info!("Switching to HID protocol mode: {}", protocol); - HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); - OutResponse::Accepted - } - - fn set_idle_ms(&mut self, id: Option, dur: u32) { - info!("Set idle rate for {:?} to {:?}", id, dur); - } - - fn get_idle_ms(&mut self, id: Option) -> Option { - info!("Get idle rate for {:?}", id); - None - } -} - -struct MyDeviceHandler { - configured: AtomicBool, -} - -impl MyDeviceHandler { - fn new() -> Self { - MyDeviceHandler { - configured: AtomicBool::new(false), - } - } -} - -impl Handler for MyDeviceHandler { - fn enabled(&mut self, enabled: bool) { - self.configured.store(false, Ordering::Relaxed); - if enabled { - info!("Device enabled"); - } else { - info!("Device disabled"); - } - } - - fn reset(&mut self) { - self.configured.store(false, Ordering::Relaxed); - info!("Bus reset, the Vbus current limit is 100mA"); - } - - fn addressed(&mut self, addr: u8) { - self.configured.store(false, Ordering::Relaxed); - info!("USB address set to: {}", addr); - } - - fn configured(&mut self, configured: bool) { - self.configured.store(configured, Ordering::Relaxed); - if configured { - info!( - "Device configured, it may now draw up to the configured current limit from Vbus." - ) - } else { - info!("Device is no longer configured, the Vbus current limit is 100mA."); - } - } -} diff --git a/src/usb.rs b/src/usb.rs new file mode 100644 index 0000000..dcbd87a --- /dev/null +++ b/src/usb.rs @@ -0,0 +1,278 @@ +use core::cell::OnceCell; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use defmt::*; +use embassy_futures::join::join; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::Driver; +use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::channel::Receiver; +use embassy_usb::UsbDevice; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidReader, HidReaderWriter, HidSubclass, HidWriter, ReportId, + RequestHandler, State, +}; +use embassy_usb::control::OutResponse; +use embassy_usb::{Builder, Config, Handler}; +use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; + +pub static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + +#[derive(Clone, Copy)] +pub enum KeyboardAction { + Press, + Depress, +} + +#[derive(Clone, Copy)] +pub struct KeyboardEvent { + pub key: char, + pub action: KeyboardAction, +} + +pub struct UsbKeyboardBuf<'d> { + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + config_descriptor: [u8; 256], + bos_descriptor: [u8; 256], + // You can also add a Microsoft OS descriptor. + msos_descriptor: [u8; 256], + control_buf: [u8; 64], + + state: OnceCell>, + device_handler: OnceCell, +} + +impl<'d> UsbKeyboardBuf<'d> { + pub const fn new() -> Self { + Self { + config_descriptor: [0; 256], + bos_descriptor: [0; 256], + msos_descriptor: [0; 256], + control_buf: [0; 64], + state: OnceCell::new(), + device_handler: OnceCell::new(), + } + } +} + +pub struct UsbKeyboard<'d> { + usb: UsbDevice<'d, Driver<'d, USB>>, + reader: HidReader<'d, Driver<'d, USB>, 1>, + writer: HidWriter<'d, Driver<'d, USB>, 8>, +} + +impl<'d> UsbKeyboard<'d> { + // HidWriter<'_, Driver<'_, USB>, 8> + pub fn create_usb(buf: &'d mut UsbKeyboardBuf<'d>, driver: Driver<'d, USB>) -> Self { + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Nicholas Mello"); + config.product = Some("Pi Pico Arcade Keyboard"); + config.serial_number = Some("00000001"); + config.max_power = 100; + config.max_packet_size_0 = 64; + config.composite_with_iads = false; + config.device_class = 0; + config.device_sub_class = 0; + config.device_protocol = 0; + + if buf.state.set(State::new()).is_err() { + defmt::panic!("USB State already initalized"); + } + + let mut builder = Builder::new( + driver, + config, + &mut buf.config_descriptor, + &mut buf.bos_descriptor, + &mut buf.msos_descriptor, + &mut buf.control_buf, + ); + + if buf.device_handler.set(MyDeviceHandler::new()).is_err() { + defmt::panic!("USB Device Handler already initialized"); + } + + builder.handler(buf.device_handler.get_mut().unwrap()); + + // Create classes on the builder. + let config = embassy_usb::class::hid::Config { + report_descriptor: KeyboardReport::desc(), + request_handler: None, + poll_ms: 60, + max_packet_size: 64, + hid_subclass: HidSubclass::Boot, + hid_boot_protocol: HidBootProtocol::Keyboard, + }; + let hid = + HidReaderWriter::<_, 1, 8>::new(&mut builder, buf.state.get_mut().unwrap(), config); + + let (reader, writer) = hid.split(); + + let usb = builder.build(); + + Self { + usb, + reader, + writer, + } + } + + pub async fn run<'ch, M, const N: usize>(self, receiver: Receiver<'ch, M, KeyboardEvent, N>) + where + M: RawMutex, + { + let mut request_handler = MyRequestHandler {}; + + let Self { + mut usb, + reader, + mut writer, + } = self; + + let reader_fut = async { + reader.run(false, &mut request_handler).await; + }; + + let writer_fut = async { + loop { + let event = receiver.receive().await; + + match event { + KeyboardEvent { + key: 'a', + action: KeyboardAction::Press, + } => { + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 + { + match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + // Create a report with the A key pressed. (no shift modifier) + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } + } + KeyboardEvent { + key: 'a', + action: KeyboardAction::Depress, + } => { + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 + { + match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } + } + _ => { + defmt::panic!("Unexpected event!"); + } + } + } + }; + + join(usb.run(), join(reader_fut, writer_fut)).await; + } +} + +struct MyRequestHandler {} + +impl RequestHandler for MyRequestHandler { + fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option { + info!("Get report for {:?}", id); + None + } + + fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse { + info!("Set report for {:?}: {=[u8]}", id, data); + OutResponse::Accepted + } + + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + + fn set_idle_ms(&mut self, id: Option, dur: u32) { + info!("Set idle rate for {:?} to {:?}", id, dur); + } + + fn get_idle_ms(&mut self, id: Option) -> Option { + info!("Get idle rate for {:?}", id); + None + } +} + +struct MyDeviceHandler { + configured: AtomicBool, +} + +impl MyDeviceHandler { + fn new() -> Self { + MyDeviceHandler { + configured: AtomicBool::new(false), + } + } +} + +impl Handler for MyDeviceHandler { + fn enabled(&mut self, enabled: bool) { + self.configured.store(false, Ordering::Relaxed); + if enabled { + info!("Device enabled"); + } else { + info!("Device disabled"); + } + } + + fn reset(&mut self) { + self.configured.store(false, Ordering::Relaxed); + info!("Bus reset, the Vbus current limit is 100mA"); + } + + fn addressed(&mut self, addr: u8) { + self.configured.store(false, Ordering::Relaxed); + info!("USB address set to: {}", addr); + } + + fn configured(&mut self, configured: bool) { + self.configured.store(configured, Ordering::Relaxed); + if configured { + info!( + "Device configured, it may now draw up to the configured current limit from Vbus." + ) + } else { + info!("Device is no longer configured, the Vbus current limit is 100mA."); + } + } +}