From 9ba63a65eeb09f84897a954c89f5e180dd0a1173 Mon Sep 17 00:00:00 2001 From: decaday Date: Thu, 12 Dec 2024 20:08:32 +0800 Subject: [PATCH] refactor(usb): migrate usb driver code to crate `musb` --- Cargo.toml | 7 +- src/lib.rs | 2 +- src/usb.rs | 127 ++++++++++++++++++++ src/usb/bus.rs | 253 ---------------------------------------- src/usb/control_pipe.rs | 185 ----------------------------- src/usb/driver.rs | 193 ------------------------------ src/usb/endpoint.rs | 133 --------------------- src/usb/mod.rs | 166 -------------------------- 8 files changed, 133 insertions(+), 933 deletions(-) create mode 100644 src/usb.rs delete mode 100644 src/usb/bus.rs delete mode 100644 src/usb/control_pipe.rs delete mode 100644 src/usb/driver.rs delete mode 100644 src/usb/endpoint.rs delete mode 100644 src/usb/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 784e8da..cea70ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,9 @@ log = { version = "0.4", optional = true } critical-section = "1.2" cfg-if = "1.0.0" portable-atomic = { version = "1", features = ["unsafe-assume-single-core", "require-cas"], optional = true } +# musb = { version = "0.1.0", optional = true, features = ["prebuild"] } +musb = { git = "https://github.com/decaday/musb.git", optional = true, features = ["prebuild"] } +# musb = { path = "../musb", optional = true , features = ["prebuild"] } futures-util = { version = "0.3.30", default-features = false } embassy-hal-internal = { version = "0.2.0", features = [ @@ -87,11 +90,11 @@ exti = [] # PY32F07x: the IN and OUT buffers of the same endpoint being shared # When this feature is enabled, the In and Out of an endpoint will not be used at the same time, except for ep0. # PY32F403: IN and OUT do not share FIFO, this feature is invalid -allow-ep-shared-fifo = [] +allow-ep-shared-fifo = ["musb/allow-ep-shared-fifo"] py32f030k28 = ["py32-metapac/py32f030k28"] py32f030f16 = ["py32-metapac/py32f030f16"] -py32f072c1b = ["py32-metapac/py32f072c1b"] +py32f072c1b = ["py32-metapac/py32f072c1b", "dep:musb", "musb/builtin-py32f07x"] # As of 2023-12-04, this driver is implemented using CC1 as the halfway rollover interrupt, and any # additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST diff --git a/src/lib.rs b/src/lib.rs index 43cdbb5..a8ed315 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ pub mod time_driver; #[cfg(feature = "time-driver-systick")] pub mod systick_time_driver; pub mod gpio; -#[cfg(usb)] +#[cfg(feature = "py32f072c1b")] pub mod usb; #[cfg(feature = "exti")] diff --git a/src/usb.rs b/src/usb.rs new file mode 100644 index 0000000..0f00455 --- /dev/null +++ b/src/usb.rs @@ -0,0 +1,127 @@ +/// Universal Serial Bus (USB) +/// +/// The USB peripheral IP in PY32 is a mini Mentor USB (musb), +/// featuring a fixed FIFO size and with some register functionalities masked. +/// +/// See more: https://github.com/decaday/musb +/// +/// For the PY32F07x series, IN and OUT endpoints for the same endpoint share a FIFO. +/// By default, we don't use a single endpoint simultaneously for IN and OUT directions. +/// However, you can enable the `allow-ep-shared-fifo` feature to use an endpoint's IN +/// and OUT capabilities concurrently. + +use core::marker::PhantomData; +use embassy_usb_driver as driver; + +use crate::rcc::{self, RccPeripheral}; +use crate::{interrupt, Peripheral}; +use crate::interrupt::typelevel::Interrupt; + +use embassy_usb_driver::EndpointType; + +use musb::{MusbDriver, + Endpoint, + ControlPipe, + UsbInstance, + Bus, + Out, + In, +}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + musb::on_interrupt::(); + } +} + +/// USB driver. +pub struct Driver<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + inner: MusbDriver<'d, UsbInstance>, +} + +impl<'d, T: Instance> Driver<'d, T> { + /// Create a new USB driver. + pub fn new( + _usb: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + _dp: impl Peripheral

> + 'd, + _dm: impl Peripheral

> + 'd, + ) -> Self { + let freq = T::frequency(); + if freq.0 != 48_000_000 { + panic!("USB clock (PLL) must be 48MHz"); + } + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + rcc::enable_and_reset::(); + + #[cfg(feature = "time")] + embassy_time::block_for(embassy_time::Duration::from_millis(100)); + #[cfg(not(feature = "time"))] + cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 10); + + Self { + inner: MusbDriver::new(), + phantom: PhantomData, + } + } +} + +impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { + type EndpointOut = Endpoint<'d, UsbInstance, Out>; + type EndpointIn = Endpoint<'d, UsbInstance, In>; + type ControlPipe = ControlPipe<'d, UsbInstance>; + type Bus = Bus<'d, UsbInstance>; + + fn alloc_endpoint_in( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + ) -> Result { + self.inner.alloc_endpoint(ep_type, max_packet_size, interval_ms, false) + } + + fn alloc_endpoint_out( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + ) -> Result { + self.inner.alloc_endpoint(ep_type, max_packet_size, interval_ms, false) + } + + fn start(self, control_max_packet_size: u16) -> (Bus<'d, UsbInstance>, ControlPipe<'d, UsbInstance>) { + self.inner.start(control_max_packet_size) + } + } + +trait SealedInstance {} + +/// USB instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + 'static { + /// Interrupt for this USB instance. + type Interrupt: interrupt::typelevel::Interrupt; +} + +// Internal PHY pins +pin_trait!(DpPin, Instance); +pin_trait!(DmPin, Instance); + +foreach_interrupt!( + ($inst:ident, usb, $block:ident, LP, $irq:ident) => { + impl SealedInstance for crate::peripherals::$inst {} + + impl Instance for crate::peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } + }; +); \ No newline at end of file diff --git a/src/usb/bus.rs b/src/usb/bus.rs deleted file mode 100644 index 1dc14db..0000000 --- a/src/usb/bus.rs +++ /dev/null @@ -1,253 +0,0 @@ -use super::*; - -/// USB bus. -pub struct Bus<'d, T: Instance> { - pub(super) phantom: PhantomData<&'d mut T>, - pub(super) ep_confs: [EndPointConfig; EP_COUNT], - pub(super) inited: bool, -} - -impl<'d, T: Instance> driver::Bus for Bus<'d, T> { - async fn poll(&mut self) -> Event { - poll_fn(move |cx| { - BUS_WAKER.register(cx.waker()); - - let regs = T::regs(); - - // TODO: implement VBUS detection. - if !self.inited { - self.inited = true; - return Poll::Ready(Event::PowerDetected); - } - - if IRQ_RESUME.load(Ordering::Acquire) { - IRQ_RESUME.store(false, Ordering::Relaxed); - return Poll::Ready(Event::Resume); - } - - if IRQ_RESET.load(Ordering::Acquire) { - IRQ_RESET.store(false, Ordering::Relaxed); - - regs.power().write(|w| w.set_suspend_mode(true)); - // for index in 1..EP_COUNT { - // regs.index().write(|w| w.set_index(index as _)); - // regs.in_csr1().modify(|w| w.set_flush_fifo(true)); - // } - - trace!("RESET"); - - for w in &EP_IN_WAKERS { - w.wake() - } - for w in &EP_OUT_WAKERS { - w.wake() - } - - return Poll::Ready(Event::Reset); - } - - if IRQ_SUSPEND.load(Ordering::Acquire) { - IRQ_SUSPEND.store(false, Ordering::Relaxed); - return Poll::Ready(Event::Suspend); - } - - Poll::Pending - }) - .await - } - - fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { - // This can race, so do a retry loop. - let reg = T::regs(); - let ep_index = ep_addr.index(); - if ep_index != 0 { - reg.index().write(|w| w.set_index(ep_index as _)); - } - match ep_addr.direction() { - Direction::In => { - if ep_index == 0 { - // usb_ep0_state = USB_EP0_STATE_STALL; - - reg.ep0_csr().write(|w| { - w.set_send_stall(stalled); - if stalled { w.set_serviced_out_pkt_rdy(true); } - }); - - // while !reg.ep0_csr().read().sent_stall() {} - } - else { - reg.in_csr1().write(|w| { - w.set_send_stall(stalled); - if !stalled { - w.set_sent_stall(false); - w.set_clr_data_tog(true); - } - }); - // while !reg.in_csr1().read().sent_stall() {} - } - EP_IN_WAKERS[ep_addr.index()].wake(); - } - Direction::Out => { - if ep_index == 0 { - // usb_ep0_state = USB_EP0_STATE_STALL; - - reg.ep0_csr().write(|w| { - w.set_send_stall(stalled); - if stalled { w.set_serviced_out_pkt_rdy(true); } - }); - // while !reg.ep0_csr().read().sent_stall() {} - } - else { - reg.out_csr1().write(|w| { - w.set_send_stall(stalled); - if !stalled { - w.set_sent_stall(false); - w.set_clr_data_tog(true); - } - }); - // while !reg.out_csr1().read().sent_stall() {} - } - EP_IN_WAKERS[ep_addr.index()].wake(); - EP_OUT_WAKERS[ep_addr.index()].wake(); - } - } - } - - fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { - let reg = T::regs(); - let ep_index = ep_addr.index(); - if ep_index != 0 { - reg.index().write(|w| w.set_index(ep_index as _)); - } - - if ep_index == 0 { - // TODO: py32 offiial CherryUsb port returns false directly for EP0 - reg.ep0_csr().read().send_stall() - } else { - match ep_addr.direction() { - Direction::In => reg.in_csr1().read().send_stall(), - Direction::Out => reg.out_csr1().read().send_stall(), - } - } - } - - fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { - trace!("set_enabled {:x} {}", ep_addr, enabled); - let ep_index = ep_addr.index(); - - if enabled { - T::regs().index().write(|w| w.set_index(ep_index as u8)); - match ep_addr.direction() { - Direction::Out => { - if ep_index == 0 { - T::regs().int_in1e().modify(|w| - w.set_ep0(true)) - } else { - T::regs().int_out1e().modify(|w| - w.set_epout(ep_index - 1, true) - ); - } - - // T::regs().out_csr2().write(|w| { - // w.set_auto_clear(true); - // }); - - T::regs().max_pkt_out().write(|w| - w.set_max_pkt_size(self.ep_confs[ep_index].out_max_fifo_size_btyes) - ); - - T::regs().out_csr1().write(|w| { - w.set_clr_data_tog(true); - }); - - //TODO: DMA - - if self.ep_confs[ep_index].ep_type == EndpointType::Isochronous { - T::regs().out_csr2().write(|w| { - w.set_iso(true); - }); - } - - if T::regs().out_csr1().read().out_pkt_rdy() { - T::regs().out_csr1().modify(|w| - w.set_flush_fifo(true) - ); - } - - let flags = EP_OUT_ENABLED.load(Ordering::Acquire) | ep_index as u8; - EP_OUT_ENABLED.store(flags, Ordering::Release); - // Wake `Endpoint::wait_enabled()` - EP_OUT_WAKERS[ep_index].wake(); - } - Direction::In => { - if ep_index == 0 { - T::regs().int_in1e().modify(|w| - w.set_ep0(true)) - } else { - T::regs().int_in1e().modify(|w| - w.set_epin(ep_index - 1, true) - ); - } - - // T::regs().in_csr2().write(|w| { - // w.set_auto_set(true); - // }); - - // TODO: DMA - - T::regs().max_pkt_in().write(|w| - w.set_max_pkt_size(self.ep_confs[ep_index].in_max_fifo_size_btyes) - ); - - T::regs().in_csr1().write(|w| { - w.set_clr_data_tog(true); - }); - - if self.ep_confs[ep_index].ep_type == EndpointType::Isochronous { - T::regs().in_csr2().write(|w| { - w.set_iso(true); - }); - } - T::regs().in_csr2().write(|w| w.set_mode(Mode::IN)); - - if T::regs().in_csr1().read().fifo_not_empty() { - T::regs().in_csr1().modify(|w| - w.set_flush_fifo(true) - ); - } - - let flags = EP_IN_ENABLED.load(Ordering::Acquire) | ep_index as u8; - EP_IN_ENABLED.store(flags, Ordering::Release); - // Wake `Endpoint::wait_enabled()` - EP_IN_WAKERS[ep_index].wake(); - } - } - } - else { - // py32 offiial CherryUsb port does nothing when disable an endpoint - match ep_addr.direction() { - Direction::Out => { - let flags = EP_OUT_ENABLED.load(Ordering::Acquire) & !(ep_index as u8); - EP_OUT_ENABLED.store(flags, Ordering::Release); - } - Direction::In => { - let flags = EP_IN_ENABLED.load(Ordering::Acquire) & !(ep_index as u8); - EP_IN_ENABLED.store(flags, Ordering::Release); - } - } - } - } - - async fn enable(&mut self) { - T::regs().int_usb().write(|w| { - w.set_reset(true); - w.set_suspend(true); - w.set_resume(true); - }); - } - async fn disable(&mut self) {} - - async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { - Err(Unsupported) - } -} \ No newline at end of file diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs deleted file mode 100644 index ab7a61a..0000000 --- a/src/usb/control_pipe.rs +++ /dev/null @@ -1,185 +0,0 @@ -use super::*; - -/// USB control pipe. -pub struct ControlPipe<'d, T: Instance> { - pub(super) _phantom: PhantomData<&'d mut T>, - pub(super) max_packet_size: u16, - pub(super) ep_in: Endpoint<'d, T, In>, - pub(super) ep_out: Endpoint<'d, T, Out>, -} - -impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { - fn max_packet_size(&self) -> usize { - usize::from(self.max_packet_size) - } - - async fn setup(&mut self) -> [u8; 8] { - let regs = T::regs(); - loop { - trace!("SETUP read waiting"); - poll_fn(|cx| { - EP_OUT_WAKERS[0].register(cx.waker()); - - regs.index().write(|w| w.set_index(0)); - - if regs.ep0_csr().read().out_pkt_rdy() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - if regs.ep0_count().read().count() != 8 { - trace!("SETUP read failed: {:?}", regs.ep0_count().read().count()); - continue; - } - - let mut buf = [0; 8]; - (&mut buf).into_iter().for_each(|b| - *b = regs.fifo(0).read().data() - ); - regs.ep0_csr().modify(|w| w.set_serviced_out_pkt_rdy(true)); - - trace!("SETUP read ok"); - return buf; - } - } - - async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result { - trace!("control: data_out len={} first={} last={}", buf.len(), first, last); - - let regs = T::regs(); - - let _ = poll_fn(|cx| { - EP_OUT_WAKERS[0].register(cx.waker()); - // STC uses same usb IP with py32 (mentor usb), - // which said it is nessery to set index to 0 - regs.index().write(|w| w.set_index(0)); - let ready = regs.ep0_csr().read().out_pkt_rdy(); - if ready { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - regs.index().write(|w| w.set_index(0)); - - let read_count = regs.ep0_count().read().count(); - if read_count as usize > buf.len() { - return Err(EndpointError::BufferOverflow); - } - - if read_count as u16 > self.ep_out.info.max_packet_size { - return Err(EndpointError::BufferOverflow); - } - - buf.into_iter().for_each(|b| - *b = regs.fifo(0).read().data() - ); - regs.ep0_csr().modify(|w| w.set_serviced_out_pkt_rdy(true)); - trace!("READ OK, rx_len = {}", read_count); - - Ok(read_count as usize) - } - - async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError> { - trace!("control: data_in len={} first={} last={}", data.len(), first, last); - - if data.len() > self.ep_in.info.max_packet_size as usize { - return Err(EndpointError::BufferOverflow); - } - - let regs = T::regs(); - - trace!("WRITE WAITING"); - - let _ = poll_fn(|cx| { - EP_IN_WAKERS[0].register(cx.waker()); - regs.index().write(|w| w.set_index(0)); - - // TODO: use fifo_not_empty? - let unready = regs.ep0_csr().read().in_pkt_rdy(); - - if unready { - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await; - - regs.index().write(|w| w.set_index(0)); - - data.into_iter().for_each(|b| - regs.fifo(0).write(|w| w.set_data(*b)) - ); - - regs.ep0_csr().modify(|w| { - w.set_in_pkt_rdy(true); - if last { w.set_data_end(true); } - }); - Ok(()) - } - - async fn accept(&mut self) { - trace!("control: accept"); - - let regs = T::regs(); - regs.index().write(|w| w.set_index(0)); - - // zero length - regs.ep0_csr().modify(|w| { - w.set_in_pkt_rdy(true); - // w.set_data_end(true); - }); - - cortex_m::asm::delay(10000); - - // Wait is needed, so that we don't set the address too soon, breaking the status stage. - // (embassy-usb sets the address after accept() returns) - poll_fn(|cx| { - EP_IN_WAKERS[0].register(cx.waker()); - regs.index().write(|w| w.set_index(0)); - - // A zero-length OUT data packet is used to indicate the end of a Control transfer. In normal operation, such packets should only - // be received after the entire length of the device request has been transferred (i.e. after the CPU has set DataEnd). If, however, the - // host sends a zero-length OUT data packet before the entire length of device request has been transferred, this signals the - // premature end of the transfer. In this case, the MUSBMHDRC will automatically flush any IN token loaded by CPU ready for the - // Data phase from the FIFO and set SetupEnd. - if regs.ep0_csr().read().setup_end() { - regs.ep0_csr().write(|w| w.set_serviced_setup_end(false)); - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - trace!("control: accept OK"); - } - - async fn reject(&mut self) { - let regs = T::regs(); - trace!("control: reject"); - - // Set IN+OUT to stall - regs.index().write(|w| w.set_index(0)); - regs.ep0_csr().modify(|w| { - w.set_send_stall(true); - w.set_serviced_out_pkt_rdy(true); - }); - - // TODO: async waiting for Sent Stall? - } - - async fn accept_set_address(&mut self, addr: u8) { - self.accept().await; - - let regs = T::regs(); - trace!("setting addr: {}", addr); - regs.addr().write(|w| w.set_addr(addr)); - } -} \ No newline at end of file diff --git a/src/usb/driver.rs b/src/usb/driver.rs deleted file mode 100644 index 6cf64e3..0000000 --- a/src/usb/driver.rs +++ /dev/null @@ -1,193 +0,0 @@ -use super::*; - -/// USB driver. -pub struct Driver<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - alloc: [EndpointData; EP_COUNT], -} - -impl<'d, T: Instance> Driver<'d, T> { - /// Create a new USB driver. - pub fn new( - _usb: impl Peripheral

+ 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - _dp: impl Peripheral

> + 'd, - _dm: impl Peripheral

> + 'd, - ) -> Self { - let freq = T::frequency(); - if freq.0 != 48_000_000 { - panic!("USB clock (PLL) must be 48MHz"); - } - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - rcc::enable_and_reset::(); - - let regs = T::regs(); - - regs.index().write(|w| w.set_index(0)); - - #[cfg(feature = "time")] - embassy_time::block_for(embassy_time::Duration::from_millis(100)); - #[cfg(not(feature = "time"))] - cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 10); - - // Initialize the bus so that it signals that power is available - BUS_WAKER.wake(); - - Self { - phantom: PhantomData, - alloc: [EndpointData { - ep_conf: EndPointConfig { - ep_type: EndpointType::Bulk, - in_max_fifo_size_btyes: 1, - out_max_fifo_size_btyes: 1, - }, - used_in: false, - used_out: false, - }; EP_COUNT], - } - } - - fn alloc_endpoint( - &mut self, - ep_type: EndpointType, - max_packet_size: u16, - interval_ms: u8, - is_ep0: bool, - ) -> Result, driver::EndpointAllocError> { - trace!( - "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", - ep_type, - max_packet_size, - interval_ms, - D::dir() - ); - - - let index = if is_ep0 { - Some((0, &mut self.alloc[0])) - } - else { - self.alloc.iter_mut().enumerate().find(|(i, ep)| { - if *i == 0 { - return false; // reserved for control pipe - } - let used = ep.used_out || ep.used_in; - - #[cfg(all(not(feature = "allow-ep-shared-fifo"), py32f072))] - if used { return false } - - #[cfg(py32f072)] - if ((max_packet_size + 7) / 8) as u8 > MAX_FIFO_SIZE_BTYES[*i] { - return false; - } - - #[cfg(py32f403)] - if ((max_packet_size + 7) / 8) as u8 > MAX_FIFO_SIZE_BTYES { - panic!("max_packet_size > MAX_FIFO_SIZE"); - } - - let used_dir = match D::dir() { - Direction::Out => ep.used_out, - Direction::In => ep.used_in, - }; - !used || (ep.ep_conf.ep_type == ep_type && !used_dir) - }) - }; - - let (index, ep) = match index { - Some(x) => x, - None => return Err(EndpointAllocError), - }; - - ep.ep_conf.ep_type = ep_type; - - - T::regs().index().write(|w| w.set_index(index as u8)); - match D::dir() { - Direction::Out => { - assert!(!ep.used_out); - ep.used_out = true; - - ep.ep_conf.out_max_fifo_size_btyes = calc_max_fifo_size_btyes(max_packet_size); - } - Direction::In => { - assert!(!ep.used_in); - ep.used_in = true; - - ep.ep_conf.in_max_fifo_size_btyes = calc_max_fifo_size_btyes(max_packet_size); - } - }; - - Ok(Endpoint { - _phantom: PhantomData, - info: EndpointInfo { - addr: EndpointAddress::from_parts(index, D::dir()), - ep_type, - max_packet_size, - interval_ms, - }, - }) - } -} - -impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { - type EndpointOut = Endpoint<'d, T, Out>; - type EndpointIn = Endpoint<'d, T, In>; - type ControlPipe = ControlPipe<'d, T>; - type Bus = Bus<'d, T>; - - fn alloc_endpoint_in( - &mut self, - ep_type: EndpointType, - max_packet_size: u16, - interval_ms: u8, - ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval_ms, false) - } - - fn alloc_endpoint_out( - &mut self, - ep_type: EndpointType, - max_packet_size: u16, - interval_ms: u8, - ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval_ms, false) - } - - fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { - let ep_out = self - .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0, true) - .unwrap(); - let ep_in = self - .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0, true) - .unwrap(); - - trace!("enabled"); - - let mut ep_confs = [EndPointConfig { - ep_type: EndpointType::Bulk, - in_max_fifo_size_btyes: 1, - out_max_fifo_size_btyes: 1, - }; EP_COUNT]; - - for i in 0..EP_COUNT { - ep_confs[i] = self.alloc[i].ep_conf; - } - - ( - Bus { - phantom: PhantomData, - ep_confs, - inited: false, - }, - ControlPipe { - _phantom: PhantomData, - max_packet_size: control_max_packet_size, - ep_out, - ep_in, - }, - ) - } -} \ No newline at end of file diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs deleted file mode 100644 index c217b27..0000000 --- a/src/usb/endpoint.rs +++ /dev/null @@ -1,133 +0,0 @@ -use super::*; - -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(super) struct EndpointData { - pub(super) ep_conf: EndPointConfig, // only valid if used_in || used_out - pub(super) used_in: bool, - pub(super) used_out: bool, -} - -/// USB endpoint. -pub struct Endpoint<'d, T: Instance, D> { - pub(super) _phantom: PhantomData<(&'d mut T, D)>, - pub(super) info: EndpointInfo, -} - -// impl<'d, T: Instance, > driver::Endpoint for Endpoint<'d, T, In> { -impl<'d, T: Instance, D: Dir> driver::Endpoint for Endpoint<'d, T, D> { - fn info(&self) -> &EndpointInfo { - &self.info - } - - async fn wait_enabled(&mut self) { - let _ = poll_fn(|cx| { - let index = self.info.addr.index(); - - let enabled = match self.info.addr.direction() { - Direction::Out => { - EP_OUT_WAKERS[index].register(cx.waker()); - EP_OUT_ENABLED.load(Ordering::Acquire) & (index as u8) != 0 - }, - Direction::In => { - EP_IN_WAKERS[index].register(cx.waker()); - EP_IN_ENABLED.load(Ordering::Acquire) & (index as u8) != 0 - } - }; - if enabled { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - trace!("Endpoint {:#X} wait enabled OK", self.info.addr); - } -} - -impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { - async fn read(&mut self, buf: &mut [u8]) -> Result { - trace!("READ WAITING, buf.len() = {}", buf.len()); - let index = self.info.addr.index(); - let regs = T::regs(); - - let _ = poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); - regs.index().write(|w| w.set_index(index as _)); - let ready = regs.out_csr1().read().out_pkt_rdy(); - - if ready { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - regs.index().write(|w| w.set_index(index as _)); - - let read_count = regs.out_count().read().count(); - - if read_count as usize > buf.len() { - return Err(EndpointError::BufferOverflow); - } - - buf.into_iter().for_each(|b| - *b = regs.fifo(index).read().data() - ); - regs.out_csr1().modify(|w| w.set_out_pkt_rdy(false)); - trace!("READ OK, rx_len = {}", read_count); - - Ok(read_count as usize) - } -} - -impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { - async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { - if buf.len() > self.info.max_packet_size as usize { - return Err(EndpointError::BufferOverflow); - } - - let index = self.info.addr.index(); - let regs = T::regs(); - - trace!("WRITE WAITING len = {}", buf.len()); - - let _ = poll_fn(|cx| { - EP_IN_WAKERS[index].register(cx.waker()); - regs.index().write(|w| w.set_index(index as _)); - - let unready = regs.in_csr1().read().in_pkt_rdy(); - - if unready { - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await; - - regs.index().write(|w| w.set_index(index as _)); - - if buf.len() == 0 { - regs.in_csr1().modify(|w| w.set_in_pkt_rdy(true)); - } else { - buf.into_iter().for_each(|b| - regs.fifo(index).write(|w| w.set_data(*b)) - ); - - regs.in_csr1().modify(|w| w.set_in_pkt_rdy(true)); - } - trace!("WRITE OK"); - - Ok(()) - } -} - -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(super) struct EndPointConfig { - pub(super) ep_type: EndpointType, - pub(super) in_max_fifo_size_btyes: u8, - pub(super) out_max_fifo_size_btyes: u8, -} \ No newline at end of file diff --git a/src/usb/mod.rs b/src/usb/mod.rs deleted file mode 100644 index 8480ecd..0000000 --- a/src/usb/mod.rs +++ /dev/null @@ -1,166 +0,0 @@ -/// Universal Serial Bus (USB) -/// -/// The USB peripheral IP in PY32 is a mini Mentor USB (musb), -/// featuring a fixed FIFO size and with some register functionalities masked. -/// -/// For the PY32F07x series, IN and OUT endpoints for the same endpoint share a FIFO. -/// By default, we don't use a single endpoint simultaneously for IN and OUT directions. -/// However, you can enable the `allow-ep-shared-fifo` feature to use an endpoint's IN -/// and OUT capabilities concurrently. - -use core::future::poll_fn; -use core::marker::PhantomData; -use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; -use core::task::Poll; - -use embassy_sync::waitqueue::AtomicWaker; -use embassy_usb_driver as driver; -use embassy_usb_driver::{ - Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, -}; - -use crate::pac::usb::vals::Mode; -use crate::rcc::{self, RccPeripheral}; -use crate::{interrupt, Peripheral}; -use crate::interrupt::typelevel::Interrupt; - -mod endpoint; -pub use endpoint::Endpoint; -use endpoint::{EndpointData, EndPointConfig}; - -#[path ="driver.rs"] -mod usb_driver; -pub use usb_driver::Driver; - -mod bus; -pub use bus::Bus; - -mod control_pipe; -pub use control_pipe::ControlPipe; - -#[cfg(py32f072)] -const EP_COUNT: usize = 6; -#[cfg(py32f403)] -const EP_COUNT: usize = 8; - -#[cfg(py32f072)] -const MAX_FIFO_SIZE_BTYES: [u8; EP_COUNT] = [8, 8, 16, 16, 16, 64]; - -#[cfg(py32f403)] -const MAX_FIFO_SIZE_BTYES: u8 = 8; - -const NEW_AW: AtomicWaker = AtomicWaker::new(); - -static BUS_WAKER: AtomicWaker = NEW_AW; - -static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; -static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; - -static IRQ_RESET: AtomicBool = AtomicBool::new(false); -static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); -static IRQ_RESUME: AtomicBool = AtomicBool::new(false); -static EP_IN_ENABLED: AtomicU8 = AtomicU8::new(0); -static EP_OUT_ENABLED: AtomicU8 = AtomicU8::new(0); - -fn calc_max_fifo_size_btyes(len: u16) -> u8 { - let btyes = ((len + 7) / 8) as u8; - if btyes > 8 { - panic!("Invalid length: {}", len); - } - btyes -} - -/// Interrupt handler. -pub struct InterruptHandler { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - unsafe fn on_interrupt() { - let int_usb = T::regs().int_usb().read(); - if int_usb.reset() { - IRQ_RESET.store(true, Ordering::SeqCst); - BUS_WAKER.wake(); - } - if int_usb.suspend() { - IRQ_SUSPEND.store(true, Ordering::SeqCst); - BUS_WAKER.wake(); - } - if int_usb.resume() { - IRQ_RESUME.store(true, Ordering::SeqCst); - BUS_WAKER.wake(); - } - - let int_in = T::regs().int_in1().read(); - let int_out = T::regs().int_out1().read(); - if int_in.ep0() { - EP_IN_WAKERS[0].wake(); - EP_OUT_WAKERS[0].wake(); - } - - for index in 1..EP_COUNT { - if int_in.epin(index - 1) { - EP_IN_WAKERS[index].wake(); - } - if int_out.epout(index - 1) { - EP_OUT_WAKERS[index].wake(); - } - if T::regs().in_csr1().read().underrun(){ - T::regs().in_csr1().modify(|w| w.set_underrun(false)); - warn!("Underrun: ep {}", index); - } - - } - - } - -} - -trait Dir { - fn dir() -> Direction; -} - -/// Marker type for the "IN" direction. -pub enum In {} -impl Dir for In { - fn dir() -> Direction { - Direction::In - } -} - -/// Marker type for the "OUT" direction. -pub enum Out {} -impl Dir for Out { - fn dir() -> Direction { - Direction::Out - } -} - -trait SealedInstance { - fn regs() -> crate::pac::usb::Usb; -} - -/// USB instance trait. -#[allow(private_bounds)] -pub trait Instance: SealedInstance + RccPeripheral + 'static { - /// Interrupt for this USB instance. - type Interrupt: interrupt::typelevel::Interrupt; -} - -// Internal PHY pins -pin_trait!(DpPin, Instance); -pin_trait!(DmPin, Instance); - -foreach_interrupt!( - ($inst:ident, usb, $block:ident, LP, $irq:ident) => { - impl SealedInstance for crate::peripherals::$inst { - fn regs() -> crate::pac::usb::Usb { - crate::pac::$inst - } - } - - impl Instance for crate::peripherals::$inst { - type Interrupt = crate::interrupt::typelevel::$irq; - } - }; -); \ No newline at end of file