From 0a06561a0381bf26061e5ef64f19c78e6364e8f1 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Sat, 20 Jul 2024 15:44:51 +0200 Subject: [PATCH 01/21] First attempt of making some generic code --- avr-hal-generic/Cargo.toml | 1 + avr-hal-generic/src/lib.rs | 1 + avr-hal-generic/src/usb.rs | 167 +++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 avr-hal-generic/src/usb.rs diff --git a/avr-hal-generic/Cargo.toml b/avr-hal-generic/Cargo.toml index 16f81a3638..994b27ae01 100644 --- a/avr-hal-generic/Cargo.toml +++ b/avr-hal-generic/Cargo.toml @@ -26,6 +26,7 @@ embedded-storage = "0.2" embedded-hal = "1.0" embedded-hal-bus = "0.1" unwrap-infallible = "0.1.5" +usb-device = "0.3.2" [dependencies.embedded-hal-v0] version = "0.2.3" diff --git a/avr-hal-generic/src/lib.rs b/avr-hal-generic/src/lib.rs index 41d3c9d688..db16ca33fd 100644 --- a/avr-hal-generic/src/lib.rs +++ b/avr-hal-generic/src/lib.rs @@ -22,6 +22,7 @@ pub mod simple_pwm; pub mod spi; pub mod usart; pub mod wdt; +pub mod usb; /// Prelude containing all HAL traits pub mod prelude { diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs new file mode 100644 index 0000000000..e0bfa63ccd --- /dev/null +++ b/avr-hal-generic/src/usb.rs @@ -0,0 +1,167 @@ +use core::cell::Cell; + +use usb_device::{bus::UsbBus, UsbError}; +use avr_device::interrupt::{CriticalSection, Mutex as AvrDMutex}; + +const EP_SIZE_8: u8 = 0b000; +const EP_SIZE_16: u8 = 0b001; +const EP_SIZE_32: u8 = 0b010; +const EP_SIZE_64: u8 = 0b011; +const EP_SIZE_128: u8 = 0b100; +const EP_SIZE_256: u8 = 0b101; +const EP_SIZE_512: u8 = 0b110; + +#[derive(Default)] +struct EndpointTableEntry { + is_allocated: bool, + eptype_bits: u8, + epdir_bit: bool, + epsize_bits: u8, +} + +impl EndpointTableEntry { + fn buffer_size(&self) -> usize { + match self.epsize_bits { + EP_SIZE_8 => 8, + EP_SIZE_16 => 16, + EP_SIZE_32 => 32, + EP_SIZE_64 => 64, + EP_SIZE_128 => 128, + EP_SIZE_256 => 256, + EP_SIZE_512 => 512, + _ => unreachable!(), + } + } +} + +#[macro_export] +macro_rules! create_usb_bus { + ( + $USB_DEVICE:ty, + $MAX_ENDPOINTS:literal, + ) => { + pub struct AvrUsbBus { + usb: AvrDMutex<$USB_DEVICE>, + suspend_notifier: AvrDMutex, + pending_ins: AvrDMutex>, + endpoints: [EndpointTableEntry; $MAX_ENDPOINTS], + dpram_usage: u16, // TODO: This need to be extracted + } + + impl UsbBus<()> { + /// Create a new UsbBus without power-saving functionality. + /// + /// If you would like to disable the PLL when the USB peripheral is + /// suspended, then construct the bus with [`UsbBus::with_suspend_notifier`]. + pub fn new(usb: $USB_DEVICE) -> UsbBusAllocator { + Self::with_suspend_notifier(usb, ()) + } + } + + impl UsbBus { + /// Create a UsbBus with a suspend and resume handler. + /// + /// If you want the PLL to be automatically disabled when the USB peripheral + /// is suspended, then you can pass the PLL resource here; for example: + /// + /// ``` + /// use avr_device::atmega32u4::Peripherals; + /// use atmega_usbd::UsbBus; + /// + /// let dp = Peripherals.take().unwrap(); + /// // ... (other initialization stuff) + /// let bus = UsbBus::with_suspend_notifier(dp.USB_DEVICE, dp.PLL); + /// ``` + /// + /// **Note: If you are using the PLL output for other peripherals like the + /// high-speed timer, then disabling the PLL may affect the behavior of + /// those peripherals.** In such cases, you can either use [`UsbBus::new`] + /// to leave the PLL running, or implement [`SuspendNotifier`] yourself, + /// with some custom logic to gracefully shut down the PLL in cooperation + /// with your other peripherals. + pub fn with_suspend_notifier(usb: $USB_DEVICE, suspend_notifier: S) -> UsbBusAllocator { + UsbBusAllocator::new(Self { + usb: AvrDMutex::new(usb), + suspend_notifier: AvrDMutex::new(suspend_notifier), + pending_ins: AvrDMutex::new(Cell::new(0)), + endpoints: Default::default(), + dpram_usage: 0, + }) + } + + fn active_endpoints(&self) -> impl Iterator { + self.endpoints + .iter() + .enumerate() // why enumerate then immediately drop? + .filter(|&(_, ep)| ep.is_allocated) + } + + fn set_current_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { + if index >= MAX_ENDPOINTS { + return Err(UsbError::InvalidEndpoint); + } + let usb = self.usb.borrow(cs); + // TODO: the rest of this needs to be abstracted + if usb.usbcon.read().clk().bit_is_set() { + return Err(UsbError::InvalidState); + } + usb.uenum.write(|w| w.bits(index as u8)); + if usb.uenum.read().bits() & 0b111 != (index as u8) { + return Err(UsbError::InvalidState); + } + Ok(()) + } + + fn endpoint_byte_count(&self, cs: CriticalSection) -> u16 { + let usb = self.usb.borrow(cs); + // FIXME: Potential for desync here? LUFA doesn't seem to care. + ((usb.uebchx.read().bits() as u16) << 8) | (usb.uebclx.read().bits() as u16) + } + + fn configure_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { + let usb = self.usb.borrow(cs); + self.set_current_endpoint(cs, index)?; + let endpoint = &self.endpoints[index]; + + usb.ueconx.modify(|_, w| w.epen().set_bit()); + usb.uecfg1x.modify(|_, w| w.alloc().clear_bit()); + + usb.uecfg0x.write(|w| { + w.epdir() + .bit(endpoint.epdir_bit) + .eptype() + .bits(endpoint.eptype_bits) + }); + usb.uecfg1x + .write(|w| w.epbk().bits(0).epsize().bits(endpoint.epsize_bits)); + usb.uecfg1x.modify(|_, w| w.alloc().set_bit()); + + assert!( + usb.uesta0x.read().cfgok().bit_is_set(), + "could not configure endpoint {}", + index + ); + + usb.ueienx + .modify(|_, w| w.rxoute().set_bit().rxstpe().set_bit()); + Ok(()) + } + } + } +} + + +/// Receiver for handling suspend and resume events from the USB device. +/// +/// See [`UsbBus::with_suspend_notifier`] for more details. +pub trait SuspendNotifier: Send + Sized + 'static { + /// Called by `UsbBus` when the USB peripheral has been suspended and the + /// PLL is safe to shut down. + fn suspend(&self) {} + + /// Called by `UsbBus` when the USB peripheral is about to resume and is + /// waiting for PLL to be enabled. + /// + /// This function should block until PLL lock has been established. + fn resume(&self) {} +} \ No newline at end of file From 8cb57459bff96f57f9fbf9cc04cb7b7cd6a8047c Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Wed, 24 Jul 2024 08:03:56 -0400 Subject: [PATCH 02/21] Just muting some non-issues --- arduino-hal/src/port/leonardo.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/arduino-hal/src/port/leonardo.rs b/arduino-hal/src/port/leonardo.rs index c25b5a89b9..4460fdf271 100644 --- a/arduino-hal/src/port/leonardo.rs +++ b/arduino-hal/src/port/leonardo.rs @@ -1,5 +1,6 @@ pub use atmega_hal::port::{mode, Pin, PinMode, PinOps}; +#[allow(non_camel_case_types)] // Mute LedRx and LedTx complaints avr_hal_generic::renamed_pins! { /// Pins of the **Arduino Leonardo**. /// From 13f0b069b7f9faf5701bb7f60e54b7d302d6a583 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Wed, 24 Jul 2024 08:04:26 -0400 Subject: [PATCH 03/21] Switching to using Macro 2.0 --- avr-hal-generic/src/lib.rs | 1 + avr-hal-generic/src/usb.rs | 269 ++++++++++++++++++++++--------------- 2 files changed, 162 insertions(+), 108 deletions(-) diff --git a/avr-hal-generic/src/lib.rs b/avr-hal-generic/src/lib.rs index db16ca33fd..9560a2c817 100644 --- a/avr-hal-generic/src/lib.rs +++ b/avr-hal-generic/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] #![cfg_attr(avr_hal_asm_macro, feature(asm_experimental_arch))] #![cfg_attr(not(avr_hal_asm_macro), feature(llvm_asm))] +#![feature(decl_macro)] pub use embedded_hal as hal; pub use embedded_hal_v0 as hal_v0; diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs index e0bfa63ccd..a8ab9e7463 100644 --- a/avr-hal-generic/src/usb.rs +++ b/avr-hal-generic/src/usb.rs @@ -1,6 +1,6 @@ use core::cell::Cell; -use usb_device::{bus::UsbBus, UsbError}; +use usb_device::{bus::{UsbBus, PollResult}, UsbDirection, UsbError, class_prelude::UsbBusAllocator, endpoint::{EndpointAddress, EndpointType}, Result as UsbResult}; use avr_device::interrupt::{CriticalSection, Mutex as AvrDMutex}; const EP_SIZE_8: u8 = 0b000; @@ -12,7 +12,7 @@ const EP_SIZE_256: u8 = 0b101; const EP_SIZE_512: u8 = 0b110; #[derive(Default)] -struct EndpointTableEntry { +pub struct EndpointTableEntry { // REVIEW: what should the scoping be here? is_allocated: bool, eptype_bits: u8, epdir_bit: bool, @@ -34,118 +34,169 @@ impl EndpointTableEntry { } } -#[macro_export] -macro_rules! create_usb_bus { - ( - $USB_DEVICE:ty, - $MAX_ENDPOINTS:literal, - ) => { - pub struct AvrUsbBus { - usb: AvrDMutex<$USB_DEVICE>, - suspend_notifier: AvrDMutex, - pending_ins: AvrDMutex>, - endpoints: [EndpointTableEntry; $MAX_ENDPOINTS], - dpram_usage: u16, // TODO: This need to be extracted - } - - impl UsbBus<()> { - /// Create a new UsbBus without power-saving functionality. - /// - /// If you would like to disable the PLL when the USB peripheral is - /// suspended, then construct the bus with [`UsbBus::with_suspend_notifier`]. - pub fn new(usb: $USB_DEVICE) -> UsbBusAllocator { - Self::with_suspend_notifier(usb, ()) - } +// Using Macro 2.0 here while not stable yet makes this code a lot more readable and easier to write +pub macro create_usb_bus ( + $USB_DEVICE:ty, + $MAX_ENDPOINTS:literal, +) { + pub struct AvrUsbBus { + usb: AvrDMutex<$USB_DEVICE>, + suspend_notifier: AvrDMutex, + pending_ins: AvrDMutex>, + endpoints: [EndpointTableEntry; $MAX_ENDPOINTS], + dpram_usage: u16, // TODO: This need to be extracted + } + + impl AvrUsbBus<()> { + /// Create a new UsbBus without power-saving functionality. + /// + /// If you would like to disable the PLL when the USB peripheral is + /// suspended, then construct the bus with [`UsbBus::with_suspend_notifier`]. + pub fn new(usb: $USB_DEVICE) -> UsbBusAllocator { + Self::with_suspend_notifier(usb, ()) + } + } + + impl AvrUsbBus { + /// Create a UsbBus with a suspend and resume handler. + /// + /// If you want the PLL to be automatically disabled when the USB peripheral + /// is suspended, then you can pass the PLL resource here; for example: + /// + /// ``` + /// use avr_device::atmega32u4::Peripherals; + /// use atmega_usbd::UsbBus; + /// + /// let dp = Peripherals.take().unwrap(); + /// // ... (other initialization stuff) + /// let bus = UsbBus::with_suspend_notifier(dp.USB_DEVICE, dp.PLL); + /// ``` + /// + /// **Note: If you are using the PLL output for other peripherals like the + /// high-speed timer, then disabling the PLL may affect the behavior of + /// those peripherals.** In such cases, you can either use [`UsbBus::new`] + /// to leave the PLL running, or implement [`SuspendNotifier`] yourself, + /// with some custom logic to gracefully shut down the PLL in cooperation + /// with your other peripherals. + pub fn with_suspend_notifier(usb: $USB_DEVICE, suspend_notifier: S) -> UsbBusAllocator { + UsbBusAllocator::new(Self { + usb: AvrDMutex::new(usb), + suspend_notifier: AvrDMutex::new(suspend_notifier), + pending_ins: AvrDMutex::new(Cell::new(0)), + endpoints: Default::default(), + dpram_usage: 0, + }) } - - impl UsbBus { - /// Create a UsbBus with a suspend and resume handler. - /// - /// If you want the PLL to be automatically disabled when the USB peripheral - /// is suspended, then you can pass the PLL resource here; for example: - /// - /// ``` - /// use avr_device::atmega32u4::Peripherals; - /// use atmega_usbd::UsbBus; - /// - /// let dp = Peripherals.take().unwrap(); - /// // ... (other initialization stuff) - /// let bus = UsbBus::with_suspend_notifier(dp.USB_DEVICE, dp.PLL); - /// ``` - /// - /// **Note: If you are using the PLL output for other peripherals like the - /// high-speed timer, then disabling the PLL may affect the behavior of - /// those peripherals.** In such cases, you can either use [`UsbBus::new`] - /// to leave the PLL running, or implement [`SuspendNotifier`] yourself, - /// with some custom logic to gracefully shut down the PLL in cooperation - /// with your other peripherals. - pub fn with_suspend_notifier(usb: $USB_DEVICE, suspend_notifier: S) -> UsbBusAllocator { - UsbBusAllocator::new(Self { - usb: AvrDMutex::new(usb), - suspend_notifier: AvrDMutex::new(suspend_notifier), - pending_ins: AvrDMutex::new(Cell::new(0)), - endpoints: Default::default(), - dpram_usage: 0, - }) - } - fn active_endpoints(&self) -> impl Iterator { - self.endpoints - .iter() - .enumerate() // why enumerate then immediately drop? - .filter(|&(_, ep)| ep.is_allocated) - } + fn active_endpoints(&self) -> impl Iterator { + self.endpoints + .iter() + .enumerate() // why enumerate then immediately drop? + .filter(|&(_, ep)| ep.is_allocated) + } - fn set_current_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { - if index >= MAX_ENDPOINTS { - return Err(UsbError::InvalidEndpoint); - } - let usb = self.usb.borrow(cs); - // TODO: the rest of this needs to be abstracted - if usb.usbcon.read().clk().bit_is_set() { - return Err(UsbError::InvalidState); - } - usb.uenum.write(|w| w.bits(index as u8)); - if usb.uenum.read().bits() & 0b111 != (index as u8) { - return Err(UsbError::InvalidState); - } - Ok(()) + fn set_current_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { + if index >= $MAX_ENDPOINTS { + return Err(UsbError::InvalidEndpoint); } - - fn endpoint_byte_count(&self, cs: CriticalSection) -> u16 { - let usb = self.usb.borrow(cs); - // FIXME: Potential for desync here? LUFA doesn't seem to care. - ((usb.uebchx.read().bits() as u16) << 8) | (usb.uebclx.read().bits() as u16) + let usb = self.usb.borrow(cs); + // TODO: the rest of this needs to be abstracted + if usb.usbcon.read().frzclk().bit_is_set() { + return Err(UsbError::InvalidState); + } + usb.uenum.write(|w| w.bits(index as u8)); + if usb.uenum.read().bits() & 0b111 != (index as u8) { + return Err(UsbError::InvalidState); } + Ok(()) + } - fn configure_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { - let usb = self.usb.borrow(cs); - self.set_current_endpoint(cs, index)?; - let endpoint = &self.endpoints[index]; - - usb.ueconx.modify(|_, w| w.epen().set_bit()); - usb.uecfg1x.modify(|_, w| w.alloc().clear_bit()); - - usb.uecfg0x.write(|w| { - w.epdir() - .bit(endpoint.epdir_bit) - .eptype() - .bits(endpoint.eptype_bits) - }); - usb.uecfg1x - .write(|w| w.epbk().bits(0).epsize().bits(endpoint.epsize_bits)); - usb.uecfg1x.modify(|_, w| w.alloc().set_bit()); - - assert!( - usb.uesta0x.read().cfgok().bit_is_set(), - "could not configure endpoint {}", - index - ); + fn endpoint_byte_count(&self, cs: CriticalSection) -> u16 { + let usb = self.usb.borrow(cs); + // FIXME: Potential for desync here? LUFA doesn't seem to care. + ((usb.uebchx.read().bits() as u16) << 8) | (usb.uebclx.read().bits() as u16) + } + + fn configure_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { + let usb = self.usb.borrow(cs); + self.set_current_endpoint(cs, index)?; + let endpoint = &self.endpoints[index]; + + usb.ueconx.modify(|_, w| w.epen().set_bit()); + usb.uecfg1x.modify(|_, w| w.alloc().clear_bit()); + + usb.uecfg0x.write(|w| { + w.epdir() + .bit(endpoint.epdir_bit) + .eptype() + .bits(endpoint.eptype_bits) + }); + usb.uecfg1x + .write(|w| w.epbk().bits(0).epsize().bits(endpoint.epsize_bits)); + usb.uecfg1x.modify(|_, w| w.alloc().set_bit()); + + assert!( + usb.uesta0x.read().cfgok().bit_is_set(), + "could not configure endpoint {}", + index + ); + + usb.ueienx + .modify(|_, w| w.rxoute().set_bit().rxstpe().set_bit()); + Ok(()) + } + } + + impl UsbBus for AvrUsbBus { + fn alloc_ep( + &mut self, + ep_dir: UsbDirection, + ep_addr: Option, + ep_type: EndpointType, + max_packet_size: u16, + _interval: u8, + ) -> Result { + unimplemented!() + } + + fn enable(&mut self) { + unimplemented!() + } + + fn reset(&self) { + unimplemented!() + } + + fn set_device_address(&self, addr: u8) { + unimplemented!() + } + + fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> UsbResult { + unimplemented!() + } + + fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> UsbResult { + unimplemented!() + } + + fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool) { + unimplemented!() + } - usb.ueienx - .modify(|_, w| w.rxoute().set_bit().rxstpe().set_bit()); - Ok(()) - } + fn is_stalled(&self, ep_addr: EndpointAddress) -> bool { + unimplemented!() + } + + fn suspend(&self) { + unimplemented!() + } + + fn resume(&self) { + unimplemented!() + } + + fn poll(&self) -> PollResult { + unimplemented!() } } } @@ -164,4 +215,6 @@ pub trait SuspendNotifier: Send + Sized + 'static { /// /// This function should block until PLL lock has been established. fn resume(&self) {} -} \ No newline at end of file +} + +impl SuspendNotifier for () {} \ No newline at end of file From e7c382058d7eeaf7abd6523989657261c98b2fde Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Wed, 24 Jul 2024 08:05:29 -0400 Subject: [PATCH 04/21] Moving into atmega-hal --- mcu/atmega-hal/src/lib.rs | 4 ++++ mcu/atmega-hal/src/usb.rs | 11 +++++++++++ 2 files changed, 15 insertions(+) create mode 100644 mcu/atmega-hal/src/usb.rs diff --git a/mcu/atmega-hal/src/lib.rs b/mcu/atmega-hal/src/lib.rs index e2ce9bf2e1..810d6f40d5 100644 --- a/mcu/atmega-hal/src/lib.rs +++ b/mcu/atmega-hal/src/lib.rs @@ -145,6 +145,10 @@ pub mod eeprom; #[cfg(feature = "device-selected")] pub use eeprom::Eeprom; +// REVIEW: This maybe should only be turned on for USBs +#[cfg(feature = "device-selected")] +pub mod usb; + pub struct Atmega; #[cfg(any(feature = "atmega48p", feature = "atmega168", feature = "atmega328p"))] diff --git a/mcu/atmega-hal/src/usb.rs b/mcu/atmega-hal/src/usb.rs new file mode 100644 index 0000000000..65bb71b114 --- /dev/null +++ b/mcu/atmega-hal/src/usb.rs @@ -0,0 +1,11 @@ + +pub use avr_hal_generic::usb::*; + +#[cfg(feature = "atmega32u4")] +use avr_device::atmega32u4::USB_DEVICE; + + +avr_hal_generic::usb::create_usb_bus! { + USB_DEVICE, + 8, +} \ No newline at end of file From 8d0f94eb6cc8a4dcb10ab66e414c6df61739f0f1 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Thu, 25 Jul 2024 08:14:29 -0400 Subject: [PATCH 05/21] Switch to using pac for writing less code --- mcu/atmega-hal/src/usb.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mcu/atmega-hal/src/usb.rs b/mcu/atmega-hal/src/usb.rs index 65bb71b114..9ccb935732 100644 --- a/mcu/atmega-hal/src/usb.rs +++ b/mcu/atmega-hal/src/usb.rs @@ -1,9 +1,10 @@ pub use avr_hal_generic::usb::*; -#[cfg(feature = "atmega32u4")] -use avr_device::atmega32u4::USB_DEVICE; +#[cfg(any(feature = "atmega32u4", feature = "atmega8u2"))] +use crate::pac::USB_DEVICE; +// use crate::pac::USB0 as USB_DEVICE; avr_hal_generic::usb::create_usb_bus! { USB_DEVICE, From dfc1521381ef06df6e328b0e14a03211ad1167c7 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:09:42 -0400 Subject: [PATCH 06/21] First impl for UsbBus --- avr-hal-generic/src/usb.rs | 366 +++++++++++++++++++++++++++++++++++-- mcu/atmega-hal/src/lib.rs | 6 +- mcu/atmega-hal/src/usb.rs | 102 ++++++++++- 3 files changed, 450 insertions(+), 24 deletions(-) diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs index a8ab9e7463..38d12dc716 100644 --- a/avr-hal-generic/src/usb.rs +++ b/avr-hal-generic/src/usb.rs @@ -1,7 +1,22 @@ -use core::cell::Cell; +#![feature(negative_impls)] + +use core::{cell::Cell, cmp::max}; use usb_device::{bus::{UsbBus, PollResult}, UsbDirection, UsbError, class_prelude::UsbBusAllocator, endpoint::{EndpointAddress, EndpointType}, Result as UsbResult}; -use avr_device::interrupt::{CriticalSection, Mutex as AvrDMutex}; +use avr_device::{ + asm::delay_cycles, + interrupt::{self, CriticalSection, Mutex as AvrDMutex} +}; + +// REVIEW: Do any of the guys below here need to be abstracted? + +const EP_TYPE_CONTROL: u8 = 0b00; +const EP_TYPE_ISOCHRONOUS: u8 = 0b01; +const EP_TYPE_BULK: u8 = 0b10; +const EP_TYPE_INTERRUPT: u8 = 0b11; + +const EP_DIR_IN: bool = true; +const EP_DIR_OUT: bool = false; const EP_SIZE_8: u8 = 0b000; const EP_SIZE_16: u8 = 0b001; @@ -13,14 +28,14 @@ const EP_SIZE_512: u8 = 0b110; #[derive(Default)] pub struct EndpointTableEntry { // REVIEW: what should the scoping be here? - is_allocated: bool, + is_allocated: bool, // REVIEW: i dont think this should be pub eptype_bits: u8, epdir_bit: bool, epsize_bits: u8, } impl EndpointTableEntry { - fn buffer_size(&self) -> usize { + pub fn buffer_size(&self) -> usize { match self.epsize_bits { EP_SIZE_8 => 8, EP_SIZE_16 => 16, @@ -37,8 +52,16 @@ impl EndpointTableEntry { // Using Macro 2.0 here while not stable yet makes this code a lot more readable and easier to write pub macro create_usb_bus ( $USB_DEVICE:ty, - $MAX_ENDPOINTS:literal, + $UDINT:ty, + $UEINTX:ty, + $USBINT:ty, + $MAX_ENDPOINTS:ident, + $ENDPOINT_MAX_BUFSIZE:ident, + $DPRAM_SIZE:ident, ) { + + // MARK: - AvrUsbBus + pub struct AvrUsbBus { usb: AvrDMutex<$USB_DEVICE>, suspend_notifier: AvrDMutex, @@ -111,7 +134,7 @@ pub macro create_usb_bus ( Ok(()) } - fn endpoint_byte_count(&self, cs: CriticalSection) -> u16 { + fn endpoint_byte_count(&self, cs: CriticalSection) -> u16 { // REVIEW: should this conditionally be a u8 let usb = self.usb.borrow(cs); // FIXME: Potential for desync here? LUFA doesn't seem to care. ((usb.uebchx.read().bits() as u16) << 8) | (usb.uebclx.read().bits() as u16) @@ -156,52 +179,359 @@ pub macro create_usb_bus ( max_packet_size: u16, _interval: u8, ) -> Result { - unimplemented!() + // Ignore duplicate ep0 allocation by usb_device. + // Endpoints can only be configured once, and + // control endpoint must be configured as "OUT". + if ep_addr == Some(EndpointAddress::from_parts(0, UsbDirection::In)) { + return Ok(ep_addr.unwrap()); + } + + let ep_addr = match ep_addr { + Some(addr) if !self.endpoints[addr.index()].is_allocated => addr, + _ => { + // Find next free endpoint + let index = self + .endpoints + .iter() + .enumerate() + .skip(1) + .find_map(|(index, ep)| { + if !ep.is_allocated && max_packet_size <= $ENDPOINT_MAX_BUFSIZE[index] { + Some(index) + } else { + None + } + }) + .ok_or(UsbError::EndpointOverflow)?; + EndpointAddress::from_parts(index, ep_dir) + } + }; + let entry = &mut self.endpoints[ep_addr.index()]; + entry.eptype_bits = match ep_type { + EndpointType::Control => EP_TYPE_CONTROL, + EndpointType::Isochronous { .. } => EP_TYPE_ISOCHRONOUS, + EndpointType::Bulk => EP_TYPE_BULK, + EndpointType::Interrupt => EP_TYPE_INTERRUPT, + }; + entry.epdir_bit = match ep_dir { + UsbDirection::Out => EP_DIR_OUT, + UsbDirection::In => EP_DIR_IN, + }; + let ep_size = max(8, max_packet_size.next_power_of_two()); + if $DPRAM_SIZE - self.dpram_usage < ep_size { + return Err(UsbError::EndpointMemoryOverflow); + } + entry.epsize_bits = match ep_size { + 8 => EP_SIZE_8, + 16 => EP_SIZE_16, + 32 => EP_SIZE_32, + 64 => EP_SIZE_64, + 128 => EP_SIZE_128, + 256 => EP_SIZE_256, + 512 => EP_SIZE_512, + _ => return Err(UsbError::EndpointMemoryOverflow), + }; + + // Configuration succeeded, commit/finalize: + entry.is_allocated = true; + self.dpram_usage += ep_size; + Ok(ep_addr) } fn enable(&mut self) { - unimplemented!() + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + usb.uhwcon.modify(|_, w| w.uvrege().set_bit()); + usb.usbcon + .modify(|_, w| w.usbe().set_bit().otgpade().set_bit()); + // NB: FRZCLK cannot be set/cleared when USBE=0, and + // cannot be modified at the same time. + usb.usbcon + .modify(|_, w| w.frzclk().clear_bit().vbuste().set_bit()); + + for (index, _ep) in self.active_endpoints() { + self.configure_endpoint(cs, index).unwrap(); + } + + usb.udcon.modify(|_, w| w.detach().clear_bit()); + usb.udien + .modify(|_, w| w.eorste().set_bit().sofe().set_bit()); + }); } fn reset(&self) { - unimplemented!() + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + usb.udint.modify(|_, w| w.eorsti().clear_bit()); + + for (index, _ep) in self.active_endpoints() { + self.configure_endpoint(cs, index).unwrap(); + } + + usb.udint + .clear_interrupts(|w| w.wakeupi().clear_bit().suspi().clear_bit()); + usb.udien + .modify(|_, w| w.wakeupe().clear_bit().suspe().set_bit()); + }) } fn set_device_address(&self, addr: u8) { - unimplemented!() + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + usb.udaddr.modify(|_, w| w.uadd().bits(addr)); + // NB: ADDEN and UADD shall not be written at the same time. + usb.udaddr.modify(|_, w| w.adden().set_bit()); + }); } fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> UsbResult { - unimplemented!() + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + self.set_current_endpoint(cs, ep_addr.index())?; + let endpoint = &self.endpoints[ep_addr.index()]; + + // Different logic is needed for control endpoints: + // - The FIFOCON and RWAL fields are irrelevant with CONTROL endpoints. + // - TXINI ... shall be cleared by firmware to **send the + // packet and to clear the endpoint bank.** + if endpoint.eptype_bits == EP_TYPE_CONTROL { + if usb.ueintx.read().txini().bit_is_clear() { + return Err(UsbError::WouldBlock); + } + + let buffer_size = endpoint.buffer_size(); + if buf.len() > buffer_size { + return Err(UsbError::BufferOverflow); + } + + for &byte in buf { + usb.uedatx.write(|w| w.bits(byte)) + } + + usb.ueintx.clear_interrupts(|w| w.txini().clear_bit()); + } else { + if usb.ueintx.read().txini().bit_is_clear() { + return Err(UsbError::WouldBlock); + } + //NB: RXOUTI serves as KILLBK for IN endpoints and needs to stay zero: + usb.ueintx + .clear_interrupts(|w| w.txini().clear_bit().rxouti().clear_bit()); + + for &byte in buf { + if usb.ueintx.read().rwal().bit_is_clear() { + return Err(UsbError::BufferOverflow); + } + usb.uedatx.write(|w| w.bits(byte)); + } + + //NB: RXOUTI serves as KILLBK for IN endpoints and needs to stay zero: + usb.ueintx + .clear_interrupts(|w| w.fifocon().clear_bit().rxouti().clear_bit()); + } + + let pending_ins = self.pending_ins.borrow(cs); + pending_ins.set(pending_ins.get() | 1 << ep_addr.index()); + + Ok(buf.len()) + }) } fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> UsbResult { - unimplemented!() + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + self.set_current_endpoint(cs, ep_addr.index())?; + let endpoint = &self.endpoints[ep_addr.index()]; + + // Different logic is needed for control endpoints: + // - The FIFOCON and RWAL fields are irrelevant with CONTROL endpoints. + // - RXSTPI/RXOUTI ... shall be cleared by firmware to **send the + // packet and to clear the endpoint bank.** + if endpoint.eptype_bits == EP_TYPE_CONTROL { + let ueintx = usb.ueintx.read(); + if ueintx.rxouti().bit_is_clear() && ueintx.rxstpi().bit_is_clear() { + return Err(UsbError::WouldBlock); + } + + let bytes_to_read = self.endpoint_byte_count(cs) as usize; + if bytes_to_read > buf.len() { + return Err(UsbError::BufferOverflow); + } + + for slot in &mut buf[..bytes_to_read] { + *slot = usb.uedatx.read().bits(); + } + usb.ueintx + .clear_interrupts(|w| w.rxouti().clear_bit().rxstpi().clear_bit()); + + Ok(bytes_to_read) + } else { + if usb.ueintx.read().rxouti().bit_is_clear() { + return Err(UsbError::WouldBlock); + } + usb.ueintx.clear_interrupts(|w| w.rxouti().clear_bit()); + + let mut bytes_read = 0; + for slot in buf { + if usb.ueintx.read().rwal().bit_is_clear() { + break; + } + *slot = usb.uedatx.read().bits(); + bytes_read += 1; + } + + if usb.ueintx.read().rwal().bit_is_set() { + return Err(UsbError::BufferOverflow); + } + + usb.ueintx.clear_interrupts(|w| w.fifocon().clear_bit()); + Ok(bytes_read) + } + }) } fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool) { - unimplemented!() + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + if self.set_current_endpoint(cs, ep_addr.index()).is_ok() { + usb.ueconx + .modify(|_, w| w.stallrq().bit(stalled).stallrqc().bit(!stalled)); + } + }); } fn is_stalled(&self, ep_addr: EndpointAddress) -> bool { - unimplemented!() + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + if self.set_current_endpoint(cs, ep_addr.index()).is_ok() { + // NB: The datasheet says STALLRQ is write-only but LUFA reads from it... + usb.ueconx.read().stallrq().bit_is_set() + } else { + false + } + }) } fn suspend(&self) { - unimplemented!() + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + usb.udint + .clear_interrupts(|w| w.suspi().clear_bit().wakeupi().clear_bit()); + usb.udien + .modify(|_, w| w.wakeupe().set_bit().suspe().clear_bit()); + usb.usbcon.modify(|_, w| w.frzclk().set_bit()); + + self.suspend_notifier.borrow(cs).suspend(); + }); } fn resume(&self) { - unimplemented!() + interrupt::free(|cs| { + self.suspend_notifier.borrow(cs).resume(); + + let usb = self.usb.borrow(cs); + usb.usbcon.modify(|_, w| w.frzclk().clear_bit()); + usb.udint + .clear_interrupts(|w| w.wakeupi().clear_bit().suspi().clear_bit()); + usb.udien + .modify(|_, w| w.wakeupe().clear_bit().suspe().set_bit()); + }); } fn poll(&self) -> PollResult { - unimplemented!() + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + + let usbint = usb.usbint.read(); + let udint = usb.udint.read(); + let udien = usb.udien.read(); + if usbint.vbusti().bit_is_set() { + usb.usbint.clear_interrupts(|w| w.vbusti().clear_bit()); + if usb.usbsta.read().vbus().bit_is_set() { + return PollResult::Resume; + } else { + return PollResult::Suspend; + } + } + if udint.suspi().bit_is_set() && udien.suspe().bit_is_set() { + return PollResult::Suspend; + } + if udint.wakeupi().bit_is_set() && udien.wakeupe().bit_is_set() { + return PollResult::Resume; + } + if udint.eorsti().bit_is_set() { + return PollResult::Reset; + } + if udint.sofi().bit_is_set() { + usb.udint.clear_interrupts(|w| w.sofi().clear_bit()); + } + + // Can only query endpoints while clock is running + // (e.g. not in suspend state) + if usb.usbcon.read().frzclk().bit_is_clear() { + let mut ep_out = 0u8; + let mut ep_setup = 0u8; + let mut ep_in_complete = 0u8; + let pending_ins = self.pending_ins.borrow(cs); + + for (index, _ep) in self.active_endpoints() { + if self.set_current_endpoint(cs, index).is_err() { + // Endpoint selection has stopped working... + break; + } + + let ueintx = usb.ueintx.read(); + if ueintx.rxouti().bit_is_set() { + ep_out |= 1 << index; + } + if ueintx.rxstpi().bit_is_set() { + ep_setup |= 1 << index; + } + if pending_ins.get() & (1 << index) != 0 && ueintx.txini().bit_is_set() { + ep_in_complete |= 1 << index; + pending_ins.set(pending_ins.get() & !(1 << index)); + } + } + if ep_out | ep_setup | ep_in_complete != 0 { + return PollResult::Data { + ep_out: ep_out as u16, + ep_in_complete: ep_in_complete as u16, + ep_setup: ep_setup as u16, + }; + } + } + + PollResult::None + }) + } + + fn force_reset(&self) -> UsbResult<()> { + // 22.9 "It is possible to re-enumerate a device, simply by setting and + // clearing the DETACH bit (but firmware must take in account a + // debouncing delay of some milliseconds)." + + interrupt::free(|cs| { + self.usb + .borrow(cs) + .udcon + .modify(|_, w| w.detach().set_bit()); + }); + + // Delay for at least 1ms (exactly 1ms at 16 MHz) + // to allow the host to detect the change. + delay_cycles(16000); + + interrupt::free(|cs| { + self.usb + .borrow(cs) + .udcon + .modify(|_, w| w.detach().clear_bit()); + }); + + Ok(()) } } } - /// Receiver for handling suspend and resume events from the USB device. /// /// See [`UsbBus::with_suspend_notifier`] for more details. diff --git a/mcu/atmega-hal/src/lib.rs b/mcu/atmega-hal/src/lib.rs index 810d6f40d5..25728df968 100644 --- a/mcu/atmega-hal/src/lib.rs +++ b/mcu/atmega-hal/src/lib.rs @@ -145,8 +145,10 @@ pub mod eeprom; #[cfg(feature = "device-selected")] pub use eeprom::Eeprom; -// REVIEW: This maybe should only be turned on for USBs -#[cfg(feature = "device-selected")] +#[cfg(any( + feature = "atmega8u2", + feature = "atmega32u4", +))] pub mod usb; pub struct Atmega; diff --git a/mcu/atmega-hal/src/usb.rs b/mcu/atmega-hal/src/usb.rs index 9ccb935732..ef33fe25ae 100644 --- a/mcu/atmega-hal/src/usb.rs +++ b/mcu/atmega-hal/src/usb.rs @@ -1,12 +1,106 @@ pub use avr_hal_generic::usb::*; +// MARK: - Type Imports + #[cfg(any(feature = "atmega32u4", feature = "atmega8u2"))] -use crate::pac::USB_DEVICE; +use crate::pac::{ + usb_device::{udint, UDINT, ueintx, UEINTX}, + USB_DEVICE +}; + +#[cfg(feature = "atmega32u4")] +use crate::pac::usb_device::{usbint, USBINT}; + +#[cfg(feature = "atmega8u2")] +type USBINT = !; // atmega8u2 does not have USBINT register +#[cfg(feature = "atmega8u2")] +const usbint: USBINT = !(); // atmega8u2 does not have USBINT register + +// MARK: - Constants by device -// use crate::pac::USB0 as USB_DEVICE; +#[cfg(feature = "atmega32u4")] +const MAX_ENDPOINTS: usize = 7; +#[cfg(feature = "atmega32u4")] +const ENDPOINT_MAX_BUFSIZE: [u16; MAX_ENDPOINTS] = [64, 256, 64, 64, 64, 64, 64]; +#[cfg(feature = "atmega32u4")] +const DPRAM_SIZE: u16 = 832; + +#[cfg(feature = "atmega8u2")] +const MAX_ENDPOINTS: usize = 5; +#[cfg(feature = "atmega8u2")] +const ENDPOINT_MAX_BUFSIZE: [u16; MAX_ENDPOINTS] = [64, 64, 64, 64, 64]; +#[cfg(feature = "atmega8u2")] +const DPRAM_SIZE: u16 = 176; avr_hal_generic::usb::create_usb_bus! { USB_DEVICE, - 8, -} \ No newline at end of file + UDINT, // REVIEW: how should i be passing these down? (do we want to pass the registers too or just the type?) + UEINTX, + USBINT, + MAX_ENDPOINTS, + ENDPOINT_MAX_BUFSIZE, + DPRAM_SIZE, +} + +/// Extension trait for conveniently clearing AVR interrupt flag registers. +/// +/// To clear an interrupt flag, a zero bit must be written. However, there are +/// several other hazards to take into consideration: +/// +/// 1. If you read-modify-write, it is possible that an interrupt flag will be +/// set by hardware in between the read and write, and writing the zero that +/// you previously read will clear that flag as well. So, use a default value +/// of all ones and specifically clear the bits you want. HOWEVER: +/// +/// 2. Some bits of the interrupt flag register are reserved, and it is +/// specified that they should not be written as ones. +/// +/// Implementers of this trait should provide an initial value to the callback +/// with all _known_ interrupt flags set to the value that has no effect (which +/// is 1, in most cases) +pub trait ClearInterrupts { // This trait must live here due to the orphan rule + type Writer; + + fn clear_interrupts(&self, f: F) + where + for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer; +} + +impl ClearInterrupts for UDINT { + type Writer = udint::W; + + fn clear_interrupts(&self, f: F) + where + for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer, + { + // Bits 1,7 reserved as do not set. Setting all other bits has no effect + self.write(|w| f(unsafe { w.bits(0x7d) })) + } +} + +impl ClearInterrupts for UEINTX { + type Writer = ueintx::W; + + fn clear_interrupts(&self, f: F) + where + for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer, + { + // Bit 5 read-only. Setting all other bits has no effect, EXCEPT: + // - RXOUTI/KILLBK should not be set for "IN" endpoints (XXX end-user beware) + self.write(|w| f(unsafe { w.bits(0xdf) })) + } +} + +#[cfg(not(feature = "atmega8u2"))] +impl ClearInterrupts for USBINT { + type Writer = usbint::W; + + fn clear_interrupts(&self, f: F) + where + for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer, + { + // Bits 7:1 are reserved as do not set. + self.write(|w| f(unsafe { w.bits(0x01) })) + } +} From 16ca5892129a05104e53ec905fac3a5b7b06dd2a Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:14:36 -0400 Subject: [PATCH 07/21] Moving SuspendNotifier outside due to orphan rule --- avr-hal-generic/src/usb.rs | 24 ++++-------------------- mcu/atmega-hal/src/usb.rs | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs index 38d12dc716..f5c4072cf1 100644 --- a/avr-hal-generic/src/usb.rs +++ b/avr-hal-generic/src/usb.rs @@ -52,6 +52,7 @@ impl EndpointTableEntry { // Using Macro 2.0 here while not stable yet makes this code a lot more readable and easier to write pub macro create_usb_bus ( $USB_DEVICE:ty, + $SuspendNotifier:path, $UDINT:ty, $UEINTX:ty, $USBINT:ty, @@ -62,7 +63,7 @@ pub macro create_usb_bus ( // MARK: - AvrUsbBus - pub struct AvrUsbBus { + pub struct AvrUsbBus { usb: AvrDMutex<$USB_DEVICE>, suspend_notifier: AvrDMutex, pending_ins: AvrDMutex>, @@ -80,7 +81,7 @@ pub macro create_usb_bus ( } } - impl AvrUsbBus { + impl AvrUsbBus { /// Create a UsbBus with a suspend and resume handler. /// /// If you want the PLL to be automatically disabled when the USB peripheral @@ -170,7 +171,7 @@ pub macro create_usb_bus ( } } - impl UsbBus for AvrUsbBus { + impl UsbBus for AvrUsbBus { fn alloc_ep( &mut self, ep_dir: UsbDirection, @@ -531,20 +532,3 @@ pub macro create_usb_bus ( } } } - -/// Receiver for handling suspend and resume events from the USB device. -/// -/// See [`UsbBus::with_suspend_notifier`] for more details. -pub trait SuspendNotifier: Send + Sized + 'static { - /// Called by `UsbBus` when the USB peripheral has been suspended and the - /// PLL is safe to shut down. - fn suspend(&self) {} - - /// Called by `UsbBus` when the USB peripheral is about to resume and is - /// waiting for PLL to be enabled. - /// - /// This function should block until PLL lock has been established. - fn resume(&self) {} -} - -impl SuspendNotifier for () {} \ No newline at end of file diff --git a/mcu/atmega-hal/src/usb.rs b/mcu/atmega-hal/src/usb.rs index ef33fe25ae..1f5142de7a 100644 --- a/mcu/atmega-hal/src/usb.rs +++ b/mcu/atmega-hal/src/usb.rs @@ -6,7 +6,7 @@ pub use avr_hal_generic::usb::*; #[cfg(any(feature = "atmega32u4", feature = "atmega8u2"))] use crate::pac::{ usb_device::{udint, UDINT, ueintx, UEINTX}, - USB_DEVICE + USB_DEVICE, PLL, }; #[cfg(feature = "atmega32u4")] @@ -35,6 +35,7 @@ const DPRAM_SIZE: u16 = 176; avr_hal_generic::usb::create_usb_bus! { USB_DEVICE, + SuspendNotifier, UDINT, // REVIEW: how should i be passing these down? (do we want to pass the registers too or just the type?) UEINTX, USBINT, @@ -104,3 +105,33 @@ impl ClearInterrupts for USBINT { self.write(|w| f(unsafe { w.bits(0x01) })) } } + +/// Receiver for handling suspend and resume events from the USB device. +/// +/// See [`UsbBus::with_suspend_notifier`] for more details. +pub trait SuspendNotifier: Send + Sized + 'static { + /// Called by `UsbBus` when the USB peripheral has been suspended and the + /// PLL is safe to shut down. + fn suspend(&self) {} + + /// Called by `UsbBus` when the USB peripheral is about to resume and is + /// waiting for PLL to be enabled. + /// + /// This function should block until PLL lock has been established. + fn resume(&self) {} +} + +impl SuspendNotifier for () {} + +impl SuspendNotifier for PLL { + fn suspend(&self) { + self.pllcsr.modify(|_, w| w.plle().clear_bit()); + } + + fn resume(&self) { + self.pllcsr + .modify(|_, w| w.pindiv().set_bit().plle().set_bit()); + + while self.pllcsr.read().plock().bit_is_clear() {} + } +} From e4608f36280c5325309c99129f7c68d846d9837c Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:15:12 -0400 Subject: [PATCH 08/21] Removing no longer used parts of macro --- avr-hal-generic/src/usb.rs | 3 --- mcu/atmega-hal/src/usb.rs | 8 -------- 2 files changed, 11 deletions(-) diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs index f5c4072cf1..c039bb960b 100644 --- a/avr-hal-generic/src/usb.rs +++ b/avr-hal-generic/src/usb.rs @@ -53,9 +53,6 @@ impl EndpointTableEntry { pub macro create_usb_bus ( $USB_DEVICE:ty, $SuspendNotifier:path, - $UDINT:ty, - $UEINTX:ty, - $USBINT:ty, $MAX_ENDPOINTS:ident, $ENDPOINT_MAX_BUFSIZE:ident, $DPRAM_SIZE:ident, diff --git a/mcu/atmega-hal/src/usb.rs b/mcu/atmega-hal/src/usb.rs index 1f5142de7a..995a3a4c96 100644 --- a/mcu/atmega-hal/src/usb.rs +++ b/mcu/atmega-hal/src/usb.rs @@ -12,11 +12,6 @@ use crate::pac::{ #[cfg(feature = "atmega32u4")] use crate::pac::usb_device::{usbint, USBINT}; -#[cfg(feature = "atmega8u2")] -type USBINT = !; // atmega8u2 does not have USBINT register -#[cfg(feature = "atmega8u2")] -const usbint: USBINT = !(); // atmega8u2 does not have USBINT register - // MARK: - Constants by device #[cfg(feature = "atmega32u4")] @@ -36,9 +31,6 @@ const DPRAM_SIZE: u16 = 176; avr_hal_generic::usb::create_usb_bus! { USB_DEVICE, SuspendNotifier, - UDINT, // REVIEW: how should i be passing these down? (do we want to pass the registers too or just the type?) - UEINTX, - USBINT, MAX_ENDPOINTS, ENDPOINT_MAX_BUFSIZE, DPRAM_SIZE, From e81e8751867fe0955d61bd4d928f61918256c018 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:46:57 -0400 Subject: [PATCH 09/21] Some macro and code changes for 8u2 device --- avr-hal-generic/src/usb.rs | 59 +++++++++++++++++++++++++++----------- mcu/atmega-hal/src/usb.rs | 6 ++++ 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs index c039bb960b..c09a9f06e5 100644 --- a/avr-hal-generic/src/usb.rs +++ b/avr-hal-generic/src/usb.rs @@ -56,6 +56,8 @@ pub macro create_usb_bus ( $MAX_ENDPOINTS:ident, $ENDPOINT_MAX_BUFSIZE:ident, $DPRAM_SIZE:ident, + $limited_inter_and_vbus:meta, + $not_limited_inter_and_vbus:meta ) { // MARK: - AvrUsbBus @@ -125,7 +127,9 @@ pub macro create_usb_bus ( if usb.usbcon.read().frzclk().bit_is_set() { return Err(UsbError::InvalidState); } - usb.uenum.write(|w| w.bits(index as u8)); + unsafe { // TODO: investigate unsafety here (only occurs in atmega8u2?) + usb.uenum.write(|w| w.bits(index as u8)); + } if usb.uenum.read().bits() & 0b111 != (index as u8) { return Err(UsbError::InvalidState); } @@ -135,7 +139,14 @@ pub macro create_usb_bus ( fn endpoint_byte_count(&self, cs: CriticalSection) -> u16 { // REVIEW: should this conditionally be a u8 let usb = self.usb.borrow(cs); // FIXME: Potential for desync here? LUFA doesn't seem to care. - ((usb.uebchx.read().bits() as u16) << 8) | (usb.uebclx.read().bits() as u16) + #[$not_limited_inter_and_vbus] + { + return ((usb.uebchx.read().bits() as u16) << 8) | (usb.uebclx.read().bits() as u16) + } + #[$limited_inter_and_vbus] + { // REVIEW: is this the correct formula? + return (usb.uebclx.read().bits() as u16) // u16? + } } fn configure_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { @@ -239,13 +250,24 @@ pub macro create_usb_bus ( fn enable(&mut self) { interrupt::free(|cs| { let usb = self.usb.borrow(cs); - usb.uhwcon.modify(|_, w| w.uvrege().set_bit()); - usb.usbcon - .modify(|_, w| w.usbe().set_bit().otgpade().set_bit()); - // NB: FRZCLK cannot be set/cleared when USBE=0, and - // cannot be modified at the same time. - usb.usbcon - .modify(|_, w| w.frzclk().clear_bit().vbuste().set_bit()); + #[$not_limited_inter_and_vbus] + { + usb.uhwcon.modify(|_, w| w.uvrege().set_bit()); // REVIEW: what is the uhwcon and the pad regulator? + usb.usbcon + .modify(|_, w| w.usbe().set_bit().otgpade().set_bit()); + // NB: FRZCLK cannot be set/cleared when USBE=0, and + // cannot be modified at the same time. + usb.usbcon + .modify(|_, w| w.frzclk().clear_bit().vbuste().set_bit()); + } + #[$limited_inter_and_vbus] + { + usb.usbcon + .modify(|_, w| w.usbe().set_bit()); + // NB: FRZCLK cannot be set/cleared when USBE=0, and + // cannot be modified at the same time. + usb.usbcon.modify(|_, w| w.frzclk().clear_bit()); + } for (index, _ep) in self.active_endpoints() { self.configure_endpoint(cs, index).unwrap(); @@ -439,17 +461,22 @@ pub macro create_usb_bus ( interrupt::free(|cs| { let usb = self.usb.borrow(cs); - let usbint = usb.usbint.read(); let udint = usb.udint.read(); let udien = usb.udien.read(); - if usbint.vbusti().bit_is_set() { - usb.usbint.clear_interrupts(|w| w.vbusti().clear_bit()); - if usb.usbsta.read().vbus().bit_is_set() { - return PollResult::Resume; - } else { - return PollResult::Suspend; + + #[$not_limited_inter_and_vbus] + { + let usbint = usb.usbint.read(); + if usbint.vbusti().bit_is_set() { + usb.usbint.clear_interrupts(|w| w.vbusti().clear_bit()); + if usb.usbsta.read().vbus().bit_is_set() { + return PollResult::Resume; + } else { + return PollResult::Suspend; + } } } + if udint.suspi().bit_is_set() && udien.suspe().bit_is_set() { return PollResult::Suspend; } diff --git a/mcu/atmega-hal/src/usb.rs b/mcu/atmega-hal/src/usb.rs index 995a3a4c96..6a706908bd 100644 --- a/mcu/atmega-hal/src/usb.rs +++ b/mcu/atmega-hal/src/usb.rs @@ -34,6 +34,8 @@ avr_hal_generic::usb::create_usb_bus! { MAX_ENDPOINTS, ENDPOINT_MAX_BUFSIZE, DPRAM_SIZE, + cfg(feature = "atmega8u2"), + cfg(feature = "atmega32u4") } /// Extension trait for conveniently clearing AVR interrupt flag registers. @@ -121,8 +123,12 @@ impl SuspendNotifier for PLL { } fn resume(&self) { + #[cfg(feature = "atmega32u4")] self.pllcsr .modify(|_, w| w.pindiv().set_bit().plle().set_bit()); + #[cfg(feature = "atmega8u2")] + self.pllcsr + .modify(|_, w| w.pllp().val_0x05().plle().set_bit()); // REVIEW: is val_0x05 or val_0x03 correct? or something else? while self.pllcsr.read().plock().bit_is_clear() {} } From 52fb66b99e5718b5e41ad65e8cc07f6a40559dec Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:47:33 -0400 Subject: [PATCH 10/21] Switching to tabs --- avr-hal-generic/src/usb.rs | 998 ++++++++++++++++++------------------- mcu/atmega-hal/src/usb.rs | 110 ++-- 2 files changed, 554 insertions(+), 554 deletions(-) diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs index c09a9f06e5..6750fe491a 100644 --- a/avr-hal-generic/src/usb.rs +++ b/avr-hal-generic/src/usb.rs @@ -4,8 +4,8 @@ use core::{cell::Cell, cmp::max}; use usb_device::{bus::{UsbBus, PollResult}, UsbDirection, UsbError, class_prelude::UsbBusAllocator, endpoint::{EndpointAddress, EndpointType}, Result as UsbResult}; use avr_device::{ - asm::delay_cycles, - interrupt::{self, CriticalSection, Mutex as AvrDMutex} + asm::delay_cycles, + interrupt::{self, CriticalSection, Mutex as AvrDMutex} }; // REVIEW: Do any of the guys below here need to be abstracted? @@ -28,531 +28,531 @@ const EP_SIZE_512: u8 = 0b110; #[derive(Default)] pub struct EndpointTableEntry { // REVIEW: what should the scoping be here? - is_allocated: bool, // REVIEW: i dont think this should be pub - eptype_bits: u8, - epdir_bit: bool, - epsize_bits: u8, + is_allocated: bool, // REVIEW: i dont think this should be pub + eptype_bits: u8, + epdir_bit: bool, + epsize_bits: u8, } impl EndpointTableEntry { - pub fn buffer_size(&self) -> usize { - match self.epsize_bits { - EP_SIZE_8 => 8, - EP_SIZE_16 => 16, - EP_SIZE_32 => 32, - EP_SIZE_64 => 64, - EP_SIZE_128 => 128, - EP_SIZE_256 => 256, - EP_SIZE_512 => 512, - _ => unreachable!(), - } - } + pub fn buffer_size(&self) -> usize { + match self.epsize_bits { + EP_SIZE_8 => 8, + EP_SIZE_16 => 16, + EP_SIZE_32 => 32, + EP_SIZE_64 => 64, + EP_SIZE_128 => 128, + EP_SIZE_256 => 256, + EP_SIZE_512 => 512, + _ => unreachable!(), + } + } } // Using Macro 2.0 here while not stable yet makes this code a lot more readable and easier to write pub macro create_usb_bus ( - $USB_DEVICE:ty, - $SuspendNotifier:path, - $MAX_ENDPOINTS:ident, - $ENDPOINT_MAX_BUFSIZE:ident, - $DPRAM_SIZE:ident, - $limited_inter_and_vbus:meta, - $not_limited_inter_and_vbus:meta + $USB_DEVICE:ty, + $SuspendNotifier:path, + $MAX_ENDPOINTS:ident, + $ENDPOINT_MAX_BUFSIZE:ident, + $DPRAM_SIZE:ident, + $limited_inter_and_vbus:meta, + $not_limited_inter_and_vbus:meta ) { - // MARK: - AvrUsbBus + // MARK: - AvrUsbBus - pub struct AvrUsbBus { - usb: AvrDMutex<$USB_DEVICE>, - suspend_notifier: AvrDMutex, - pending_ins: AvrDMutex>, - endpoints: [EndpointTableEntry; $MAX_ENDPOINTS], - dpram_usage: u16, // TODO: This need to be extracted - } + pub struct AvrUsbBus { + usb: AvrDMutex<$USB_DEVICE>, + suspend_notifier: AvrDMutex, + pending_ins: AvrDMutex>, + endpoints: [EndpointTableEntry; $MAX_ENDPOINTS], + dpram_usage: u16, // TODO: This need to be extracted + } - impl AvrUsbBus<()> { - /// Create a new UsbBus without power-saving functionality. - /// - /// If you would like to disable the PLL when the USB peripheral is - /// suspended, then construct the bus with [`UsbBus::with_suspend_notifier`]. - pub fn new(usb: $USB_DEVICE) -> UsbBusAllocator { - Self::with_suspend_notifier(usb, ()) - } - } - - impl AvrUsbBus { - /// Create a UsbBus with a suspend and resume handler. - /// - /// If you want the PLL to be automatically disabled when the USB peripheral - /// is suspended, then you can pass the PLL resource here; for example: - /// - /// ``` - /// use avr_device::atmega32u4::Peripherals; - /// use atmega_usbd::UsbBus; - /// - /// let dp = Peripherals.take().unwrap(); - /// // ... (other initialization stuff) - /// let bus = UsbBus::with_suspend_notifier(dp.USB_DEVICE, dp.PLL); - /// ``` - /// - /// **Note: If you are using the PLL output for other peripherals like the - /// high-speed timer, then disabling the PLL may affect the behavior of - /// those peripherals.** In such cases, you can either use [`UsbBus::new`] - /// to leave the PLL running, or implement [`SuspendNotifier`] yourself, - /// with some custom logic to gracefully shut down the PLL in cooperation - /// with your other peripherals. - pub fn with_suspend_notifier(usb: $USB_DEVICE, suspend_notifier: S) -> UsbBusAllocator { - UsbBusAllocator::new(Self { - usb: AvrDMutex::new(usb), - suspend_notifier: AvrDMutex::new(suspend_notifier), - pending_ins: AvrDMutex::new(Cell::new(0)), - endpoints: Default::default(), - dpram_usage: 0, - }) - } + impl AvrUsbBus<()> { + /// Create a new UsbBus without power-saving functionality. + /// + /// If you would like to disable the PLL when the USB peripheral is + /// suspended, then construct the bus with [`UsbBus::with_suspend_notifier`]. + pub fn new(usb: $USB_DEVICE) -> UsbBusAllocator { + Self::with_suspend_notifier(usb, ()) + } + } + + impl AvrUsbBus { + /// Create a UsbBus with a suspend and resume handler. + /// + /// If you want the PLL to be automatically disabled when the USB peripheral + /// is suspended, then you can pass the PLL resource here; for example: + /// + /// ``` + /// use avr_device::atmega32u4::Peripherals; + /// use atmega_usbd::UsbBus; + /// + /// let dp = Peripherals.take().unwrap(); + /// // ... (other initialization stuff) + /// let bus = UsbBus::with_suspend_notifier(dp.USB_DEVICE, dp.PLL); + /// ``` + /// + /// **Note: If you are using the PLL output for other peripherals like the + /// high-speed timer, then disabling the PLL may affect the behavior of + /// those peripherals.** In such cases, you can either use [`UsbBus::new`] + /// to leave the PLL running, or implement [`SuspendNotifier`] yourself, + /// with some custom logic to gracefully shut down the PLL in cooperation + /// with your other peripherals. + pub fn with_suspend_notifier(usb: $USB_DEVICE, suspend_notifier: S) -> UsbBusAllocator { + UsbBusAllocator::new(Self { + usb: AvrDMutex::new(usb), + suspend_notifier: AvrDMutex::new(suspend_notifier), + pending_ins: AvrDMutex::new(Cell::new(0)), + endpoints: Default::default(), + dpram_usage: 0, + }) + } - fn active_endpoints(&self) -> impl Iterator { - self.endpoints - .iter() - .enumerate() // why enumerate then immediately drop? - .filter(|&(_, ep)| ep.is_allocated) - } + fn active_endpoints(&self) -> impl Iterator { + self.endpoints + .iter() + .enumerate() // why enumerate then immediately drop? + .filter(|&(_, ep)| ep.is_allocated) + } - fn set_current_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { - if index >= $MAX_ENDPOINTS { - return Err(UsbError::InvalidEndpoint); - } - let usb = self.usb.borrow(cs); - // TODO: the rest of this needs to be abstracted - if usb.usbcon.read().frzclk().bit_is_set() { - return Err(UsbError::InvalidState); - } - unsafe { // TODO: investigate unsafety here (only occurs in atmega8u2?) - usb.uenum.write(|w| w.bits(index as u8)); - } - if usb.uenum.read().bits() & 0b111 != (index as u8) { - return Err(UsbError::InvalidState); - } - Ok(()) - } + fn set_current_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { + if index >= $MAX_ENDPOINTS { + return Err(UsbError::InvalidEndpoint); + } + let usb = self.usb.borrow(cs); + // TODO: the rest of this needs to be abstracted + if usb.usbcon.read().frzclk().bit_is_set() { + return Err(UsbError::InvalidState); + } + unsafe { // TODO: investigate unsafety here (only occurs in atmega8u2?) + usb.uenum.write(|w| w.bits(index as u8)); + } + if usb.uenum.read().bits() & 0b111 != (index as u8) { + return Err(UsbError::InvalidState); + } + Ok(()) + } - fn endpoint_byte_count(&self, cs: CriticalSection) -> u16 { // REVIEW: should this conditionally be a u8 - let usb = self.usb.borrow(cs); - // FIXME: Potential for desync here? LUFA doesn't seem to care. - #[$not_limited_inter_and_vbus] - { - return ((usb.uebchx.read().bits() as u16) << 8) | (usb.uebclx.read().bits() as u16) - } - #[$limited_inter_and_vbus] - { // REVIEW: is this the correct formula? - return (usb.uebclx.read().bits() as u16) // u16? - } - } + fn endpoint_byte_count(&self, cs: CriticalSection) -> u16 { // REVIEW: should this conditionally be a u8 + let usb = self.usb.borrow(cs); + // FIXME: Potential for desync here? LUFA doesn't seem to care. + #[$not_limited_inter_and_vbus] + { + return ((usb.uebchx.read().bits() as u16) << 8) | (usb.uebclx.read().bits() as u16) + } + #[$limited_inter_and_vbus] + { // REVIEW: is this the correct formula? + return (usb.uebclx.read().bits() as u16) // u16? + } + } - fn configure_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { - let usb = self.usb.borrow(cs); - self.set_current_endpoint(cs, index)?; - let endpoint = &self.endpoints[index]; - - usb.ueconx.modify(|_, w| w.epen().set_bit()); - usb.uecfg1x.modify(|_, w| w.alloc().clear_bit()); - - usb.uecfg0x.write(|w| { - w.epdir() - .bit(endpoint.epdir_bit) - .eptype() - .bits(endpoint.eptype_bits) - }); - usb.uecfg1x - .write(|w| w.epbk().bits(0).epsize().bits(endpoint.epsize_bits)); - usb.uecfg1x.modify(|_, w| w.alloc().set_bit()); - - assert!( - usb.uesta0x.read().cfgok().bit_is_set(), - "could not configure endpoint {}", - index - ); - - usb.ueienx - .modify(|_, w| w.rxoute().set_bit().rxstpe().set_bit()); - Ok(()) - } - } + fn configure_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { + let usb = self.usb.borrow(cs); + self.set_current_endpoint(cs, index)?; + let endpoint = &self.endpoints[index]; + + usb.ueconx.modify(|_, w| w.epen().set_bit()); + usb.uecfg1x.modify(|_, w| w.alloc().clear_bit()); + + usb.uecfg0x.write(|w| { + w.epdir() + .bit(endpoint.epdir_bit) + .eptype() + .bits(endpoint.eptype_bits) + }); + usb.uecfg1x + .write(|w| w.epbk().bits(0).epsize().bits(endpoint.epsize_bits)); + usb.uecfg1x.modify(|_, w| w.alloc().set_bit()); + + assert!( + usb.uesta0x.read().cfgok().bit_is_set(), + "could not configure endpoint {}", + index + ); + + usb.ueienx + .modify(|_, w| w.rxoute().set_bit().rxstpe().set_bit()); + Ok(()) + } + } - impl UsbBus for AvrUsbBus { - fn alloc_ep( - &mut self, - ep_dir: UsbDirection, - ep_addr: Option, - ep_type: EndpointType, - max_packet_size: u16, - _interval: u8, - ) -> Result { - // Ignore duplicate ep0 allocation by usb_device. - // Endpoints can only be configured once, and - // control endpoint must be configured as "OUT". - if ep_addr == Some(EndpointAddress::from_parts(0, UsbDirection::In)) { - return Ok(ep_addr.unwrap()); - } + impl UsbBus for AvrUsbBus { + fn alloc_ep( + &mut self, + ep_dir: UsbDirection, + ep_addr: Option, + ep_type: EndpointType, + max_packet_size: u16, + _interval: u8, + ) -> Result { + // Ignore duplicate ep0 allocation by usb_device. + // Endpoints can only be configured once, and + // control endpoint must be configured as "OUT". + if ep_addr == Some(EndpointAddress::from_parts(0, UsbDirection::In)) { + return Ok(ep_addr.unwrap()); + } - let ep_addr = match ep_addr { - Some(addr) if !self.endpoints[addr.index()].is_allocated => addr, - _ => { - // Find next free endpoint - let index = self - .endpoints - .iter() - .enumerate() - .skip(1) - .find_map(|(index, ep)| { - if !ep.is_allocated && max_packet_size <= $ENDPOINT_MAX_BUFSIZE[index] { - Some(index) - } else { - None - } - }) - .ok_or(UsbError::EndpointOverflow)?; - EndpointAddress::from_parts(index, ep_dir) - } - }; - let entry = &mut self.endpoints[ep_addr.index()]; - entry.eptype_bits = match ep_type { - EndpointType::Control => EP_TYPE_CONTROL, - EndpointType::Isochronous { .. } => EP_TYPE_ISOCHRONOUS, - EndpointType::Bulk => EP_TYPE_BULK, - EndpointType::Interrupt => EP_TYPE_INTERRUPT, - }; - entry.epdir_bit = match ep_dir { - UsbDirection::Out => EP_DIR_OUT, - UsbDirection::In => EP_DIR_IN, - }; - let ep_size = max(8, max_packet_size.next_power_of_two()); - if $DPRAM_SIZE - self.dpram_usage < ep_size { - return Err(UsbError::EndpointMemoryOverflow); - } - entry.epsize_bits = match ep_size { - 8 => EP_SIZE_8, - 16 => EP_SIZE_16, - 32 => EP_SIZE_32, - 64 => EP_SIZE_64, - 128 => EP_SIZE_128, - 256 => EP_SIZE_256, - 512 => EP_SIZE_512, - _ => return Err(UsbError::EndpointMemoryOverflow), - }; + let ep_addr = match ep_addr { + Some(addr) if !self.endpoints[addr.index()].is_allocated => addr, + _ => { + // Find next free endpoint + let index = self + .endpoints + .iter() + .enumerate() + .skip(1) + .find_map(|(index, ep)| { + if !ep.is_allocated && max_packet_size <= $ENDPOINT_MAX_BUFSIZE[index] { + Some(index) + } else { + None + } + }) + .ok_or(UsbError::EndpointOverflow)?; + EndpointAddress::from_parts(index, ep_dir) + } + }; + let entry = &mut self.endpoints[ep_addr.index()]; + entry.eptype_bits = match ep_type { + EndpointType::Control => EP_TYPE_CONTROL, + EndpointType::Isochronous { .. } => EP_TYPE_ISOCHRONOUS, + EndpointType::Bulk => EP_TYPE_BULK, + EndpointType::Interrupt => EP_TYPE_INTERRUPT, + }; + entry.epdir_bit = match ep_dir { + UsbDirection::Out => EP_DIR_OUT, + UsbDirection::In => EP_DIR_IN, + }; + let ep_size = max(8, max_packet_size.next_power_of_two()); + if $DPRAM_SIZE - self.dpram_usage < ep_size { + return Err(UsbError::EndpointMemoryOverflow); + } + entry.epsize_bits = match ep_size { + 8 => EP_SIZE_8, + 16 => EP_SIZE_16, + 32 => EP_SIZE_32, + 64 => EP_SIZE_64, + 128 => EP_SIZE_128, + 256 => EP_SIZE_256, + 512 => EP_SIZE_512, + _ => return Err(UsbError::EndpointMemoryOverflow), + }; - // Configuration succeeded, commit/finalize: - entry.is_allocated = true; - self.dpram_usage += ep_size; - Ok(ep_addr) - } + // Configuration succeeded, commit/finalize: + entry.is_allocated = true; + self.dpram_usage += ep_size; + Ok(ep_addr) + } - fn enable(&mut self) { - interrupt::free(|cs| { - let usb = self.usb.borrow(cs); - #[$not_limited_inter_and_vbus] - { - usb.uhwcon.modify(|_, w| w.uvrege().set_bit()); // REVIEW: what is the uhwcon and the pad regulator? - usb.usbcon - .modify(|_, w| w.usbe().set_bit().otgpade().set_bit()); - // NB: FRZCLK cannot be set/cleared when USBE=0, and - // cannot be modified at the same time. - usb.usbcon - .modify(|_, w| w.frzclk().clear_bit().vbuste().set_bit()); - } - #[$limited_inter_and_vbus] - { - usb.usbcon - .modify(|_, w| w.usbe().set_bit()); - // NB: FRZCLK cannot be set/cleared when USBE=0, and - // cannot be modified at the same time. - usb.usbcon.modify(|_, w| w.frzclk().clear_bit()); - } - - for (index, _ep) in self.active_endpoints() { - self.configure_endpoint(cs, index).unwrap(); - } - - usb.udcon.modify(|_, w| w.detach().clear_bit()); - usb.udien - .modify(|_, w| w.eorste().set_bit().sofe().set_bit()); - }); - } + fn enable(&mut self) { + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + #[$not_limited_inter_and_vbus] + { + usb.uhwcon.modify(|_, w| w.uvrege().set_bit()); // REVIEW: what is the uhwcon and the pad regulator? + usb.usbcon + .modify(|_, w| w.usbe().set_bit().otgpade().set_bit()); + // NB: FRZCLK cannot be set/cleared when USBE=0, and + // cannot be modified at the same time. + usb.usbcon + .modify(|_, w| w.frzclk().clear_bit().vbuste().set_bit()); + } + #[$limited_inter_and_vbus] + { + usb.usbcon + .modify(|_, w| w.usbe().set_bit()); + // NB: FRZCLK cannot be set/cleared when USBE=0, and + // cannot be modified at the same time. + usb.usbcon.modify(|_, w| w.frzclk().clear_bit()); + } + + for (index, _ep) in self.active_endpoints() { + self.configure_endpoint(cs, index).unwrap(); + } + + usb.udcon.modify(|_, w| w.detach().clear_bit()); + usb.udien + .modify(|_, w| w.eorste().set_bit().sofe().set_bit()); + }); + } - fn reset(&self) { - interrupt::free(|cs| { - let usb = self.usb.borrow(cs); - usb.udint.modify(|_, w| w.eorsti().clear_bit()); - - for (index, _ep) in self.active_endpoints() { - self.configure_endpoint(cs, index).unwrap(); - } - - usb.udint - .clear_interrupts(|w| w.wakeupi().clear_bit().suspi().clear_bit()); - usb.udien - .modify(|_, w| w.wakeupe().clear_bit().suspe().set_bit()); - }) - } + fn reset(&self) { + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + usb.udint.modify(|_, w| w.eorsti().clear_bit()); + + for (index, _ep) in self.active_endpoints() { + self.configure_endpoint(cs, index).unwrap(); + } + + usb.udint + .clear_interrupts(|w| w.wakeupi().clear_bit().suspi().clear_bit()); + usb.udien + .modify(|_, w| w.wakeupe().clear_bit().suspe().set_bit()); + }) + } - fn set_device_address(&self, addr: u8) { - interrupt::free(|cs| { - let usb = self.usb.borrow(cs); - usb.udaddr.modify(|_, w| w.uadd().bits(addr)); - // NB: ADDEN and UADD shall not be written at the same time. - usb.udaddr.modify(|_, w| w.adden().set_bit()); - }); - } + fn set_device_address(&self, addr: u8) { + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + usb.udaddr.modify(|_, w| w.uadd().bits(addr)); + // NB: ADDEN and UADD shall not be written at the same time. + usb.udaddr.modify(|_, w| w.adden().set_bit()); + }); + } - fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> UsbResult { - interrupt::free(|cs| { - let usb = self.usb.borrow(cs); - self.set_current_endpoint(cs, ep_addr.index())?; - let endpoint = &self.endpoints[ep_addr.index()]; - - // Different logic is needed for control endpoints: - // - The FIFOCON and RWAL fields are irrelevant with CONTROL endpoints. - // - TXINI ... shall be cleared by firmware to **send the - // packet and to clear the endpoint bank.** - if endpoint.eptype_bits == EP_TYPE_CONTROL { - if usb.ueintx.read().txini().bit_is_clear() { - return Err(UsbError::WouldBlock); - } - - let buffer_size = endpoint.buffer_size(); - if buf.len() > buffer_size { - return Err(UsbError::BufferOverflow); - } - - for &byte in buf { - usb.uedatx.write(|w| w.bits(byte)) - } - - usb.ueintx.clear_interrupts(|w| w.txini().clear_bit()); - } else { - if usb.ueintx.read().txini().bit_is_clear() { - return Err(UsbError::WouldBlock); - } - //NB: RXOUTI serves as KILLBK for IN endpoints and needs to stay zero: - usb.ueintx - .clear_interrupts(|w| w.txini().clear_bit().rxouti().clear_bit()); - - for &byte in buf { - if usb.ueintx.read().rwal().bit_is_clear() { - return Err(UsbError::BufferOverflow); - } - usb.uedatx.write(|w| w.bits(byte)); - } - - //NB: RXOUTI serves as KILLBK for IN endpoints and needs to stay zero: - usb.ueintx - .clear_interrupts(|w| w.fifocon().clear_bit().rxouti().clear_bit()); - } - - let pending_ins = self.pending_ins.borrow(cs); - pending_ins.set(pending_ins.get() | 1 << ep_addr.index()); - - Ok(buf.len()) - }) - } + fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> UsbResult { + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + self.set_current_endpoint(cs, ep_addr.index())?; + let endpoint = &self.endpoints[ep_addr.index()]; + + // Different logic is needed for control endpoints: + // - The FIFOCON and RWAL fields are irrelevant with CONTROL endpoints. + // - TXINI ... shall be cleared by firmware to **send the + // packet and to clear the endpoint bank.** + if endpoint.eptype_bits == EP_TYPE_CONTROL { + if usb.ueintx.read().txini().bit_is_clear() { + return Err(UsbError::WouldBlock); + } + + let buffer_size = endpoint.buffer_size(); + if buf.len() > buffer_size { + return Err(UsbError::BufferOverflow); + } + + for &byte in buf { + usb.uedatx.write(|w| w.bits(byte)) + } + + usb.ueintx.clear_interrupts(|w| w.txini().clear_bit()); + } else { + if usb.ueintx.read().txini().bit_is_clear() { + return Err(UsbError::WouldBlock); + } + //NB: RXOUTI serves as KILLBK for IN endpoints and needs to stay zero: + usb.ueintx + .clear_interrupts(|w| w.txini().clear_bit().rxouti().clear_bit()); + + for &byte in buf { + if usb.ueintx.read().rwal().bit_is_clear() { + return Err(UsbError::BufferOverflow); + } + usb.uedatx.write(|w| w.bits(byte)); + } + + //NB: RXOUTI serves as KILLBK for IN endpoints and needs to stay zero: + usb.ueintx + .clear_interrupts(|w| w.fifocon().clear_bit().rxouti().clear_bit()); + } + + let pending_ins = self.pending_ins.borrow(cs); + pending_ins.set(pending_ins.get() | 1 << ep_addr.index()); + + Ok(buf.len()) + }) + } - fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> UsbResult { - interrupt::free(|cs| { - let usb = self.usb.borrow(cs); - self.set_current_endpoint(cs, ep_addr.index())?; - let endpoint = &self.endpoints[ep_addr.index()]; - - // Different logic is needed for control endpoints: - // - The FIFOCON and RWAL fields are irrelevant with CONTROL endpoints. - // - RXSTPI/RXOUTI ... shall be cleared by firmware to **send the - // packet and to clear the endpoint bank.** - if endpoint.eptype_bits == EP_TYPE_CONTROL { - let ueintx = usb.ueintx.read(); - if ueintx.rxouti().bit_is_clear() && ueintx.rxstpi().bit_is_clear() { - return Err(UsbError::WouldBlock); - } - - let bytes_to_read = self.endpoint_byte_count(cs) as usize; - if bytes_to_read > buf.len() { - return Err(UsbError::BufferOverflow); - } - - for slot in &mut buf[..bytes_to_read] { - *slot = usb.uedatx.read().bits(); - } - usb.ueintx - .clear_interrupts(|w| w.rxouti().clear_bit().rxstpi().clear_bit()); - - Ok(bytes_to_read) - } else { - if usb.ueintx.read().rxouti().bit_is_clear() { - return Err(UsbError::WouldBlock); - } - usb.ueintx.clear_interrupts(|w| w.rxouti().clear_bit()); - - let mut bytes_read = 0; - for slot in buf { - if usb.ueintx.read().rwal().bit_is_clear() { - break; - } - *slot = usb.uedatx.read().bits(); - bytes_read += 1; - } - - if usb.ueintx.read().rwal().bit_is_set() { - return Err(UsbError::BufferOverflow); - } - - usb.ueintx.clear_interrupts(|w| w.fifocon().clear_bit()); - Ok(bytes_read) - } - }) - } + fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> UsbResult { + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + self.set_current_endpoint(cs, ep_addr.index())?; + let endpoint = &self.endpoints[ep_addr.index()]; + + // Different logic is needed for control endpoints: + // - The FIFOCON and RWAL fields are irrelevant with CONTROL endpoints. + // - RXSTPI/RXOUTI ... shall be cleared by firmware to **send the + // packet and to clear the endpoint bank.** + if endpoint.eptype_bits == EP_TYPE_CONTROL { + let ueintx = usb.ueintx.read(); + if ueintx.rxouti().bit_is_clear() && ueintx.rxstpi().bit_is_clear() { + return Err(UsbError::WouldBlock); + } + + let bytes_to_read = self.endpoint_byte_count(cs) as usize; + if bytes_to_read > buf.len() { + return Err(UsbError::BufferOverflow); + } + + for slot in &mut buf[..bytes_to_read] { + *slot = usb.uedatx.read().bits(); + } + usb.ueintx + .clear_interrupts(|w| w.rxouti().clear_bit().rxstpi().clear_bit()); + + Ok(bytes_to_read) + } else { + if usb.ueintx.read().rxouti().bit_is_clear() { + return Err(UsbError::WouldBlock); + } + usb.ueintx.clear_interrupts(|w| w.rxouti().clear_bit()); + + let mut bytes_read = 0; + for slot in buf { + if usb.ueintx.read().rwal().bit_is_clear() { + break; + } + *slot = usb.uedatx.read().bits(); + bytes_read += 1; + } + + if usb.ueintx.read().rwal().bit_is_set() { + return Err(UsbError::BufferOverflow); + } + + usb.ueintx.clear_interrupts(|w| w.fifocon().clear_bit()); + Ok(bytes_read) + } + }) + } - fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool) { - interrupt::free(|cs| { - let usb = self.usb.borrow(cs); - if self.set_current_endpoint(cs, ep_addr.index()).is_ok() { - usb.ueconx - .modify(|_, w| w.stallrq().bit(stalled).stallrqc().bit(!stalled)); - } - }); - } - - fn is_stalled(&self, ep_addr: EndpointAddress) -> bool { - interrupt::free(|cs| { - let usb = self.usb.borrow(cs); - if self.set_current_endpoint(cs, ep_addr.index()).is_ok() { - // NB: The datasheet says STALLRQ is write-only but LUFA reads from it... - usb.ueconx.read().stallrq().bit_is_set() - } else { - false - } - }) - } + fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool) { + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + if self.set_current_endpoint(cs, ep_addr.index()).is_ok() { + usb.ueconx + .modify(|_, w| w.stallrq().bit(stalled).stallrqc().bit(!stalled)); + } + }); + } + + fn is_stalled(&self, ep_addr: EndpointAddress) -> bool { + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + if self.set_current_endpoint(cs, ep_addr.index()).is_ok() { + // NB: The datasheet says STALLRQ is write-only but LUFA reads from it... + usb.ueconx.read().stallrq().bit_is_set() + } else { + false + } + }) + } - fn suspend(&self) { - interrupt::free(|cs| { - let usb = self.usb.borrow(cs); - usb.udint - .clear_interrupts(|w| w.suspi().clear_bit().wakeupi().clear_bit()); - usb.udien - .modify(|_, w| w.wakeupe().set_bit().suspe().clear_bit()); - usb.usbcon.modify(|_, w| w.frzclk().set_bit()); - - self.suspend_notifier.borrow(cs).suspend(); - }); - } + fn suspend(&self) { + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + usb.udint + .clear_interrupts(|w| w.suspi().clear_bit().wakeupi().clear_bit()); + usb.udien + .modify(|_, w| w.wakeupe().set_bit().suspe().clear_bit()); + usb.usbcon.modify(|_, w| w.frzclk().set_bit()); + + self.suspend_notifier.borrow(cs).suspend(); + }); + } - fn resume(&self) { - interrupt::free(|cs| { - self.suspend_notifier.borrow(cs).resume(); - - let usb = self.usb.borrow(cs); - usb.usbcon.modify(|_, w| w.frzclk().clear_bit()); - usb.udint - .clear_interrupts(|w| w.wakeupi().clear_bit().suspi().clear_bit()); - usb.udien - .modify(|_, w| w.wakeupe().clear_bit().suspe().set_bit()); - }); - } + fn resume(&self) { + interrupt::free(|cs| { + self.suspend_notifier.borrow(cs).resume(); + + let usb = self.usb.borrow(cs); + usb.usbcon.modify(|_, w| w.frzclk().clear_bit()); + usb.udint + .clear_interrupts(|w| w.wakeupi().clear_bit().suspi().clear_bit()); + usb.udien + .modify(|_, w| w.wakeupe().clear_bit().suspe().set_bit()); + }); + } - fn poll(&self) -> PollResult { - interrupt::free(|cs| { - let usb = self.usb.borrow(cs); - - let udint = usb.udint.read(); - let udien = usb.udien.read(); + fn poll(&self) -> PollResult { + interrupt::free(|cs| { + let usb = self.usb.borrow(cs); + + let udint = usb.udint.read(); + let udien = usb.udien.read(); - #[$not_limited_inter_and_vbus] - { - let usbint = usb.usbint.read(); - if usbint.vbusti().bit_is_set() { - usb.usbint.clear_interrupts(|w| w.vbusti().clear_bit()); - if usb.usbsta.read().vbus().bit_is_set() { - return PollResult::Resume; - } else { - return PollResult::Suspend; - } - } - } + #[$not_limited_inter_and_vbus] + { + let usbint = usb.usbint.read(); + if usbint.vbusti().bit_is_set() { + usb.usbint.clear_interrupts(|w| w.vbusti().clear_bit()); + if usb.usbsta.read().vbus().bit_is_set() { + return PollResult::Resume; + } else { + return PollResult::Suspend; + } + } + } - if udint.suspi().bit_is_set() && udien.suspe().bit_is_set() { - return PollResult::Suspend; - } - if udint.wakeupi().bit_is_set() && udien.wakeupe().bit_is_set() { - return PollResult::Resume; - } - if udint.eorsti().bit_is_set() { - return PollResult::Reset; - } - if udint.sofi().bit_is_set() { - usb.udint.clear_interrupts(|w| w.sofi().clear_bit()); - } - - // Can only query endpoints while clock is running - // (e.g. not in suspend state) - if usb.usbcon.read().frzclk().bit_is_clear() { - let mut ep_out = 0u8; - let mut ep_setup = 0u8; - let mut ep_in_complete = 0u8; - let pending_ins = self.pending_ins.borrow(cs); - - for (index, _ep) in self.active_endpoints() { - if self.set_current_endpoint(cs, index).is_err() { - // Endpoint selection has stopped working... - break; - } - - let ueintx = usb.ueintx.read(); - if ueintx.rxouti().bit_is_set() { - ep_out |= 1 << index; - } - if ueintx.rxstpi().bit_is_set() { - ep_setup |= 1 << index; - } - if pending_ins.get() & (1 << index) != 0 && ueintx.txini().bit_is_set() { - ep_in_complete |= 1 << index; - pending_ins.set(pending_ins.get() & !(1 << index)); - } - } - if ep_out | ep_setup | ep_in_complete != 0 { - return PollResult::Data { - ep_out: ep_out as u16, - ep_in_complete: ep_in_complete as u16, - ep_setup: ep_setup as u16, - }; - } - } - - PollResult::None - }) - } + if udint.suspi().bit_is_set() && udien.suspe().bit_is_set() { + return PollResult::Suspend; + } + if udint.wakeupi().bit_is_set() && udien.wakeupe().bit_is_set() { + return PollResult::Resume; + } + if udint.eorsti().bit_is_set() { + return PollResult::Reset; + } + if udint.sofi().bit_is_set() { + usb.udint.clear_interrupts(|w| w.sofi().clear_bit()); + } + + // Can only query endpoints while clock is running + // (e.g. not in suspend state) + if usb.usbcon.read().frzclk().bit_is_clear() { + let mut ep_out = 0u8; + let mut ep_setup = 0u8; + let mut ep_in_complete = 0u8; + let pending_ins = self.pending_ins.borrow(cs); + + for (index, _ep) in self.active_endpoints() { + if self.set_current_endpoint(cs, index).is_err() { + // Endpoint selection has stopped working... + break; + } + + let ueintx = usb.ueintx.read(); + if ueintx.rxouti().bit_is_set() { + ep_out |= 1 << index; + } + if ueintx.rxstpi().bit_is_set() { + ep_setup |= 1 << index; + } + if pending_ins.get() & (1 << index) != 0 && ueintx.txini().bit_is_set() { + ep_in_complete |= 1 << index; + pending_ins.set(pending_ins.get() & !(1 << index)); + } + } + if ep_out | ep_setup | ep_in_complete != 0 { + return PollResult::Data { + ep_out: ep_out as u16, + ep_in_complete: ep_in_complete as u16, + ep_setup: ep_setup as u16, + }; + } + } + + PollResult::None + }) + } - fn force_reset(&self) -> UsbResult<()> { - // 22.9 "It is possible to re-enumerate a device, simply by setting and - // clearing the DETACH bit (but firmware must take in account a - // debouncing delay of some milliseconds)." - - interrupt::free(|cs| { - self.usb - .borrow(cs) - .udcon - .modify(|_, w| w.detach().set_bit()); - }); - - // Delay for at least 1ms (exactly 1ms at 16 MHz) - // to allow the host to detect the change. - delay_cycles(16000); - - interrupt::free(|cs| { - self.usb - .borrow(cs) - .udcon - .modify(|_, w| w.detach().clear_bit()); - }); - - Ok(()) - } - } + fn force_reset(&self) -> UsbResult<()> { + // 22.9 "It is possible to re-enumerate a device, simply by setting and + // clearing the DETACH bit (but firmware must take in account a + // debouncing delay of some milliseconds)." + + interrupt::free(|cs| { + self.usb + .borrow(cs) + .udcon + .modify(|_, w| w.detach().set_bit()); + }); + + // Delay for at least 1ms (exactly 1ms at 16 MHz) + // to allow the host to detect the change. + delay_cycles(16000); + + interrupt::free(|cs| { + self.usb + .borrow(cs) + .udcon + .modify(|_, w| w.detach().clear_bit()); + }); + + Ok(()) + } + } } diff --git a/mcu/atmega-hal/src/usb.rs b/mcu/atmega-hal/src/usb.rs index 6a706908bd..9130069074 100644 --- a/mcu/atmega-hal/src/usb.rs +++ b/mcu/atmega-hal/src/usb.rs @@ -55,81 +55,81 @@ avr_hal_generic::usb::create_usb_bus! { /// with all _known_ interrupt flags set to the value that has no effect (which /// is 1, in most cases) pub trait ClearInterrupts { // This trait must live here due to the orphan rule - type Writer; + type Writer; - fn clear_interrupts(&self, f: F) - where - for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer; + fn clear_interrupts(&self, f: F) + where + for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer; } impl ClearInterrupts for UDINT { - type Writer = udint::W; - - fn clear_interrupts(&self, f: F) - where - for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer, - { - // Bits 1,7 reserved as do not set. Setting all other bits has no effect - self.write(|w| f(unsafe { w.bits(0x7d) })) - } + type Writer = udint::W; + + fn clear_interrupts(&self, f: F) + where + for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer, + { + // Bits 1,7 reserved as do not set. Setting all other bits has no effect + self.write(|w| f(unsafe { w.bits(0x7d) })) + } } impl ClearInterrupts for UEINTX { - type Writer = ueintx::W; - - fn clear_interrupts(&self, f: F) - where - for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer, - { - // Bit 5 read-only. Setting all other bits has no effect, EXCEPT: - // - RXOUTI/KILLBK should not be set for "IN" endpoints (XXX end-user beware) - self.write(|w| f(unsafe { w.bits(0xdf) })) - } + type Writer = ueintx::W; + + fn clear_interrupts(&self, f: F) + where + for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer, + { + // Bit 5 read-only. Setting all other bits has no effect, EXCEPT: + // - RXOUTI/KILLBK should not be set for "IN" endpoints (XXX end-user beware) + self.write(|w| f(unsafe { w.bits(0xdf) })) + } } #[cfg(not(feature = "atmega8u2"))] impl ClearInterrupts for USBINT { - type Writer = usbint::W; - - fn clear_interrupts(&self, f: F) - where - for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer, - { - // Bits 7:1 are reserved as do not set. - self.write(|w| f(unsafe { w.bits(0x01) })) - } + type Writer = usbint::W; + + fn clear_interrupts(&self, f: F) + where + for<'w> F: FnOnce(&mut Self::Writer) -> &mut Self::Writer, + { + // Bits 7:1 are reserved as do not set. + self.write(|w| f(unsafe { w.bits(0x01) })) + } } /// Receiver for handling suspend and resume events from the USB device. /// /// See [`UsbBus::with_suspend_notifier`] for more details. pub trait SuspendNotifier: Send + Sized + 'static { - /// Called by `UsbBus` when the USB peripheral has been suspended and the - /// PLL is safe to shut down. - fn suspend(&self) {} - - /// Called by `UsbBus` when the USB peripheral is about to resume and is - /// waiting for PLL to be enabled. - /// - /// This function should block until PLL lock has been established. - fn resume(&self) {} + /// Called by `UsbBus` when the USB peripheral has been suspended and the + /// PLL is safe to shut down. + fn suspend(&self) {} + + /// Called by `UsbBus` when the USB peripheral is about to resume and is + /// waiting for PLL to be enabled. + /// + /// This function should block until PLL lock has been established. + fn resume(&self) {} } impl SuspendNotifier for () {} impl SuspendNotifier for PLL { - fn suspend(&self) { - self.pllcsr.modify(|_, w| w.plle().clear_bit()); - } - - fn resume(&self) { - #[cfg(feature = "atmega32u4")] - self.pllcsr - .modify(|_, w| w.pindiv().set_bit().plle().set_bit()); - #[cfg(feature = "atmega8u2")] - self.pllcsr - .modify(|_, w| w.pllp().val_0x05().plle().set_bit()); // REVIEW: is val_0x05 or val_0x03 correct? or something else? - - while self.pllcsr.read().plock().bit_is_clear() {} - } + fn suspend(&self) { + self.pllcsr.modify(|_, w| w.plle().clear_bit()); + } + + fn resume(&self) { + #[cfg(feature = "atmega32u4")] + self.pllcsr + .modify(|_, w| w.pindiv().set_bit().plle().set_bit()); + #[cfg(feature = "atmega8u2")] + self.pllcsr + .modify(|_, w| w.pllp().val_0x05().plle().set_bit()); // REVIEW: is val_0x05 or val_0x03 correct? or something else? + + while self.pllcsr.read().plock().bit_is_clear() {} + } } From a87be9114d33c65eb49929e86204739422bd6191 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Mon, 29 Jul 2024 19:16:38 -0400 Subject: [PATCH 11/21] Passing AvrGenericUsbBus name for viz purposes --- avr-hal-generic/src/usb.rs | 13 +++++++------ mcu/atmega-hal/src/lib.rs | 5 +++++ mcu/atmega-hal/src/usb.rs | 8 +++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs index 6750fe491a..9743544205 100644 --- a/avr-hal-generic/src/usb.rs +++ b/avr-hal-generic/src/usb.rs @@ -51,6 +51,7 @@ impl EndpointTableEntry { // Using Macro 2.0 here while not stable yet makes this code a lot more readable and easier to write pub macro create_usb_bus ( + $AvrGenericUsbBus:ident, $USB_DEVICE:ty, $SuspendNotifier:path, $MAX_ENDPOINTS:ident, @@ -58,11 +59,11 @@ pub macro create_usb_bus ( $DPRAM_SIZE:ident, $limited_inter_and_vbus:meta, $not_limited_inter_and_vbus:meta -) { +) { // could stand to make the above a bit more readable - // MARK: - AvrUsbBus + // MARK: - AvrGenericUsbBus - pub struct AvrUsbBus { + pub struct $AvrGenericUsbBus { usb: AvrDMutex<$USB_DEVICE>, suspend_notifier: AvrDMutex, pending_ins: AvrDMutex>, @@ -70,7 +71,7 @@ pub macro create_usb_bus ( dpram_usage: u16, // TODO: This need to be extracted } - impl AvrUsbBus<()> { + impl $AvrGenericUsbBus<()> { /// Create a new UsbBus without power-saving functionality. /// /// If you would like to disable the PLL when the USB peripheral is @@ -80,7 +81,7 @@ pub macro create_usb_bus ( } } - impl AvrUsbBus { + impl $AvrGenericUsbBus { /// Create a UsbBus with a suspend and resume handler. /// /// If you want the PLL to be automatically disabled when the USB peripheral @@ -179,7 +180,7 @@ pub macro create_usb_bus ( } } - impl UsbBus for AvrUsbBus { + impl UsbBus for $AvrGenericUsbBus { fn alloc_ep( &mut self, ep_dir: UsbDirection, diff --git a/mcu/atmega-hal/src/lib.rs b/mcu/atmega-hal/src/lib.rs index 25728df968..0403f9da45 100644 --- a/mcu/atmega-hal/src/lib.rs +++ b/mcu/atmega-hal/src/lib.rs @@ -150,6 +150,11 @@ pub use eeprom::Eeprom; feature = "atmega32u4", ))] pub mod usb; +#[cfg(any( + feature = "atmega8u2", + feature = "atmega32u4", +))] +pub use usb::AvrUsbBus; pub struct Atmega; diff --git a/mcu/atmega-hal/src/usb.rs b/mcu/atmega-hal/src/usb.rs index 9130069074..5d0bc615f2 100644 --- a/mcu/atmega-hal/src/usb.rs +++ b/mcu/atmega-hal/src/usb.rs @@ -1,5 +1,4 @@ - -pub use avr_hal_generic::usb::*; +use avr_hal_generic::usb::create_usb_bus; // MARK: - Type Imports @@ -28,7 +27,8 @@ const ENDPOINT_MAX_BUFSIZE: [u16; MAX_ENDPOINTS] = [64, 64, 64, 64, 64]; #[cfg(feature = "atmega8u2")] const DPRAM_SIZE: u16 = 176; -avr_hal_generic::usb::create_usb_bus! { +create_usb_bus! { + AvrGenericUsbBus, // we have to pass the name along for visibility reasons USB_DEVICE, SuspendNotifier, MAX_ENDPOINTS, @@ -38,6 +38,8 @@ avr_hal_generic::usb::create_usb_bus! { cfg(feature = "atmega32u4") } +pub type AvrUsbBus = AvrGenericUsbBus; + /// Extension trait for conveniently clearing AVR interrupt flag registers. /// /// To clear an interrupt flag, a zero bit must be written. However, there are From fc15a3f7375210a65a031634353cd9b8fae9ac5b Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Mon, 29 Jul 2024 19:16:50 -0400 Subject: [PATCH 12/21] Exposing in arduino hal --- arduino-hal/src/lib.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/arduino-hal/src/lib.rs b/arduino-hal/src/lib.rs index 5e78dc3318..f87f0b1225 100644 --- a/arduino-hal/src/lib.rs +++ b/arduino-hal/src/lib.rs @@ -175,11 +175,21 @@ pub mod usart { pub type UsartReader = crate::hal::usart::UsartReader; } - #[doc(no_inline)] #[cfg(feature = "mcu-atmega")] pub use usart::Usart; +#[cfg(feature = "arduino-leonardo")] +pub mod usb { + pub use atmega_hal::usb::*; + + pub type AvrUsbBus = crate::hal::usb::AvrUsbBus; +} +#[doc(no_inline)] +#[cfg(feature = "arduino-leonardo")] +pub use usb::AvrUsbBus; + + #[cfg(feature = "board-selected")] pub mod eeprom { pub use crate::hal::eeprom::{Eeprom, EepromOps, OutOfBoundsError}; @@ -340,3 +350,4 @@ macro_rules! default_serial { ) }; } + From 652ee6af85ad7c86b6a994568c3ce3f5ae53c33e Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Mon, 29 Jul 2024 22:31:14 -0400 Subject: [PATCH 13/21] Escaping hygiene manually --- avr-hal-generic/src/usb.rs | 10 ++++++---- mcu/atmega-hal/src/usb.rs | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs index 9743544205..2c50ca8bef 100644 --- a/avr-hal-generic/src/usb.rs +++ b/avr-hal-generic/src/usb.rs @@ -51,7 +51,9 @@ impl EndpointTableEntry { // Using Macro 2.0 here while not stable yet makes this code a lot more readable and easier to write pub macro create_usb_bus ( - $AvrGenericUsbBus:ident, + $AvrGenericUsbBus:ident, // TODO: get rid of this when hygiene escaping is a thing (see #39412) + $new:ident, // literally the keyword `new` // what cruel world do we live in + $with_suspend_notifier:ident, $USB_DEVICE:ty, $SuspendNotifier:path, $MAX_ENDPOINTS:ident, @@ -76,8 +78,8 @@ pub macro create_usb_bus ( /// /// If you would like to disable the PLL when the USB peripheral is /// suspended, then construct the bus with [`UsbBus::with_suspend_notifier`]. - pub fn new(usb: $USB_DEVICE) -> UsbBusAllocator { - Self::with_suspend_notifier(usb, ()) + pub fn $new(usb: $USB_DEVICE) -> UsbBusAllocator { + Self::$with_suspend_notifier(usb, ()) } } @@ -102,7 +104,7 @@ pub macro create_usb_bus ( /// to leave the PLL running, or implement [`SuspendNotifier`] yourself, /// with some custom logic to gracefully shut down the PLL in cooperation /// with your other peripherals. - pub fn with_suspend_notifier(usb: $USB_DEVICE, suspend_notifier: S) -> UsbBusAllocator { + pub fn $with_suspend_notifier(usb: $USB_DEVICE, suspend_notifier: S) -> UsbBusAllocator { UsbBusAllocator::new(Self { usb: AvrDMutex::new(usb), suspend_notifier: AvrDMutex::new(suspend_notifier), diff --git a/mcu/atmega-hal/src/usb.rs b/mcu/atmega-hal/src/usb.rs index 5d0bc615f2..1598bf59b9 100644 --- a/mcu/atmega-hal/src/usb.rs +++ b/mcu/atmega-hal/src/usb.rs @@ -29,6 +29,8 @@ const DPRAM_SIZE: u16 = 176; create_usb_bus! { AvrGenericUsbBus, // we have to pass the name along for visibility reasons + new, + with_suspend_notifier, USB_DEVICE, SuspendNotifier, MAX_ENDPOINTS, @@ -38,8 +40,6 @@ create_usb_bus! { cfg(feature = "atmega32u4") } -pub type AvrUsbBus = AvrGenericUsbBus; - /// Extension trait for conveniently clearing AVR interrupt flag registers. /// /// To clear an interrupt flag, a zero bit must be written. However, there are @@ -135,3 +135,5 @@ impl SuspendNotifier for PLL { while self.pllcsr.read().plock().bit_is_clear() {} } } + +pub type AvrUsbBus = AvrGenericUsbBus; From 3d3ad86ece83e738b02c7fbe0e86d555a24adda5 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Mon, 29 Jul 2024 22:37:29 -0400 Subject: [PATCH 14/21] Adding a new macro for making a UsbBus --- arduino-hal/Cargo.toml | 3 ++- arduino-hal/src/lib.rs | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/arduino-hal/Cargo.toml b/arduino-hal/Cargo.toml index 1f400c73f3..ac1e0b793a 100644 --- a/arduino-hal/Cargo.toml +++ b/arduino-hal/Cargo.toml @@ -20,7 +20,7 @@ board-selected = [] mcu-atmega = [] mcu-attiny = [] arduino-diecimila = ["mcu-atmega", "atmega-hal/atmega168", "board-selected"] -arduino-leonardo = ["mcu-atmega", "atmega-hal/atmega32u4", "board-selected"] +arduino-leonardo = ["mcu-atmega", "atmega-hal/atmega32u4", "board-selected", "usb-device"] arduino-mega2560 = ["mcu-atmega", "atmega-hal/atmega2560", "board-selected"] arduino-mega1280 = ["mcu-atmega", "atmega-hal/atmega1280", "board-selected"] arduino-nano = ["mcu-atmega", "atmega-hal/atmega328p", "atmega-hal/enable-extra-adc", "board-selected"] @@ -39,6 +39,7 @@ docsrs = ["arduino-uno"] cfg-if = "1" embedded-hal = "1.0" ufmt = "0.2.0" +usb-device = { version = "0.3.2", optional = true } [dependencies.embedded-hal-v0] version = "0.2.3" diff --git a/arduino-hal/src/lib.rs b/arduino-hal/src/lib.rs index f87f0b1225..95b833811f 100644 --- a/arduino-hal/src/lib.rs +++ b/arduino-hal/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![feature(doc_cfg)] +#![feature(decl_macro)] //! `arduino-hal` //! ============= @@ -179,9 +180,11 @@ pub mod usart { #[cfg(feature = "mcu-atmega")] pub use usart::Usart; +#[cfg(feature = "arduino-leonardo")] +use usb_device::bus::UsbBusAllocator; #[cfg(feature = "arduino-leonardo")] pub mod usb { - pub use atmega_hal::usb::*; + pub use crate::hal::usb::*; pub type AvrUsbBus = crate::hal::usb::AvrUsbBus; } @@ -351,3 +354,16 @@ macro_rules! default_serial { }; } +/// Convenience macro to instantiate the [`UsbBus`] driver for this board. +/// +/// # Example +/// ```no_run +/// TODO +/// ``` +#[cfg(feature = "arduino-leonardo")] +pub macro default_usb_bus ($usb:expr, $pll:expr) { + unsafe { + static mut USB_BUS: Option> = None; + $crate::AvrUsbBus::with_suspend_notifier($usb, $pll) + }; +} From ba329eb98c57ffee1d2feaa79ef36f3d735cf715 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Mon, 29 Jul 2024 22:59:11 -0400 Subject: [PATCH 15/21] Adding a decl-macro for new usb devices --- arduino-hal/src/lib.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/arduino-hal/src/lib.rs b/arduino-hal/src/lib.rs index 95b833811f..93379bc403 100644 --- a/arduino-hal/src/lib.rs +++ b/arduino-hal/src/lib.rs @@ -181,7 +181,7 @@ pub mod usart { pub use usart::Usart; #[cfg(feature = "arduino-leonardo")] -use usb_device::bus::UsbBusAllocator; +use usb_device::{bus::UsbBusAllocator, device::{UsbDeviceBuilder, UsbVidPid}}; #[cfg(feature = "arduino-leonardo")] pub mod usb { pub use crate::hal::usb::*; @@ -364,6 +364,23 @@ macro_rules! default_serial { pub macro default_usb_bus ($usb:expr, $pll:expr) { unsafe { static mut USB_BUS: Option> = None; - $crate::AvrUsbBus::with_suspend_notifier($usb, $pll) + &*USB_BUS.insert($crate::AvrUsbBus::with_suspend_notifier($usb, $pll)) }; } + +/// Convenience macro to instantiate the [`UsbDevice`] driver for this board. +/// +/// # Example +/// ```no_run +/// TODO +/// ``` +#[cfg(feature = "arduino-leonardo")] +pub macro default_usb_device ($usb_bus:expr, $vid:expr, $pid:expr, $strings:expr) { + UsbDeviceBuilder::new( + $usb_bus, + UsbVidPid($vid, $pid), + ) + .strings(&[$strings]) + .unwrap() + .build() +} \ No newline at end of file From 71cc082b2f737bf70ce239e110f0af72c25919bc Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Mon, 29 Jul 2024 23:29:04 -0400 Subject: [PATCH 16/21] Adding the original hello world example --- examples/arduino-leonardo/Cargo.toml | 3 + .../arduino-leonardo/src/bin/leonardo-usb.rs | 190 ++++++++++++++++++ .../arduino-leonardo/src/bin/std_stub/mod.rs | 26 +++ 3 files changed, 219 insertions(+) create mode 100644 examples/arduino-leonardo/src/bin/leonardo-usb.rs create mode 100644 examples/arduino-leonardo/src/bin/std_stub/mod.rs diff --git a/examples/arduino-leonardo/Cargo.toml b/examples/arduino-leonardo/Cargo.toml index 832e5d4318..7a61e37abe 100644 --- a/examples/arduino-leonardo/Cargo.toml +++ b/examples/arduino-leonardo/Cargo.toml @@ -10,6 +10,9 @@ panic-halt = "0.2.0" ufmt = "0.2.0" nb = "1.1.0" embedded-hal = "1.0" +avr-device = "0.5.4" +usb-device = "0.3" +usbd-hid = "0.8" [dependencies.embedded-hal-v0] version = "0.2.3" diff --git a/examples/arduino-leonardo/src/bin/leonardo-usb.rs b/examples/arduino-leonardo/src/bin/leonardo-usb.rs new file mode 100644 index 0000000000..77c454ec16 --- /dev/null +++ b/examples/arduino-leonardo/src/bin/leonardo-usb.rs @@ -0,0 +1,190 @@ +//! A "Hello World" example that can be run on an Arduino Leonardo. +//! +//! # Usage +//! +//! 1. (Optional) Connect a pushbutton switch to the D2 pin of the Leonardo, and +//! connect the other pin of the switch to GND. +//! +//! 2. Connect the Leonardo to the computer with a USB cable. +//! +//! 3. Make sure [Ravedude](https://github.com/Rahix/avr-hal/tree/main/ravedude) +//! is installed. Then "run" the example to deploy it to the Arduino: +//! +//! ``` +//! cargo run --release --example arduino_keyboard +//! ``` +//! +//! 4. Open Notepad (or whatever editor or text input of your choosing). Press +//! the button (or if you are not using one, short D2 to GND with a jumper). You +//! should see it type "Hello World" + +#![no_std] +#![no_main] +#![feature(lang_items)] +#![feature(abi_avr_interrupt)] +#![deny(unsafe_op_in_unsafe_fn)] + +mod std_stub; + +use arduino_hal::{pac::PLL, port::{mode::{Input, Output, PullUp}, Pin}, usb::{AvrGenericUsbBus, SuspendNotifier}, AvrUsbBus}; + +use avr_device::{asm::sleep, interrupt}; + +use usb_device::{descriptor::lang_id::LangID, device::UsbDevice, prelude::StringDescriptors}; +use usbd_hid::{ + descriptor::{KeyboardReport, SerializedDescriptor}, + hid_class::HIDClass, +}; + +const PAYLOAD: &[u8] = b"Hello World"; + +#[arduino_hal::entry] +fn main() -> ! { + let dp = arduino_hal::Peripherals::take().unwrap(); + let pins = arduino_hal::pins!(dp); + let pll = dp.PLL; + let usb = dp.USB_DEVICE; + + let status = pins.d13.into_output(); + let trigger = pins.d2.into_pull_up_input(); + + // Configure PLL interface + // prescale 16MHz crystal -> 8MHz + pll.pllcsr.write(|w| w.pindiv().set_bit()); + // 96MHz PLL output; /1.5 for 64MHz timers, /2 for 48MHz USB + pll.pllfrq + .write(|w| w.pdiv().mhz96().plltm().factor_15().pllusb().set_bit()); + + // Enable PLL + pll.pllcsr.modify(|_, w| w.plle().set_bit()); + + // Check PLL lock + while pll.pllcsr.read().plock().bit_is_clear() {} + + let usb_bus = arduino_hal::default_usb_bus!(usb, pll); + + let hid_class = HIDClass::new(usb_bus, KeyboardReport::desc(), 1); + let strings = StringDescriptors::new(LangID::EN) + .manufacturer("Foo") + .product("Bar"); + let usb_device = arduino_hal::default_usb_device!(usb_bus, 0x1209, 0x0001, strings); + + unsafe { + USB_CTX = Some(UsbContext { + usb_device, + hid_class, + current_index: 0, + pressed: false, + indicator: status.downgrade(), + trigger: trigger.downgrade(), + }); + } + + unsafe { interrupt::enable() }; + loop { + sleep(); + } +} + +static mut USB_CTX: Option> = None; + +#[interrupt(atmega32u4)] +fn USB_GEN() { + unsafe { poll_usb() }; +} + +#[interrupt(atmega32u4)] +fn USB_COM() { + unsafe { poll_usb() }; +} + +/// # Safety +/// +/// This function assumes that it is being called within an +/// interrupt context. +unsafe fn poll_usb() { + // Safety: There must be no other overlapping borrows of USB_CTX. + // - By the safety contract of this function, we are in an interrupt + // context. + // - The main thread is not borrowing USB_CTX. The only access is the + // assignment during initialization. It cannot overlap because it is + // before the call to `interrupt::enable()`. + // - No other interrupts are accessing USB_CTX, because no other interrupts + // are in the middle of execution. GIE is automatically cleared for the + // duration of the interrupt, and is not re-enabled within any ISRs. + let ctx = unsafe { USB_CTX.as_mut().unwrap() }; + ctx.poll(); +} + +struct UsbContext { + usb_device: UsbDevice<'static, AvrGenericUsbBus>, + hid_class: HIDClass<'static, AvrGenericUsbBus>, + current_index: usize, + pressed: bool, + indicator: Pin, + trigger: Pin>, +} + +impl UsbContext { + fn poll(&mut self) { + if self.trigger.is_low() { + let next_report = if self.pressed { + BLANK_REPORT + } else { + PAYLOAD + .get(self.current_index) + .copied() + .and_then(ascii_to_report) + .unwrap_or(BLANK_REPORT) + }; + + if self.hid_class.push_input(&next_report).is_ok() { + if self.pressed && self.current_index < PAYLOAD.len() { + self.current_index += 1; + } + self.pressed = !self.pressed; + } + } else { + self.current_index = 0; + self.pressed = false; + self.hid_class.push_input(&BLANK_REPORT).ok(); + } + + if self.usb_device.poll(&mut [&mut self.hid_class]) { + let mut report_buf = [0u8; 1]; + + if self.hid_class.pull_raw_output(&mut report_buf).is_ok() { + if report_buf[0] & 2 != 0 { + self.indicator.set_high(); + } else { + self.indicator.set_low(); + } + } + } + } +} + +const BLANK_REPORT: KeyboardReport = KeyboardReport { + modifier: 0, + reserved: 0, + leds: 0, + keycodes: [0; 6], +}; + +fn ascii_to_report(c: u8) -> Option { + let (keycode, shift) = if c.is_ascii_alphabetic() { + (c.to_ascii_lowercase() - b'a' + 0x04, c.is_ascii_uppercase()) + } else { + match c { + b' ' => (0x2c, false), + _ => return None, + } + }; + + let mut report = BLANK_REPORT; + if shift { + report.modifier |= 0x2; + } + report.keycodes[0] = keycode; + Some(report) +} diff --git a/examples/arduino-leonardo/src/bin/std_stub/mod.rs b/examples/arduino-leonardo/src/bin/std_stub/mod.rs new file mode 100644 index 0000000000..ca05936f82 --- /dev/null +++ b/examples/arduino-leonardo/src/bin/std_stub/mod.rs @@ -0,0 +1,26 @@ +//! Replacement for avr-std-stub with a custom panic handler. + +use core::panic::PanicInfo; + +use arduino_hal::{delay_ms, pins, Peripherals}; + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + let dp = unsafe { Peripherals::steal() }; + let pins = pins!(dp); + let mut status = pins.d13.into_output(); + loop { + status.set_high(); + delay_ms(100); + status.set_low(); + delay_ms(100); + status.set_high(); + delay_ms(300); + status.set_low(); + delay_ms(500); + } +} + +#[lang = "eh_personality"] +#[no_mangle] +pub unsafe extern "C" fn rust_eh_personality() {} \ No newline at end of file From 8f9d21915b90a4049cd2c6816323017b760a4830 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Fri, 2 Aug 2024 18:43:17 -0400 Subject: [PATCH 17/21] Making necessary functions public --- avr-hal-generic/src/usb.rs | 21 +++++++++---------- .../arduino-leonardo/src/bin/leonardo-usb.rs | 18 ++++++++-------- .../arduino-leonardo/src/bin/std_stub/mod.rs | 8 +++++-- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs index 2c50ca8bef..c8069b1dec 100644 --- a/avr-hal-generic/src/usb.rs +++ b/avr-hal-generic/src/usb.rs @@ -1,5 +1,3 @@ -#![feature(negative_impls)] - use core::{cell::Cell, cmp::max}; use usb_device::{bus::{UsbBus, PollResult}, UsbDirection, UsbError, class_prelude::UsbBusAllocator, endpoint::{EndpointAddress, EndpointType}, Result as UsbResult}; @@ -61,16 +59,17 @@ pub macro create_usb_bus ( $DPRAM_SIZE:ident, $limited_inter_and_vbus:meta, $not_limited_inter_and_vbus:meta + // TODO: add a visibility restriction here so not everything is just blatantly `pub` ) { // could stand to make the above a bit more readable // MARK: - AvrGenericUsbBus pub struct $AvrGenericUsbBus { - usb: AvrDMutex<$USB_DEVICE>, - suspend_notifier: AvrDMutex, - pending_ins: AvrDMutex>, - endpoints: [EndpointTableEntry; $MAX_ENDPOINTS], - dpram_usage: u16, // TODO: This need to be extracted + pub usb: AvrDMutex<$USB_DEVICE>, + pub suspend_notifier: AvrDMutex, + pub pending_ins: AvrDMutex>, + pub endpoints: [EndpointTableEntry; $MAX_ENDPOINTS], + pub dpram_usage: u16, } impl $AvrGenericUsbBus<()> { @@ -114,14 +113,14 @@ pub macro create_usb_bus ( }) } - fn active_endpoints(&self) -> impl Iterator { + pub fn active_endpoints(&self) -> impl Iterator { self.endpoints .iter() .enumerate() // why enumerate then immediately drop? .filter(|&(_, ep)| ep.is_allocated) } - fn set_current_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { + pub fn set_current_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { if index >= $MAX_ENDPOINTS { return Err(UsbError::InvalidEndpoint); } @@ -139,7 +138,7 @@ pub macro create_usb_bus ( Ok(()) } - fn endpoint_byte_count(&self, cs: CriticalSection) -> u16 { // REVIEW: should this conditionally be a u8 + pub fn endpoint_byte_count(&self, cs: CriticalSection) -> u16 { // REVIEW: should this conditionally be a u8 let usb = self.usb.borrow(cs); // FIXME: Potential for desync here? LUFA doesn't seem to care. #[$not_limited_inter_and_vbus] @@ -152,7 +151,7 @@ pub macro create_usb_bus ( } } - fn configure_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { + pub fn configure_endpoint(&self, cs: CriticalSection, index: usize) -> Result<(), UsbError> { let usb = self.usb.borrow(cs); self.set_current_endpoint(cs, index)?; let endpoint = &self.endpoints[index]; diff --git a/examples/arduino-leonardo/src/bin/leonardo-usb.rs b/examples/arduino-leonardo/src/bin/leonardo-usb.rs index 77c454ec16..b0f0ddd476 100644 --- a/examples/arduino-leonardo/src/bin/leonardo-usb.rs +++ b/examples/arduino-leonardo/src/bin/leonardo-usb.rs @@ -30,7 +30,7 @@ use arduino_hal::{pac::PLL, port::{mode::{Input, Output, PullUp}, Pin}, usb::{Av use avr_device::{asm::sleep, interrupt}; -use usb_device::{descriptor::lang_id::LangID, device::UsbDevice, prelude::StringDescriptors}; +use usb_device::{bus::UsbBusAllocator, descriptor::lang_id::LangID, device::UsbDevice, prelude::StringDescriptors}; use usbd_hid::{ descriptor::{KeyboardReport, SerializedDescriptor}, hid_class::HIDClass, @@ -61,13 +61,13 @@ fn main() -> ! { // Check PLL lock while pll.pllcsr.read().plock().bit_is_clear() {} - let usb_bus = arduino_hal::default_usb_bus!(usb, pll); + let usb_bus: &UsbBusAllocator> = arduino_hal::default_usb_bus!(usb, pll); - let hid_class = HIDClass::new(usb_bus, KeyboardReport::desc(), 1); + let hid_class: HIDClass> = HIDClass::new(usb_bus, KeyboardReport::desc(), 1); let strings = StringDescriptors::new(LangID::EN) .manufacturer("Foo") .product("Bar"); - let usb_device = arduino_hal::default_usb_device!(usb_bus, 0x1209, 0x0001, strings); + let usb_device: UsbDevice> = arduino_hal::default_usb_device!(usb_bus, 0x1209, 0x0001, strings); unsafe { USB_CTX = Some(UsbContext { @@ -86,7 +86,7 @@ fn main() -> ! { } } -static mut USB_CTX: Option> = None; +static mut USB_CTX: Option = None; #[interrupt(atmega32u4)] fn USB_GEN() { @@ -116,16 +116,16 @@ unsafe fn poll_usb() { ctx.poll(); } -struct UsbContext { - usb_device: UsbDevice<'static, AvrGenericUsbBus>, - hid_class: HIDClass<'static, AvrGenericUsbBus>, +struct UsbContext { + usb_device: UsbDevice<'static, AvrGenericUsbBus>, + hid_class: HIDClass<'static, AvrGenericUsbBus>, current_index: usize, pressed: bool, indicator: Pin, trigger: Pin>, } -impl UsbContext { +impl UsbContext { fn poll(&mut self) { if self.trigger.is_low() { let next_report = if self.pressed { diff --git a/examples/arduino-leonardo/src/bin/std_stub/mod.rs b/examples/arduino-leonardo/src/bin/std_stub/mod.rs index ca05936f82..405222f31d 100644 --- a/examples/arduino-leonardo/src/bin/std_stub/mod.rs +++ b/examples/arduino-leonardo/src/bin/std_stub/mod.rs @@ -15,9 +15,13 @@ fn panic(_info: &PanicInfo) -> ! { status.set_low(); delay_ms(100); status.set_high(); - delay_ms(300); + delay_ms(100); + status.set_low(); + delay_ms(100); + status.set_high(); + delay_ms(400); status.set_low(); - delay_ms(500); + delay_ms(1000); } } From 2f1509ce3e9360345add66ac03c17a5e178b7bba Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Fri, 2 Aug 2024 22:43:19 -0400 Subject: [PATCH 18/21] A better error handler --- examples/arduino-leonardo/src/bin/std_stub/mod.rs | 15 +++++++++++++++ rust-toolchain.toml | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/arduino-leonardo/src/bin/std_stub/mod.rs b/examples/arduino-leonardo/src/bin/std_stub/mod.rs index 405222f31d..c5f90d1af6 100644 --- a/examples/arduino-leonardo/src/bin/std_stub/mod.rs +++ b/examples/arduino-leonardo/src/bin/std_stub/mod.rs @@ -1,7 +1,9 @@ +#![feature(panic_info_message)] //! Replacement for avr-std-stub with a custom panic handler. use core::panic::PanicInfo; +use arduino_hal::prelude::*; use arduino_hal::{delay_ms, pins, Peripherals}; #[panic_handler] @@ -9,6 +11,19 @@ fn panic(_info: &PanicInfo) -> ! { let dp = unsafe { Peripherals::steal() }; let pins = pins!(dp); let mut status = pins.d13.into_output(); + let mut serial = arduino_hal::default_serial!(dp, pins, 57600); + + ufmt::uwriteln!(&mut serial, "I panicked because {}!\r", _info.message().as_str().unwrap_or("useless")).unwrap_infallible(); + ufmt::uwriteln!(&mut serial, "I panicked at {}!\r", _info.location().unwrap().file()).unwrap_infallible(); + + delay_ms(100); + + ufmt::uwriteln!(&mut serial, "I panicked on {}!\r", _info.location().unwrap().line()).unwrap_infallible(); + + if let Some(s) = _info.payload().downcast_ref::<&str>() { + ufmt::uwriteln!(&mut serial, "More info: {}!\r", s).unwrap_infallible(); + } + loop { status.set_high(); delay_ms(100); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 270cc88989..3b2e62d072 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-03-22" +channel = "nightly-2024-07-22" components = [ "rust-src" ] profile = "minimal" From b504d117468b9b52dbd3c1db69114a004c70efdb Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Sat, 3 Aug 2024 00:07:31 -0400 Subject: [PATCH 19/21] Even better panic handling --- .../arduino-leonardo/src/bin/std_stub/mod.rs | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/examples/arduino-leonardo/src/bin/std_stub/mod.rs b/examples/arduino-leonardo/src/bin/std_stub/mod.rs index c5f90d1af6..d83065aec1 100644 --- a/examples/arduino-leonardo/src/bin/std_stub/mod.rs +++ b/examples/arduino-leonardo/src/bin/std_stub/mod.rs @@ -1,28 +1,48 @@ #![feature(panic_info_message)] //! Replacement for avr-std-stub with a custom panic handler. +use core::fmt::{self, Debug}; use core::panic::PanicInfo; -use arduino_hal::prelude::*; +use arduino_hal::hal::port::{PD2, PD3}; +use arduino_hal::hal::Atmega; +use arduino_hal::pac::USART1; +use arduino_hal::port::mode::{Input, Output}; +use arduino_hal::port::Pin; +use arduino_hal::{prelude::*, Usart}; use arduino_hal::{delay_ms, pins, Peripherals}; #[panic_handler] fn panic(_info: &PanicInfo) -> ! { + use ::core::fmt::Write as _; + let dp = unsafe { Peripherals::steal() }; let pins = pins!(dp); let mut status = pins.d13.into_output(); let mut serial = arduino_hal::default_serial!(dp, pins, 57600); - ufmt::uwriteln!(&mut serial, "I panicked because {}!\r", _info.message().as_str().unwrap_or("useless")).unwrap_infallible(); - ufmt::uwriteln!(&mut serial, "I panicked at {}!\r", _info.location().unwrap().file()).unwrap_infallible(); + struct UartWriter { + uart: Usart, Pin> + } + impl ::core::fmt::Write for UartWriter { + fn write_str(&mut self, s: &str) -> ::core::fmt::Result { + ufmt::uwriteln!(&mut self.uart, "{}", s).unwrap_infallible(); + Ok(()) + } + } + + // ufmt::uwriteln!(&mut serial, "I panicked at {}!\r", _info.location().unwrap().file()).unwrap_infallible(); + + // delay_ms(100); - delay_ms(100); + // ufmt::uwriteln!(&mut serial, "I panicked on {}!\r", _info.location().unwrap().line()).unwrap_infallible(); - ufmt::uwriteln!(&mut serial, "I panicked on {}!\r", _info.location().unwrap().line()).unwrap_infallible(); + // if let Some(s) = _info.payload().downcast_ref::<&str>() { + // ufmt::uwriteln!(&mut serial, "More info: {}!\r", s).unwrap_infallible(); + // } - if let Some(s) = _info.payload().downcast_ref::<&str>() { - ufmt::uwriteln!(&mut serial, "More info: {}!\r", s).unwrap_infallible(); - } + let mut uart = UartWriter { uart: serial }; + ::core::writeln!(uart, "{}", _info).ok(); loop { status.set_high(); From 08bfd2488eeae7363e1e86e40d39026d7e003456 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Sat, 3 Aug 2024 01:34:04 -0400 Subject: [PATCH 20/21] Small nit changes --- arduino-hal/src/lib.rs | 2 +- avr-hal-generic/src/usb.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/arduino-hal/src/lib.rs b/arduino-hal/src/lib.rs index 93379bc403..6ce4a98ad6 100644 --- a/arduino-hal/src/lib.rs +++ b/arduino-hal/src/lib.rs @@ -378,7 +378,7 @@ pub macro default_usb_bus ($usb:expr, $pll:expr) { pub macro default_usb_device ($usb_bus:expr, $vid:expr, $pid:expr, $strings:expr) { UsbDeviceBuilder::new( $usb_bus, - UsbVidPid($vid, $pid), + UsbVidPid($vid, $pid) ) .strings(&[$strings]) .unwrap() diff --git a/avr-hal-generic/src/usb.rs b/avr-hal-generic/src/usb.rs index c8069b1dec..f0c91cb9b6 100644 --- a/avr-hal-generic/src/usb.rs +++ b/avr-hal-generic/src/usb.rs @@ -129,9 +129,14 @@ pub macro create_usb_bus ( if usb.usbcon.read().frzclk().bit_is_set() { return Err(UsbError::InvalidState); } + + #[$limited_inter_and_vbus] unsafe { // TODO: investigate unsafety here (only occurs in atmega8u2?) usb.uenum.write(|w| w.bits(index as u8)); } + #[$not_limited_inter_and_vbus] + usb.uenum.modify(|_, w| w.bits(index as u8)); + if usb.uenum.read().bits() & 0b111 != (index as u8) { return Err(UsbError::InvalidState); } @@ -463,12 +468,14 @@ pub macro create_usb_bus ( interrupt::free(|cs| { let usb = self.usb.borrow(cs); + #[$not_limited_inter_and_vbus] + let usbint = usb.usbint.read(); + let udint = usb.udint.read(); let udien = usb.udien.read(); #[$not_limited_inter_and_vbus] { - let usbint = usb.usbint.read(); if usbint.vbusti().bit_is_set() { usb.usbint.clear_interrupts(|w| w.vbusti().clear_bit()); if usb.usbsta.read().vbus().bit_is_set() { From 8a47b1f66136ddf60870bcfc9f50395a528dc101 Mon Sep 17 00:00:00 2001 From: supersimple33 <40609224+supersimple33@users.noreply.github.com> Date: Sat, 3 Aug 2024 13:20:22 -0400 Subject: [PATCH 21/21] cleaner error handles --- .../arduino-leonardo/src/bin/std_stub/mod.rs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/arduino-leonardo/src/bin/std_stub/mod.rs b/examples/arduino-leonardo/src/bin/std_stub/mod.rs index d83065aec1..5fd154a7ba 100644 --- a/examples/arduino-leonardo/src/bin/std_stub/mod.rs +++ b/examples/arduino-leonardo/src/bin/std_stub/mod.rs @@ -1,7 +1,7 @@ #![feature(panic_info_message)] //! Replacement for avr-std-stub with a custom panic handler. -use core::fmt::{self, Debug}; +// use core::fmt::{self, Debug}; use core::panic::PanicInfo; use arduino_hal::hal::port::{PD2, PD3}; @@ -14,22 +14,22 @@ use arduino_hal::{delay_ms, pins, Peripherals}; #[panic_handler] fn panic(_info: &PanicInfo) -> ! { - use ::core::fmt::Write as _; + // use ::core::fmt::Write as _; let dp = unsafe { Peripherals::steal() }; let pins = pins!(dp); let mut status = pins.d13.into_output(); - let mut serial = arduino_hal::default_serial!(dp, pins, 57600); + // let mut serial = arduino_hal::default_serial!(dp, pins, 57600); - struct UartWriter { - uart: Usart, Pin> - } - impl ::core::fmt::Write for UartWriter { - fn write_str(&mut self, s: &str) -> ::core::fmt::Result { - ufmt::uwriteln!(&mut self.uart, "{}", s).unwrap_infallible(); - Ok(()) - } - } + // struct UartWriter { + // uart: Usart, Pin> + // } + // impl ::core::fmt::Write for UartWriter { + // fn write_str(&mut self, s: &str) -> ::core::fmt::Result { + // ufmt::uwriteln!(&mut self.uart, "{}", s).unwrap_infallible(); + // Ok(()) + // } + // } // ufmt::uwriteln!(&mut serial, "I panicked at {}!\r", _info.location().unwrap().file()).unwrap_infallible(); @@ -41,8 +41,8 @@ fn panic(_info: &PanicInfo) -> ! { // ufmt::uwriteln!(&mut serial, "More info: {}!\r", s).unwrap_infallible(); // } - let mut uart = UartWriter { uart: serial }; - ::core::writeln!(uart, "{}", _info).ok(); + // let mut uart = UartWriter { uart: serial }; + // ::core::writeln!(uart, "{}", _info).ok(); loop { status.set_high();