Skip to content

Commit

Permalink
refactor(usb): migrate usb driver code to crate musb
Browse files Browse the repository at this point in the history
  • Loading branch information
decaday committed Dec 12, 2024
1 parent 6feab84 commit 9ba63a6
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 933 deletions.
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
127 changes: 127 additions & 0 deletions src/usb.rs
Original file line number Diff line number Diff line change
@@ -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<T: Instance> {
_phantom: PhantomData<T>,
}

impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
musb::on_interrupt::<UsbInstance>();
}
}

/// 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<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
_dp: impl Peripheral<P = impl DpPin<T>> + 'd,
_dm: impl Peripheral<P = impl DmPin<T>> + '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::<T>();

#[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::EndpointIn, driver::EndpointAllocError> {
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::EndpointOut, driver::EndpointAllocError> {
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;
}
};
);
Loading

0 comments on commit 9ba63a6

Please sign in to comment.