From 47cbc57f91f4893a2ee11b8a443d3d482dace7f2 Mon Sep 17 00:00:00 2001 From: Dummyc0m Date: Sun, 3 Nov 2024 00:50:51 -0700 Subject: [PATCH] usbhs: implement USBHS USB device driver Implement the Embassy USB driver for the USBHS peripheral. The implementation largely mirrors the OTG_FS driver and has similar limitation such as non-configurable endpoint buffer sizes, in/out on the same endpoint index, lack of iso transfer, and untested bulk transfer. This change requires the latest fixes in ch32-metapac due to incorrect striding of the endpoint control registers. Tested on CH32V305 with the following applications: - USB HID device, for testing non-control endpoints interrupt transfer. - USB DFU, for testing the control endpoint. Co-authored-by: Harry Brooke Co-authored-by: Codetector Co-authored-by: Dummyc0m --- build.rs | 2 + src/lib.rs | 3 + src/usbhs/control.rs | 189 ++++++++++++++++++ src/usbhs/endpoint.rs | 205 ++++++++++++++++++++ src/usbhs/mod.rs | 434 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 833 insertions(+) create mode 100644 src/usbhs/control.rs create mode 100644 src/usbhs/endpoint.rs create mode 100644 src/usbhs/mod.rs diff --git a/build.rs b/build.rs index 37417c0..de63ab4 100644 --- a/build.rs +++ b/build.rs @@ -391,6 +391,8 @@ fn main() { // USB is splitted into multiple impls (("usbd", "DP"), quote!(crate::usbd::DpPin)), (("usbd", "DM"), quote!(crate::usbd::DmPin)), + (("usbhs", "DP"), quote!(crate::usbhs::DpPin)), + (("usbhs", "DM"), quote!(crate::usbhs::DmPin)), // USBPD, handled by usbpd/mod.rs //(("usbpd", "CC1"), quote!(crate::usbpd::Cc1Pin)), //(("usbpd", "CC2"), quote!(crate::usbpd::Cc2Pin)), diff --git a/src/lib.rs b/src/lib.rs index bfc9769..98a3037 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,9 @@ pub mod otg_fs; #[cfg(usbd)] pub mod usbd; +#[cfg(usbhs_v3)] +pub mod usbhs; + #[cfg(usbpd)] pub mod usbpd; diff --git a/src/usbhs/control.rs b/src/usbhs/control.rs new file mode 100644 index 0000000..5923dca --- /dev/null +++ b/src/usbhs/control.rs @@ -0,0 +1,189 @@ +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::Poll; + +use ch32_metapac::usbhs::vals::{EpRxResponse, EpTog, EpTxResponse, UsbToken}; +use embassy_usb_driver::EndpointError; + +use super::endpoint::Endpoint; +use super::{Instance, EP_WAKERS}; +use crate::usb::InOut; + +pub struct ControlPipe<'d, T: Instance> { + _phantom: PhantomData<&'d mut T>, + ep0: Endpoint<'d, T, InOut>, +} + +impl<'d, T: Instance> ControlPipe<'d, T> { + pub(crate) fn new(ep0: Endpoint<'d, T, InOut>) -> Self { + Self { + _phantom: PhantomData, + ep0, + } + } +} + +impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { + fn max_packet_size(&self) -> usize { + use embassy_usb_driver::Endpoint; + + self.ep0.info().max_packet_size as usize + } + + async fn setup(&mut self) -> [u8; 8] { + poll_fn(|ctx| { + EP_WAKERS[0].register(ctx.waker()); + let r = T::regs(); + let d = T::dregs(); + + let flag = r.int_fg().read(); + + if flag.setup_act() { + d.ep_rx_ctrl(0).write(|w| w.set_mask_uep_r_res(EpRxResponse::NAK)); + d.ep_tx_ctrl(0).write(|w| w.set_mask_uep_t_res(EpTxResponse::NAK)); + + let mut data: [u8; 8] = [0; 8]; + self.ep0.data.buffer.read_volatile(&mut data[..]); + r.int_fg().write(|w| { + w.set_setup_act(true); + w.set_transfer(true); + }); + critical_section::with(|_| { + r.int_en().modify(|w| { + w.set_setup_act(true); + w.set_transfer(true); + }); + }); + Poll::Ready(data) + } else { + Poll::Pending + } + }) + .await + } + + async fn data_out(&mut self, buf: &mut [u8], first: bool, _last: bool) -> Result { + let d = T::dregs(); + + if buf.len() > self.ep0.data.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); + } + + if first { + d.ep_rx_ctrl(0).write(|v| { + v.set_mask_uep_r_tog(EpTog::DATA1); + v.set_mask_uep_r_res(EpRxResponse::ACK); + }) + } else { + d.ep_rx_ctrl(0).modify(|v| { + v.set_mask_uep_r_res(EpRxResponse::ACK); + v.set_mask_uep_r_tog(if let EpTog::DATA0 = v.mask_uep_r_tog() { + EpTog::DATA1 + } else { + EpTog::DATA0 + }); + }) + } + + self.ep0.data_out(buf).await + } + + async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError> { + let d = T::dregs(); + let r = T::regs(); + + self.ep0.data_in_write_buffer(data)?; + + if first { + d.ep_tx_ctrl(0).write(|v| { + v.set_mask_uep_t_res(EpTxResponse::ACK); + v.set_mask_uep_t_tog(EpTog::DATA1); + }) + } else { + d.ep_tx_ctrl(0).modify(|v| { + v.set_mask_uep_t_res(EpTxResponse::ACK); + v.set_mask_uep_t_tog(if let EpTog::DATA0 = v.mask_uep_t_tog() { + EpTog::DATA1 + } else { + EpTog::DATA0 + }); + }); + } + + self.ep0.data_in_transfer().await?; + + if last { + // status stage = recieve, DATA1, ack + d.ep_rx_ctrl(0).write(|w| { + w.set_mask_uep_r_tog(EpTog::DATA1); + w.set_mask_uep_r_res(EpRxResponse::ACK); + }); + poll_fn(|ctx| { + EP_WAKERS[0].register(ctx.waker()); + + let transfer = d.int_fg().read().transfer(); + let status = d.int_st().read(); + + if transfer && status.endp() == 0 { + let res = match status.token() { + UsbToken::OUT => { + if r.rx_len().read() != 0 { + error!("Expected 0 len OUT stage, found non-zero len, aborting"); + Poll::Ready(Err(EndpointError::BufferOverflow)) + } else { + // Set the EP back to NAK so that we are "not ready to recieve" + Poll::Ready(Ok(())) + } + } + token => { + error!("Got {} instead of OUT token", token.to_bits()); + Poll::Ready(Err(EndpointError::Disabled)) + } + }; + + d.ep_rx_ctrl(0).write(|v| { + v.set_mask_uep_r_res(EpRxResponse::NAK); + }); + d.int_fg().write(|w| w.set_transfer(true)); + critical_section::with(|_| r.int_en().modify(|v| v.set_transfer(true))); + res + } else { + Poll::Pending + } + }) + .await?; + } + Ok(()) + } + + async fn accept(&mut self) { + let d = T::dregs(); + + d.ep_t_len(0).write(|w| w.set_len(0)); + d.ep_tx_ctrl(0).modify(|w| { + // status stage starts with DATA1 + w.set_mask_uep_t_tog(EpTog::DATA1); + w.set_mask_uep_t_res(EpTxResponse::ACK); + }); + + unwrap!(self.ep0.data_in_transfer().await); + } + + async fn reject(&mut self) { + let d = T::dregs(); + + d.ep_tx_ctrl(0).modify(|v| { + v.set_mask_uep_t_res(EpTxResponse::STALL); + }); + d.ep_rx_ctrl(0).modify(|v| { + v.set_mask_uep_r_res(EpRxResponse::STALL); + }); + } + + async fn accept_set_address(&mut self, addr: u8) { + let r = T::regs(); + trace!("accepting address: {}", addr); + self.accept().await; + r.dev_ad().write(|w| w.set_addr(addr)); + } +} diff --git a/src/usbhs/endpoint.rs b/src/usbhs/endpoint.rs new file mode 100644 index 0000000..c45de64 --- /dev/null +++ b/src/usbhs/endpoint.rs @@ -0,0 +1,205 @@ +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::Poll; + +use ch32_metapac::usbhs::vals::{EpRxResponse, EpTog, EpTxResponse, UsbToken}; +use embassy_usb_driver::{Direction, EndpointError, EndpointIn, EndpointInfo, EndpointOut, EndpointType}; + +use super::{EndpointData, Instance, EP_WAKERS}; +use crate::usb::{Dir, In, Out}; + +pub struct Endpoint<'d, T: Instance, D: Dir> { + _phantom: PhantomData<(&'d mut T, D)>, + info: EndpointInfo, + pub(crate) data: EndpointData<'d>, +} + +impl<'d, T: Instance, D: Dir> Endpoint<'d, T, D> { + pub(crate) fn new(info: EndpointInfo, data: EndpointData<'d>) -> Self { + let index = info.addr.index(); + T::dregs().ep_max_len(index).write(|v| v.set_len(info.max_packet_size)); + if info.ep_type != EndpointType::Control { + match info.addr.direction() { + Direction::Out => { + T::dregs().ep_rx_dma(index - 1).write_value(data.buffer.addr() as u32); + } + Direction::In => { + T::dregs().ep_tx_dma(index - 1).write_value(data.buffer.addr() as u32); + } + } + } + Self { + _phantom: PhantomData, + info, + data, + } + } + + async fn wait_enabled_internal(&mut self) { + poll_fn(|ctx| { + EP_WAKERS[self.info.addr.index() as usize].register(ctx.waker()); + if self.is_enabled() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + } + + fn is_enabled(&self) -> bool { + match self.info.addr.direction() { + Direction::Out => T::dregs().ep_config().read().r_en(self.info.addr.index() - 1), + Direction::In => T::dregs().ep_config().read().t_en(self.info.addr.index() - 1), + } + } + + pub(crate) async fn data_out(&mut self, buf: &mut [u8]) -> Result { + let r = T::regs(); + let d = T::dregs(); + let index = self.info.addr.index(); + + poll_fn(|ctx| { + super::EP_WAKERS[index].register(ctx.waker()); + + let transfer = d.int_fg().read().transfer(); + let status = r.int_st().read(); + if transfer && status.endp() == index as u8 { + let res = match status.token() { + UsbToken::OUT => { + let len = r.rx_len().read() as usize; + if len == buf.len() { + self.data.buffer.read_volatile(&mut buf[..len]); + Poll::Ready(Ok(len)) + } else { + Poll::Ready(Err(EndpointError::BufferOverflow)) + } + } + token => { + error!("unexpected USB Token {}, aborting", token.to_bits()); + Poll::Ready(Err(EndpointError::Disabled)) + } + }; + + d.ep_rx_ctrl(index).modify(|v| { + v.set_mask_uep_r_res(EpRxResponse::NAK); + }); + + d.int_fg().write(|w| w.set_transfer(true)); + critical_section::with(|_| d.int_en().modify(|w| w.set_transfer(true))); + res + } else { + Poll::Pending + } + }) + .await + } + + pub(crate) fn data_in_write_buffer(&mut self, data: &[u8]) -> Result<(), EndpointError> { + let d = T::dregs(); + let index = self.info.addr.index(); + + if data.len() > self.data.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); + } + + self.data.buffer.write_volatile(data); + + d.ep_t_len(index).write(|v| v.set_len(data.len() as u16)); + Ok(()) + } + + pub(crate) async fn data_in_transfer(&mut self) -> Result<(), EndpointError> { + let d = T::dregs(); + let r = T::regs(); + let index = self.info.addr.index(); + + poll_fn(|ctx| { + EP_WAKERS[index].register(ctx.waker()); + + let transfer = d.int_fg().read().transfer(); + let status = r.int_st().read(); + if transfer && status.endp() == index as u8 { + let res = match status.token() { + UsbToken::IN => Poll::Ready(Ok(())), + token => { + error!("unexpected USB Token {}, aborting", token.to_bits()); + Poll::Ready(Err(EndpointError::Disabled)) + } + }; + d.ep_tx_ctrl(index).modify(|v| { + v.set_mask_uep_t_res(EpTxResponse::NAK); + }); + + d.int_fg().write(|w| w.set_transfer(true)); + critical_section::with(|_| d.int_en().modify(|w| w.set_transfer(true))); + res + } else { + Poll::Pending + } + }) + .await + } +} + +impl<'d, T: Instance, D: Dir> embassy_usb_driver::Endpoint for Endpoint<'d, T, D> { + fn info(&self) -> &EndpointInfo { + &self.info + } + + async fn wait_enabled(&mut self) { + self.wait_enabled_internal().await + } +} + +impl<'d, T: Instance> EndpointOut for Endpoint<'d, T, Out> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + if !self.is_enabled() { + error!("read from disabled ep {}", self.info.addr.index()); + return Err(EndpointError::Disabled); + } + + let d = T::dregs(); + let index = self.info.addr.index(); + + if buf.len() > self.data.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); + } + + d.ep_rx_ctrl(index).modify(|v| { + v.set_mask_uep_r_res(EpRxResponse::ACK); + v.set_mask_uep_r_tog(if let EpTog::DATA0 = v.mask_uep_r_tog() { + EpTog::DATA1 + } else { + EpTog::DATA0 + }); + }); + + self.data_out(buf).await + } +} + +impl<'d, T: Instance> EndpointIn for Endpoint<'d, T, In> { + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + if !self.is_enabled() { + error!("write to disabled ep {}", self.info.addr.index()); + return Err(EndpointError::Disabled); + } + + let d = T::dregs(); + let index = self.info.addr.index(); + + self.data_in_write_buffer(buf)?; + + d.ep_tx_ctrl(index).modify(|v| { + v.set_mask_uep_t_res(EpTxResponse::ACK); + v.set_mask_uep_t_tog(if let EpTog::DATA0 = v.mask_uep_t_tog() { + EpTog::DATA1 + } else { + EpTog::DATA0 + }); + }); + + self.data_in_transfer().await + } +} diff --git a/src/usbhs/mod.rs b/src/usbhs/mod.rs new file mode 100644 index 0000000..bf4eca5 --- /dev/null +++ b/src/usbhs/mod.rs @@ -0,0 +1,434 @@ +//! USBHS device mode peripheral driver +//! Note that this currently only implements device mode +//! +//!
+//! There's a lot of TODOs and panics where things are not implemented +//!
+//! +//! The same endpoint index cannot be used for both In/Out transfers. +//! This is a limitation of the implementation and should be fixed. +//! +//! List of things that is tested +//! Untested but expected to work items are noted as well +//! +//! Control Pipe: +//! - [x] Receive `SETUP` packet (Host -> Dev) +//! - [x] Send `IN` packet (Dev -> Host). +//! - [x] Receive `OUT` packet (Host -> Dev). +//! +//! Other Endpoints: +//! - [x] Interrupt Out +//! - [x] Interrupt In +//! - [ ] Bulk Out (Expected to work but not tested) +//! - [ ] Bulk In (Expected to work but not tested) +//! - [ ] Isochronous Out (Does not work) +//! - [ ] Isochronous In (Does not work) +//! +//! Other Features: +//! - [ ] Set endpoint stall +//! - [ ] Get endpoint stall status +//! - [ ] Remote wakeup +//! + +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::Poll; + +use ch32_metapac::usbhs::regs::{EpBufMod, EpConfig, EpType}; +use ch32_metapac::usbhs::vals::{EpRxResponse, EpTog, EpTxResponse, SpeedType, UsbToken}; +use control::ControlPipe; +use embassy_sync::waitqueue::AtomicWaker; +use embassy_usb_driver::{ + Direction, EndpointAddress, EndpointAllocError, EndpointInfo, EndpointType, Event, Unsupported, +}; +use endpoint::Endpoint; + +use crate::gpio::{AFType, Speed}; +use crate::interrupt::typelevel::Interrupt; +use crate::usb::{Dir, EndpointBufferAllocator, EndpointData, EndpointDataBuffer, In, Out}; +use crate::{interrupt, Peripheral}; + +pub mod control; +mod endpoint; + +const MAX_NR_EP: usize = 16; +const EP_MAX_PACKET_SIZE: u16 = 64; + +const NEW_AW: AtomicWaker = AtomicWaker::new(); +static BUS_WAKER: AtomicWaker = NEW_AW; +static EP_WAKERS: [AtomicWaker; MAX_NR_EP] = [NEW_AW; MAX_NR_EP]; + +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let flag = r.int_fg().read(); + + assert!(!flag.fifo_ov()); + + if flag.bus_rst() { + //mask the interrupt and let the main thread handle it + r.int_en().modify(|w| w.set_bus_rst(false)); + BUS_WAKER.wake(); + }; + if flag.suspend() { + //mask the interrupt and let the main thread handle it + r.int_en().modify(|w| w.set_suspend(false)); + BUS_WAKER.wake(); + }; + if flag.setup_act() { + //mask the interrupt and let the main thread handle it + r.int_en().modify(|w| w.set_setup_act(false)); + EP_WAKERS[0].wake(); + }; + if flag.transfer() { + let status = r.int_st().read(); + + match status.token() { + UsbToken::OUT | UsbToken::IN => { + r.int_en().modify(|w| w.set_transfer(false)); + EP_WAKERS[status.endp() as usize].wake(); + } + // SETUP + t @ (UsbToken::SOF | UsbToken::SETUP) => { + panic!("received token {}", t as u8) + } + } + } + if flag.hst_sof() { + r.int_fg().write(|v| v.set_hst_sof(true)); + } + } +} + +pub struct WakeupInterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for WakeupInterruptHandler { + unsafe fn on_interrupt() { + todo!() + } +} + +pub struct Driver<'d, T: Instance, const NR_EP: usize> { + phantom: PhantomData<&'d T>, + allocator: EndpointBufferAllocator<'d, NR_EP>, + next_ep_addr: u8, +} + +impl<'d, T: Instance, const NR_EP: usize> Driver<'d, T, NR_EP> { + pub fn new( + _peri: impl Peripheral

+ 'd, + _irqs: impl interrupt::typelevel::Binding> + + interrupt::typelevel::Binding> + + 'd, + dp: impl Peripheral

+ 'd>, + dm: impl Peripheral

+ 'd>, + ep_buffer: &'d mut [EndpointDataBuffer; NR_EP], + ) -> Self { + assert!(ep_buffer.len() > 0); + let dp = dp.into_ref(); + let dm = dm.into_ref(); + + dp.set_as_af_output(AFType::OutputPushPull, Speed::High); + dm.set_as_af_output(AFType::OutputPushPull, Speed::High); + + T::enable_and_reset(); + + let allocator = EndpointBufferAllocator::new(ep_buffer); + + let r = T::regs(); + let h = T::hregs(); + + r.ctrl().write(|w| { + w.set_clr_all(true); + w.set_reset_sie(true); + }); + + // Sleep for 10uS from WCH C code + embassy_time::block_for(embassy_time::Duration::from_micros(10)); + + // Following WCH C code.... unclear why clr_all is not also cleared here + r.ctrl().modify(|w| w.set_reset_sie(false)); + h.ctrl().write(|w| w.set_phy_suspendm(true)); + + r.ctrl().write(|w| { + w.set_int_busy(true); + w.set_dma_en(true); + // TODO: HS, Configurable? + w.set_speed_type(SpeedType::HIGHSPEED); + }); + + r.int_en().write(|w| { + w.set_dev_nak(false); + w.set_fifo_ov(true); + w.set_setup_act(true); + w.set_suspend(true); + w.set_bus_rst(true); + w.set_transfer(true); + }); + + Self { + phantom: PhantomData, + allocator, + // 0 is reserved for the control endpoint + next_ep_addr: 1, + } + } + + fn alloc_ep_address(&mut self) -> u8 { + if self.next_ep_addr as usize >= MAX_NR_EP { + panic!("ep addr overflow") + } + + let addr = self.next_ep_addr; + self.next_ep_addr += 1; + addr + } + + fn alloc_endpoint( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + dir: Direction, + ) -> Result, EndpointAllocError> { + let ep_addr = self.alloc_ep_address(); + let data = self.allocator.alloc_endpoint(max_packet_size)?; + + Ok(Endpoint::new( + EndpointInfo { + // todo fix in embassy usb driver ep_addr should be u8 with top bit unset + addr: EndpointAddress::from_parts(ep_addr as usize, dir), + ep_type, + max_packet_size, + interval_ms, + }, + data, + )) + } +} + +impl<'d, T: Instance, const NR_EP: usize> embassy_usb_driver::Driver<'d> for Driver<'d, T, NR_EP> { + 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_out( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + ) -> Result { + self.alloc_endpoint(ep_type, max_packet_size, interval_ms, Direction::Out) + } + + 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, Direction::In) + } + + fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { + let d = T::dregs(); + + let ep0_buf = self.allocator.alloc_endpoint(control_max_packet_size).unwrap(); + d.ep0_dma().write_value(ep0_buf.addr() as u32); + d.ep_max_len(0).write(|w| w.set_len(64)); + d.ep_rx_ctrl(0).write(|w| w.set_mask_uep_r_res(EpRxResponse::NAK)); + d.ep_tx_ctrl(0).write(|w| w.set_mask_uep_t_res(EpTxResponse::NAK)); + + let ep0 = Endpoint::new( + EndpointInfo { + // todo fix in embassy usb driver ep_addr should be u8 with top bit unset + addr: EndpointAddress::from_parts(0, Direction::Out), + ep_type: EndpointType::Control, + max_packet_size: EP_MAX_PACKET_SIZE, + interval_ms: 0, + }, + ep0_buf, + ); + + critical_section::with(|_| { + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + }); + + ( + Bus { + _phantom: PhantomData, + fake_power_on: false, + }, + ControlPipe::new(ep0), + ) + } +} + +pub struct Bus<'d, T: Instance> { + _phantom: PhantomData<&'d mut T>, + fake_power_on: bool, +} + +impl<'d, T: Instance> Bus<'d, T> { + fn bus_reset(&mut self) { + let regs = T::regs(); + let d = T::dregs(); + + // Reset device address + regs.dev_ad().write(|v| { + v.set_addr(0); + }); + + // Reset endpoint state on bus reset + // EP0 get ACK/NAK so it can recieve setup + d.ep_rx_ctrl(0).write(|v| v.set_mask_uep_r_res(EpRxResponse::ACK)); + d.ep_tx_ctrl(0).write(|v| v.set_mask_uep_t_res(EpTxResponse::NAK)); + + // Mark all other EPs as NAK + for i in 1..=15 { + d.ep_tx_ctrl(i).write(|v| v.set_mask_uep_t_res(EpTxResponse::NAK)); + d.ep_rx_ctrl(i).write(|v| v.set_mask_uep_r_res(EpRxResponse::NAK)); + } + + // Disable all endpoints [1, 15] + d.ep_config().write_value(EpConfig::default()); + d.ep_type().write_value(EpType::default()); + d.ep_buf_mod().write_value(EpBufMod::default()); + } +} + +impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { + async fn enable(&mut self) { + critical_section::with(|_| T::regs().ctrl().modify(|v| v.set_dev_pu_en(true))); + } + + async fn disable(&mut self) {} + + async fn poll(&mut self) -> Event { + // TODO: VBUS detection + if !self.fake_power_on { + self.fake_power_on = true; + return Event::PowerDetected; + } + poll_fn(|cx| { + BUS_WAKER.register(cx.waker()); + + let r = T::regs(); + let flag = r.int_fg().read(); + + if flag.bus_rst() { + self.bus_reset(); + r.int_fg().write(|w| w.set_bus_rst(true)); + critical_section::with(|_| { + r.int_en().modify(|w| w.set_bus_rst(true)); + }); + Poll::Ready(Event::Reset) + } else if flag.suspend() { + let mis_st = r.mis_st().read(); + r.int_fg().write(|w| w.set_suspend(true)); + critical_section::with(|_| { + r.int_en().modify(|w| w.set_suspend(true)); + }); + if mis_st.suspend() { + Poll::Ready(Event::Suspend) + } else { + Poll::Ready(Event::Resume) + } + } else { + Poll::Pending + } + }) + .await + } + + fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { + let index = ep_addr.index(); + + critical_section::with(|_| match ep_addr.direction() { + Direction::Out => { + T::dregs().ep_config().modify(|v| v.set_r_en(index - 1, enabled)); + T::dregs().ep_rx_ctrl(index).write(|v| { + v.set_mask_uep_r_tog(EpTog::DATA1); + v.set_mask_uep_r_res(EpRxResponse::NAK); + v.set_r_tog_auto(false); + }); + } + Direction::In => { + T::dregs().ep_config().modify(|v| v.set_t_en(index - 1, enabled)); + T::dregs().ep_tx_ctrl(index).write(|v| { + v.set_mask_uep_t_tog(EpTog::DATA1); + v.set_mask_uep_t_res(EpTxResponse::NAK); + v.set_t_tog_auto(false); + }); + } + }); + EP_WAKERS[ep_addr.index()].wake(); + } + + fn endpoint_set_stalled(&mut self, _ep_addr: EndpointAddress, _stalled: bool) { + todo!() + } + + fn endpoint_is_stalled(&mut self, _ep_addr: EndpointAddress) -> bool { + todo!() + } + + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + todo!() + } +} + +/// USB endpoint. +trait SealedInstance: crate::peripheral::RccPeripheral { + fn regs() -> crate::pac::usbhs::Usb; + fn dregs() -> crate::pac::usbhs::Usbd; + fn hregs() -> crate::pac::usbhs::Usbh; +} + +/// UsbHs peripheral instance +#[allow(private_bounds)] +pub trait Instance: SealedInstance + 'static { + /// Regular interrupt for this instance + type Interrupt: interrupt::typelevel::Interrupt; + /// Wakeup interrupt for this instance + type WakeupInterrupt: interrupt::typelevel::Interrupt; +} + +foreach_peripheral!( + (usbhs, $inst:ident) => { + use crate::peripherals; + impl SealedInstance for peripherals::$inst { + fn regs() -> crate::pac::usbhs::Usb { + crate::pac::$inst + } + + fn dregs() -> crate::pac::usbhs::Usbd { + unsafe { + crate::pac::usbhs::Usbd::from_ptr(crate::pac::$inst.as_ptr()) + } + } + + fn hregs() -> crate::pac::usbhs::Usbh { + unsafe { + crate::pac::usbhs::Usbh::from_ptr(crate::pac::$inst.as_ptr()) + } + } + } + + impl Instance for peripherals::$inst { + type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL; + type WakeupInterrupt = crate::_generated::peripheral_interrupts::$inst::WAKEUP; + } + }; +); + +pin_trait!(DmPin, Instance); +pin_trait!(DpPin, Instance);