From ee4680923dbad977525cda7c2ab7bf0b49f0627a Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sun, 7 Jul 2024 01:05:52 +0800 Subject: [PATCH 01/43] wip(usb): add phy and dcd initialization Signed-off-by: HaoboGu --- Cargo.toml | 2 + src/lib.rs | 1 + src/usb/mod.rs | 280 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 src/usb/mod.rs diff --git a/Cargo.toml b/Cargo.toml index f171a09..e963cf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,8 @@ embedded-hal-async = "1.0.0" chrono = { version = "0.4.38", default-features = false, optional = true } mcan = { version = "0.5.0", optional = true } +xhci = "0.9.2" +bitfield-struct = "0.8" [build-dependencies] # hpm-metapac = { path = "../hpm-data/build/hpm-metapac", default-features = false, features = [ diff --git a/src/lib.rs b/src/lib.rs index e88d171..c2d62a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,7 @@ pub mod mbx; pub mod mcan; pub mod spi; pub mod uart; +pub mod usb; #[cfg(femc)] pub mod femc; diff --git a/src/usb/mod.rs b/src/usb/mod.rs new file mode 100644 index 0000000..f2aeee5 --- /dev/null +++ b/src/usb/mod.rs @@ -0,0 +1,280 @@ +use bitfield_struct::bitfield; +use embedded_hal::delay::DelayNs; +use riscv::delay::McycleDelay; +use xhci; + +use crate::pac::usb::regs::*; + +#[cfg(usb_v67)] +const ENDPOINT_COUNT: u8 = 8; +#[cfg(usb_v53)] +const ENDPOINT_COUNT: u8 = 16; + +#[allow(unused)] +pub struct Usb { + info: &'static Info, + delay: McycleDelay, +} + +pub struct EpConfig { + transfer: u8, + ep_addr: EpAddr, + max_packet_size: u16, +} + +#[bitfield(u8)] +struct EpAddr { + #[bits(4)] + ep_num: u8, + #[bits(3)] + _reserved: u8, + #[bits(1)] + dir: bool, +} + +/// Usb transfer type +pub enum TransferType { + Control = 0b00, + Isochronous = 0b01, + Bulk = 0b10, + Interrupt = 0b11, +} + +impl Usb { + fn phy_init(&mut self) { + let r = &self.info.regs; + + // Enable dp/dm pulldown + // In hpm_sdk, this operation is done by `ptr->PHY_CTRL0 &= ~0x001000E0u`. + // But there's corresponding bits in register, so we write the register directly here. + let phy_ctrl0 = r.phy_ctrl0().read().0 & (!0x001000E0); + r.phy_ctrl0().write_value(PhyCtrl0(phy_ctrl0)); + + r.otg_ctrl0().modify(|w| { + w.set_otg_utmi_suspendm_sw(false); + w.set_otg_utmi_reset_sw(true); + }); + + r.phy_ctrl1().modify(|w| { + w.set_utmi_cfg_rst_n(false); + }); + + // Wait for reset status + while r.otg_ctrl0().read().otg_utmi_reset_sw() {} + + // Set suspend + r.otg_ctrl0().modify(|w| { + w.set_otg_utmi_suspendm_sw(true); + }); + + // Delay at least 1us + self.delay.delay_us(5); + + r.otg_ctrl0().modify(|w| { + // Disable dm/dp wakeup + w.set_otg_wkdpdmchg_en(false); + // Clear reset sw + w.set_otg_utmi_reset_sw(false); + }); + + // OTG utmi clock detection + r.phy_status().modify(|w| w.set_utmi_clk_valid(true)); + while r.phy_status().read().utmi_clk_valid() == false {} + + // Reset and set suspend + r.phy_ctrl1().modify(|w| { + w.set_utmi_cfg_rst_n(true); + w.set_utmi_otg_suspendm(true); + }); + } + + fn phy_deinit(&mut self) { + let r = &self.info.regs; + + r.otg_ctrl0().modify(|w| { + w.set_otg_utmi_suspendm_sw(true); + w.set_otg_utmi_reset_sw(false); + }); + + r.phy_ctrl1().modify(|w| { + w.set_utmi_cfg_rst_n(false); + w.set_utmi_otg_suspendm(false); + }); + } + + fn dcd_bus_reset(&mut self) { + let r = &self.info.regs; + + // For each endpoint, first set the transfer type to ANY type other than control. + // This is because the default transfer type is control, according to hpm_sdk, + // leaving an un-configured endpoint control will cause undefined behavior + // for the data PID tracking on the active endpoint. + for i in 0..ENDPOINT_COUNT { + r.endptctrl(i as usize).write(|w| { + w.set_txt(TransferType::Bulk as u8); + w.set_rxt(TransferType::Bulk as u8); + }); + } + + // Clear all registers + // TODO: CHECK: In hpm_sdk, are those registers REALLY cleared? + r.endptnak().write_value(Endptnak::default()); + r.endptnaken().write_value(Endptnaken(0)); + r.usbsts().write_value(Usbsts::default()); + r.endptsetupstat().write_value(Endptsetupstat::default()); + r.endptcomplete().write_value(Endptcomplete::default()); + + while r.endptprime().read().0 != 0 {} + + r.endptflush().write_value(Endptflush(0xFFFFFFFF)); + + while r.endptflush().read().0 != 0 {} + } + + /// Initialize USB device controller driver + fn dcd_init(&mut self) { + // Initialize phy first + self.phy_init(); + + let r = &self.info.regs; + + // Reset controller + r.usbcmd().modify(|w| w.set_rst(true)); + while r.usbcmd().read().rst() {} + + // Set mode to device IMMEDIATELY after reset + r.usbmode().modify(|w| w.set_cm(0b10)); + + r.usbmode().modify(|w| { + // Set little endian + w.set_es(false); + // Disable setup lockout, please refer to "Control Endpoint Operation" section in RM + w.set_slom(false); + }); + + r.portsc1().modify(|w| { + // Parallel interface signal + w.set_sts(false); + // Parallel transceiver width + w.set_ptw(false); + // TODO: Set fullspeed mode + // w.set_pfsc(true); + }); + + // Do not use interrupt threshold + r.usbcmd().modify(|w| { + w.set_itc(0); + }); + + // Enable VBUS discharge + r.otgsc().modify(|w| { + w.set_vd(true); + }); + } + + /// Deinitialize USB device controller driver + fn dcd_deinit(&mut self) { + let r = &self.info.regs; + + // Stop first + r.usbcmd().modify(|w| w.set_rs(false)); + + // Reset controller + r.usbcmd().modify(|w| w.set_rst(true)); + while r.usbcmd().read().rst() {} + + // Disable phy + self.phy_deinit(); + + // Reset endpoint list address register, status register and interrupt enable register + r.endptlistaddr().write_value(Endptlistaddr(0)); + r.usbsts().write_value(Usbsts::default()); + r.usbintr().write_value(Usbintr(0)); + } + + /// Connect by enabling internal pull-up resistor on D+/D- + fn dcd_connect(&mut self) { + let r = &self.info.regs; + + r.usbcmd().modify(|w| { + w.set_rs(true); + }); + } + + /// Disconnect by disabling internal pull-up resistor on D+/D- + fn dcd_disconnect(&mut self) { + let r = &self.info.regs; + + // Stop + r.usbcmd().modify(|w| { + w.set_rs(false); + }); + + // Pullup DP to make the phy switch into full speed mode + r.usbcmd().modify(|w| { + w.set_rs(true); + }); + + // Clear sof flag and wait + r.usbsts().modify(|w| { + w.set_sri(true); + }); + while r.usbsts().read().sri() == false {} + + // Disconnect + r.usbcmd().modify(|w| { + w.set_rs(false); + }); + } +} + +impl Usb { + fn endpoint_open(&mut self, ep_config: EpConfig) { + let r = &self.info.regs; + + let ep_num = ep_config.ep_addr.ep_num(); + let dir = ep_config.ep_addr.dir(); + let ep_idx = 2 * ep_num + dir as u8; + + // Max EP count: 16 + if ep_num >= ENDPOINT_COUNT { + // TODO: return false + } + + // Prepare queue head + // TODO + let link = xhci::ring::trb::Link::new(); + + // Open endpoint + self.dcd_endpoint_open(ep_config); + } + + fn dcd_endpoint_open(&mut self, ep_config: EpConfig) { + let r = &self.info.regs; + + let ep_num = ep_config.ep_addr.ep_num(); + let dir = ep_config.ep_addr.dir(); + let ep_idx = 2 * ep_num + dir as u8; + + // Enable EP control + r.endptctrl(ep_num as usize).modify(|w| { + // Clear the RXT or TXT bits + if dir { + w.set_txt(0); + w.set_txe(true); + w.set_txr(true); + // TODO: Better impl? For example, make transfer a bitfield struct + w.0 |= (ep_config.transfer as u32) << 18; + } else { + w.set_rxt(0); + w.set_rxe(true); + w.set_rxr(true); + w.0 |= (ep_config.transfer as u32) << 2; + } + }); + } +} + +struct Info { + regs: crate::pac::usb::Usb, +} From 4e6a40acf011d2b05607544107318a0929ad732e Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sun, 7 Jul 2024 18:18:39 +0800 Subject: [PATCH 02/43] wip(usb): add device and endpoint operations Signed-off-by: HaoboGu --- src/usb/dcd.rs | 130 ++++++++++++++++++ src/usb/device.rs | 58 ++++++++ src/usb/endpoint.rs | 157 ++++++++++++++++++++++ src/usb/hcd.rs | 44 +++++++ src/usb/mod.rs | 313 ++++++++++++++++++++------------------------ 5 files changed, 529 insertions(+), 173 deletions(-) create mode 100644 src/usb/dcd.rs create mode 100644 src/usb/device.rs create mode 100644 src/usb/endpoint.rs create mode 100644 src/usb/hcd.rs diff --git a/src/usb/dcd.rs b/src/usb/dcd.rs new file mode 100644 index 0000000..446055e --- /dev/null +++ b/src/usb/dcd.rs @@ -0,0 +1,130 @@ +use hpm_metapac::usb::regs::*; + +use super::{TransferType, Usb, ENDPOINT_COUNT}; + +impl Usb { + pub(crate) fn dcd_bus_reset(&mut self) { + let r = &self.info.regs; + + // For each endpoint, first set the transfer type to ANY type other than control. + // This is because the default transfer type is control, according to hpm_sdk, + // leaving an un-configured endpoint control will cause undefined behavior + // for the data PID tracking on the active endpoint. + for i in 0..ENDPOINT_COUNT { + r.endptctrl(i as usize).write(|w| { + w.set_txt(TransferType::Bulk as u8); + w.set_rxt(TransferType::Bulk as u8); + }); + } + + // Clear all registers + // TODO: CHECK: In hpm_sdk, are those registers REALLY cleared? + r.endptnak().write_value(Endptnak::default()); + r.endptnaken().write_value(Endptnaken(0)); + r.usbsts().write_value(Usbsts::default()); + r.endptsetupstat().write_value(Endptsetupstat::default()); + r.endptcomplete().write_value(Endptcomplete::default()); + + while r.endptprime().read().0 != 0 {} + + r.endptflush().write_value(Endptflush(0xFFFFFFFF)); + + while r.endptflush().read().0 != 0 {} + } + + /// Initialize USB device controller driver + pub(crate) fn dcd_init(&mut self) { + // Initialize phy first + self.phy_init(); + + let r = &self.info.regs; + + // Reset controller + r.usbcmd().modify(|w| w.set_rst(true)); + while r.usbcmd().read().rst() {} + + // Set mode to device IMMEDIATELY after reset + r.usbmode().modify(|w| w.set_cm(0b10)); + + r.usbmode().modify(|w| { + // Set little endian + w.set_es(false); + // Disable setup lockout, please refer to "Control Endpoint Operation" section in RM + w.set_slom(false); + }); + + r.portsc1().modify(|w| { + // Parallel interface signal + w.set_sts(false); + // Parallel transceiver width + w.set_ptw(false); + // TODO: Set fullspeed mode + // w.set_pfsc(true); + }); + + // Do not use interrupt threshold + r.usbcmd().modify(|w| { + w.set_itc(0); + }); + + // Enable VBUS discharge + r.otgsc().modify(|w| { + w.set_vd(true); + }); + } + + /// Deinitialize USB device controller driver + fn dcd_deinit(&mut self) { + let r = &self.info.regs; + + // Stop first + r.usbcmd().modify(|w| w.set_rs(false)); + + // Reset controller + r.usbcmd().modify(|w| w.set_rst(true)); + while r.usbcmd().read().rst() {} + + // Disable phy + self.phy_deinit(); + + // Reset endpoint list address register, status register and interrupt enable register + r.endptlistaddr().write_value(Endptlistaddr(0)); + r.usbsts().write_value(Usbsts::default()); + r.usbintr().write_value(Usbintr(0)); + } + + /// Connect by enabling internal pull-up resistor on D+/D- + fn dcd_connect(&mut self) { + let r = &self.info.regs; + + r.usbcmd().modify(|w| { + w.set_rs(true); + }); + } + + /// Disconnect by disabling internal pull-up resistor on D+/D- + fn dcd_disconnect(&mut self) { + let r = &self.info.regs; + + // Stop + r.usbcmd().modify(|w| { + w.set_rs(false); + }); + + // Pullup DP to make the phy switch into full speed mode + r.usbcmd().modify(|w| { + w.set_rs(true); + }); + + // Clear sof flag and wait + r.usbsts().modify(|w| { + w.set_sri(true); + }); + while r.usbsts().read().sri() == false {} + + // Disconnect + r.usbcmd().modify(|w| { + w.set_rs(false); + }); + } +} diff --git a/src/usb/device.rs b/src/usb/device.rs new file mode 100644 index 0000000..f1f4f82 --- /dev/null +++ b/src/usb/device.rs @@ -0,0 +1,58 @@ +//! Usb device API +//! + +use super::{DcdData, QueueHead, QueueTransferDescriptor, Usb}; + +impl Usb { + fn device_qhd_get(&self, ep_idx: u8) -> &QueueHead { + &self.dcd_data.qhd[ep_idx as usize] + } + + fn device_qtd_get4(&self, ep_idx: u8) -> &QueueTransferDescriptor { + &self.dcd_data.qtd[ep_idx as usize * 8] + } + + fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { + let r = &self.info.regs; + + self.dcd_bus_reset(); + + self.dcd_data = DcdData::default(); + // Setup control endpoints(0 OUT, 1 IN) + self.dcd_data.qhd[0].cap.set_zero_length_termination(true); + self.dcd_data.qhd[1].cap.set_zero_length_termination(true); + self.dcd_data.qhd[0].cap.set_max_packet_size(ep0_max_packet_size); + self.dcd_data.qhd[1].cap.set_max_packet_size(ep0_max_packet_size); + + // Set the next pointer INVALID + // TODO: replacement? + self.dcd_data.qhd[0].qtd_overlay.next = 1; + self.dcd_data.qhd[1].qtd_overlay.next = 1; + + // Set for OUT only + self.dcd_data.qhd[0].cap.set_int_on_step(true); + } + + fn device_init(&mut self, int_mask: u32) { + // Clear dcd data first + self.dcd_data = DcdData::default(); + + // Initialize controller + self.dcd_init(); + + let r = &self.info.regs; + // Set endpoint list address + // TODO: Check if this is correct + let addr = self.dcd_data.qhd.as_ptr() as u32; + r.endptlistaddr().write(|w| w.set_epbase(addr)); + + // Clear status + r.usbsts().modify(|w| w.0 = 0); + + // Enable interrupts + r.usbintr().modify(|w| w.0 = w.0 | int_mask); + + // Connect + r.usbcmd().modify(|w| w.set_rs(true)); + } +} diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs new file mode 100644 index 0000000..6d4f1c5 --- /dev/null +++ b/src/usb/endpoint.rs @@ -0,0 +1,157 @@ +use hpm_metapac::usb::regs::Endptprime; + +use super::{EpAddr, EpConfig, QueueHead, TransferType, Usb, ENDPOINT_COUNT}; + +impl Usb { + fn endpoint_open(&mut self, ep_config: EpConfig) { + let r = &self.info.regs; + + let ep_num = ep_config.ep_addr.ep_num(); + let dir = ep_config.ep_addr.dir(); + let ep_idx = 2 * ep_num + dir as u8; + + // Max EP count: 16 + if ep_num >= ENDPOINT_COUNT { + // TODO: return false + } + + // Prepare queue head + self.dcd_data.qhd[ep_idx as usize] = QueueHead::default(); + self.dcd_data.qhd[ep_idx as usize].cap.set_zero_length_termination(true); + self.dcd_data.qhd[ep_idx as usize] + .cap + .set_max_packet_size(ep_config.max_packet_size & 0x7FF); + self.dcd_data.qhd[ep_idx as usize].qtd_overlay.next = 1; // Set next to invalid + if ep_config.transfer == TransferType::Isochronous as u8 { + self.dcd_data.qhd[ep_idx as usize] + .cap + .set_iso_mult(((ep_config.max_packet_size >> 11) & 0x3) as u8 + 1); + } + + // Open endpoint + self.dcd_endpoint_open(ep_config); + } + + fn dcd_endpoint_open(&mut self, ep_config: EpConfig) { + let r = &self.info.regs; + + let ep_num = ep_config.ep_addr.ep_num(); + let dir = ep_config.ep_addr.dir(); + + // Enable EP control + r.endptctrl(ep_num as usize).modify(|w| { + // Clear the RXT or TXT bits + if dir { + w.set_txt(0); + w.set_txe(true); + w.set_txr(true); + // TODO: Better impl? For example, make transfer a bitfield struct + w.0 |= (ep_config.transfer as u32) << 18; + } else { + w.set_rxt(0); + w.set_rxe(true); + w.set_rxr(true); + w.0 |= (ep_config.transfer as u32) << 2; + } + }); + } + + fn endpoint_get_type(&mut self, ep_addr: EpAddr) -> u8 { + let r = &self.info.regs; + + if ep_addr.dir() { + r.endptctrl(ep_addr.ep_num() as usize).read().txt() + } else { + r.endptctrl(ep_addr.ep_num() as usize).read().rxt() + } + } + + fn endpoint_transfer(&mut self, ep_idx: u8) { + let offset = if ep_idx % 2 == 1 { ep_idx / 2 + 16 } else { ep_idx / 2 }; + + let r = &self.info.regs; + + r.endptprime().write_value(Endptprime(1 << offset)); + } + + fn endpoint_stall(&mut self, ep_addr: EpAddr) { + let r = &self.info.regs; + + if ep_addr.dir() { + r.endptctrl(ep_addr.ep_num() as usize).modify(|w| w.set_txs(true)); + } else { + r.endptctrl(ep_addr.ep_num() as usize).modify(|w| w.set_rxs(true)); + } + } + + fn endpoint_clean_stall(&mut self, ep_addr: EpAddr) { + let r = &self.info.regs; + + r.endptctrl(ep_addr.ep_num() as usize).modify(|w| { + if ep_addr.dir() { + // Data toggle also need to be reset + w.set_txr(true); + w.set_txs(false); + } else { + w.set_rxr(true); + w.set_rxs(false); + } + }); + } + + fn dcd_endpoint_check_stall(&mut self, ep_addr: EpAddr) -> bool { + let r = &self.info.regs; + + if ep_addr.dir() { + r.endptctrl(ep_addr.ep_num() as usize).read().txs() + } else { + r.endptctrl(ep_addr.ep_num() as usize).read().rxs() + } + } + + fn endpoint_close(&mut self, ep_addr: EpAddr) { + let r = &self.info.regs; + + let ep_bit = 1 << ep_addr.ep_num(); + + // Flush the endpoint first + if ep_addr.dir() { + loop { + r.endptflush().modify(|w| w.set_fetb(ep_bit)); + while (r.endptflush().read().fetb() & ep_bit) == 1 {} + if r.endptstat().read().etbr() & ep_bit == 0 { + break; + } + } + } else { + loop { + r.endptflush().modify(|w| w.set_ferb(ep_bit)); + while (r.endptflush().read().ferb() & ep_bit) == 1 {} + if r.endptstat().read().erbr() & ep_bit == 0 { + break; + } + } + } + + // Disable endpoint + r.endptctrl(ep_addr.ep_num() as usize).write(|w| { + if ep_addr.dir() { + w.set_txt(0); + w.set_txe(false); + w.set_txs(false); + } else { + w.set_rxt(0); + w.set_rxe(false); + w.set_rxs(false); + } + }); + // Set transfer type back to ANY type other than control + r.endptctrl(ep_addr.ep_num() as usize).write(|w| { + if ep_addr.dir() { + w.set_txt(TransferType::Bulk as u8); + } else { + w.set_rxt(TransferType::Bulk as u8); + } + }); + } +} diff --git a/src/usb/hcd.rs b/src/usb/hcd.rs new file mode 100644 index 0000000..331bca5 --- /dev/null +++ b/src/usb/hcd.rs @@ -0,0 +1,44 @@ +use super::Usb; + +impl Usb { + fn hcd_init(&mut self, int_mask: u32, framelist_size: u16) -> bool { + let r = &self.info.regs; + + // hcd framelist max element is 1024 + if framelist_size > 1024 || framelist_size == 0 { + return false; + } + + let framelist_size_bf = 10 - get_first_set_bit_from_lsb(framelist_size as u32) as u8; + + if framelist_size != (1 << get_first_set_bit_from_lsb(framelist_size as u32)) { + return false; + } + + self.phy_init(); + + // Reset controller + r.usbcmd().modify(|w| w.set_rst(true)); + while r.usbcmd().read().rst() {} + + todo!() + } + + fn hcd_port_reset(&mut self) { + todo!() + } +} + +// Helper function + +fn get_first_set_bit_from_lsb(mut value: u32) -> u32 { + let mut i = 0; + if value == 0 { + return u32::MAX; + } + while value > 0 && (value & 1) == 0 { + value = value >> 1; + i += 1; + } + i +} diff --git a/src/usb/mod.rs b/src/usb/mod.rs index f2aeee5..fcaf219 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -1,10 +1,14 @@ use bitfield_struct::bitfield; use embedded_hal::delay::DelayNs; use riscv::delay::McycleDelay; -use xhci; use crate::pac::usb::regs::*; +mod dcd; +mod device; +mod endpoint; +mod hcd; + #[cfg(usb_v67)] const ENDPOINT_COUNT: u8 = 8; #[cfg(usb_v53)] @@ -14,6 +18,141 @@ const ENDPOINT_COUNT: u8 = 16; pub struct Usb { info: &'static Info, delay: McycleDelay, + dcd_data: DcdData, +} + +pub struct DcdData { + /// Queue head + pub(crate) qhd: [QueueHead; ENDPOINT_COUNT as usize * 2], + /// Queue element transfer descriptor + pub(crate) qtd: [QueueTransferDescriptor; ENDPOINT_COUNT as usize * 2 * 8], +} + +impl Default for DcdData { + fn default() -> Self { + Self { + qhd: [QueueHead::default(); ENDPOINT_COUNT as usize * 2], + qtd: [QueueTransferDescriptor::default(); ENDPOINT_COUNT as usize * 2 * 8], + } + } +} + +#[derive(Clone, Copy, Default)] +pub(crate) struct QueueHead { + // Capabilities and characteristics + pub(crate) cap: CapabilityAndCharacteristics, + // Current qTD pointer + // TODO: use index? + pub(crate) qtd_addr: u32, + + // Transfer overlay + pub(crate) qtd_overlay: QueueTransferDescriptor, + + pub(crate) setup_request: ControlRequest, + + // Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes + // thus there are 16 bytes padding free that we can make use of. + // TODO: Check memory layout + _reserved: [u8; 16], +} + +#[bitfield(u64)] +pub(crate) struct ControlRequest { + #[bits(8)] + request_type: u8, + #[bits(8)] + request: u8, + #[bits(16)] + value: u16, + #[bits(16)] + index: u16, + #[bits(16)] + length: u16, +} + +#[bitfield(u32)] +pub(crate) struct CapabilityAndCharacteristics { + #[bits(15)] + /// Number of packets executed per transaction descriptor. + /// + /// - 00: Execute N transactions as demonstrated by the + /// USB variable length protocol where N is computed using + /// Max_packet_length and the Total_bytes field in the dTD + /// - 01: Execute one transaction + /// - 10: Execute two transactions + /// - 11: Execute three transactions + /// + /// Remark: Non-isochronous endpoints must set MULT = 00. + /// + /// Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed. + num_packets_per_td: u16, + + /// Interrupt on setup. + /// + /// This bit is used on control type endpoints to indicate if + /// USBINT is set in response to a setup being received. + #[bits(1)] + int_on_step: bool, + + #[bits(11)] + max_packet_size: u16, + + #[bits(2)] + _reserved: u8, + + #[bits(1)] + zero_length_termination: bool, + + #[bits(2)] + iso_mult: u8, +} +#[derive(Clone, Copy, Default)] +struct QueueTransferDescriptor { + // Next point + // TODO: use index? + next: u32, + + token: QueueTransferDescriptorToken, + + /// Buffer Page Pointer List + /// + /// Each element in the list is a 4K page aligned, physical memory address. + /// The lower 12 bits in each pointer are reserved (except for the first one) + /// as each memory pointer must reference the start of a 4K page + buffer: [u32; 5], + + /// DCD Area + expected_bytes: u16, + + _reserved: [u8; 2], +} + +#[bitfield(u32)] +struct QueueTransferDescriptorToken { + #[bits(3)] + _r1: u8, + #[bits(1)] + xact_err: bool, + #[bits(1)] + _r2: bool, + #[bits(1)] + buffer_err: bool, + #[bits(1)] + halted: bool, + #[bits(1)] + active: bool, + #[bits(2)] + _r3: u8, + #[bits(2)] + iso_mult_override: u8, + #[bits(3)] + _r4: u8, + #[bits(1)] + int_on_complete: bool, + #[bits(15)] + total_bytes: u16, + #[bits(1)] + _r5: bool, } pub struct EpConfig { @@ -101,178 +240,6 @@ impl Usb { w.set_utmi_otg_suspendm(false); }); } - - fn dcd_bus_reset(&mut self) { - let r = &self.info.regs; - - // For each endpoint, first set the transfer type to ANY type other than control. - // This is because the default transfer type is control, according to hpm_sdk, - // leaving an un-configured endpoint control will cause undefined behavior - // for the data PID tracking on the active endpoint. - for i in 0..ENDPOINT_COUNT { - r.endptctrl(i as usize).write(|w| { - w.set_txt(TransferType::Bulk as u8); - w.set_rxt(TransferType::Bulk as u8); - }); - } - - // Clear all registers - // TODO: CHECK: In hpm_sdk, are those registers REALLY cleared? - r.endptnak().write_value(Endptnak::default()); - r.endptnaken().write_value(Endptnaken(0)); - r.usbsts().write_value(Usbsts::default()); - r.endptsetupstat().write_value(Endptsetupstat::default()); - r.endptcomplete().write_value(Endptcomplete::default()); - - while r.endptprime().read().0 != 0 {} - - r.endptflush().write_value(Endptflush(0xFFFFFFFF)); - - while r.endptflush().read().0 != 0 {} - } - - /// Initialize USB device controller driver - fn dcd_init(&mut self) { - // Initialize phy first - self.phy_init(); - - let r = &self.info.regs; - - // Reset controller - r.usbcmd().modify(|w| w.set_rst(true)); - while r.usbcmd().read().rst() {} - - // Set mode to device IMMEDIATELY after reset - r.usbmode().modify(|w| w.set_cm(0b10)); - - r.usbmode().modify(|w| { - // Set little endian - w.set_es(false); - // Disable setup lockout, please refer to "Control Endpoint Operation" section in RM - w.set_slom(false); - }); - - r.portsc1().modify(|w| { - // Parallel interface signal - w.set_sts(false); - // Parallel transceiver width - w.set_ptw(false); - // TODO: Set fullspeed mode - // w.set_pfsc(true); - }); - - // Do not use interrupt threshold - r.usbcmd().modify(|w| { - w.set_itc(0); - }); - - // Enable VBUS discharge - r.otgsc().modify(|w| { - w.set_vd(true); - }); - } - - /// Deinitialize USB device controller driver - fn dcd_deinit(&mut self) { - let r = &self.info.regs; - - // Stop first - r.usbcmd().modify(|w| w.set_rs(false)); - - // Reset controller - r.usbcmd().modify(|w| w.set_rst(true)); - while r.usbcmd().read().rst() {} - - // Disable phy - self.phy_deinit(); - - // Reset endpoint list address register, status register and interrupt enable register - r.endptlistaddr().write_value(Endptlistaddr(0)); - r.usbsts().write_value(Usbsts::default()); - r.usbintr().write_value(Usbintr(0)); - } - - /// Connect by enabling internal pull-up resistor on D+/D- - fn dcd_connect(&mut self) { - let r = &self.info.regs; - - r.usbcmd().modify(|w| { - w.set_rs(true); - }); - } - - /// Disconnect by disabling internal pull-up resistor on D+/D- - fn dcd_disconnect(&mut self) { - let r = &self.info.regs; - - // Stop - r.usbcmd().modify(|w| { - w.set_rs(false); - }); - - // Pullup DP to make the phy switch into full speed mode - r.usbcmd().modify(|w| { - w.set_rs(true); - }); - - // Clear sof flag and wait - r.usbsts().modify(|w| { - w.set_sri(true); - }); - while r.usbsts().read().sri() == false {} - - // Disconnect - r.usbcmd().modify(|w| { - w.set_rs(false); - }); - } -} - -impl Usb { - fn endpoint_open(&mut self, ep_config: EpConfig) { - let r = &self.info.regs; - - let ep_num = ep_config.ep_addr.ep_num(); - let dir = ep_config.ep_addr.dir(); - let ep_idx = 2 * ep_num + dir as u8; - - // Max EP count: 16 - if ep_num >= ENDPOINT_COUNT { - // TODO: return false - } - - // Prepare queue head - // TODO - let link = xhci::ring::trb::Link::new(); - - // Open endpoint - self.dcd_endpoint_open(ep_config); - } - - fn dcd_endpoint_open(&mut self, ep_config: EpConfig) { - let r = &self.info.regs; - - let ep_num = ep_config.ep_addr.ep_num(); - let dir = ep_config.ep_addr.dir(); - let ep_idx = 2 * ep_num + dir as u8; - - // Enable EP control - r.endptctrl(ep_num as usize).modify(|w| { - // Clear the RXT or TXT bits - if dir { - w.set_txt(0); - w.set_txe(true); - w.set_txr(true); - // TODO: Better impl? For example, make transfer a bitfield struct - w.0 |= (ep_config.transfer as u32) << 18; - } else { - w.set_rxt(0); - w.set_rxe(true); - w.set_rxr(true); - w.0 |= (ep_config.transfer as u32) << 2; - } - }); - } } struct Info { From fe0dde370cfe65a6f849dd4c559a036280fbdbb0 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Mon, 8 Jul 2024 23:37:33 +0800 Subject: [PATCH 03/43] wip(usb): add device transfer Signed-off-by: HaoboGu --- src/usb/device.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++- src/usb/endpoint.rs | 4 +- src/usb/mod.rs | 15 ++++++-- 3 files changed, 100 insertions(+), 8 deletions(-) diff --git a/src/usb/device.rs b/src/usb/device.rs index f1f4f82..c7d7221 100644 --- a/src/usb/device.rs +++ b/src/usb/device.rs @@ -1,14 +1,16 @@ //! Usb device API //! -use super::{DcdData, QueueHead, QueueTransferDescriptor, Usb}; +use super::{ + DcdData, EpAddr, Error, QueueHead, QueueTransferDescriptor, Usb, QHD_BUFFER_COUNT, QTD_COUNT_EACH_ENDPOINT, +}; impl Usb { fn device_qhd_get(&self, ep_idx: u8) -> &QueueHead { &self.dcd_data.qhd[ep_idx as usize] } - fn device_qtd_get4(&self, ep_idx: u8) -> &QueueTransferDescriptor { + fn device_qtd_get(&self, ep_idx: u8) -> &QueueTransferDescriptor { &self.dcd_data.qtd[ep_idx as usize * 8] } @@ -55,4 +57,87 @@ impl Usb { // Connect r.usbcmd().modify(|w| w.set_rs(true)); } + + fn device_endpoint_transfer(&mut self, ep_addr: EpAddr, data: &[u8]) -> Result<(), Error> { + let r = &self.info.regs; + + let ep_num = ep_addr.ep_num(); + let dir = ep_addr.dir(); + let ep_idx = (2 * ep_num + dir as u8) as usize; + + // Setup packet handling using setup lockout mechanism + // wait until ENDPTSETUPSTAT before priming data/status in response + if ep_num == 0 { + while (r.endptsetupstat().read().endptsetupstat() & 0b1) == 1 {} + } + + // + let qtd_num = (data.len() + 0x3FFF) / 0x4000; + if qtd_num > 8 { + return Err(Error::InvalidQtdNum); + } + + // Add all data to the circular queue + let ptr_qhd = &self.dcd_data.qhd[ep_idx]; + let mut i = 0; + let mut data_offset = 0; + let mut remaining_bytes = data.len(); + loop { + let mut ptr_qtd = self.dcd_data.qtd[ep_idx * QTD_COUNT_EACH_ENDPOINT + i]; + i += 1; + + let transfer_bytes = if remaining_bytes > 0x4000 { + remaining_bytes -= 0x4000; + 0x4000 + } else { + remaining_bytes = 0; + data.len() + }; + + // TODO: qtd init + ptr_qtd = QueueTransferDescriptor::default(); + ptr_qtd.token.set_active(true); + ptr_qtd.token.set_total_bytes(transfer_bytes as u16); + ptr_qtd.expected_bytes = transfer_bytes as u16; + // Fill data into qtd + ptr_qtd.buffer[0] = data[data_offset] as u32; + for i in 1..QHD_BUFFER_COUNT { + // TODO: WHY the buffer is filled in this way? + ptr_qtd.buffer[i] |= (ptr_qtd.buffer[i - 1] & 0xFFFFF000) + 4096; + } + + if remaining_bytes == 0 { + ptr_qtd.token.set_int_on_complete(true); + } + + data_offset += transfer_bytes; + + // Linked list operations + // Set circular link + if i == 1 { + // Set the FIRST qtd + // first_ptr_qtd = ptr_qtd; + } else { + // Set prev_ptr's next to current + // prev_ptr_qtd.next = &ptr_qtd as *const _ as u32; + } + + // Update prev_ptr_qtd to current + // prev_ptr_qtd = &ptr_qtd; + + + // Check the remaining_bytes + if remaining_bytes == 0 { + break; + } + } + + // Set current qhd's overlay to the first qtd of linked list + // ptr_qhd.qtd_overlay.next = first_ptr_qtd as u32; + + // Then call dcd_endpoint_transfer + self.endpoint_transfer(ep_idx as u8); + + Ok(()) + } } diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index 6d4f1c5..cb0fb6f 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -11,7 +11,7 @@ impl Usb { let ep_idx = 2 * ep_num + dir as u8; // Max EP count: 16 - if ep_num >= ENDPOINT_COUNT { + if ep_num >= ENDPOINT_COUNT as u8 { // TODO: return false } @@ -66,7 +66,7 @@ impl Usb { } } - fn endpoint_transfer(&mut self, ep_idx: u8) { + pub(crate) fn endpoint_transfer(&mut self, ep_idx: u8) { let offset = if ep_idx % 2 == 1 { ep_idx / 2 + 16 } else { ep_idx / 2 }; let r = &self.info.regs; diff --git a/src/usb/mod.rs b/src/usb/mod.rs index fcaf219..7bd4943 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -10,9 +10,12 @@ mod endpoint; mod hcd; #[cfg(usb_v67)] -const ENDPOINT_COUNT: u8 = 8; +const ENDPOINT_COUNT: usize = 8; #[cfg(usb_v53)] -const ENDPOINT_COUNT: u8 = 16; +const ENDPOINT_COUNT: usize = 16; + +const QTD_COUNT_EACH_ENDPOINT: usize = 8; +const QHD_BUFFER_COUNT: usize = 5; #[allow(unused)] pub struct Usb { @@ -25,7 +28,7 @@ pub struct DcdData { /// Queue head pub(crate) qhd: [QueueHead; ENDPOINT_COUNT as usize * 2], /// Queue element transfer descriptor - pub(crate) qtd: [QueueTransferDescriptor; ENDPOINT_COUNT as usize * 2 * 8], + pub(crate) qtd: [QueueTransferDescriptor; ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize], } impl Default for DcdData { @@ -119,7 +122,7 @@ struct QueueTransferDescriptor { /// Each element in the list is a 4K page aligned, physical memory address. /// The lower 12 bits in each pointer are reserved (except for the first one) /// as each memory pointer must reference the start of a 4K page - buffer: [u32; 5], + buffer: [u32; QHD_BUFFER_COUNT], /// DCD Area expected_bytes: u16, @@ -242,6 +245,10 @@ impl Usb { } } +pub enum Error { + InvalidQtdNum, +} + struct Info { regs: crate::pac::usb::Usb, } From 1bd49c61e553d5d74fea6ccdecb82374d0478253 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Fri, 12 Jul 2024 22:59:19 +0800 Subject: [PATCH 04/43] feat(usb): add more usb low level driver function Signed-off-by: HaoboGu --- src/usb/dcd.rs | 14 +++++++++++++- src/usb/device.rs | 10 ++++++++++ src/usb/endpoint.rs | 18 ++++++++++++++---- src/usb/hcd.rs | 3 +++ src/usb/host.rs | 22 ++++++++++++++++++++++ src/usb/mod.rs | 18 ++++++++++++++++-- 6 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 src/usb/host.rs diff --git a/src/usb/dcd.rs b/src/usb/dcd.rs index 446055e..a725d13 100644 --- a/src/usb/dcd.rs +++ b/src/usb/dcd.rs @@ -1,3 +1,6 @@ +//! Device controller driver for USB peripheral +//! + use hpm_metapac::usb::regs::*; use super::{TransferType, Usb, ENDPOINT_COUNT}; @@ -73,8 +76,17 @@ impl Usb { }); } + pub(crate) fn dcd_set_address(&mut self, addr: u8) { + let r = &self.info.regs; + + r.deviceaddr().modify(|w| { + w.set_usbadr(addr); + w.set_usbadra(true); + }); + } + /// Deinitialize USB device controller driver - fn dcd_deinit(&mut self) { + pub(crate) fn dcd_deinit(&mut self) { let r = &self.info.regs; // Stop first diff --git a/src/usb/device.rs b/src/usb/device.rs index c7d7221..b106f78 100644 --- a/src/usb/device.rs +++ b/src/usb/device.rs @@ -35,7 +35,9 @@ impl Usb { self.dcd_data.qhd[0].cap.set_int_on_step(true); } + // Used in `usb_dc_init` fn device_init(&mut self, int_mask: u32) { + // Clear dcd data first self.dcd_data = DcdData::default(); @@ -58,6 +60,10 @@ impl Usb { r.usbcmd().modify(|w| w.set_rs(true)); } + fn device_deinit(&mut self) { + self.dcd_deinit(); + } + fn device_endpoint_transfer(&mut self, ep_addr: EpAddr, data: &[u8]) -> Result<(), Error> { let r = &self.info.regs; @@ -140,4 +146,8 @@ impl Usb { Ok(()) } + + pub(crate) fn device_endpoint_close(&mut self, ep_addr: EpAddr) { + self.dcd_endpoint_close(ep_addr); + } } diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index cb0fb6f..e99c22d 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -3,7 +3,7 @@ use hpm_metapac::usb::regs::Endptprime; use super::{EpAddr, EpConfig, QueueHead, TransferType, Usb, ENDPOINT_COUNT}; impl Usb { - fn endpoint_open(&mut self, ep_config: EpConfig) { + fn device_endpoint_open(&mut self, ep_config: EpConfig) { let r = &self.info.regs; let ep_num = ep_config.ep_addr.ep_num(); @@ -74,7 +74,7 @@ impl Usb { r.endptprime().write_value(Endptprime(1 << offset)); } - fn endpoint_stall(&mut self, ep_addr: EpAddr) { + fn device_endpoint_stall(&mut self, ep_addr: EpAddr) { let r = &self.info.regs; if ep_addr.dir() { @@ -84,7 +84,7 @@ impl Usb { } } - fn endpoint_clean_stall(&mut self, ep_addr: EpAddr) { + fn device_endpoint_clean_stall(&mut self, ep_addr: EpAddr) { let r = &self.info.regs; r.endptctrl(ep_addr.ep_num() as usize).modify(|w| { @@ -109,7 +109,7 @@ impl Usb { } } - fn endpoint_close(&mut self, ep_addr: EpAddr) { + pub(crate) fn dcd_endpoint_close(&mut self, ep_addr: EpAddr) { let r = &self.info.regs; let ep_bit = 1 << ep_addr.ep_num(); @@ -154,4 +154,14 @@ impl Usb { } }); } + + pub(crate) fn ep_is_stalled(&mut self, ep_addr: EpAddr) -> bool { + let r = &self.info.regs; + + if ep_addr.dir() { + r.endptctrl(ep_addr.ep_num() as usize).read().txs() + } else { + r.endptctrl(ep_addr.ep_num() as usize).read().rxs() + } + } } diff --git a/src/usb/hcd.rs b/src/usb/hcd.rs index 331bca5..ade86f4 100644 --- a/src/usb/hcd.rs +++ b/src/usb/hcd.rs @@ -1,3 +1,6 @@ +//! Host controller driver for USB peripheral +//! + use super::Usb; impl Usb { diff --git a/src/usb/host.rs b/src/usb/host.rs new file mode 100644 index 0000000..868f816 --- /dev/null +++ b/src/usb/host.rs @@ -0,0 +1,22 @@ +use super::Usb; + +impl Usb { + fn host_init(&mut self) { + let r = &self.info.regs; + + r.usbmode().modify(|w| { + // Set mode to host, must be done IMMEDIATELY after reset + w.set_cm(0b11); + + // Little endian + w.set_es(false); + }); + + r.portsc1().modify(|w| { + w.set_sts(false); + w.set_ptw(false); + }); + + r.usbcmd().modify(|w| w.set_itc(0)); + } +} diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 7bd4943..a6988a7 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -8,6 +8,7 @@ mod dcd; mod device; mod endpoint; mod hcd; +mod host; #[cfg(usb_v67)] const ENDPOINT_COUNT: usize = 8; @@ -24,10 +25,13 @@ pub struct Usb { dcd_data: DcdData, } +#[repr(C, align(32))] pub struct DcdData { /// Queue head + /// NON-CACHABLE pub(crate) qhd: [QueueHead; ENDPOINT_COUNT as usize * 2], /// Queue element transfer descriptor + /// NON-CACHABLE pub(crate) qtd: [QueueTransferDescriptor; ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize], } @@ -41,11 +45,11 @@ impl Default for DcdData { } #[derive(Clone, Copy, Default)] +#[repr(C, align(32))] pub(crate) struct QueueHead { // Capabilities and characteristics pub(crate) cap: CapabilityAndCharacteristics, // Current qTD pointer - // TODO: use index? pub(crate) qtd_addr: u32, // Transfer overlay @@ -110,9 +114,9 @@ pub(crate) struct CapabilityAndCharacteristics { iso_mult: u8, } #[derive(Clone, Copy, Default)] +#[repr(C, align(32))] struct QueueTransferDescriptor { // Next point - // TODO: use index? next: u32, token: QueueTransferDescriptorToken, @@ -243,6 +247,16 @@ impl Usb { w.set_utmi_otg_suspendm(false); }); } + + /// Get port speed: 00: full speed, 01: low speed, 10: high speed, 11: undefined + /// TODO: Use enum + pub(crate) fn get_port_speed(&mut self) -> u8 { + let r = &self.info.regs; + + r.portsc1().read().pspd() + } + + } pub enum Error { From bd619dd89243a1b67834e5acdb234904a222c371 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Tue, 16 Jul 2024 18:24:03 +0800 Subject: [PATCH 05/43] feat(usb): impl usb driver in embassy Signed-off-by: Haobo Gu --- Cargo.toml | 1 + src/usb/bus.rs | 58 ++++++++++ src/usb/control_pipe.rs | 41 +++++++ src/usb/dcd.rs | 13 +-- src/usb/device.rs | 23 ++-- src/usb/endpoint.rs | 114 +++++++++++++------- src/usb/hcd.rs | 8 +- src/usb/host.rs | 6 +- src/usb/mod.rs | 230 ++++++++++++++++++++++++++++++++++++---- 9 files changed, 407 insertions(+), 87 deletions(-) create mode 100644 src/usb/bus.rs create mode 100644 src/usb/control_pipe.rs diff --git a/Cargo.toml b/Cargo.toml index e963cf4..99ee875 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ embassy-sync = { version = "0.6.0", optional = true } embassy-futures = { version = "0.1.1", optional = true } embassy-hal-internal = { version = "0.2.0", default-features = false } embassy-time = { version = "0.3.0", optional = true } +embassy-usb-driver = { version = "0.1.0", features = ["defmt"] } critical-section = "1.1.2" riscv-rt = { version = "0.12.2", optional = true } diff --git a/src/usb/bus.rs b/src/usb/bus.rs new file mode 100644 index 0000000..9d94e42 --- /dev/null +++ b/src/usb/bus.rs @@ -0,0 +1,58 @@ +use embassy_usb_driver::{EndpointAddress, Event, Unsupported}; + +use crate::usb::EpConfig; + +use super::Bus; + +impl embassy_usb_driver::Bus for Bus { + async fn enable(&mut self) { + // TODO: dcd init or phy init? + self.dcd_init(); + // self.phy_init(); + + // s + self.dcd_connect(); + } + + async fn disable(&mut self) { + // TODO: dcd deinit or phy deinit? + self.dcd_deinit(); + // self.phy_deinit(); + } + + async fn poll(&mut self) -> Event { + todo!() + } + + fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { + let ep_config = EpConfig { + transfer: todo!(), + ep_addr, + max_packet_size: todo!(), + }; + if enabled { + self.device_endpoint_open( + ep_config + ); + } else { + self.device_endpoint_close(ep_addr); + } + todo!() + } + + fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { + if stalled { + self.device_endpoint_stall(ep_addr); + } else { + self.device_endpoint_clean_stall(ep_addr); + } + } + + fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { + self.dcd_endpoint_check_stall(ep_addr) + } + + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + todo!() + } +} diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs new file mode 100644 index 0000000..38a7cdd --- /dev/null +++ b/src/usb/control_pipe.rs @@ -0,0 +1,41 @@ +use super::endpoint::Endpoint; + + + + + +pub(crate) struct ControlPipe { + pub(crate) max_packet_size: usize, + pub(crate) ep_in: Endpoint, + pub(crate) ep_out: Endpoint, +} + +impl embassy_usb_driver::ControlPipe for ControlPipe { + fn max_packet_size(&self) -> usize { + self.max_packet_size + } + + async fn setup(&mut self) -> [u8; 8] { + todo!() + } + + async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result { + todo!() + } + + async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), embassy_usb_driver::EndpointError> { + todo!() + } + + async fn accept(&mut self) { + todo!() + } + + async fn reject(&mut self) { + todo!() + } + + async fn accept_set_address(&mut self, addr: u8) { + todo!() + } +} \ No newline at end of file diff --git a/src/usb/dcd.rs b/src/usb/dcd.rs index a725d13..04d947c 100644 --- a/src/usb/dcd.rs +++ b/src/usb/dcd.rs @@ -1,11 +1,12 @@ //! Device controller driver for USB peripheral //! +use embassy_usb_driver::EndpointType; use hpm_metapac::usb::regs::*; -use super::{TransferType, Usb, ENDPOINT_COUNT}; +use super::{Bus, ENDPOINT_COUNT}; -impl Usb { +impl Bus { pub(crate) fn dcd_bus_reset(&mut self) { let r = &self.info.regs; @@ -15,8 +16,8 @@ impl Usb { // for the data PID tracking on the active endpoint. for i in 0..ENDPOINT_COUNT { r.endptctrl(i as usize).write(|w| { - w.set_txt(TransferType::Bulk as u8); - w.set_rxt(TransferType::Bulk as u8); + w.set_txt(EndpointType::Bulk as u8); + w.set_rxt(EndpointType::Bulk as u8); }); } @@ -106,7 +107,7 @@ impl Usb { } /// Connect by enabling internal pull-up resistor on D+/D- - fn dcd_connect(&mut self) { + pub(crate) fn dcd_connect(&mut self) { let r = &self.info.regs; r.usbcmd().modify(|w| { @@ -115,7 +116,7 @@ impl Usb { } /// Disconnect by disabling internal pull-up resistor on D+/D- - fn dcd_disconnect(&mut self) { + pub(crate) fn dcd_disconnect(&mut self) { let r = &self.info.regs; // Stop diff --git a/src/usb/device.rs b/src/usb/device.rs index b106f78..19dc73c 100644 --- a/src/usb/device.rs +++ b/src/usb/device.rs @@ -2,19 +2,19 @@ //! use super::{ - DcdData, EpAddr, Error, QueueHead, QueueTransferDescriptor, Usb, QHD_BUFFER_COUNT, QTD_COUNT_EACH_ENDPOINT, + DcdData, EndpointAddress, Error, QueueHead, QueueTransferDescriptor, Bus, QHD_BUFFER_COUNT, QTD_COUNT_EACH_ENDPOINT, }; -impl Usb { - fn device_qhd_get(&self, ep_idx: u8) -> &QueueHead { +impl Bus { + pub(crate) fn device_qhd_get(&self, ep_idx: u8) -> &QueueHead { &self.dcd_data.qhd[ep_idx as usize] } - fn device_qtd_get(&self, ep_idx: u8) -> &QueueTransferDescriptor { + pub(crate) fn device_qtd_get(&self, ep_idx: u8) -> &QueueTransferDescriptor { &self.dcd_data.qtd[ep_idx as usize * 8] } - fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { + pub(crate) fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { let r = &self.info.regs; self.dcd_bus_reset(); @@ -36,7 +36,7 @@ impl Usb { } // Used in `usb_dc_init` - fn device_init(&mut self, int_mask: u32) { + pub(crate) fn device_init(&mut self, int_mask: u32) { // Clear dcd data first self.dcd_data = DcdData::default(); @@ -60,16 +60,15 @@ impl Usb { r.usbcmd().modify(|w| w.set_rs(true)); } - fn device_deinit(&mut self) { + pub(crate) fn device_deinit(&mut self) { self.dcd_deinit(); } - fn device_endpoint_transfer(&mut self, ep_addr: EpAddr, data: &[u8]) -> Result<(), Error> { + pub(crate) fn device_endpoint_transfer(&mut self, ep_addr: EndpointAddress, data: &[u8]) -> Result<(), Error> { let r = &self.info.regs; - let ep_num = ep_addr.ep_num(); - let dir = ep_addr.dir(); - let ep_idx = (2 * ep_num + dir as u8) as usize; + let ep_num = ep_addr.index(); + let ep_idx = 2 * ep_num + ep_addr.is_in() as usize; // Setup packet handling using setup lockout mechanism // wait until ENDPTSETUPSTAT before priming data/status in response @@ -147,7 +146,7 @@ impl Usb { Ok(()) } - pub(crate) fn device_endpoint_close(&mut self, ep_addr: EpAddr) { + pub(crate) fn device_endpoint_close(&mut self, ep_addr: EndpointAddress) { self.dcd_endpoint_close(ep_addr); } } diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index e99c22d..ba45223 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -1,17 +1,52 @@ +use embassy_usb_driver::{Direction, EndpointAddress, EndpointIn, EndpointInfo, EndpointOut, EndpointType}; use hpm_metapac::usb::regs::Endptprime; -use super::{EpAddr, EpConfig, QueueHead, TransferType, Usb, ENDPOINT_COUNT}; +use super::{EpConfig, Info, QueueHead, Bus, ENDPOINT_COUNT}; -impl Usb { - fn device_endpoint_open(&mut self, ep_config: EpConfig) { +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub(crate) struct EndpointAllocInfo { + pub(crate) ep_type: EndpointType, + pub(crate) used_in: bool, + pub(crate) used_out: bool, +} +pub(crate) struct Endpoint { + pub(crate) info: EndpointInfo, + // TODO +} + +impl embassy_usb_driver::Endpoint for Endpoint { + fn info(&self) -> &embassy_usb_driver::EndpointInfo { + todo!() + } + + async fn wait_enabled(&mut self) { + todo!() + } +} + +impl EndpointOut for Endpoint { + async fn read(&mut self, buf: &mut [u8]) -> Result { + todo!() + } +} + +impl EndpointIn for Endpoint { + async fn write(&mut self, buf: &[u8]) -> Result<(), embassy_usb_driver::EndpointError> { + todo!() + } +} + +impl Bus { + + + pub(crate) fn device_endpoint_open(&mut self, ep_config: EpConfig) { let r = &self.info.regs; - let ep_num = ep_config.ep_addr.ep_num(); - let dir = ep_config.ep_addr.dir(); - let ep_idx = 2 * ep_num + dir as u8; + let ep_num = ep_config.ep_addr.index(); + let ep_idx = 2 * ep_num + ep_config.ep_addr.is_in() as usize; // Max EP count: 16 - if ep_num >= ENDPOINT_COUNT as u8 { + if ep_num >= ENDPOINT_COUNT { // TODO: return false } @@ -22,7 +57,7 @@ impl Usb { .cap .set_max_packet_size(ep_config.max_packet_size & 0x7FF); self.dcd_data.qhd[ep_idx as usize].qtd_overlay.next = 1; // Set next to invalid - if ep_config.transfer == TransferType::Isochronous as u8 { + if ep_config.transfer == EndpointType::Isochronous as u8 { self.dcd_data.qhd[ep_idx as usize] .cap .set_iso_mult(((ep_config.max_packet_size >> 11) & 0x3) as u8 + 1); @@ -32,16 +67,15 @@ impl Usb { self.dcd_endpoint_open(ep_config); } - fn dcd_endpoint_open(&mut self, ep_config: EpConfig) { + pub(crate) fn dcd_endpoint_open(&mut self, ep_config: EpConfig) { let r = &self.info.regs; - let ep_num = ep_config.ep_addr.ep_num(); - let dir = ep_config.ep_addr.dir(); + let ep_num = ep_config.ep_addr.index(); // Enable EP control r.endptctrl(ep_num as usize).modify(|w| { // Clear the RXT or TXT bits - if dir { + if ep_config.ep_addr.is_in() { w.set_txt(0); w.set_txe(true); w.set_txr(true); @@ -56,13 +90,13 @@ impl Usb { }); } - fn endpoint_get_type(&mut self, ep_addr: EpAddr) -> u8 { + pub(crate) fn endpoint_get_type(&mut self, ep_addr: EndpointAddress) -> u8 { let r = &self.info.regs; - if ep_addr.dir() { - r.endptctrl(ep_addr.ep_num() as usize).read().txt() + if ep_addr.is_in() { + r.endptctrl(ep_addr.index() as usize).read().txt() } else { - r.endptctrl(ep_addr.ep_num() as usize).read().rxt() + r.endptctrl(ep_addr.index() as usize).read().rxt() } } @@ -74,21 +108,21 @@ impl Usb { r.endptprime().write_value(Endptprime(1 << offset)); } - fn device_endpoint_stall(&mut self, ep_addr: EpAddr) { + pub(crate) fn device_endpoint_stall(&mut self, ep_addr: EndpointAddress) { let r = &self.info.regs; - if ep_addr.dir() { - r.endptctrl(ep_addr.ep_num() as usize).modify(|w| w.set_txs(true)); + if ep_addr.is_in() { + r.endptctrl(ep_addr.index() as usize).modify(|w| w.set_txs(true)); } else { - r.endptctrl(ep_addr.ep_num() as usize).modify(|w| w.set_rxs(true)); + r.endptctrl(ep_addr.index() as usize).modify(|w| w.set_rxs(true)); } } - fn device_endpoint_clean_stall(&mut self, ep_addr: EpAddr) { + pub(crate) fn device_endpoint_clean_stall(&mut self, ep_addr: EndpointAddress) { let r = &self.info.regs; - r.endptctrl(ep_addr.ep_num() as usize).modify(|w| { - if ep_addr.dir() { + r.endptctrl(ep_addr.index() as usize).modify(|w| { + if ep_addr.is_in() { // Data toggle also need to be reset w.set_txr(true); w.set_txs(false); @@ -99,23 +133,23 @@ impl Usb { }); } - fn dcd_endpoint_check_stall(&mut self, ep_addr: EpAddr) -> bool { + pub(crate) fn dcd_endpoint_check_stall(&mut self, ep_addr: EndpointAddress) -> bool { let r = &self.info.regs; - if ep_addr.dir() { - r.endptctrl(ep_addr.ep_num() as usize).read().txs() + if ep_addr.is_in() { + r.endptctrl(ep_addr.index() as usize).read().txs() } else { - r.endptctrl(ep_addr.ep_num() as usize).read().rxs() + r.endptctrl(ep_addr.index() as usize).read().rxs() } } - pub(crate) fn dcd_endpoint_close(&mut self, ep_addr: EpAddr) { + pub(crate) fn dcd_endpoint_close(&mut self, ep_addr: EndpointAddress) { let r = &self.info.regs; - let ep_bit = 1 << ep_addr.ep_num(); + let ep_bit = 1 << ep_addr.index(); // Flush the endpoint first - if ep_addr.dir() { + if ep_addr.is_in() { loop { r.endptflush().modify(|w| w.set_fetb(ep_bit)); while (r.endptflush().read().fetb() & ep_bit) == 1 {} @@ -134,8 +168,8 @@ impl Usb { } // Disable endpoint - r.endptctrl(ep_addr.ep_num() as usize).write(|w| { - if ep_addr.dir() { + r.endptctrl(ep_addr.index() as usize).write(|w| { + if ep_addr.is_in() { w.set_txt(0); w.set_txe(false); w.set_txs(false); @@ -146,22 +180,22 @@ impl Usb { } }); // Set transfer type back to ANY type other than control - r.endptctrl(ep_addr.ep_num() as usize).write(|w| { - if ep_addr.dir() { - w.set_txt(TransferType::Bulk as u8); + r.endptctrl(ep_addr.index() as usize).write(|w| { + if ep_addr.is_in() { + w.set_txt(EndpointType::Bulk as u8); } else { - w.set_rxt(TransferType::Bulk as u8); + w.set_rxt(EndpointType::Bulk as u8); } }); } - pub(crate) fn ep_is_stalled(&mut self, ep_addr: EpAddr) -> bool { + pub(crate) fn ep_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { let r = &self.info.regs; - if ep_addr.dir() { - r.endptctrl(ep_addr.ep_num() as usize).read().txs() + if ep_addr.is_in() { + r.endptctrl(ep_addr.index() as usize).read().txs() } else { - r.endptctrl(ep_addr.ep_num() as usize).read().rxs() + r.endptctrl(ep_addr.index() as usize).read().rxs() } } } diff --git a/src/usb/hcd.rs b/src/usb/hcd.rs index ade86f4..1e719b5 100644 --- a/src/usb/hcd.rs +++ b/src/usb/hcd.rs @@ -1,10 +1,10 @@ //! Host controller driver for USB peripheral //! -use super::Usb; +use super::Bus; -impl Usb { - fn hcd_init(&mut self, int_mask: u32, framelist_size: u16) -> bool { +impl Bus { + pub(crate) fn hcd_init(&mut self, int_mask: u32, framelist_size: u16) -> bool { let r = &self.info.regs; // hcd framelist max element is 1024 @@ -27,7 +27,7 @@ impl Usb { todo!() } - fn hcd_port_reset(&mut self) { + pub(crate) fn hcd_port_reset(&mut self) { todo!() } } diff --git a/src/usb/host.rs b/src/usb/host.rs index 868f816..050be79 100644 --- a/src/usb/host.rs +++ b/src/usb/host.rs @@ -1,7 +1,7 @@ -use super::Usb; +use super::Bus; -impl Usb { - fn host_init(&mut self) { +impl Bus { + pub(crate) fn host_init(&mut self) { let r = &self.info.regs; r.usbmode().modify(|w| { diff --git a/src/usb/mod.rs b/src/usb/mod.rs index a6988a7..d2a782d 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -1,9 +1,18 @@ +use core::marker::PhantomData; + use bitfield_struct::bitfield; +use control_pipe::ControlPipe; +use embassy_hal_internal::{into_ref, Peripheral}; +use embassy_sync::waitqueue::AtomicWaker; +use embassy_usb_driver::{Direction, Driver, EndpointAddress, EndpointAllocError, EndpointInfo, EndpointType}; use embedded_hal::delay::DelayNs; +use endpoint::{Endpoint, EndpointAllocInfo}; use riscv::delay::McycleDelay; use crate::pac::usb::regs::*; +mod bus; +mod control_pipe; mod dcd; mod device; mod endpoint; @@ -19,7 +28,7 @@ const QTD_COUNT_EACH_ENDPOINT: usize = 8; const QHD_BUFFER_COUNT: usize = 5; #[allow(unused)] -pub struct Usb { +pub struct Bus { info: &'static Info, delay: McycleDelay, dcd_data: DcdData, @@ -164,29 +173,11 @@ struct QueueTransferDescriptorToken { pub struct EpConfig { transfer: u8, - ep_addr: EpAddr, + ep_addr: EndpointAddress, max_packet_size: u16, } -#[bitfield(u8)] -struct EpAddr { - #[bits(4)] - ep_num: u8, - #[bits(3)] - _reserved: u8, - #[bits(1)] - dir: bool, -} - -/// Usb transfer type -pub enum TransferType { - Control = 0b00, - Isochronous = 0b01, - Bulk = 0b10, - Interrupt = 0b11, -} - -impl Usb { +impl Bus { fn phy_init(&mut self) { let r = &self.info.regs; @@ -255,14 +246,209 @@ impl Usb { r.portsc1().read().pspd() } +} + +pub struct UsbDriver<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + info: &'static Info, + endpoints: [EndpointAllocInfo; ENDPOINT_COUNT], +} + +impl<'d, T: Instance> UsbDriver<'d, T> { + pub fn new(dp: impl Peripheral

> + 'd, dm: impl Peripheral

> + 'd) -> Self { + into_ref!(dp, dm); + + // suppress "unused" warnings. + let _ = (dp, dm); + + UsbDriver { + phantom: PhantomData, + info: T::info(), + endpoints: [EndpointAllocInfo { + ep_type: EndpointType::Bulk, + used_in: false, + used_out: false, + }; ENDPOINT_COUNT], + } + } + + /// Find the free endpoint + pub(crate) fn find_free_endpoint(&mut self, ep_type: EndpointType, dir: Direction) -> Option { + self.endpoints + .iter_mut() + .enumerate() + .find(|(i, ep)| { + if *i == 0 && ep_type != EndpointType::Control { + return false; // reserved for control pipe + } + let used = ep.used_out || ep.used_in; + let used_dir = match dir { + Direction::Out => ep.used_out, + Direction::In => ep.used_in, + }; + !used || (ep.ep_type == ep_type && !used_dir) + }) + .map(|(i, _)| i) + } +} + +impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { + type EndpointOut = Endpoint; + + type EndpointIn = Endpoint; + + type ControlPipe = ControlPipe; + + type Bus = Bus; + + /// Allocates an OUT endpoint. + /// + /// This method is called by the USB stack to allocate endpoints. + /// It can only be called before [`start`](Self::start) is called. + /// + /// # Arguments + /// + /// * `ep_type` - the endpoint's type. + /// * `max_packet_size` - Maximum packet size in bytes. + /// * `interval_ms` - Polling interval parameter for interrupt endpoints. + fn alloc_endpoint_out( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + ) -> Result { + let ep_idx = self + .find_free_endpoint(ep_type, Direction::Out) + .ok_or(EndpointAllocError)?; + + self.endpoints[ep_idx].used_out = true; + Ok(Endpoint { + info: EndpointInfo { + addr: EndpointAddress::from_parts(ep_idx, Direction::Out), + ep_type, + max_packet_size, + interval_ms, + }, + }) + } + /// Allocates an IN endpoint. + /// + /// This method is called by the USB stack to allocate endpoints. + /// It can only be called before [`start`](Self::start) is called. + /// + /// # Arguments + /// + /// * `ep_type` - the endpoint's type. + /// * `max_packet_size` - Maximum packet size in bytes. + /// * `interval_ms` - Polling interval parameter for interrupt endpoints. + fn alloc_endpoint_in( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + ) -> Result { + let ep_idx = self + .find_free_endpoint(ep_type, Direction::In) + .ok_or(EndpointAllocError)?; + + self.endpoints[ep_idx].used_out = true; + Ok(Endpoint { + info: EndpointInfo { + addr: EndpointAddress::from_parts(ep_idx, Direction::In), + ep_type, + max_packet_size, + interval_ms, + }, + }) + } + /// Start operation of the USB device. + /// + /// This returns the `Bus` and `ControlPipe` instances that are used to operate + /// the USB device. Additionally, this makes all the previously allocated endpoints + /// start operating. + /// + /// This consumes the `Driver` instance, so it's no longer possible to allocate more + /// endpoints. + fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { + // Set control endpoint first + let ep_out = self + .alloc_endpoint_out(EndpointType::Control, control_max_packet_size, 0) + .unwrap(); + let ep_in = self + .alloc_endpoint_in(EndpointType::Control, control_max_packet_size, 0) + .unwrap(); + assert_eq!(ep_out.info.addr.index(), 0); + assert_eq!(ep_in.info.addr.index(), 0); + + // FIXME: Do nothing now, but check whether we should start the usb device here? + // `Bus` has a `enable` function, which enables the USB peri + // But the comment says this function makes all the allocated endpoints **start operating** + // self.dcd_init(); + + ( + Self::Bus { + info: self.info, + dcd_data: todo!(), + delay: todo!(), + }, + Self::ControlPipe { + max_packet_size: control_max_packet_size as usize, + ep_in, + ep_out, + }, + ) + } } pub enum Error { InvalidQtdNum, } -struct Info { +pub(super) struct Info { regs: crate::pac::usb::Usb, } + +// TODO: USB STATE? +struct State { + #[allow(unused)] + waker: AtomicWaker, +} + +impl State { + const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } +} + +peri_trait!( + irqs: [Interrupt], +); + +foreach_peripheral!( + (usb, $inst:ident) => { + #[allow(private_interfaces)] + impl SealedInstance for crate::peripherals::$inst { + fn info() -> &'static Info { + static INFO: Info = Info{ + regs: crate::pac::$inst, + }; + &INFO + } + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } + } + + impl Instance for crate::peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$inst; + } + }; +); + +pin_trait!(DmPin, Instance); +pin_trait!(DpPin, Instance); From d9736d4694def944a207a7816a30f7e0da7582f7 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Thu, 18 Jul 2024 18:43:00 +0800 Subject: [PATCH 06/43] refactor(usb): reorganize usb mod, make dcd data static mut Signed-off-by: Haobo Gu --- src/usb/bus.rs | 250 +++++++++++++++++++++++++++++++++++++--- src/usb/control_pipe.rs | 13 ++- src/usb/dcd.rs | 143 ----------------------- src/usb/device.rs | 127 +++----------------- src/usb/endpoint.rs | 139 ++++++++++++++++------ src/usb/hcd.rs | 2 +- src/usb/mod.rs | 232 ++++++++++++++++++++----------------- 7 files changed, 492 insertions(+), 414 deletions(-) delete mode 100644 src/usb/dcd.rs diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 9d94e42..65e41a8 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -1,45 +1,57 @@ -use embassy_usb_driver::{EndpointAddress, Event, Unsupported}; +use embassy_usb_driver::{EndpointAddress, EndpointType, Event, Unsupported}; +use embedded_hal::delay::DelayNs; +use hpm_metapac::usb::regs::*; +use super::{Bus, ENDPOINT_COUNT}; use crate::usb::EpConfig; -use super::Bus; - impl embassy_usb_driver::Bus for Bus { + /// Enable the USB peripheral. async fn enable(&mut self) { // TODO: dcd init or phy init? self.dcd_init(); // self.phy_init(); - // s + // TODO: + // Set endpoint list address + // Clear status + // Enable interrupt mask self.dcd_connect(); } + /// Disable and powers down the USB peripheral. async fn disable(&mut self) { // TODO: dcd deinit or phy deinit? self.dcd_deinit(); // self.phy_deinit(); } + /// Wait for a bus-related event. + /// + /// This method should asynchronously wait for an event to happen, then + /// return it. See [`Event`] for the list of events this method should return. async fn poll(&mut self) -> Event { todo!() } + /// Enable or disable an endpoint. fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { - let ep_config = EpConfig { - transfer: todo!(), - ep_addr, - max_packet_size: todo!(), - }; if enabled { - self.device_endpoint_open( - ep_config - ); + let ep_data = self.endpoints[ep_addr.index()]; + assert!(ep_data.addr == ep_addr); + self.device_endpoint_open(EpConfig { + transfer: ep_data.ep_type as u8, + ep_addr, + max_packet_size: ep_data.max_packet_size, + }); } else { self.device_endpoint_close(ep_addr); } - todo!() } + /// Set or clear the STALL condition for an endpoint. + /// + /// If the endpoint is an OUT endpoint, it should be prepared to receive data again. fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { if stalled { self.device_endpoint_stall(ep_addr); @@ -48,11 +60,223 @@ impl embassy_usb_driver::Bus for Bus { } } + /// Get whether the STALL condition is set for an endpoint. fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { self.dcd_endpoint_check_stall(ep_addr) } + /// Initiate a remote wakeup of the host by the device. + /// + /// # Errors + /// + /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support + /// remote wakeup or it has not been enabled at creation time. async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { todo!() } } + +impl Bus { + pub(crate) fn phy_init(&mut self) { + let r = &self.info.regs; + + // Enable dp/dm pulldown + // In hpm_sdk, this operation is done by `ptr->PHY_CTRL0 &= ~0x001000E0u`. + // But there's corresponding bits in register, so we write the register directly here. + let phy_ctrl0 = r.phy_ctrl0().read().0 & (!0x001000E0); + r.phy_ctrl0().write_value(PhyCtrl0(phy_ctrl0)); + + r.otg_ctrl0().modify(|w| { + w.set_otg_utmi_suspendm_sw(false); + w.set_otg_utmi_reset_sw(true); + }); + + r.phy_ctrl1().modify(|w| { + w.set_utmi_cfg_rst_n(false); + }); + + // Wait for reset status + while r.otg_ctrl0().read().otg_utmi_reset_sw() {} + + // Set suspend + r.otg_ctrl0().modify(|w| { + w.set_otg_utmi_suspendm_sw(true); + }); + + // Delay at least 1us + self.delay.delay_us(5); + + r.otg_ctrl0().modify(|w| { + // Disable dm/dp wakeup + w.set_otg_wkdpdmchg_en(false); + // Clear reset sw + w.set_otg_utmi_reset_sw(false); + }); + + // OTG utmi clock detection + r.phy_status().modify(|w| w.set_utmi_clk_valid(true)); + while r.phy_status().read().utmi_clk_valid() == false {} + + // Reset and set suspend + r.phy_ctrl1().modify(|w| { + w.set_utmi_cfg_rst_n(true); + w.set_utmi_otg_suspendm(true); + }); + } + + pub(crate) fn phy_deinit(&mut self) { + let r = &self.info.regs; + + r.otg_ctrl0().modify(|w| { + w.set_otg_utmi_suspendm_sw(true); + w.set_otg_utmi_reset_sw(false); + }); + + r.phy_ctrl1().modify(|w| { + w.set_utmi_cfg_rst_n(false); + w.set_utmi_otg_suspendm(false); + }); + } + + /// Get port speed: 00: full speed, 01: low speed, 10: high speed, 11: undefined + /// TODO: Use enum + pub(crate) fn get_port_speed(&mut self) -> u8 { + let r = &self.info.regs; + + r.portsc1().read().pspd() + } + + pub(crate) fn dcd_bus_reset(&mut self) { + let r = &self.info.regs; + + // For each endpoint, first set the transfer type to ANY type other than control. + // This is because the default transfer type is control, according to hpm_sdk, + // leaving an un-configured endpoint control will cause undefined behavior + // for the data PID tracking on the active endpoint. + for i in 0..ENDPOINT_COUNT { + r.endptctrl(i as usize).write(|w| { + w.set_txt(EndpointType::Bulk as u8); + w.set_rxt(EndpointType::Bulk as u8); + }); + } + + // Clear all registers + // TODO: CHECK: In hpm_sdk, are those registers REALLY cleared? + r.endptnak().write_value(Endptnak::default()); + r.endptnaken().write_value(Endptnaken(0)); + r.usbsts().write_value(Usbsts::default()); + r.endptsetupstat().write_value(Endptsetupstat::default()); + r.endptcomplete().write_value(Endptcomplete::default()); + + while r.endptprime().read().0 != 0 {} + + r.endptflush().write_value(Endptflush(0xFFFFFFFF)); + + while r.endptflush().read().0 != 0 {} + } + + /// Initialize USB device controller driver + pub(crate) fn dcd_init(&mut self) { + // Initialize phy first + self.phy_init(); + + let r = &self.info.regs; + + // Reset controller + r.usbcmd().modify(|w| w.set_rst(true)); + while r.usbcmd().read().rst() {} + + // Set mode to device IMMEDIATELY after reset + r.usbmode().modify(|w| w.set_cm(0b10)); + + r.usbmode().modify(|w| { + // Set little endian + w.set_es(false); + // Disable setup lockout, please refer to "Control Endpoint Operation" section in RM + w.set_slom(false); + }); + + r.portsc1().modify(|w| { + // Parallel interface signal + w.set_sts(false); + // Parallel transceiver width + w.set_ptw(false); + // TODO: Set fullspeed mode + // w.set_pfsc(true); + }); + + // Do not use interrupt threshold + r.usbcmd().modify(|w| { + w.set_itc(0); + }); + + // Enable VBUS discharge + r.otgsc().modify(|w| { + w.set_vd(true); + }); + } + + pub(crate) fn dcd_set_address(&mut self, addr: u8) { + let r = &self.info.regs; + + r.deviceaddr().modify(|w| { + w.set_usbadr(addr); + w.set_usbadra(true); + }); + } + + /// Deinitialize USB device controller driver + pub(crate) fn dcd_deinit(&mut self) { + let r = &self.info.regs; + + // Stop first + r.usbcmd().modify(|w| w.set_rs(false)); + + // Reset controller + r.usbcmd().modify(|w| w.set_rst(true)); + while r.usbcmd().read().rst() {} + + // Disable phy + self.phy_deinit(); + + // Reset endpoint list address register, status register and interrupt enable register + r.endptlistaddr().write_value(Endptlistaddr(0)); + r.usbsts().write_value(Usbsts::default()); + r.usbintr().write_value(Usbintr(0)); + } + + /// Connect by enabling internal pull-up resistor on D+/D- + pub(crate) fn dcd_connect(&mut self) { + let r = &self.info.regs; + + r.usbcmd().modify(|w| { + w.set_rs(true); + }); + } + + /// Disconnect by disabling internal pull-up resistor on D+/D- + pub(crate) fn dcd_disconnect(&mut self) { + let r = &self.info.regs; + + // Stop + r.usbcmd().modify(|w| { + w.set_rs(false); + }); + + // Pullup DP to make the phy switch into full speed mode + r.usbcmd().modify(|w| { + w.set_rs(true); + }); + + // Clear sof flag and wait + r.usbsts().modify(|w| { + w.set_sri(true); + }); + while r.usbsts().read().sri() == false {} + + // Disconnect + r.usbcmd().modify(|w| { + w.set_rs(false); + }); + } +} diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 38a7cdd..3e20ce0 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -1,9 +1,5 @@ use super::endpoint::Endpoint; - - - - pub(crate) struct ControlPipe { pub(crate) max_packet_size: usize, pub(crate) ep_in: Endpoint, @@ -19,7 +15,12 @@ impl embassy_usb_driver::ControlPipe for ControlPipe { todo!() } - async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result { + async fn data_out( + &mut self, + buf: &mut [u8], + first: bool, + last: bool, + ) -> Result { todo!() } @@ -38,4 +39,4 @@ impl embassy_usb_driver::ControlPipe for ControlPipe { async fn accept_set_address(&mut self, addr: u8) { todo!() } -} \ No newline at end of file +} diff --git a/src/usb/dcd.rs b/src/usb/dcd.rs deleted file mode 100644 index 04d947c..0000000 --- a/src/usb/dcd.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! Device controller driver for USB peripheral -//! - -use embassy_usb_driver::EndpointType; -use hpm_metapac::usb::regs::*; - -use super::{Bus, ENDPOINT_COUNT}; - -impl Bus { - pub(crate) fn dcd_bus_reset(&mut self) { - let r = &self.info.regs; - - // For each endpoint, first set the transfer type to ANY type other than control. - // This is because the default transfer type is control, according to hpm_sdk, - // leaving an un-configured endpoint control will cause undefined behavior - // for the data PID tracking on the active endpoint. - for i in 0..ENDPOINT_COUNT { - r.endptctrl(i as usize).write(|w| { - w.set_txt(EndpointType::Bulk as u8); - w.set_rxt(EndpointType::Bulk as u8); - }); - } - - // Clear all registers - // TODO: CHECK: In hpm_sdk, are those registers REALLY cleared? - r.endptnak().write_value(Endptnak::default()); - r.endptnaken().write_value(Endptnaken(0)); - r.usbsts().write_value(Usbsts::default()); - r.endptsetupstat().write_value(Endptsetupstat::default()); - r.endptcomplete().write_value(Endptcomplete::default()); - - while r.endptprime().read().0 != 0 {} - - r.endptflush().write_value(Endptflush(0xFFFFFFFF)); - - while r.endptflush().read().0 != 0 {} - } - - /// Initialize USB device controller driver - pub(crate) fn dcd_init(&mut self) { - // Initialize phy first - self.phy_init(); - - let r = &self.info.regs; - - // Reset controller - r.usbcmd().modify(|w| w.set_rst(true)); - while r.usbcmd().read().rst() {} - - // Set mode to device IMMEDIATELY after reset - r.usbmode().modify(|w| w.set_cm(0b10)); - - r.usbmode().modify(|w| { - // Set little endian - w.set_es(false); - // Disable setup lockout, please refer to "Control Endpoint Operation" section in RM - w.set_slom(false); - }); - - r.portsc1().modify(|w| { - // Parallel interface signal - w.set_sts(false); - // Parallel transceiver width - w.set_ptw(false); - // TODO: Set fullspeed mode - // w.set_pfsc(true); - }); - - // Do not use interrupt threshold - r.usbcmd().modify(|w| { - w.set_itc(0); - }); - - // Enable VBUS discharge - r.otgsc().modify(|w| { - w.set_vd(true); - }); - } - - pub(crate) fn dcd_set_address(&mut self, addr: u8) { - let r = &self.info.regs; - - r.deviceaddr().modify(|w| { - w.set_usbadr(addr); - w.set_usbadra(true); - }); - } - - /// Deinitialize USB device controller driver - pub(crate) fn dcd_deinit(&mut self) { - let r = &self.info.regs; - - // Stop first - r.usbcmd().modify(|w| w.set_rs(false)); - - // Reset controller - r.usbcmd().modify(|w| w.set_rst(true)); - while r.usbcmd().read().rst() {} - - // Disable phy - self.phy_deinit(); - - // Reset endpoint list address register, status register and interrupt enable register - r.endptlistaddr().write_value(Endptlistaddr(0)); - r.usbsts().write_value(Usbsts::default()); - r.usbintr().write_value(Usbintr(0)); - } - - /// Connect by enabling internal pull-up resistor on D+/D- - pub(crate) fn dcd_connect(&mut self) { - let r = &self.info.regs; - - r.usbcmd().modify(|w| { - w.set_rs(true); - }); - } - - /// Disconnect by disabling internal pull-up resistor on D+/D- - pub(crate) fn dcd_disconnect(&mut self) { - let r = &self.info.regs; - - // Stop - r.usbcmd().modify(|w| { - w.set_rs(false); - }); - - // Pullup DP to make the phy switch into full speed mode - r.usbcmd().modify(|w| { - w.set_rs(true); - }); - - // Clear sof flag and wait - r.usbsts().modify(|w| { - w.set_sri(true); - }); - while r.usbsts().read().sri() == false {} - - // Disconnect - r.usbcmd().modify(|w| { - w.set_rs(false); - }); - } -} diff --git a/src/usb/device.rs b/src/usb/device.rs index 19dc73c..94c29c4 100644 --- a/src/usb/device.rs +++ b/src/usb/device.rs @@ -1,45 +1,30 @@ //! Usb device API //! -use super::{ - DcdData, EndpointAddress, Error, QueueHead, QueueTransferDescriptor, Bus, QHD_BUFFER_COUNT, QTD_COUNT_EACH_ENDPOINT, -}; +use super::{reset_dcd_data, Bus, DcdData, EndpointAddress, QueueHead, QueueTransferDescriptor, DCD_DATA}; -impl Bus { - pub(crate) fn device_qhd_get(&self, ep_idx: u8) -> &QueueHead { - &self.dcd_data.qhd[ep_idx as usize] - } - - pub(crate) fn device_qtd_get(&self, ep_idx: u8) -> &QueueTransferDescriptor { - &self.dcd_data.qtd[ep_idx as usize * 8] - } +pub(crate) fn device_qhd_get(ep_idx: u8) -> &'static QueueHead { + unsafe { &DCD_DATA.qhd[ep_idx as usize] } +} +pub(crate) fn device_qtd_get(ep_idx: u8) -> &'static QueueTransferDescriptor { + unsafe { &DCD_DATA.qtd[ep_idx as usize * 8] } +} +impl Bus { pub(crate) fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { - let r = &self.info.regs; - self.dcd_bus_reset(); - self.dcd_data = DcdData::default(); - // Setup control endpoints(0 OUT, 1 IN) - self.dcd_data.qhd[0].cap.set_zero_length_termination(true); - self.dcd_data.qhd[1].cap.set_zero_length_termination(true); - self.dcd_data.qhd[0].cap.set_max_packet_size(ep0_max_packet_size); - self.dcd_data.qhd[1].cap.set_max_packet_size(ep0_max_packet_size); - - // Set the next pointer INVALID - // TODO: replacement? - self.dcd_data.qhd[0].qtd_overlay.next = 1; - self.dcd_data.qhd[1].qtd_overlay.next = 1; - - // Set for OUT only - self.dcd_data.qhd[0].cap.set_int_on_step(true); + unsafe { + reset_dcd_data(ep0_max_packet_size); + } } // Used in `usb_dc_init` pub(crate) fn device_init(&mut self, int_mask: u32) { - // Clear dcd data first - self.dcd_data = DcdData::default(); + unsafe { + DCD_DATA = DcdData::default(); + } // Initialize controller self.dcd_init(); @@ -47,7 +32,7 @@ impl Bus { let r = &self.info.regs; // Set endpoint list address // TODO: Check if this is correct - let addr = self.dcd_data.qhd.as_ptr() as u32; + let addr = unsafe { DCD_DATA.qhd.as_ptr() as u32 }; r.endptlistaddr().write(|w| w.set_epbase(addr)); // Clear status @@ -64,88 +49,6 @@ impl Bus { self.dcd_deinit(); } - pub(crate) fn device_endpoint_transfer(&mut self, ep_addr: EndpointAddress, data: &[u8]) -> Result<(), Error> { - let r = &self.info.regs; - - let ep_num = ep_addr.index(); - let ep_idx = 2 * ep_num + ep_addr.is_in() as usize; - - // Setup packet handling using setup lockout mechanism - // wait until ENDPTSETUPSTAT before priming data/status in response - if ep_num == 0 { - while (r.endptsetupstat().read().endptsetupstat() & 0b1) == 1 {} - } - - // - let qtd_num = (data.len() + 0x3FFF) / 0x4000; - if qtd_num > 8 { - return Err(Error::InvalidQtdNum); - } - - // Add all data to the circular queue - let ptr_qhd = &self.dcd_data.qhd[ep_idx]; - let mut i = 0; - let mut data_offset = 0; - let mut remaining_bytes = data.len(); - loop { - let mut ptr_qtd = self.dcd_data.qtd[ep_idx * QTD_COUNT_EACH_ENDPOINT + i]; - i += 1; - - let transfer_bytes = if remaining_bytes > 0x4000 { - remaining_bytes -= 0x4000; - 0x4000 - } else { - remaining_bytes = 0; - data.len() - }; - - // TODO: qtd init - ptr_qtd = QueueTransferDescriptor::default(); - ptr_qtd.token.set_active(true); - ptr_qtd.token.set_total_bytes(transfer_bytes as u16); - ptr_qtd.expected_bytes = transfer_bytes as u16; - // Fill data into qtd - ptr_qtd.buffer[0] = data[data_offset] as u32; - for i in 1..QHD_BUFFER_COUNT { - // TODO: WHY the buffer is filled in this way? - ptr_qtd.buffer[i] |= (ptr_qtd.buffer[i - 1] & 0xFFFFF000) + 4096; - } - - if remaining_bytes == 0 { - ptr_qtd.token.set_int_on_complete(true); - } - - data_offset += transfer_bytes; - - // Linked list operations - // Set circular link - if i == 1 { - // Set the FIRST qtd - // first_ptr_qtd = ptr_qtd; - } else { - // Set prev_ptr's next to current - // prev_ptr_qtd.next = &ptr_qtd as *const _ as u32; - } - - // Update prev_ptr_qtd to current - // prev_ptr_qtd = &ptr_qtd; - - - // Check the remaining_bytes - if remaining_bytes == 0 { - break; - } - } - - // Set current qhd's overlay to the first qtd of linked list - // ptr_qhd.qtd_overlay.next = first_ptr_qtd as u32; - - // Then call dcd_endpoint_transfer - self.endpoint_transfer(ep_idx as u8); - - Ok(()) - } - pub(crate) fn device_endpoint_close(&mut self, ep_addr: EndpointAddress) { self.dcd_endpoint_close(ep_addr); } diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index ba45223..acb66f8 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -1,22 +1,114 @@ use embassy_usb_driver::{Direction, EndpointAddress, EndpointIn, EndpointInfo, EndpointOut, EndpointType}; use hpm_metapac::usb::regs::Endptprime; -use super::{EpConfig, Info, QueueHead, Bus, ENDPOINT_COUNT}; +use super::{ + prepare_qhd, Bus, EpConfig, Error, Info, QueueTransferDescriptor, DCD_DATA, ENDPOINT_COUNT, QHD_BUFFER_COUNT, + QTD_COUNT_EACH_ENDPOINT, +}; -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub(crate) struct EndpointAllocInfo { - pub(crate) ep_type: EndpointType, - pub(crate) used_in: bool, - pub(crate) used_out: bool, -} +// #[derive(Copy, Clone)] pub(crate) struct Endpoint { pub(crate) info: EndpointInfo, - // TODO + pub(crate) usb_info: &'static Info, +} + +impl Endpoint { + pub(crate) fn start_transfer(&mut self) { + let ep_idx = self.info.addr.index(); + + let offset = if ep_idx % 2 == 1 { ep_idx / 2 + 16 } else { ep_idx / 2 }; + + self.usb_info.regs.endptprime().write_value(Endptprime(1 << offset)); + } + + pub(crate) fn transfer(&mut self, data: &[u8]) -> Result<(), Error> { + let r = &self.usb_info.regs; + + let ep_num = self.info.addr.index(); + let ep_idx = 2 * ep_num + self.info.addr.is_in() as usize; + + // Setup packet handling using setup lockout mechanism + // wait until ENDPTSETUPSTAT before priming data/status in response + if ep_num == 0 { + while (r.endptsetupstat().read().endptsetupstat() & 0b1) == 1 {} + } + + let qtd_num = (data.len() + 0x3FFF) / 0x4000; + if qtd_num > 8 { + return Err(Error::InvalidQtdNum); + } + + // Convert data's address, TODO: how to do it in Rust? + // data = core_local_mem_to_sys_address(data); + + // Add all data to the circular queue + let mut prev_qtd: Option = None; + let mut first_qtd: Option = None; + let mut i = 0; + let mut data_offset = 0; + let mut remaining_bytes = data.len(); + loop { + let mut qtd = unsafe { DCD_DATA.qtd[ep_idx * QTD_COUNT_EACH_ENDPOINT + i] }; + i += 1; + + let transfer_bytes = if remaining_bytes > 0x4000 { + remaining_bytes -= 0x4000; + 0x4000 + } else { + remaining_bytes = 0; + data.len() + }; + + // Initialize qtd + qtd = QueueTransferDescriptor::default(); + qtd.token.set_active(true); + qtd.token.set_total_bytes(transfer_bytes as u16); + qtd.expected_bytes = transfer_bytes as u16; + // Fill data into qtd + // FIXME: Fill correct data + qtd.buffer[0] = data.as_ptr() as u32 + 4096 * data_offset; + for i in 1..QHD_BUFFER_COUNT { + // TODO: WHY the buffer is filled in this way? + qtd.buffer[i] |= (qtd.buffer[i - 1] & 0xFFFFF000) + 4096; + } + + // + if remaining_bytes == 0 { + qtd.token.set_int_on_complete(true); + } + + data_offset += transfer_bytes as u32; + + // Linked list operations + // Set circular link + if let Some(mut prev_qtd) = prev_qtd { + prev_qtd.next = &qtd as *const _ as u32; + } else { + first_qtd = Some(qtd); + } + prev_qtd = Some(qtd); + + // Check the remaining_bytes + if remaining_bytes == 0 { + break; + } + } + + // FIXME: update qhd's overlay + unsafe { + DCD_DATA.qhd[ep_idx].qtd_overlay.next = &(first_qtd.unwrap()) as *const _ as u32; + } + + // Start transfer + self.start_transfer(); + + Ok(()) + } } impl embassy_usb_driver::Endpoint for Endpoint { fn info(&self) -> &embassy_usb_driver::EndpointInfo { - todo!() + &self.info } async fn wait_enabled(&mut self) { @@ -37,31 +129,14 @@ impl EndpointIn for Endpoint { } impl Bus { - - pub(crate) fn device_endpoint_open(&mut self, ep_config: EpConfig) { - let r = &self.info.regs; - - let ep_num = ep_config.ep_addr.index(); - let ep_idx = 2 * ep_num + ep_config.ep_addr.is_in() as usize; - // Max EP count: 16 - if ep_num >= ENDPOINT_COUNT { + if ep_config.ep_addr.index() >= ENDPOINT_COUNT { // TODO: return false } // Prepare queue head - self.dcd_data.qhd[ep_idx as usize] = QueueHead::default(); - self.dcd_data.qhd[ep_idx as usize].cap.set_zero_length_termination(true); - self.dcd_data.qhd[ep_idx as usize] - .cap - .set_max_packet_size(ep_config.max_packet_size & 0x7FF); - self.dcd_data.qhd[ep_idx as usize].qtd_overlay.next = 1; // Set next to invalid - if ep_config.transfer == EndpointType::Isochronous as u8 { - self.dcd_data.qhd[ep_idx as usize] - .cap - .set_iso_mult(((ep_config.max_packet_size >> 11) & 0x3) as u8 + 1); - } + unsafe { prepare_qhd(&ep_config) }; // Open endpoint self.dcd_endpoint_open(ep_config); @@ -100,14 +175,6 @@ impl Bus { } } - pub(crate) fn endpoint_transfer(&mut self, ep_idx: u8) { - let offset = if ep_idx % 2 == 1 { ep_idx / 2 + 16 } else { ep_idx / 2 }; - - let r = &self.info.regs; - - r.endptprime().write_value(Endptprime(1 << offset)); - } - pub(crate) fn device_endpoint_stall(&mut self, ep_addr: EndpointAddress) { let r = &self.info.regs; diff --git a/src/usb/hcd.rs b/src/usb/hcd.rs index 1e719b5..9d458be 100644 --- a/src/usb/hcd.rs +++ b/src/usb/hcd.rs @@ -1,5 +1,5 @@ //! Host controller driver for USB peripheral -//! +//! use super::Bus; diff --git a/src/usb/mod.rs b/src/usb/mod.rs index d2a782d..c445bc4 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -5,15 +5,11 @@ use control_pipe::ControlPipe; use embassy_hal_internal::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{Direction, Driver, EndpointAddress, EndpointAllocError, EndpointInfo, EndpointType}; -use embedded_hal::delay::DelayNs; -use endpoint::{Endpoint, EndpointAllocInfo}; +use endpoint::Endpoint; use riscv::delay::McycleDelay; -use crate::pac::usb::regs::*; - mod bus; mod control_pipe; -mod dcd; mod device; mod endpoint; mod hcd; @@ -26,13 +22,10 @@ const ENDPOINT_COUNT: usize = 16; const QTD_COUNT_EACH_ENDPOINT: usize = 8; const QHD_BUFFER_COUNT: usize = 5; - -#[allow(unused)] -pub struct Bus { - info: &'static Info, - delay: McycleDelay, - dcd_data: DcdData, -} +static mut DCD_DATA: DcdData = DcdData { + qhd: [QueueHead::new(); ENDPOINT_COUNT as usize * 2], + qtd: [QueueTransferDescriptor::new(); ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize], +}; #[repr(C, align(32))] pub struct DcdData { @@ -44,6 +37,42 @@ pub struct DcdData { pub(crate) qtd: [QueueTransferDescriptor; ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize], } +pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { + DCD_DATA.qhd = [QueueHead::new(); ENDPOINT_COUNT as usize * 2]; + DCD_DATA.qtd = [QueueTransferDescriptor::new(); ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize]; + + DCD_DATA.qhd[0].cap.set_zero_length_termination(true); + DCD_DATA.qhd[1].cap.set_zero_length_termination(true); + DCD_DATA.qhd[0].cap.set_max_packet_size(ep0_max_packet_size); + DCD_DATA.qhd[1].cap.set_max_packet_size(ep0_max_packet_size); + + // Set the next pointer INVALID + // TODO: replacement? + DCD_DATA.qhd[0].qtd_overlay.next = 1; + DCD_DATA.qhd[1].qtd_overlay.next = 1; + + // Set for OUT only + DCD_DATA.qhd[0].cap.set_int_on_step(true); +} + +pub(crate) unsafe fn prepare_qhd(ep_config: &EpConfig) { + let ep_num = ep_config.ep_addr.index(); + let ep_idx = 2 * ep_num + ep_config.ep_addr.is_in() as usize; + + // Prepare queue head + DCD_DATA.qhd[ep_idx as usize] = QueueHead::default(); + DCD_DATA.qhd[ep_idx as usize].cap.set_zero_length_termination(true); + DCD_DATA.qhd[ep_idx as usize] + .cap + .set_max_packet_size(ep_config.max_packet_size & 0x7FF); + DCD_DATA.qhd[ep_idx as usize].qtd_overlay.next = 1; // Set next to invalid + if ep_config.transfer == EndpointType::Isochronous as u8 { + DCD_DATA.qhd[ep_idx as usize] + .cap + .set_iso_mult(((ep_config.max_packet_size >> 11) & 0x3) as u8 + 1); + } +} + impl Default for DcdData { fn default() -> Self { Self { @@ -54,7 +83,7 @@ impl Default for DcdData { } #[derive(Clone, Copy, Default)] -#[repr(C, align(32))] +#[repr(align(32))] pub(crate) struct QueueHead { // Capabilities and characteristics pub(crate) cap: CapabilityAndCharacteristics, @@ -65,11 +94,25 @@ pub(crate) struct QueueHead { pub(crate) qtd_overlay: QueueTransferDescriptor, pub(crate) setup_request: ControlRequest, - // Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes // thus there are 16 bytes padding free that we can make use of. // TODO: Check memory layout - _reserved: [u8; 16], + // _reserved: [u8; 16], +} + +impl QueueHead { + const fn new() -> Self { + Self { + cap: CapabilityAndCharacteristics::new(), + qtd_addr: 0, + qtd_overlay: QueueTransferDescriptor::new(), + setup_request: ControlRequest::new(), + } + } + + pub(crate) fn set_next_overlay(&mut self, next: u32) { + self.qtd_overlay.next = next; + } } #[bitfield(u64)] @@ -143,6 +186,18 @@ struct QueueTransferDescriptor { _reserved: [u8; 2], } +impl QueueTransferDescriptor { + const fn new() -> Self { + QueueTransferDescriptor { + next: 0, + token: QueueTransferDescriptorToken::new(), + buffer: [0; QHD_BUFFER_COUNT], + expected_bytes: 0, + _reserved: [0; 2], + } + } +} + #[bitfield(u32)] struct QueueTransferDescriptorToken { #[bits(3)] @@ -171,87 +226,46 @@ struct QueueTransferDescriptorToken { _r5: bool, } +#[allow(unused)] +pub struct Bus { + info: &'static Info, + endpoints: [EndpointInfo; ENDPOINT_COUNT], + delay: McycleDelay, +} + pub struct EpConfig { + /// Endpoint type transfer: u8, ep_addr: EndpointAddress, max_packet_size: u16, } -impl Bus { - fn phy_init(&mut self) { - let r = &self.info.regs; - - // Enable dp/dm pulldown - // In hpm_sdk, this operation is done by `ptr->PHY_CTRL0 &= ~0x001000E0u`. - // But there's corresponding bits in register, so we write the register directly here. - let phy_ctrl0 = r.phy_ctrl0().read().0 & (!0x001000E0); - r.phy_ctrl0().write_value(PhyCtrl0(phy_ctrl0)); - - r.otg_ctrl0().modify(|w| { - w.set_otg_utmi_suspendm_sw(false); - w.set_otg_utmi_reset_sw(true); - }); - - r.phy_ctrl1().modify(|w| { - w.set_utmi_cfg_rst_n(false); - }); - - // Wait for reset status - while r.otg_ctrl0().read().otg_utmi_reset_sw() {} - - // Set suspend - r.otg_ctrl0().modify(|w| { - w.set_otg_utmi_suspendm_sw(true); - }); - - // Delay at least 1us - self.delay.delay_us(5); - - r.otg_ctrl0().modify(|w| { - // Disable dm/dp wakeup - w.set_otg_wkdpdmchg_en(false); - // Clear reset sw - w.set_otg_utmi_reset_sw(false); - }); - - // OTG utmi clock detection - r.phy_status().modify(|w| w.set_utmi_clk_valid(true)); - while r.phy_status().read().utmi_clk_valid() == false {} - - // Reset and set suspend - r.phy_ctrl1().modify(|w| { - w.set_utmi_cfg_rst_n(true); - w.set_utmi_otg_suspendm(true); - }); - } - - fn phy_deinit(&mut self) { - let r = &self.info.regs; - - r.otg_ctrl0().modify(|w| { - w.set_otg_utmi_suspendm_sw(true); - w.set_otg_utmi_reset_sw(false); - }); - - r.phy_ctrl1().modify(|w| { - w.set_utmi_cfg_rst_n(false); - w.set_utmi_otg_suspendm(false); - }); - } - - /// Get port speed: 00: full speed, 01: low speed, 10: high speed, 11: undefined - /// TODO: Use enum - pub(crate) fn get_port_speed(&mut self) -> u8 { - let r = &self.info.regs; +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub(crate) struct EndpointAllocData { + pub(crate) info: EndpointInfo, + pub(crate) used_in: bool, + pub(crate) used_out: bool, +} - r.portsc1().read().pspd() +impl Default for EndpointAllocData { + fn default() -> Self { + Self { + info: EndpointInfo { + addr: EndpointAddress::from_parts(0, Direction::Out), + max_packet_size: 0, + ep_type: EndpointType::Bulk, + interval_ms: 0, + }, + used_in: false, + used_out: false, + } } } pub struct UsbDriver<'d, T: Instance> { phantom: PhantomData<&'d mut T>, info: &'static Info, - endpoints: [EndpointAllocInfo; ENDPOINT_COUNT], + endpoints: [EndpointAllocData; ENDPOINT_COUNT], } impl<'d, T: Instance> UsbDriver<'d, T> { @@ -264,11 +278,7 @@ impl<'d, T: Instance> UsbDriver<'d, T> { UsbDriver { phantom: PhantomData, info: T::info(), - endpoints: [EndpointAllocInfo { - ep_type: EndpointType::Bulk, - used_in: false, - used_out: false, - }; ENDPOINT_COUNT], + endpoints: [EndpointAllocData::default(); ENDPOINT_COUNT], } } @@ -286,7 +296,7 @@ impl<'d, T: Instance> UsbDriver<'d, T> { Direction::Out => ep.used_out, Direction::In => ep.used_in, }; - !used || (ep.ep_type == ep_type && !used_dir) + !used || (ep.info.ep_type == ep_type && !used_dir) }) .map(|(i, _)| i) } @@ -321,14 +331,17 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { .find_free_endpoint(ep_type, Direction::Out) .ok_or(EndpointAllocError)?; + let ep = EndpointInfo { + addr: EndpointAddress::from_parts(ep_idx, Direction::Out), + ep_type, + max_packet_size, + interval_ms, + }; self.endpoints[ep_idx].used_out = true; + self.endpoints[ep_idx].info = ep.clone(); Ok(Endpoint { - info: EndpointInfo { - addr: EndpointAddress::from_parts(ep_idx, Direction::Out), - ep_type, - max_packet_size, - interval_ms, - }, + info: ep, + usb_info: self.info, }) } @@ -352,14 +365,17 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { .find_free_endpoint(ep_type, Direction::In) .ok_or(EndpointAllocError)?; + let ep = EndpointInfo { + addr: EndpointAddress::from_parts(ep_idx, Direction::In), + ep_type, + max_packet_size, + interval_ms, + }; self.endpoints[ep_idx].used_out = true; + self.endpoints[ep_idx].info = ep.clone(); Ok(Endpoint { - info: EndpointInfo { - addr: EndpointAddress::from_parts(ep_idx, Direction::In), - ep_type, - max_packet_size, - interval_ms, - }, + info: ep, + usb_info: self.info, }) } @@ -387,11 +403,21 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { // But the comment says this function makes all the allocated endpoints **start operating** // self.dcd_init(); + let mut eps: [EndpointInfo; ENDPOINT_COUNT] = [EndpointInfo { + addr: EndpointAddress::from(0), + ep_type: EndpointType::Bulk, + max_packet_size: 0, + interval_ms: 0, + }; ENDPOINT_COUNT]; + for i in 0..ENDPOINT_COUNT { + eps[i] = self.endpoints[i].info; + } + ( Self::Bus { info: self.info, - dcd_data: todo!(), - delay: todo!(), + endpoints: eps, + delay: McycleDelay::new(crate::sysctl::clocks().cpu0.0), }, Self::ControlPipe { max_packet_size: control_max_packet_size as usize, From b02e4de45053b3c6d0ab66a6a02cfa981ed0b006 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Thu, 18 Jul 2024 18:55:18 +0800 Subject: [PATCH 07/43] refactor(usb): move qtd initialization to dcd data struct Signed-off-by: Haobo Gu --- src/usb/endpoint.rs | 23 ++++++----------------- src/usb/mod.rs | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index acb66f8..d597971 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -1,9 +1,8 @@ -use embassy_usb_driver::{Direction, EndpointAddress, EndpointIn, EndpointInfo, EndpointOut, EndpointType}; +use embassy_usb_driver::{EndpointAddress, EndpointIn, EndpointInfo, EndpointOut, EndpointType}; use hpm_metapac::usb::regs::Endptprime; use super::{ - prepare_qhd, Bus, EpConfig, Error, Info, QueueTransferDescriptor, DCD_DATA, ENDPOINT_COUNT, QHD_BUFFER_COUNT, - QTD_COUNT_EACH_ENDPOINT, + prepare_qhd, Bus, EpConfig, Error, Info, QueueTransferDescriptor, DCD_DATA, ENDPOINT_COUNT, QTD_COUNT_EACH_ENDPOINT, }; // #[derive(Copy, Clone)] @@ -60,24 +59,14 @@ impl Endpoint { }; // Initialize qtd - qtd = QueueTransferDescriptor::default(); - qtd.token.set_active(true); - qtd.token.set_total_bytes(transfer_bytes as u16); - qtd.expected_bytes = transfer_bytes as u16; - // Fill data into qtd - // FIXME: Fill correct data - qtd.buffer[0] = data.as_ptr() as u32 + 4096 * data_offset; - for i in 1..QHD_BUFFER_COUNT { - // TODO: WHY the buffer is filled in this way? - qtd.buffer[i] |= (qtd.buffer[i - 1] & 0xFFFFF000) + 4096; - } + qtd.reinit_with(&data[data_offset..], transfer_bytes, remaining_bytes); - // + // Last chunk of the data if remaining_bytes == 0 { - qtd.token.set_int_on_complete(true); + qtd.set_token_int_on_complete(true); } - data_offset += transfer_bytes as u32; + data_offset += transfer_bytes; // Linked list operations // Set circular link diff --git a/src/usb/mod.rs b/src/usb/mod.rs index c445bc4..08c4b9d 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -196,6 +196,29 @@ impl QueueTransferDescriptor { _reserved: [0; 2], } } + + pub(crate) fn reinit_with(&mut self, data: &[u8], transfer_bytes: usize, remaining_bytes: usize) { + // Initialize qtd + self.next = 0; + self.token = QueueTransferDescriptorToken::new(); + self.buffer = [0; QHD_BUFFER_COUNT]; + self.expected_bytes = 0; + + self.token.set_active(true); + self.token.set_total_bytes(transfer_bytes as u16); + self.expected_bytes = transfer_bytes as u16; + // Fill data into qtd + // FIXME: Fill correct data + self.buffer[0] = data.as_ptr() as u32; + for i in 1..QHD_BUFFER_COUNT { + // TODO: WHY the buffer is filled in this way? + self.buffer[i] |= (self.buffer[i - 1] & 0xFFFFF000) + 4096; + } + } + + pub(crate) fn set_token_int_on_complete(&mut self, value: bool) { + self.token.set_int_on_complete(value); + } } #[bitfield(u32)] From 464f8ba3e895c4cb64148714f515952d0f57c9d2 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Thu, 18 Jul 2024 23:37:45 +0800 Subject: [PATCH 08/43] feat(usb): finish main part of usb driver Signed-off-by: HaoboGu --- src/usb/bus.rs | 137 ++++++++++++++++++++++++++++++- src/usb/control_pipe.rs | 72 ++++++++++++++--- src/usb/endpoint.rs | 175 ++++++++-------------------------------- src/usb/hcd.rs | 47 ----------- src/usb/host.rs | 22 ----- src/usb/mod.rs | 21 +++-- 6 files changed, 243 insertions(+), 231 deletions(-) delete mode 100644 src/usb/hcd.rs delete mode 100644 src/usb/host.rs diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 65e41a8..5e107dc 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -2,7 +2,7 @@ use embassy_usb_driver::{EndpointAddress, EndpointType, Event, Unsupported}; use embedded_hal::delay::DelayNs; use hpm_metapac::usb::regs::*; -use super::{Bus, ENDPOINT_COUNT}; +use super::{init_qhd, Bus, ENDPOINT_COUNT}; use crate::usb::EpConfig; impl embassy_usb_driver::Bus for Bus { @@ -279,4 +279,139 @@ impl Bus { w.set_rs(false); }); } + pub(crate) fn device_endpoint_open(&mut self, ep_config: EpConfig) { + if ep_config.ep_addr.index() >= ENDPOINT_COUNT { + // TODO: return false + } + + // Prepare queue head + unsafe { init_qhd(&ep_config) }; + + // Open endpoint + self.dcd_endpoint_open(ep_config); + } + + pub(crate) fn dcd_endpoint_open(&mut self, ep_config: EpConfig) { + let r = &self.info.regs; + + let ep_num = ep_config.ep_addr.index(); + + // Enable EP control + r.endptctrl(ep_num as usize).modify(|w| { + // Clear the RXT or TXT bits + if ep_config.ep_addr.is_in() { + w.set_txt(0); + w.set_txe(true); + w.set_txr(true); + // TODO: Better impl? For example, make transfer a bitfield struct + w.0 |= (ep_config.transfer as u32) << 18; + } else { + w.set_rxt(0); + w.set_rxe(true); + w.set_rxr(true); + w.0 |= (ep_config.transfer as u32) << 2; + } + }); + } + + pub(crate) fn endpoint_get_type(&mut self, ep_addr: EndpointAddress) -> u8 { + let r = &self.info.regs; + + if ep_addr.is_in() { + r.endptctrl(ep_addr.index() as usize).read().txt() + } else { + r.endptctrl(ep_addr.index() as usize).read().rxt() + } + } + + pub(crate) fn device_endpoint_stall(&mut self, ep_addr: EndpointAddress) { + let r = &self.info.regs; + + if ep_addr.is_in() { + r.endptctrl(ep_addr.index() as usize).modify(|w| w.set_txs(true)); + } else { + r.endptctrl(ep_addr.index() as usize).modify(|w| w.set_rxs(true)); + } + } + + pub(crate) fn device_endpoint_clean_stall(&mut self, ep_addr: EndpointAddress) { + let r = &self.info.regs; + + r.endptctrl(ep_addr.index() as usize).modify(|w| { + if ep_addr.is_in() { + // Data toggle also need to be reset + w.set_txr(true); + w.set_txs(false); + } else { + w.set_rxr(true); + w.set_rxs(false); + } + }); + } + + pub(crate) fn dcd_endpoint_check_stall(&mut self, ep_addr: EndpointAddress) -> bool { + let r = &self.info.regs; + + if ep_addr.is_in() { + r.endptctrl(ep_addr.index() as usize).read().txs() + } else { + r.endptctrl(ep_addr.index() as usize).read().rxs() + } + } + + pub(crate) fn dcd_endpoint_close(&mut self, ep_addr: EndpointAddress) { + let r = &self.info.regs; + + let ep_bit = 1 << ep_addr.index(); + + // Flush the endpoint first + if ep_addr.is_in() { + loop { + r.endptflush().modify(|w| w.set_fetb(ep_bit)); + while (r.endptflush().read().fetb() & ep_bit) == 1 {} + if r.endptstat().read().etbr() & ep_bit == 0 { + break; + } + } + } else { + loop { + r.endptflush().modify(|w| w.set_ferb(ep_bit)); + while (r.endptflush().read().ferb() & ep_bit) == 1 {} + if r.endptstat().read().erbr() & ep_bit == 0 { + break; + } + } + } + + // Disable endpoint + r.endptctrl(ep_addr.index() as usize).write(|w| { + if ep_addr.is_in() { + w.set_txt(0); + w.set_txe(false); + w.set_txs(false); + } else { + w.set_rxt(0); + w.set_rxe(false); + w.set_rxs(false); + } + }); + // Set transfer type back to ANY type other than control + r.endptctrl(ep_addr.index() as usize).write(|w| { + if ep_addr.is_in() { + w.set_txt(EndpointType::Bulk as u8); + } else { + w.set_rxt(EndpointType::Bulk as u8); + } + }); + } + + pub(crate) fn ep_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { + let r = &self.info.regs; + + if ep_addr.is_in() { + r.endptctrl(ep_addr.index() as usize).read().txs() + } else { + r.endptctrl(ep_addr.index() as usize).read().rxs() + } + } } diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 3e20ce0..d8717d8 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -1,42 +1,88 @@ +use core::marker::PhantomData; + +use embassy_usb_driver::EndpointError; +use hpm_metapac::usb::regs::Endptsetupstat; + use super::endpoint::Endpoint; +use super::Instance; +use crate::usb::{init_qhd, EpConfig, DCD_DATA}; -pub(crate) struct ControlPipe { +pub(crate) struct ControlPipe<'d, T: Instance> { + pub(crate) phantom: PhantomData<&'d mut T>, pub(crate) max_packet_size: usize, pub(crate) ep_in: Endpoint, pub(crate) ep_out: Endpoint, } -impl embassy_usb_driver::ControlPipe for ControlPipe { +impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { fn max_packet_size(&self) -> usize { self.max_packet_size } async fn setup(&mut self) -> [u8; 8] { - todo!() + unsafe { + init_qhd(&EpConfig { + // Must be EndpointType::Control + transfer: self.ep_out.info.ep_type as u8, + ep_addr: self.ep_out.info.addr, + max_packet_size: self.ep_out.info.max_packet_size, + }) + } + + // TODO: 1. Wait for SETUP packet(interrupt here) + + // 2. Read setup packet from qhd buffer + // See hpm_sdk/middleware/cherryusb/port/hpm/usb_dc_hpm.c: 285L + let r = T::info().regs; + // TODO: clear setup status, should we clear ALL? + r.endptsetupstat().write_value(Endptsetupstat::default()); + + // Return setup packet + let setup_packet = unsafe { DCD_DATA.qhd[0].setup_request }; + + // Convert to slice + setup_packet.0.to_le_bytes() } async fn data_out( &mut self, buf: &mut [u8], - first: bool, - last: bool, + _first: bool, + _last: bool, ) -> Result { - todo!() + self.ep_out.transfer(buf).map_err(|_e| EndpointError::BufferOverflow)?; + Ok(buf.len()) } - async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), embassy_usb_driver::EndpointError> { - todo!() + async fn data_in( + &mut self, + data: &[u8], + _first: bool, + _last: bool, + ) -> Result<(), embassy_usb_driver::EndpointError> { + self.ep_in.transfer(data).map_err(|_e| EndpointError::BufferOverflow)?; + Ok(()) } - async fn accept(&mut self) { - todo!() - } + /// Accept a control request. + /// + /// Causes the STATUS packet for the current request to be ACKed. + async fn accept(&mut self) {} async fn reject(&mut self) { - todo!() + // Reject, set IN+OUT to stall + self.ep_in.set_stall(); + self.ep_out.set_stall(); } async fn accept_set_address(&mut self, addr: u8) { - todo!() + // Response with STATUS? + // self.ep_in.transfer(&[]); + + let r = T::info().regs; + r.deviceaddr().modify(|w| { + w.set_usbadr(addr); + w.set_usbadra(true); + }); } } diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index d597971..55dd037 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -1,9 +1,7 @@ -use embassy_usb_driver::{EndpointAddress, EndpointIn, EndpointInfo, EndpointOut, EndpointType}; +use embassy_usb_driver::{EndpointIn, EndpointInfo, EndpointOut}; use hpm_metapac::usb::regs::Endptprime; -use super::{ - prepare_qhd, Bus, EpConfig, Error, Info, QueueTransferDescriptor, DCD_DATA, ENDPOINT_COUNT, QTD_COUNT_EACH_ENDPOINT, -}; +use super::{Error, Info, QueueTransferDescriptor, DCD_DATA, QTD_COUNT_EACH_ENDPOINT}; // #[derive(Copy, Clone)] pub(crate) struct Endpoint { @@ -59,7 +57,7 @@ impl Endpoint { }; // Initialize qtd - qtd.reinit_with(&data[data_offset..], transfer_bytes, remaining_bytes); + qtd.reinit_with(&data[data_offset..], transfer_bytes); // Last chunk of the data if remaining_bytes == 0 { @@ -93,92 +91,21 @@ impl Endpoint { Ok(()) } -} - -impl embassy_usb_driver::Endpoint for Endpoint { - fn info(&self) -> &embassy_usb_driver::EndpointInfo { - &self.info - } - - async fn wait_enabled(&mut self) { - todo!() - } -} - -impl EndpointOut for Endpoint { - async fn read(&mut self, buf: &mut [u8]) -> Result { - todo!() - } -} -impl EndpointIn for Endpoint { - async fn write(&mut self, buf: &[u8]) -> Result<(), embassy_usb_driver::EndpointError> { - todo!() - } -} - -impl Bus { - pub(crate) fn device_endpoint_open(&mut self, ep_config: EpConfig) { - // Max EP count: 16 - if ep_config.ep_addr.index() >= ENDPOINT_COUNT { - // TODO: return false - } - - // Prepare queue head - unsafe { prepare_qhd(&ep_config) }; - - // Open endpoint - self.dcd_endpoint_open(ep_config); - } - - pub(crate) fn dcd_endpoint_open(&mut self, ep_config: EpConfig) { - let r = &self.info.regs; - - let ep_num = ep_config.ep_addr.index(); - - // Enable EP control - r.endptctrl(ep_num as usize).modify(|w| { - // Clear the RXT or TXT bits - if ep_config.ep_addr.is_in() { - w.set_txt(0); - w.set_txe(true); - w.set_txr(true); - // TODO: Better impl? For example, make transfer a bitfield struct - w.0 |= (ep_config.transfer as u32) << 18; - } else { - w.set_rxt(0); - w.set_rxe(true); - w.set_rxr(true); - w.0 |= (ep_config.transfer as u32) << 2; - } - }); - } - - pub(crate) fn endpoint_get_type(&mut self, ep_addr: EndpointAddress) -> u8 { - let r = &self.info.regs; - - if ep_addr.is_in() { - r.endptctrl(ep_addr.index() as usize).read().txt() - } else { - r.endptctrl(ep_addr.index() as usize).read().rxt() - } - } - - pub(crate) fn device_endpoint_stall(&mut self, ep_addr: EndpointAddress) { - let r = &self.info.regs; - - if ep_addr.is_in() { - r.endptctrl(ep_addr.index() as usize).modify(|w| w.set_txs(true)); + pub(crate) fn set_stall(&mut self) { + let r = &self.usb_info.regs; + if self.info.addr.is_in() { + r.endptctrl(self.info.addr.index() as usize).modify(|w| w.set_txs(true)); } else { - r.endptctrl(ep_addr.index() as usize).modify(|w| w.set_rxs(true)); + r.endptctrl(self.info.addr.index() as usize).modify(|w| w.set_rxs(true)); } } - pub(crate) fn device_endpoint_clean_stall(&mut self, ep_addr: EndpointAddress) { - let r = &self.info.regs; + pub(crate) fn clean_stall(&mut self) { + let r = &self.usb_info.regs; - r.endptctrl(ep_addr.index() as usize).modify(|w| { - if ep_addr.is_in() { + r.endptctrl(self.info.addr.index() as usize).modify(|w| { + if self.info.addr.is_in() { // Data toggle also need to be reset w.set_txr(true); w.set_txs(false); @@ -189,69 +116,37 @@ impl Bus { }); } - pub(crate) fn dcd_endpoint_check_stall(&mut self, ep_addr: EndpointAddress) -> bool { - let r = &self.info.regs; + pub(crate) fn check_stall(&self) -> bool { + let r = &self.usb_info.regs; - if ep_addr.is_in() { - r.endptctrl(ep_addr.index() as usize).read().txs() + if self.info.addr.is_in() { + r.endptctrl(self.info.addr.index() as usize).read().txs() } else { - r.endptctrl(ep_addr.index() as usize).read().rxs() + r.endptctrl(self.info.addr.index() as usize).read().rxs() } } +} - pub(crate) fn dcd_endpoint_close(&mut self, ep_addr: EndpointAddress) { - let r = &self.info.regs; - - let ep_bit = 1 << ep_addr.index(); - - // Flush the endpoint first - if ep_addr.is_in() { - loop { - r.endptflush().modify(|w| w.set_fetb(ep_bit)); - while (r.endptflush().read().fetb() & ep_bit) == 1 {} - if r.endptstat().read().etbr() & ep_bit == 0 { - break; - } - } - } else { - loop { - r.endptflush().modify(|w| w.set_ferb(ep_bit)); - while (r.endptflush().read().ferb() & ep_bit) == 1 {} - if r.endptstat().read().erbr() & ep_bit == 0 { - break; - } - } - } +impl embassy_usb_driver::Endpoint for Endpoint { + fn info(&self) -> &embassy_usb_driver::EndpointInfo { + &self.info + } - // Disable endpoint - r.endptctrl(ep_addr.index() as usize).write(|w| { - if ep_addr.is_in() { - w.set_txt(0); - w.set_txe(false); - w.set_txs(false); - } else { - w.set_rxt(0); - w.set_rxe(false); - w.set_rxs(false); - } - }); - // Set transfer type back to ANY type other than control - r.endptctrl(ep_addr.index() as usize).write(|w| { - if ep_addr.is_in() { - w.set_txt(EndpointType::Bulk as u8); - } else { - w.set_rxt(EndpointType::Bulk as u8); - } - }); + async fn wait_enabled(&mut self) { + todo!() } +} - pub(crate) fn ep_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { - let r = &self.info.regs; +impl EndpointOut for Endpoint { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.transfer(buf); + Ok(buf.len()) + } +} - if ep_addr.is_in() { - r.endptctrl(ep_addr.index() as usize).read().txs() - } else { - r.endptctrl(ep_addr.index() as usize).read().rxs() - } +impl EndpointIn for Endpoint { + async fn write(&mut self, buf: &[u8]) -> Result<(), embassy_usb_driver::EndpointError> { + self.transfer(buf); + Ok(()) } } diff --git a/src/usb/hcd.rs b/src/usb/hcd.rs deleted file mode 100644 index 9d458be..0000000 --- a/src/usb/hcd.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Host controller driver for USB peripheral -//! - -use super::Bus; - -impl Bus { - pub(crate) fn hcd_init(&mut self, int_mask: u32, framelist_size: u16) -> bool { - let r = &self.info.regs; - - // hcd framelist max element is 1024 - if framelist_size > 1024 || framelist_size == 0 { - return false; - } - - let framelist_size_bf = 10 - get_first_set_bit_from_lsb(framelist_size as u32) as u8; - - if framelist_size != (1 << get_first_set_bit_from_lsb(framelist_size as u32)) { - return false; - } - - self.phy_init(); - - // Reset controller - r.usbcmd().modify(|w| w.set_rst(true)); - while r.usbcmd().read().rst() {} - - todo!() - } - - pub(crate) fn hcd_port_reset(&mut self) { - todo!() - } -} - -// Helper function - -fn get_first_set_bit_from_lsb(mut value: u32) -> u32 { - let mut i = 0; - if value == 0 { - return u32::MAX; - } - while value > 0 && (value & 1) == 0 { - value = value >> 1; - i += 1; - } - i -} diff --git a/src/usb/host.rs b/src/usb/host.rs deleted file mode 100644 index 050be79..0000000 --- a/src/usb/host.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::Bus; - -impl Bus { - pub(crate) fn host_init(&mut self) { - let r = &self.info.regs; - - r.usbmode().modify(|w| { - // Set mode to host, must be done IMMEDIATELY after reset - w.set_cm(0b11); - - // Little endian - w.set_es(false); - }); - - r.portsc1().modify(|w| { - w.set_sts(false); - w.set_ptw(false); - }); - - r.usbcmd().modify(|w| w.set_itc(0)); - } -} diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 08c4b9d..fd5df1d 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -12,8 +12,6 @@ mod bus; mod control_pipe; mod device; mod endpoint; -mod hcd; -mod host; #[cfg(usb_v67)] const ENDPOINT_COUNT: usize = 8; @@ -52,10 +50,10 @@ pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { DCD_DATA.qhd[1].qtd_overlay.next = 1; // Set for OUT only - DCD_DATA.qhd[0].cap.set_int_on_step(true); + DCD_DATA.qhd[0].cap.set_int_on_setup(true); } -pub(crate) unsafe fn prepare_qhd(ep_config: &EpConfig) { +pub(crate) unsafe fn init_qhd(ep_config: &EpConfig) { let ep_num = ep_config.ep_addr.index(); let ep_idx = 2 * ep_num + ep_config.ep_addr.is_in() as usize; @@ -65,12 +63,16 @@ pub(crate) unsafe fn prepare_qhd(ep_config: &EpConfig) { DCD_DATA.qhd[ep_idx as usize] .cap .set_max_packet_size(ep_config.max_packet_size & 0x7FF); - DCD_DATA.qhd[ep_idx as usize].qtd_overlay.next = 1; // Set next to invalid + // Set next to invalid, T=1 + DCD_DATA.qhd[ep_idx as usize].qtd_overlay.next = 1; if ep_config.transfer == EndpointType::Isochronous as u8 { DCD_DATA.qhd[ep_idx as usize] .cap .set_iso_mult(((ep_config.max_packet_size >> 11) & 0x3) as u8 + 1); } + if ep_config.transfer == EndpointType::Control as u8 { + DCD_DATA.qhd[ep_idx as usize].cap.set_int_on_setup(true); + } } impl Default for DcdData { @@ -82,6 +84,8 @@ impl Default for DcdData { } } +pub(crate) struct QueueHeadV2([u8; 48]); + #[derive(Clone, Copy, Default)] #[repr(align(32))] pub(crate) struct QueueHead { @@ -151,7 +155,7 @@ pub(crate) struct CapabilityAndCharacteristics { /// This bit is used on control type endpoints to indicate if /// USBINT is set in response to a setup being received. #[bits(1)] - int_on_step: bool, + int_on_setup: bool, #[bits(11)] max_packet_size: u16, @@ -197,7 +201,7 @@ impl QueueTransferDescriptor { } } - pub(crate) fn reinit_with(&mut self, data: &[u8], transfer_bytes: usize, remaining_bytes: usize) { + pub(crate) fn reinit_with(&mut self, data: &[u8], transfer_bytes: usize) { // Initialize qtd self.next = 0; self.token = QueueTransferDescriptorToken::new(); @@ -330,7 +334,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { type EndpointIn = Endpoint; - type ControlPipe = ControlPipe; + type ControlPipe = ControlPipe<'a, T>; type Bus = Bus; @@ -443,6 +447,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { delay: McycleDelay::new(crate::sysctl::clocks().cpu0.0), }, Self::ControlPipe { + phantom: PhantomData, max_packet_size: control_max_packet_size as usize, ep_in, ep_out, From 5eed540736d04415cb9645ccd6cd4b18eecd99bb Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Thu, 18 Jul 2024 23:45:51 +0800 Subject: [PATCH 09/43] fix(usb): make visibility of usb structs Signed-off-by: HaoboGu --- src/usb/control_pipe.rs | 2 +- src/usb/endpoint.rs | 4 ++-- src/usb/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index d8717d8..acb7f47 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -7,7 +7,7 @@ use super::endpoint::Endpoint; use super::Instance; use crate::usb::{init_qhd, EpConfig, DCD_DATA}; -pub(crate) struct ControlPipe<'d, T: Instance> { +pub struct ControlPipe<'d, T: Instance> { pub(crate) phantom: PhantomData<&'d mut T>, pub(crate) max_packet_size: usize, pub(crate) ep_in: Endpoint, diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index 55dd037..ccadbe5 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -3,8 +3,8 @@ use hpm_metapac::usb::regs::Endptprime; use super::{Error, Info, QueueTransferDescriptor, DCD_DATA, QTD_COUNT_EACH_ENDPOINT}; -// #[derive(Copy, Clone)] -pub(crate) struct Endpoint { +#[derive(Copy, Clone)] +pub struct Endpoint { pub(crate) info: EndpointInfo, pub(crate) usb_info: &'static Info, } diff --git a/src/usb/mod.rs b/src/usb/mod.rs index fd5df1d..1852e67 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -171,7 +171,7 @@ pub(crate) struct CapabilityAndCharacteristics { } #[derive(Clone, Copy, Default)] #[repr(C, align(32))] -struct QueueTransferDescriptor { +pub(crate) struct QueueTransferDescriptor { // Next point next: u32, From 6cc3d6d41d32effe0b8e4f29927067fd60170e2f Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sat, 20 Jul 2024 21:57:34 +0800 Subject: [PATCH 10/43] refactor(usb): add address alignment check, refine comments Signed-off-by: HaoboGu --- src/usb/bus.rs | 4 +++- src/usb/endpoint.rs | 11 +++++++---- src/usb/mod.rs | 16 +++++++++++++--- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 5e107dc..c4ddc6c 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -1,3 +1,4 @@ +use defmt::error; use embassy_usb_driver::{EndpointAddress, EndpointType, Event, Unsupported}; use embedded_hal::delay::DelayNs; use hpm_metapac::usb::regs::*; @@ -281,7 +282,8 @@ impl Bus { } pub(crate) fn device_endpoint_open(&mut self, ep_config: EpConfig) { if ep_config.ep_addr.index() >= ENDPOINT_COUNT { - // TODO: return false + error!("Invalid endpoint index"); + return } // Prepare queue head diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index ccadbe5..d97ac5b 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -48,6 +48,7 @@ impl Endpoint { let mut qtd = unsafe { DCD_DATA.qtd[ep_idx * QTD_COUNT_EACH_ENDPOINT + i] }; i += 1; + // If the transfer size > 0x4000, then there should be multiple qtds in the linked list let transfer_bytes = if remaining_bytes > 0x4000 { remaining_bytes -= 0x4000; 0x4000 @@ -55,8 +56,11 @@ impl Endpoint { remaining_bytes = 0; data.len() }; + + // TODO: use data address for multi-core + // Check hpm_sdk: static inline uint32_t core_local_mem_to_sys_address() - // Initialize qtd + // Initialize qtd with the data qtd.reinit_with(&data[data_offset..], transfer_bytes); // Last chunk of the data @@ -66,8 +70,7 @@ impl Endpoint { data_offset += transfer_bytes; - // Linked list operations - // Set circular link + // Set qtd linked list if let Some(mut prev_qtd) = prev_qtd { prev_qtd.next = &qtd as *const _ as u32; } else { @@ -81,7 +84,7 @@ impl Endpoint { } } - // FIXME: update qhd's overlay + // Link qtd to qhd unsafe { DCD_DATA.qhd[ep_idx].qtd_overlay.next = &(first_qtd.unwrap()) as *const _ as u32; } diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 1852e67..8a44bc6 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -100,7 +100,6 @@ pub(crate) struct QueueHead { pub(crate) setup_request: ControlRequest, // Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes // thus there are 16 bytes padding free that we can make use of. - // TODO: Check memory layout // _reserved: [u8; 16], } @@ -211,11 +210,22 @@ impl QueueTransferDescriptor { self.token.set_active(true); self.token.set_total_bytes(transfer_bytes as u16); self.expected_bytes = transfer_bytes as u16; + + // According to the UM, buffer[0] is the start address of the transfer data. + // Buffer[0] has two parts: buffer[0] & 0xFFFFF000 is the address, and buffer[0] & 0x00000FFF is the offset. + // The offset will be updated by hardware, indicating the number of transferred data. + // So, the buffer[0] can be set directly to `data.as_ptr()`, with address + non-zero offset. + // However, buffer[1-4] cannot be set with an offset, so they MUST be 4K bytes aligned. + // That's why the buffer[1-4] is filled with a `& 0xFFFFF000`. + // To be convenient, if the data length is larger than 4K, we require the data address to be 4K bytes aligned. + if transfer_bytes > 0x1000 && data.as_ptr() as u32 % 0x1000 != 0 { + error!("The buffer[1-4] must be 4K bytes aligned"); + } + // Fill data into qtd - // FIXME: Fill correct data self.buffer[0] = data.as_ptr() as u32; for i in 1..QHD_BUFFER_COUNT { - // TODO: WHY the buffer is filled in this way? + // Fill address of next 4K bytes self.buffer[i] |= (self.buffer[i - 1] & 0xFFFFF000) + 4096; } } From 539a87cebeac2aeaa41cfb041831f8db4e15215a Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Mon, 22 Jul 2024 23:32:20 +0800 Subject: [PATCH 11/43] feat(usb): add usb pin initialization Signed-off-by: HaoboGu --- src/usb/mod.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 8a44bc6..3b69bfd 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -5,9 +5,13 @@ use control_pipe::ControlPipe; use embassy_hal_internal::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{Direction, Driver, EndpointAddress, EndpointAllocError, EndpointInfo, EndpointType}; +use embedded_hal::delay::DelayNs; use endpoint::Endpoint; use riscv::delay::McycleDelay; +use crate::gpio::Pin; +use crate::sysctl; + mod bus; mod control_pipe; mod device; @@ -219,7 +223,8 @@ impl QueueTransferDescriptor { // That's why the buffer[1-4] is filled with a `& 0xFFFFF000`. // To be convenient, if the data length is larger than 4K, we require the data address to be 4K bytes aligned. if transfer_bytes > 0x1000 && data.as_ptr() as u32 % 0x1000 != 0 { - error!("The buffer[1-4] must be 4K bytes aligned"); + // defmt::error!("The buffer[1-4] must be 4K bytes aligned"); + return } // Fill data into qtd @@ -306,9 +311,80 @@ pub struct UsbDriver<'d, T: Instance> { } impl<'d, T: Instance> UsbDriver<'d, T> { - pub fn new(dp: impl Peripheral

> + 'd, dm: impl Peripheral

> + 'd) -> Self { + pub fn new(dp: impl Peripheral

+ 'd, dm: impl Peripheral

+ 'd) -> Self { + // TODO: Initialization + // + // For HPM5300 series, DP/DM are reused with PA24/PA25. + // To use USB, the func_ctl of PA24/PA25 should be set to ANALOG, + // set IOC of PY00/01/02 aka USB0_ID, USB0_OC, USB0_PWR to USB, + // and config PIOC of PY00/01/02 as well. + // + // C code: + // + // ```c + // void init_usb_pins(void) + // { + // HPM_IOC->PAD[IOC_PAD_PA24].FUNC_CTL = IOC_PAD_FUNC_CTL_ANALOG_MASK; + // HPM_IOC->PAD[IOC_PAD_PA25].FUNC_CTL = IOC_PAD_FUNC_CTL_ANALOG_MASK; + // + // /* USB0_ID */ + // HPM_IOC->PAD[IOC_PAD_PY00].FUNC_CTL = IOC_PY00_FUNC_CTL_USB0_ID; + // /* USB0_OC */ + // HPM_IOC->PAD[IOC_PAD_PY01].FUNC_CTL = IOC_PY01_FUNC_CTL_USB0_OC; + // /* USB0_PWR */ + // HPM_IOC->PAD[IOC_PAD_PY02].FUNC_CTL = IOC_PY02_FUNC_CTL_USB0_PWR; + // + // /* PY port IO needs to configure PIOC as well */ + // HPM_PIOC->PAD[IOC_PAD_PY00].FUNC_CTL = PIOC_PY00_FUNC_CTL_SOC_GPIO_Y_00; + // HPM_PIOC->PAD[IOC_PAD_PY01].FUNC_CTL = PIOC_PY01_FUNC_CTL_SOC_GPIO_Y_01; + // HPM_PIOC->PAD[IOC_PAD_PY02].FUNC_CTL = PIOC_PY02_FUNC_CTL_SOC_GPIO_Y_02; + // } + // ``` + // + // After that, power ctrl polarity of vbus should be set + // + // ```c + // // vbus high level enable + // ptr->OTG_CTRL0 |= USB_OTG_CTRL0_OTG_POWER_MASK_MASK; + // ``` + // + // Then wait for 100ms. + // + // Since QFN48/LQFP64 have no vbus pin, there's an extra step: call `usb_phy_using_internal_vbus` to enable internal vbus + // + // ```c + // static inline void usb_phy_using_internal_vbus(USB_Type *ptr) + // { + // ptr->PHY_CTRL0 |= (USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_MASK | USB_PHY_CTRL0_SESS_VALID_OVERRIDE_MASK) + // | (USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_MASK | USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_MASK); + // } + // ``` + into_ref!(dp, dm); + // Set to analog + dp.set_as_analog(); + dm.set_as_analog(); + + // TODO: Set ID/OC/PWR in host mode + // + + // Set vbus high level enable + let r = T::info().regs; + r.otg_ctrl0().modify(|w| w.set_otg_power_mask(true)); + + // Wait for 100ms + let mut delay = McycleDelay::new(sysctl::clocks().cpu0.0); + delay.delay_ms(100); + + // Enable internal vbus + r.phy_ctrl0().modify(|w| { + w.set_vbus_valid_override(true); + w.set_sess_valid_override(true); + w.set_vbus_valid_override_en(true); + w.set_sess_valid_override_en(true); + }); + // suppress "unused" warnings. let _ = (dp, dm); From df981aeecf326b55eb3e1d3cf130684037879c18 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Tue, 23 Jul 2024 18:24:23 +0800 Subject: [PATCH 12/43] feat(usb): add usb-pin-reuse feature for hpm5300 Signed-off-by: Haobo Gu --- Cargo.toml | 2 +- build.rs | 10 ++++++++++ src/usb/mod.rs | 27 ++++++++++++++++++++------- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 99ee875..a9c86cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ rt = ["dep:riscv-rt", "hpm-metapac/rt"] defmt = ["dep:defmt"] time = ["dep:embassy-time"] chrono = ["dep:chrono"] - +usb-pin-reuse-hpm5300 = [] embassy = [ "dep:embassy-sync", "dep:embassy-futures", diff --git a/build.rs b/build.rs index d1e2903..210851a 100644 --- a/build.rs +++ b/build.rs @@ -395,8 +395,18 @@ fn main() { }) } } + // if regs.kind == "dac" } + + // Usb is special + #[cfg(feature = "usb-pin-reuse-hpm5300")] + if p.name == "USB0" { + g.extend(quote! { + pin_trait_impl!(crate::usb::DmPin, USB0, PA24, 0); + pin_trait_impl!(crate::usb::DpPin, USB0, PA25, 0); + }) + } } } diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 3b69bfd..a162b6e 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -224,7 +224,7 @@ impl QueueTransferDescriptor { // To be convenient, if the data length is larger than 4K, we require the data address to be 4K bytes aligned. if transfer_bytes > 0x1000 && data.as_ptr() as u32 % 0x1000 != 0 { // defmt::error!("The buffer[1-4] must be 4K bytes aligned"); - return + return; } // Fill data into qtd @@ -311,7 +311,11 @@ pub struct UsbDriver<'d, T: Instance> { } impl<'d, T: Instance> UsbDriver<'d, T> { - pub fn new(dp: impl Peripheral

+ 'd, dm: impl Peripheral

+ 'd) -> Self { + pub fn new( + peri: impl Peripheral

+ 'd, + #[cfg(feature = "usb-pin-reuse-hpm5300")] dm: impl Peripheral

> + 'd, + #[cfg(feature = "usb-pin-reuse-hpm5300")] dp: impl Peripheral

> + 'd, + ) -> Self { // TODO: Initialization // // For HPM5300 series, DP/DM are reused with PA24/PA25. @@ -360,11 +364,19 @@ impl<'d, T: Instance> UsbDriver<'d, T> { // } // ``` - into_ref!(dp, dm); + let r = T::info().regs; + + // Disable dp/dm pulldown + r.phy_ctrl0().modify(|w| w.0 |= 0x001000E0); - // Set to analog - dp.set_as_analog(); - dm.set_as_analog(); + #[cfg(feature = "usb-pin-reuse-hpm5300")] + { + into_ref!(dp, dm); + + // Set to analog + dp.set_as_analog(); + dm.set_as_analog(); + } // TODO: Set ID/OC/PWR in host mode // @@ -377,7 +389,8 @@ impl<'d, T: Instance> UsbDriver<'d, T> { let mut delay = McycleDelay::new(sysctl::clocks().cpu0.0); delay.delay_ms(100); - // Enable internal vbus + // Enable internal vbus when reuse pins + #[cfg(feature = "usb-pin-reuse-hpm5300")] r.phy_ctrl0().modify(|w| { w.set_vbus_valid_override(true); w.set_sess_valid_override(true); From d867fcecb60084eca262f850eb794325836ac5ac Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Wed, 24 Jul 2024 19:45:28 +0800 Subject: [PATCH 13/43] feat(example): add usb example Signed-off-by: Haobo Gu --- examples/hpm5300evk/src/bin/usb.rs | 103 +++++++++++++++++++++++++++++ src/usb/bus.rs | 6 ++ src/usb/control_pipe.rs | 9 ++- src/usb/device.rs | 4 ++ src/usb/mod.rs | 3 - 5 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 examples/hpm5300evk/src/bin/usb.rs diff --git a/examples/hpm5300evk/src/bin/usb.rs b/examples/hpm5300evk/src/bin/usb.rs new file mode 100644 index 0000000..6d66de2 --- /dev/null +++ b/examples/hpm5300evk/src/bin/usb.rs @@ -0,0 +1,103 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use hal::usb::{UsbDriver, Instance}; +use futures_util::future::join; +use defmt::info; +use {defmt_rtt as _, hpm_hal as hal, riscv_rt as _}; + +#[embassy_executor::main(entry = "hpm_hal::entry")] +async fn main(_spawner: Spawner) -> ! { + let p = hal::init(Default::default()); + + let usb_driver = hal::usb::UsbDriver::new(p.USB0, p.PA24, p.PA25); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + usb_driver, + config, + &mut config_descriptor, + &mut bos_descriptor, + &mut [], // no msos descriptors + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; + + loop { + + } +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, UsbDriver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + defmt::info!("panic"); + loop {} +} diff --git a/src/usb/bus.rs b/src/usb/bus.rs index c4ddc6c..c92f4b0 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -9,6 +9,7 @@ use crate::usb::EpConfig; impl embassy_usb_driver::Bus for Bus { /// Enable the USB peripheral. async fn enable(&mut self) { + defmt::info!("Bus::enable"); // TODO: dcd init or phy init? self.dcd_init(); // self.phy_init(); @@ -22,6 +23,7 @@ impl embassy_usb_driver::Bus for Bus { /// Disable and powers down the USB peripheral. async fn disable(&mut self) { + defmt::info!("Bus::disable"); // TODO: dcd deinit or phy deinit? self.dcd_deinit(); // self.phy_deinit(); @@ -32,11 +34,13 @@ impl embassy_usb_driver::Bus for Bus { /// This method should asynchronously wait for an event to happen, then /// return it. See [`Event`] for the list of events this method should return. async fn poll(&mut self) -> Event { + defmt::info!("Bus::poll"); todo!() } /// Enable or disable an endpoint. fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { + defmt::info!("Bus::endpoint_set_enabled"); if enabled { let ep_data = self.endpoints[ep_addr.index()]; assert!(ep_data.addr == ep_addr); @@ -54,6 +58,7 @@ impl embassy_usb_driver::Bus for Bus { /// /// If the endpoint is an OUT endpoint, it should be prepared to receive data again. fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { + defmt::info!("Bus::endpoint_set_stalled"); if stalled { self.device_endpoint_stall(ep_addr); } else { @@ -63,6 +68,7 @@ impl embassy_usb_driver::Bus for Bus { /// Get whether the STALL condition is set for an endpoint. fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { + defmt::info!("Bus::endpoint_is_stalled"); self.dcd_endpoint_check_stall(ep_addr) } diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index acb7f47..858ddb1 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -20,6 +20,7 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { } async fn setup(&mut self) -> [u8; 8] { + defmt::info!("ControlPipe::setup"); unsafe { init_qhd(&EpConfig { // Must be EndpointType::Control @@ -50,6 +51,7 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { _first: bool, _last: bool, ) -> Result { + defmt::info!("ControlPipe::dataout"); self.ep_out.transfer(buf).map_err(|_e| EndpointError::BufferOverflow)?; Ok(buf.len()) } @@ -60,6 +62,7 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { _first: bool, _last: bool, ) -> Result<(), embassy_usb_driver::EndpointError> { + defmt::info!("ControlPipe::datain"); self.ep_in.transfer(data).map_err(|_e| EndpointError::BufferOverflow)?; Ok(()) } @@ -67,15 +70,19 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { /// Accept a control request. /// /// Causes the STATUS packet for the current request to be ACKed. - async fn accept(&mut self) {} + async fn accept(&mut self) { + defmt::info!("ControlPipe::accept"); + } async fn reject(&mut self) { + defmt::info!("ControlPipe::reject"); // Reject, set IN+OUT to stall self.ep_in.set_stall(); self.ep_out.set_stall(); } async fn accept_set_address(&mut self, addr: u8) { + defmt::info!("ControlPipe::accept_set_address"); // Response with STATUS? // self.ep_in.transfer(&[]); diff --git a/src/usb/device.rs b/src/usb/device.rs index 94c29c4..d60296e 100644 --- a/src/usb/device.rs +++ b/src/usb/device.rs @@ -12,6 +12,7 @@ pub(crate) fn device_qtd_get(ep_idx: u8) -> &'static QueueTransferDescriptor { impl Bus { pub(crate) fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { + defmt::info!("Bus::device_bus_reset"); self.dcd_bus_reset(); unsafe { @@ -21,6 +22,7 @@ impl Bus { // Used in `usb_dc_init` pub(crate) fn device_init(&mut self, int_mask: u32) { + defmt::info!("Bus::device_init"); // Clear dcd data first unsafe { DCD_DATA = DcdData::default(); @@ -46,10 +48,12 @@ impl Bus { } pub(crate) fn device_deinit(&mut self) { + defmt::info!("Bus::device_deinit"); self.dcd_deinit(); } pub(crate) fn device_endpoint_close(&mut self, ep_addr: EndpointAddress) { + defmt::info!("Bus::device_edpt_close"); self.dcd_endpoint_close(ep_addr); } } diff --git a/src/usb/mod.rs b/src/usb/mod.rs index a162b6e..45b0f32 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -398,9 +398,6 @@ impl<'d, T: Instance> UsbDriver<'d, T> { w.set_sess_valid_override_en(true); }); - // suppress "unused" warnings. - let _ = (dp, dm); - UsbDriver { phantom: PhantomData, info: T::info(), From d0a59b502b82300e9272862a3e44547d180a1e51 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Wed, 24 Jul 2024 22:12:28 +0800 Subject: [PATCH 14/43] feat(usb): enable usb interrupt Signed-off-by: HaoboGu --- examples/hpm5300evk/src/bin/usb.rs | 6 +++--- src/usb/bus.rs | 1 + src/usb/control_pipe.rs | 29 +++++++++++++++++++++++++++-- src/usb/endpoint.rs | 3 ++- src/usb/mod.rs | 23 +++++++++++++++++++++++ 5 files changed, 56 insertions(+), 6 deletions(-) diff --git a/examples/hpm5300evk/src/bin/usb.rs b/examples/hpm5300evk/src/bin/usb.rs index 6d66de2..7308097 100644 --- a/examples/hpm5300evk/src/bin/usb.rs +++ b/examples/hpm5300evk/src/bin/usb.rs @@ -91,13 +91,13 @@ async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, UsbDriver<'d, T> loop { let n = class.read_packet(&mut buf).await?; let data = &buf[..n]; - info!("data: {:x}", data); + // info!("data: {:x}", data); class.write_packet(data).await?; } } #[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - defmt::info!("panic"); +fn panic(info: &core::panic::PanicInfo) -> ! { + defmt::info!("panic: {:?}", defmt::Debug2Format(&info)); loop {} } diff --git a/src/usb/bus.rs b/src/usb/bus.rs index c92f4b0..1360bbe 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -35,6 +35,7 @@ impl embassy_usb_driver::Bus for Bus { /// return it. See [`Event`] for the list of events this method should return. async fn poll(&mut self) -> Event { defmt::info!("Bus::poll"); + embassy_time::Timer::after_secs(200000).await; todo!() } diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 858ddb1..1341536 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -1,17 +1,21 @@ use core::marker::PhantomData; +use core::task::Poll; +use defmt::info; use embassy_usb_driver::EndpointError; +use futures_util::future::poll_fn; use hpm_metapac::usb::regs::Endptsetupstat; use super::endpoint::Endpoint; use super::Instance; -use crate::usb::{init_qhd, EpConfig, DCD_DATA}; +use crate::usb::{init_qhd, EpConfig, State, DCD_DATA}; pub struct ControlPipe<'d, T: Instance> { pub(crate) phantom: PhantomData<&'d mut T>, pub(crate) max_packet_size: usize, pub(crate) ep_in: Endpoint, pub(crate) ep_out: Endpoint, + pub(crate) state: &'static State, } impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { @@ -30,11 +34,31 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { }) } + let r = T::info().regs; + // TODO: 1. Wait for SETUP packet(interrupt here) + // Clear interrupt status + r.usbsts().modify(|w| w.set_ui(false)); + // Set USB interrupt enable + r.usbintr().modify(|w| w.set_ue(true)); + info!("Waiting for setup packet"); + poll_fn(|cx| { + self.state.waker.register(cx.waker()); + + if r.usbsts().read().ui() { + info!("Got setup packet"); + r.usbsts().write(|w| w.set_ui(true)); // W1C + return Poll::Ready(Ok::<(), ()>(())); + } + Poll::Pending + }) + .await; + // .unwrap(); + while !r.usbsts().read().ui() {} + info!("Got setup packet"); // 2. Read setup packet from qhd buffer // See hpm_sdk/middleware/cherryusb/port/hpm/usb_dc_hpm.c: 285L - let r = T::info().regs; // TODO: clear setup status, should we clear ALL? r.endptsetupstat().write_value(Endptsetupstat::default()); @@ -42,6 +66,7 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { let setup_packet = unsafe { DCD_DATA.qhd[0].setup_request }; // Convert to slice + defmt::trace!("setup_packet: {:?}", setup_packet); setup_packet.0.to_le_bytes() } diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index d97ac5b..cb313e0 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -136,7 +136,8 @@ impl embassy_usb_driver::Endpoint for Endpoint { } async fn wait_enabled(&mut self) { - todo!() + defmt::info!("Endpoint::wait_enabled"); + todo!(); } } diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 45b0f32..de920e7 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -1,4 +1,5 @@ use core::marker::PhantomData; +use core::task::Poll; use bitfield_struct::bitfield; use control_pipe::ControlPipe; @@ -7,9 +8,11 @@ use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{Direction, Driver, EndpointAddress, EndpointAllocError, EndpointInfo, EndpointType}; use embedded_hal::delay::DelayNs; use endpoint::Endpoint; +use futures_util::future::poll_fn; use riscv::delay::McycleDelay; use crate::gpio::Pin; +use crate::interrupt::typelevel::Interrupt as _; use crate::sysctl; mod bus; @@ -123,6 +126,7 @@ impl QueueHead { } #[bitfield(u64)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) struct ControlRequest { #[bits(8)] request_type: u8, @@ -316,6 +320,7 @@ impl<'d, T: Instance> UsbDriver<'d, T> { #[cfg(feature = "usb-pin-reuse-hpm5300")] dm: impl Peripheral

> + 'd, #[cfg(feature = "usb-pin-reuse-hpm5300")] dp: impl Peripheral

> + 'd, ) -> Self { + unsafe { T::Interrupt::enable() }; // TODO: Initialization // // For HPM5300 series, DP/DM are reused with PA24/PA25. @@ -547,6 +552,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { max_packet_size: control_max_packet_size as usize, ep_in, ep_out, + state: T::state(), }, ) } @@ -600,5 +606,22 @@ foreach_peripheral!( }; ); +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl crate::interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + on_interrupt::() + } +} + +pub unsafe fn on_interrupt() { + defmt::info!("USB interrupt"); + let r = T::info().regs; + + T::state().waker.wake(); +} + pin_trait!(DmPin, Instance); pin_trait!(DpPin, Instance); From 41685e9a2499911233a9958d0a5505858cf61503 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Thu, 25 Jul 2024 11:39:05 +0800 Subject: [PATCH 15/43] refactor(usb): re-organize usb code Signed-off-by: Haobo Gu --- src/usb/bus.rs | 63 +++++++++++++++++++++++++++++++++++++++++---- src/usb/device.rs | 50 +---------------------------------- src/usb/endpoint.rs | 9 ++++++- src/usb/mod.rs | 27 +++++-------------- 4 files changed, 73 insertions(+), 76 deletions(-) diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 1360bbe..66d7ee5 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -1,10 +1,17 @@ use defmt::error; -use embassy_usb_driver::{EndpointAddress, EndpointType, Event, Unsupported}; +use embassy_usb_driver::{EndpointAddress, EndpointInfo, EndpointType, Event, Unsupported}; use embedded_hal::delay::DelayNs; use hpm_metapac::usb::regs::*; - -use super::{init_qhd, Bus, ENDPOINT_COUNT}; -use crate::usb::EpConfig; +use riscv::delay::McycleDelay; + +use super::{init_qhd, Info, ENDPOINT_COUNT}; +use crate::usb::{reset_dcd_data, DcdData, EpConfig, DCD_DATA}; +#[allow(unused)] +pub struct Bus { + pub(crate) info: &'static Info, + pub(crate) endpoints: [EndpointInfo; ENDPOINT_COUNT], + pub(crate) delay: McycleDelay, +} impl embassy_usb_driver::Bus for Bus { /// Enable the USB peripheral. @@ -290,7 +297,7 @@ impl Bus { pub(crate) fn device_endpoint_open(&mut self, ep_config: EpConfig) { if ep_config.ep_addr.index() >= ENDPOINT_COUNT { error!("Invalid endpoint index"); - return + return; } // Prepare queue head @@ -423,4 +430,50 @@ impl Bus { r.endptctrl(ep_addr.index() as usize).read().rxs() } } + + pub(crate) fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { + defmt::info!("Bus::device_bus_reset"); + self.dcd_bus_reset(); + + unsafe { + reset_dcd_data(ep0_max_packet_size); + } + } + + // Used in `usb_dc_init` + pub(crate) fn device_init(&mut self, int_mask: u32) { + defmt::info!("Bus::device_init"); + // Clear dcd data first + unsafe { + DCD_DATA = DcdData::default(); + } + + // Initialize controller + self.dcd_init(); + + let r = &self.info.regs; + // Set endpoint list address + // TODO: Check if this is correct + let addr = unsafe { DCD_DATA.qhd.as_ptr() as u32 }; + r.endptlistaddr().write(|w| w.set_epbase(addr)); + + // Clear status + r.usbsts().modify(|w| w.0 = 0); + + // Enable interrupts + r.usbintr().modify(|w| w.0 = w.0 | int_mask); + + // Connect + r.usbcmd().modify(|w| w.set_rs(true)); + } + + pub(crate) fn device_deinit(&mut self) { + defmt::info!("Bus::device_deinit"); + self.dcd_deinit(); + } + + pub(crate) fn device_endpoint_close(&mut self, ep_addr: EndpointAddress) { + defmt::info!("Bus::device_edpt_close"); + self.dcd_endpoint_close(ep_addr); + } } diff --git a/src/usb/device.rs b/src/usb/device.rs index d60296e..a060b5e 100644 --- a/src/usb/device.rs +++ b/src/usb/device.rs @@ -1,7 +1,7 @@ //! Usb device API //! -use super::{reset_dcd_data, Bus, DcdData, EndpointAddress, QueueHead, QueueTransferDescriptor, DCD_DATA}; +use super::{QueueHead, QueueTransferDescriptor, DCD_DATA}; pub(crate) fn device_qhd_get(ep_idx: u8) -> &'static QueueHead { unsafe { &DCD_DATA.qhd[ep_idx as usize] } @@ -9,51 +9,3 @@ pub(crate) fn device_qhd_get(ep_idx: u8) -> &'static QueueHead { pub(crate) fn device_qtd_get(ep_idx: u8) -> &'static QueueTransferDescriptor { unsafe { &DCD_DATA.qtd[ep_idx as usize * 8] } } - -impl Bus { - pub(crate) fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { - defmt::info!("Bus::device_bus_reset"); - self.dcd_bus_reset(); - - unsafe { - reset_dcd_data(ep0_max_packet_size); - } - } - - // Used in `usb_dc_init` - pub(crate) fn device_init(&mut self, int_mask: u32) { - defmt::info!("Bus::device_init"); - // Clear dcd data first - unsafe { - DCD_DATA = DcdData::default(); - } - - // Initialize controller - self.dcd_init(); - - let r = &self.info.regs; - // Set endpoint list address - // TODO: Check if this is correct - let addr = unsafe { DCD_DATA.qhd.as_ptr() as u32 }; - r.endptlistaddr().write(|w| w.set_epbase(addr)); - - // Clear status - r.usbsts().modify(|w| w.0 = 0); - - // Enable interrupts - r.usbintr().modify(|w| w.0 = w.0 | int_mask); - - // Connect - r.usbcmd().modify(|w| w.set_rs(true)); - } - - pub(crate) fn device_deinit(&mut self) { - defmt::info!("Bus::device_deinit"); - self.dcd_deinit(); - } - - pub(crate) fn device_endpoint_close(&mut self, ep_addr: EndpointAddress) { - defmt::info!("Bus::device_edpt_close"); - self.dcd_endpoint_close(ep_addr); - } -} diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index cb313e0..b117604 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -1,8 +1,15 @@ -use embassy_usb_driver::{EndpointIn, EndpointInfo, EndpointOut}; +use embassy_usb_driver::{EndpointAddress, EndpointIn, EndpointInfo, EndpointOut}; use hpm_metapac::usb::regs::Endptprime; use super::{Error, Info, QueueTransferDescriptor, DCD_DATA, QTD_COUNT_EACH_ENDPOINT}; +pub struct EpConfig { + /// Endpoint type + pub(crate) transfer: u8, + pub(crate) ep_addr: EndpointAddress, + pub(crate) max_packet_size: u16, +} + #[derive(Copy, Clone)] pub struct Endpoint { pub(crate) info: EndpointInfo, diff --git a/src/usb/mod.rs b/src/usb/mod.rs index de920e7..9718b59 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -1,17 +1,16 @@ use core::marker::PhantomData; -use core::task::Poll; use bitfield_struct::bitfield; +use bus::Bus; use control_pipe::ControlPipe; -use embassy_hal_internal::{into_ref, Peripheral}; +#[cfg(feature = "usb-pin-reuse-hpm5300")] +use embassy_hal_internal::into_ref; +use embassy_hal_internal::Peripheral; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{Direction, Driver, EndpointAddress, EndpointAllocError, EndpointInfo, EndpointType}; use embedded_hal::delay::DelayNs; -use endpoint::Endpoint; -use futures_util::future::poll_fn; +use endpoint::{Endpoint, EpConfig}; use riscv::delay::McycleDelay; - -use crate::gpio::Pin; use crate::interrupt::typelevel::Interrupt as _; use crate::sysctl; @@ -272,20 +271,6 @@ struct QueueTransferDescriptorToken { _r5: bool, } -#[allow(unused)] -pub struct Bus { - info: &'static Info, - endpoints: [EndpointInfo; ENDPOINT_COUNT], - delay: McycleDelay, -} - -pub struct EpConfig { - /// Endpoint type - transfer: u8, - ep_addr: EndpointAddress, - max_packet_size: u16, -} - #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub(crate) struct EndpointAllocData { pub(crate) info: EndpointInfo, @@ -316,7 +301,7 @@ pub struct UsbDriver<'d, T: Instance> { impl<'d, T: Instance> UsbDriver<'d, T> { pub fn new( - peri: impl Peripheral

+ 'd, + _peri: impl Peripheral

+ 'd, #[cfg(feature = "usb-pin-reuse-hpm5300")] dm: impl Peripheral

> + 'd, #[cfg(feature = "usb-pin-reuse-hpm5300")] dp: impl Peripheral

> + 'd, ) -> Self { From 76bda518745787edbc5dc9443681fb872f4cce77 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Thu, 25 Jul 2024 11:39:27 +0800 Subject: [PATCH 16/43] feat(usb): add usb interrupt handler skeleton Signed-off-by: Haobo Gu --- examples/hpm5300evk/src/bin/usb.rs | 6 ++++++ src/usb/mod.rs | 33 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/examples/hpm5300evk/src/bin/usb.rs b/examples/hpm5300evk/src/bin/usb.rs index 7308097..0b96c9a 100644 --- a/examples/hpm5300evk/src/bin/usb.rs +++ b/examples/hpm5300evk/src/bin/usb.rs @@ -1,6 +1,7 @@ #![no_main] #![no_std] #![feature(type_alias_impl_trait)] +#![feature(abi_riscv_interrupt)] use embassy_executor::Spawner; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; @@ -9,8 +10,13 @@ use embassy_usb::Builder; use hal::usb::{UsbDriver, Instance}; use futures_util::future::join; use defmt::info; +use hpm_hal::{bind_interrupts, peripherals}; use {defmt_rtt as _, hpm_hal as hal, riscv_rt as _}; +bind_interrupts!(struct Irqs { + USB0 => hal::usb::InterruptHandler; +}); + #[embassy_executor::main(entry = "hpm_hal::entry")] async fn main(_spawner: Spawner) -> ! { let p = hal::init(Default::default()); diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 9718b59..f68dc70 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -605,6 +605,39 @@ pub unsafe fn on_interrupt() { defmt::info!("USB interrupt"); let r = T::info().regs; + // Get triggered interrupts + let status = r.usbsts().read(); + let enabled_interrupts: hpm_metapac::usb::regs::Usbintr = r.usbintr().read(); + + // Clear triggered interrupts status bits + let triggered_interrupts = status.0 & enabled_interrupts.0; + r.usbsts().write(|w| w.0 = w.0 & (!triggered_interrupts)); + + // Disabled interrupt sources + if status.0 == 0 { + return; + } + + // Reset event + if status.uri() { + defmt::info!("Reset event!") + } + + // Suspend event + if status.sli() { + defmt::info!("Suspend event!") + } + + // Port change event + if status.pci() { + defmt::info!("Port change event!") + } + + // Transfer complete event + if status.ui() { + defmt::info!("Transfer complete event!") + } + T::state().waker.wake(); } From c57141155d1637c6090003a5df14134e4a151772 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Thu, 25 Jul 2024 23:49:40 +0800 Subject: [PATCH 17/43] fix(usb): move dcd init to UsbDriver Signed-off-by: HaoboGu --- src/usb/bus.rs | 15 ++++----------- src/usb/mod.rs | 48 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 66d7ee5..a432155 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -1,4 +1,4 @@ -use defmt::error; +use defmt::{error, info}; use embassy_usb_driver::{EndpointAddress, EndpointInfo, EndpointType, Event, Unsupported}; use embedded_hal::delay::DelayNs; use hpm_metapac::usb::regs::*; @@ -16,16 +16,9 @@ pub struct Bus { impl embassy_usb_driver::Bus for Bus { /// Enable the USB peripheral. async fn enable(&mut self) { + // FIXME: dcd init and dcd connect are called when initializing the Bus + // What should be done here? defmt::info!("Bus::enable"); - // TODO: dcd init or phy init? - self.dcd_init(); - // self.phy_init(); - - // TODO: - // Set endpoint list address - // Clear status - // Enable interrupt mask - self.dcd_connect(); } /// Disable and powers down the USB peripheral. @@ -111,7 +104,7 @@ impl Bus { }); // Wait for reset status - while r.otg_ctrl0().read().otg_utmi_reset_sw() {} + while !r.otg_ctrl0().read().otg_utmi_reset_sw() {} // Set suspend r.otg_ctrl0().modify(|w| { diff --git a/src/usb/mod.rs b/src/usb/mod.rs index f68dc70..e831fd3 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -3,6 +3,7 @@ use core::marker::PhantomData; use bitfield_struct::bitfield; use bus::Bus; use control_pipe::ControlPipe; +use defmt::info; #[cfg(feature = "usb-pin-reuse-hpm5300")] use embassy_hal_internal::into_ref; use embassy_hal_internal::Peripheral; @@ -10,7 +11,9 @@ use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{Direction, Driver, EndpointAddress, EndpointAllocError, EndpointInfo, EndpointType}; use embedded_hal::delay::DelayNs; use endpoint::{Endpoint, EpConfig}; +use hpm_metapac::usb::regs::Usbsts; use riscv::delay::McycleDelay; + use crate::interrupt::typelevel::Interrupt as _; use crate::sysctl; @@ -31,7 +34,7 @@ static mut DCD_DATA: DcdData = DcdData { qtd: [QueueTransferDescriptor::new(); ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize], }; -#[repr(C, align(32))] +#[repr(C, align(2048))] pub struct DcdData { /// Queue head /// NON-CACHABLE @@ -388,6 +391,8 @@ impl<'d, T: Instance> UsbDriver<'d, T> { w.set_sess_valid_override_en(true); }); + T::add_resource_group(0); + UsbDriver { phantom: PhantomData, info: T::info(), @@ -511,11 +516,6 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { assert_eq!(ep_out.info.addr.index(), 0); assert_eq!(ep_in.info.addr.index(), 0); - // FIXME: Do nothing now, but check whether we should start the usb device here? - // `Bus` has a `enable` function, which enables the USB peri - // But the comment says this function makes all the allocated endpoints **start operating** - // self.dcd_init(); - let mut eps: [EndpointInfo; ENDPOINT_COUNT] = [EndpointInfo { addr: EndpointAddress::from(0), ep_type: EndpointType::Bulk, @@ -525,13 +525,39 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { for i in 0..ENDPOINT_COUNT { eps[i] = self.endpoints[i].info; } + let mut bus = Bus { + info: self.info, + endpoints: eps, + delay: McycleDelay::new(sysctl::clocks().cpu0.0), + }; + + // Init the usb bus + bus.dcd_init(); + + { + let r = self.info.regs; + // Set endpoint list address + // FIXME: the addr should be 2K bytes aligned + unsafe { + r.endptlistaddr() + .modify(|w| w.set_epbase(DCD_DATA.qhd.as_ptr() as u32 & 0xFFFFF800)) + }; + + // Clear status + // Enable interrupt mask + r.usbsts().write_value(Usbsts::default()); + r.usbintr().write(|w| { + w.set_ue(true); + w.set_uee(true); + w.set_pce(true); + w.set_ure(true); + }); + } + + bus.dcd_connect(); ( - Self::Bus { - info: self.info, - endpoints: eps, - delay: McycleDelay::new(crate::sysctl::clocks().cpu0.0), - }, + bus, Self::ControlPipe { phantom: PhantomData, max_packet_size: control_max_packet_size as usize, From 4af0d7af621f80a89919710a8a8b905ff0e69aaf Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Fri, 26 Jul 2024 17:03:13 +0800 Subject: [PATCH 18/43] feat(usb): add interrupt handler Signed-off-by: Haobo Gu --- src/usb/bus.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++----- src/usb/mod.rs | 47 +++++++++++++++++++++++++++---- 2 files changed, 111 insertions(+), 12 deletions(-) diff --git a/src/usb/bus.rs b/src/usb/bus.rs index a432155..0970788 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -1,16 +1,21 @@ +use core::future::poll_fn; +use core::sync::atomic::Ordering; +use core::task::Poll; + use defmt::{error, info}; -use embassy_usb_driver::{EndpointAddress, EndpointInfo, EndpointType, Event, Unsupported}; +use embassy_usb_driver::{Direction, EndpointAddress, EndpointInfo, EndpointType, Event, Unsupported}; use embedded_hal::delay::DelayNs; use hpm_metapac::usb::regs::*; use riscv::delay::McycleDelay; -use super::{init_qhd, Info, ENDPOINT_COUNT}; -use crate::usb::{reset_dcd_data, DcdData, EpConfig, DCD_DATA}; +use super::{init_qhd, Info, State, ENDPOINT_COUNT}; +use crate::usb::{reset_dcd_data, DcdData, EpConfig, DCD_DATA, IRQ_RESET, IRQ_SUSPEND}; #[allow(unused)] pub struct Bus { pub(crate) info: &'static Info, pub(crate) endpoints: [EndpointInfo; ENDPOINT_COUNT], pub(crate) delay: McycleDelay, + pub(crate) state: &'static State, } impl embassy_usb_driver::Bus for Bus { @@ -35,8 +40,63 @@ impl embassy_usb_driver::Bus for Bus { /// return it. See [`Event`] for the list of events this method should return. async fn poll(&mut self) -> Event { defmt::info!("Bus::poll"); - embassy_time::Timer::after_secs(200000).await; - todo!() + let r = self.info.regs; + poll_fn(move |cx| { + self.state.waker.register(cx.waker()); + + // // TODO: implement VBUS detection. + // if !self.inited { + // self.inited = true; + // return Poll::Ready(Event::PowerDetected); + // } + + // let regs = T::regs(); + + // 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); + + info!("poll: RESET"); + // TODO: Clear all existing ep data in regs + + // Set device addr to 0 + r.deviceaddr().modify(|w| { + w.set_usbadr(0); + w.set_usbadra(true); + }); + + // Set ep0 IN/OUT + self.endpoint_open(EpConfig { + transfer: EndpointType::Control as u8, + ep_addr: EndpointAddress::from_parts(0, Direction::In), + max_packet_size: 64, + }); + self.endpoint_open(EpConfig { + transfer: EndpointType::Control as u8, + ep_addr: EndpointAddress::from_parts(0, Direction::Out), + max_packet_size: 64, + }); + + // TODO: wake all eps + + + return Poll::Ready(Event::Reset); + } + + if IRQ_SUSPEND.load(Ordering::Acquire) { + IRQ_SUSPEND.store(false, Ordering::Relaxed); + // TODO: Suspend + + return Poll::Ready(Event::Suspend); + } + + Poll::Pending + }) + .await } /// Enable or disable an endpoint. @@ -45,7 +105,7 @@ impl embassy_usb_driver::Bus for Bus { if enabled { let ep_data = self.endpoints[ep_addr.index()]; assert!(ep_data.addr == ep_addr); - self.device_endpoint_open(EpConfig { + self.endpoint_open(EpConfig { transfer: ep_data.ep_type as u8, ep_addr, max_packet_size: ep_data.max_packet_size, @@ -287,7 +347,9 @@ impl Bus { w.set_rs(false); }); } - pub(crate) fn device_endpoint_open(&mut self, ep_config: EpConfig) { + + /// usbd_endpoint_open + pub(crate) fn endpoint_open(&mut self, ep_config: EpConfig) { if ep_config.ep_addr.index() >= ENDPOINT_COUNT { error!("Invalid endpoint index"); return; diff --git a/src/usb/mod.rs b/src/usb/mod.rs index e831fd3..6325cf5 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -1,9 +1,9 @@ use core::marker::PhantomData; +use core::sync::atomic::{AtomicBool, Ordering}; use bitfield_struct::bitfield; use bus::Bus; use control_pipe::ControlPipe; -use defmt::info; #[cfg(feature = "usb-pin-reuse-hpm5300")] use embassy_hal_internal::into_ref; use embassy_hal_internal::Peripheral; @@ -22,6 +22,11 @@ mod control_pipe; mod device; mod endpoint; +static IRQ_RESET: AtomicBool = AtomicBool::new(false); +static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); +static IRQ_TRANSFER_COMPLETED: AtomicBool = AtomicBool::new(false); +static IRQ_PORT_CHANGE: AtomicBool = AtomicBool::new(false); + #[cfg(usb_v67)] const ENDPOINT_COUNT: usize = 8; #[cfg(usb_v53)] @@ -529,6 +534,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { info: self.info, endpoints: eps, delay: McycleDelay::new(sysctl::clocks().cpu0.0), + state: T::state(), }; // Init the usb bus @@ -646,22 +652,53 @@ pub unsafe fn on_interrupt() { // Reset event if status.uri() { - defmt::info!("Reset event!") + // Set IRQ_RESET signal + IRQ_RESET.store(true, Ordering::Relaxed); + + // Wake main thread. Then the reset event will be processed in Bus::poll() + T::state().waker.wake(); } // Suspend event if status.sli() { - defmt::info!("Suspend event!") + // Set IRQ_SUSPEND signal + IRQ_SUSPEND.store(true, Ordering::Relaxed); + + // Wake main thread. Then the suspend event will be processed in Bus::poll() + T::state().waker.wake(); } // Port change event if status.pci() { - defmt::info!("Port change event!") + defmt::info!("Port change event!"); + if r.portsc1().read().ccs() { + // Connected + } else { + // Disconnected + } } // Transfer complete event if status.ui() { - defmt::info!("Transfer complete event!") + defmt::info!("Transfer complete event!"); + let ep_status = r.endptstat().read(); + // Clear the status by rewrite those bits + r.endptstat().modify(|w| w.0 = w.0); + let ep_setup_status = r.endptsetupstat().read(); + if ep_setup_status.0 > 0 { + // Setup received, clear setup status first + r.endptsetupstat().modify(|w| w.0 = w.0); + // Get qhd0 + let qhd0 = unsafe { DCD_DATA.qhd[0] }; + // TODO: usbd_event_ep0_setup_complete_handler + } + + if ep_status.0 > 0 { + // Transfer completed + // TODO: for each endpoint, check endpoint bit + } + + // TODO: WAKE ENDPOINTS } T::state().waker.wake(); From 6913b5e91a0b19608e6a31e2e1bfd0e1e125c61e Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sat, 27 Jul 2024 13:52:09 +0800 Subject: [PATCH 19/43] feat(usb): refactor endpoint allocation and interrupt-waker(but waker does'nt work) Signed-off-by: HaoboGu --- examples/hpm5300evk/src/bin/usb.rs | 2 +- src/usb/bus.rs | 220 +++++++++++++++-------------- src/usb/control_pipe.rs | 18 +-- src/usb/device.rs | 11 -- src/usb/endpoint.rs | 82 +++++++---- src/usb/mod.rs | 111 ++++++++------- 6 files changed, 242 insertions(+), 202 deletions(-) delete mode 100644 src/usb/device.rs diff --git a/examples/hpm5300evk/src/bin/usb.rs b/examples/hpm5300evk/src/bin/usb.rs index 0b96c9a..45ace3e 100644 --- a/examples/hpm5300evk/src/bin/usb.rs +++ b/examples/hpm5300evk/src/bin/usb.rs @@ -77,7 +77,7 @@ async fn main(_spawner: Spawner) -> ! { join(usb_fut, echo_fut).await; loop { - + embassy_time::Timer::after_secs(1000).await; } } diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 0970788..956ebff 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -1,24 +1,27 @@ use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::Ordering; use core::task::Poll; -use defmt::{error, info}; +use defmt::error; use embassy_usb_driver::{Direction, EndpointAddress, EndpointInfo, EndpointType, Event, Unsupported}; use embedded_hal::delay::DelayNs; use hpm_metapac::usb::regs::*; use riscv::delay::McycleDelay; -use super::{init_qhd, Info, State, ENDPOINT_COUNT}; -use crate::usb::{reset_dcd_data, DcdData, EpConfig, DCD_DATA, IRQ_RESET, IRQ_SUSPEND}; -#[allow(unused)] -pub struct Bus { +use super::{init_qhd, Info, Instance, ENDPOINT_COUNT}; +use crate::usb::{reset_dcd_data, EpConfig, BUS_WAKER, IRQ_RESET, IRQ_SUSPEND}; + +pub struct Bus { + pub(crate) _phantom: PhantomData, pub(crate) info: &'static Info, - pub(crate) endpoints: [EndpointInfo; ENDPOINT_COUNT], + pub(crate) endpoints_out: [EndpointInfo; ENDPOINT_COUNT], + pub(crate) endpoints_in: [EndpointInfo; ENDPOINT_COUNT], pub(crate) delay: McycleDelay, - pub(crate) state: &'static State, + pub(crate) inited: bool, } -impl embassy_usb_driver::Bus for Bus { +impl embassy_usb_driver::Bus for Bus { /// Enable the USB peripheral. async fn enable(&mut self) { // FIXME: dcd init and dcd connect are called when initializing the Bus @@ -40,34 +43,24 @@ impl embassy_usb_driver::Bus for Bus { /// return it. See [`Event`] for the list of events this method should return. async fn poll(&mut self) -> Event { defmt::info!("Bus::poll"); - let r = self.info.regs; - poll_fn(move |cx| { - self.state.waker.register(cx.waker()); - - // // TODO: implement VBUS detection. - // if !self.inited { - // self.inited = true; - // return Poll::Ready(Event::PowerDetected); - // } - - // let regs = T::regs(); - - // if IRQ_RESUME.load(Ordering::Acquire) { - // IRQ_RESUME.store(false, Ordering::Relaxed); - // return Poll::Ready(Event::Resume); - // } + poll_fn(|cx| { + BUS_WAKER.register(cx.waker()); + let r = self.info.regs; + + // TODO: implement VBUS detection. + if !self.inited { + self.inited = true; + return Poll::Ready(Event::PowerDetected); + } + defmt::info!("WAKED POLL"); if IRQ_RESET.load(Ordering::Acquire) { IRQ_RESET.store(false, Ordering::Relaxed); - info!("poll: RESET"); // TODO: Clear all existing ep data in regs // Set device addr to 0 - r.deviceaddr().modify(|w| { - w.set_usbadr(0); - w.set_usbadra(true); - }); + self.dcd_set_address(0); // Set ep0 IN/OUT self.endpoint_open(EpConfig { @@ -81,16 +74,24 @@ impl embassy_usb_driver::Bus for Bus { max_packet_size: 64, }); - // TODO: wake all eps + // Reset bus + self.device_bus_reset(64); + // TODO: Reset all eps and wake them all to make sure they are reset + defmt::info!("poll: Reset"); return Poll::Ready(Event::Reset); } if IRQ_SUSPEND.load(Ordering::Acquire) { IRQ_SUSPEND.store(false, Ordering::Relaxed); - // TODO: Suspend + if r.portsc1().read().susp() { + // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. + let _device_adr = r.deviceaddr().read().usbadr(); + } + + defmt::info!("poll: SUSPEND"); return Poll::Ready(Event::Suspend); } @@ -103,7 +104,12 @@ impl embassy_usb_driver::Bus for Bus { fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { defmt::info!("Bus::endpoint_set_enabled"); if enabled { - let ep_data = self.endpoints[ep_addr.index()]; + let endpoint_list = if ep_addr.direction() == Direction::In { + self.endpoints_in + } else { + self.endpoints_out + }; + let ep_data = endpoint_list[ep_addr.index()]; assert!(ep_data.addr == ep_addr); self.endpoint_open(EpConfig { transfer: ep_data.ep_type as u8, @@ -144,7 +150,7 @@ impl embassy_usb_driver::Bus for Bus { } } -impl Bus { +impl Bus { pub(crate) fn phy_init(&mut self) { let r = &self.info.regs; @@ -208,11 +214,11 @@ impl Bus { /// Get port speed: 00: full speed, 01: low speed, 10: high speed, 11: undefined /// TODO: Use enum - pub(crate) fn get_port_speed(&mut self) -> u8 { - let r = &self.info.regs; + // pub(crate) fn get_port_speed(&mut self) -> u8 { + // let r = &self.info.regs; - r.portsc1().read().pspd() - } + // r.portsc1().read().pspd() + // } pub(crate) fn dcd_bus_reset(&mut self) { let r = &self.info.regs; @@ -323,30 +329,30 @@ impl Bus { } /// Disconnect by disabling internal pull-up resistor on D+/D- - pub(crate) fn dcd_disconnect(&mut self) { - let r = &self.info.regs; - - // Stop - r.usbcmd().modify(|w| { - w.set_rs(false); - }); - - // Pullup DP to make the phy switch into full speed mode - r.usbcmd().modify(|w| { - w.set_rs(true); - }); - - // Clear sof flag and wait - r.usbsts().modify(|w| { - w.set_sri(true); - }); - while r.usbsts().read().sri() == false {} - - // Disconnect - r.usbcmd().modify(|w| { - w.set_rs(false); - }); - } + // pub(crate) fn dcd_disconnect(&mut self) { + // let r = &self.info.regs; + + // // Stop + // r.usbcmd().modify(|w| { + // w.set_rs(false); + // }); + + // // Pullup DP to make the phy switch into full speed mode + // r.usbcmd().modify(|w| { + // w.set_rs(true); + // }); + + // // Clear sof flag and wait + // r.usbsts().modify(|w| { + // w.set_sri(true); + // }); + // while r.usbsts().read().sri() == false {} + + // // Disconnect + // r.usbcmd().modify(|w| { + // w.set_rs(false); + // }); + // } /// usbd_endpoint_open pub(crate) fn endpoint_open(&mut self, ep_config: EpConfig) { @@ -385,15 +391,15 @@ impl Bus { }); } - pub(crate) fn endpoint_get_type(&mut self, ep_addr: EndpointAddress) -> u8 { - let r = &self.info.regs; + // pub(crate) fn endpoint_get_type(&mut self, ep_addr: EndpointAddress) -> u8 { + // let r = &self.info.regs; - if ep_addr.is_in() { - r.endptctrl(ep_addr.index() as usize).read().txt() - } else { - r.endptctrl(ep_addr.index() as usize).read().rxt() - } - } + // if ep_addr.is_in() { + // r.endptctrl(ep_addr.index() as usize).read().txt() + // } else { + // r.endptctrl(ep_addr.index() as usize).read().rxt() + // } + // } pub(crate) fn device_endpoint_stall(&mut self, ep_addr: EndpointAddress) { let r = &self.info.regs; @@ -476,15 +482,15 @@ impl Bus { }); } - pub(crate) fn ep_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { - let r = &self.info.regs; + // pub(crate) fn ep_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { + // let r = &self.info.regs; - if ep_addr.is_in() { - r.endptctrl(ep_addr.index() as usize).read().txs() - } else { - r.endptctrl(ep_addr.index() as usize).read().rxs() - } - } + // if ep_addr.is_in() { + // r.endptctrl(ep_addr.index() as usize).read().txs() + // } else { + // r.endptctrl(ep_addr.index() as usize).read().rxs() + // } + // } pub(crate) fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { defmt::info!("Bus::device_bus_reset"); @@ -496,36 +502,36 @@ impl Bus { } // Used in `usb_dc_init` - pub(crate) fn device_init(&mut self, int_mask: u32) { - defmt::info!("Bus::device_init"); - // Clear dcd data first - unsafe { - DCD_DATA = DcdData::default(); - } - - // Initialize controller - self.dcd_init(); - - let r = &self.info.regs; - // Set endpoint list address - // TODO: Check if this is correct - let addr = unsafe { DCD_DATA.qhd.as_ptr() as u32 }; - r.endptlistaddr().write(|w| w.set_epbase(addr)); - - // Clear status - r.usbsts().modify(|w| w.0 = 0); - - // Enable interrupts - r.usbintr().modify(|w| w.0 = w.0 | int_mask); - - // Connect - r.usbcmd().modify(|w| w.set_rs(true)); - } - - pub(crate) fn device_deinit(&mut self) { - defmt::info!("Bus::device_deinit"); - self.dcd_deinit(); - } + // pub(crate) fn device_init(&mut self, int_mask: u32) { + // defmt::info!("Bus::device_init"); + // // Clear dcd data first + // unsafe { + // DCD_DATA = DcdData::default(); + // } + + // // Initialize controller + // self.dcd_init(); + + // let r = &self.info.regs; + // // Set endpoint list address + // // TODO: Check if this is correct + // let addr = unsafe { DCD_DATA.qhd.as_ptr() as u32 }; + // r.endptlistaddr().write(|w| w.set_epbase(addr)); + + // // Clear status + // r.usbsts().modify(|w| w.0 = 0); + + // // Enable interrupts + // r.usbintr().modify(|w| w.0 = w.0 | int_mask); + + // // Connect + // r.usbcmd().modify(|w| w.set_rs(true)); + // } + + // pub(crate) fn device_deinit(&mut self) { + // defmt::info!("Bus::device_deinit"); + // self.dcd_deinit(); + // } pub(crate) fn device_endpoint_close(&mut self, ep_addr: EndpointAddress) { defmt::info!("Bus::device_edpt_close"); diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 1341536..209c02c 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -8,14 +8,13 @@ use hpm_metapac::usb::regs::Endptsetupstat; use super::endpoint::Endpoint; use super::Instance; -use crate::usb::{init_qhd, EpConfig, State, DCD_DATA}; +use crate::usb::{init_qhd, EpConfig, DCD_DATA, EP_OUT_WAKERS}; pub struct ControlPipe<'d, T: Instance> { - pub(crate) phantom: PhantomData<&'d mut T>, + pub(crate) _phantom: PhantomData<&'d mut T>, pub(crate) max_packet_size: usize, pub(crate) ep_in: Endpoint, pub(crate) ep_out: Endpoint, - pub(crate) state: &'static State, } impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { @@ -42,19 +41,22 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { // Set USB interrupt enable r.usbintr().modify(|w| w.set_ue(true)); info!("Waiting for setup packet"); - poll_fn(|cx| { - self.state.waker.register(cx.waker()); + let _ = poll_fn(|cx| { + EP_OUT_WAKERS[0].register(cx.waker()); - if r.usbsts().read().ui() { + // Setup received, clear setup status first + if r.endptsetupstat().read().0 != 0 { info!("Got setup packet"); - r.usbsts().write(|w| w.set_ui(true)); // W1C + r.endptsetupstat().modify(|w| w.0 = w.0); return Poll::Ready(Ok::<(), ()>(())); } + + info!("No setup packet yet"); Poll::Pending }) .await; // .unwrap(); - while !r.usbsts().read().ui() {} + // while !r.usbsts().read().ui() {} info!("Got setup packet"); // 2. Read setup packet from qhd buffer diff --git a/src/usb/device.rs b/src/usb/device.rs deleted file mode 100644 index a060b5e..0000000 --- a/src/usb/device.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Usb device API -//! - -use super::{QueueHead, QueueTransferDescriptor, DCD_DATA}; - -pub(crate) fn device_qhd_get(ep_idx: u8) -> &'static QueueHead { - unsafe { &DCD_DATA.qhd[ep_idx as usize] } -} -pub(crate) fn device_qtd_get(ep_idx: u8) -> &'static QueueTransferDescriptor { - unsafe { &DCD_DATA.qtd[ep_idx as usize * 8] } -} diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index b117604..f9eeab6 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -1,6 +1,10 @@ +use core::future::poll_fn; +use core::task::Poll; + use embassy_usb_driver::{EndpointAddress, EndpointIn, EndpointInfo, EndpointOut}; use hpm_metapac::usb::regs::Endptprime; +use crate::usb::{EP_OUT_WAKERS, EP_IN_WAKERS}; use super::{Error, Info, QueueTransferDescriptor, DCD_DATA, QTD_COUNT_EACH_ENDPOINT}; pub struct EpConfig { @@ -111,30 +115,30 @@ impl Endpoint { } } - pub(crate) fn clean_stall(&mut self) { - let r = &self.usb_info.regs; - - r.endptctrl(self.info.addr.index() as usize).modify(|w| { - if self.info.addr.is_in() { - // Data toggle also need to be reset - w.set_txr(true); - w.set_txs(false); - } else { - w.set_rxr(true); - w.set_rxs(false); - } - }); - } - - pub(crate) fn check_stall(&self) -> bool { - let r = &self.usb_info.regs; - - if self.info.addr.is_in() { - r.endptctrl(self.info.addr.index() as usize).read().txs() - } else { - r.endptctrl(self.info.addr.index() as usize).read().rxs() - } - } + // pub(crate) fn clean_stall(&mut self) { + // let r = &self.usb_info.regs; + + // r.endptctrl(self.info.addr.index() as usize).modify(|w| { + // if self.info.addr.is_in() { + // // Data toggle also need to be reset + // w.set_txr(true); + // w.set_txs(false); + // } else { + // w.set_rxr(true); + // w.set_rxs(false); + // } + // }); + // } + + // pub(crate) fn check_stall(&self) -> bool { + // let r = &self.usb_info.regs; + + // if self.info.addr.is_in() { + // r.endptctrl(self.info.addr.index() as usize).read().txs() + // } else { + // r.endptctrl(self.info.addr.index() as usize).read().rxs() + // } + // } } impl embassy_usb_driver::Endpoint for Endpoint { @@ -144,20 +148,44 @@ impl embassy_usb_driver::Endpoint for Endpoint { async fn wait_enabled(&mut self) { defmt::info!("Endpoint::wait_enabled"); - todo!(); + let i = self.info.addr.index(); + assert!(i != 0); + poll_fn(|cx| { + // TODO: Simplify the code + if self.info.addr.is_in() { + EP_IN_WAKERS[i].register(cx.waker()); + // Check if the endpoint is enabled + if self.usb_info.regs.endptctrl(i).read().txe() { + defmt::info!("Endpoint::wait_enabled: enabled"); + Poll::Ready(()) + } else { + Poll::Pending + } + } else { + EP_OUT_WAKERS[i].register(cx.waker()); + // Check if the endpoint is enabled + if self.usb_info.regs.endptctrl(i).read().rxe() { + defmt::info!("Endpoint::wait_enabled: enabled"); + Poll::Ready(()) + } else { + Poll::Pending + } + } + }).await; + defmt::info!("endpoint {} enabled", self.info.addr.index()); } } impl EndpointOut for Endpoint { async fn read(&mut self, buf: &mut [u8]) -> Result { - self.transfer(buf); + self.transfer(buf).unwrap(); Ok(buf.len()) } } impl EndpointIn for Endpoint { async fn write(&mut self, buf: &[u8]) -> Result<(), embassy_usb_driver::EndpointError> { - self.transfer(buf); + self.transfer(buf).unwrap(); Ok(()) } } diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 6325cf5..5c091fa 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -19,7 +19,6 @@ use crate::sysctl; mod bus; mod control_pipe; -mod device; mod endpoint; static IRQ_RESET: AtomicBool = AtomicBool::new(false); @@ -27,6 +26,11 @@ static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); static IRQ_TRANSFER_COMPLETED: AtomicBool = AtomicBool::new(false); static IRQ_PORT_CHANGE: AtomicBool = AtomicBool::new(false); +const AW_NEW: AtomicWaker = AtomicWaker::new(); +static EP_IN_WAKERS: [AtomicWaker; ENDPOINT_COUNT] = [AW_NEW; ENDPOINT_COUNT]; +static EP_OUT_WAKERS: [AtomicWaker; ENDPOINT_COUNT] = [AW_NEW; ENDPOINT_COUNT]; +static BUS_WAKER: AtomicWaker = AtomicWaker::new(); + #[cfg(usb_v67)] const ENDPOINT_COUNT: usize = 8; #[cfg(usb_v53)] @@ -282,21 +286,19 @@ struct QueueTransferDescriptorToken { #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub(crate) struct EndpointAllocData { pub(crate) info: EndpointInfo, - pub(crate) used_in: bool, - pub(crate) used_out: bool, + pub(crate) used: bool, } -impl Default for EndpointAllocData { - fn default() -> Self { +impl EndpointAllocData { + fn new(dir: Direction) -> Self { Self { info: EndpointInfo { - addr: EndpointAddress::from_parts(0, Direction::Out), + addr: EndpointAddress::from_parts(0, dir), max_packet_size: 0, ep_type: EndpointType::Bulk, interval_ms: 0, }, - used_in: false, - used_out: false, + used: false, } } } @@ -304,7 +306,8 @@ impl Default for EndpointAllocData { pub struct UsbDriver<'d, T: Instance> { phantom: PhantomData<&'d mut T>, info: &'static Info, - endpoints: [EndpointAllocData; ENDPOINT_COUNT], + endpoints_in: [EndpointAllocData; ENDPOINT_COUNT], + endpoints_out: [EndpointAllocData; ENDPOINT_COUNT], } impl<'d, T: Instance> UsbDriver<'d, T> { @@ -398,28 +401,31 @@ impl<'d, T: Instance> UsbDriver<'d, T> { T::add_resource_group(0); + // Initialize the bus so that it signals that power is available + BUS_WAKER.wake(); + UsbDriver { phantom: PhantomData, info: T::info(), - endpoints: [EndpointAllocData::default(); ENDPOINT_COUNT], + endpoints_in: [EndpointAllocData::new(Direction::In); ENDPOINT_COUNT], + endpoints_out: [EndpointAllocData::new(Direction::Out); ENDPOINT_COUNT], } } /// Find the free endpoint pub(crate) fn find_free_endpoint(&mut self, ep_type: EndpointType, dir: Direction) -> Option { - self.endpoints - .iter_mut() + let endpoint_list = match dir { + Direction::Out => &mut self.endpoints_out, + Direction::In => &mut self.endpoints_in, + }; + endpoint_list + .iter() .enumerate() .find(|(i, ep)| { if *i == 0 && ep_type != EndpointType::Control { return false; // reserved for control pipe } - let used = ep.used_out || ep.used_in; - let used_dir = match dir { - Direction::Out => ep.used_out, - Direction::In => ep.used_in, - }; - !used || (ep.info.ep_type == ep_type && !used_dir) + !ep.used }) .map(|(i, _)| i) } @@ -432,7 +438,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { type ControlPipe = ControlPipe<'a, T>; - type Bus = Bus; + type Bus = Bus; /// Allocates an OUT endpoint. /// @@ -460,8 +466,8 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { max_packet_size, interval_ms, }; - self.endpoints[ep_idx].used_out = true; - self.endpoints[ep_idx].info = ep.clone(); + self.endpoints_out[ep_idx].used = true; + self.endpoints_out[ep_idx].info = ep.clone(); Ok(Endpoint { info: ep, usb_info: self.info, @@ -494,8 +500,8 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { max_packet_size, interval_ms, }; - self.endpoints[ep_idx].used_out = true; - self.endpoints[ep_idx].info = ep.clone(); + self.endpoints_in[ep_idx].used = true; + self.endpoints_in[ep_idx].info = ep.clone(); Ok(Endpoint { info: ep, usb_info: self.info, @@ -521,20 +527,29 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { assert_eq!(ep_out.info.addr.index(), 0); assert_eq!(ep_in.info.addr.index(), 0); - let mut eps: [EndpointInfo; ENDPOINT_COUNT] = [EndpointInfo { - addr: EndpointAddress::from(0), + let mut endpoints_in: [EndpointInfo; ENDPOINT_COUNT] = [EndpointInfo { + addr: EndpointAddress::from_parts(0, Direction::In), + ep_type: EndpointType::Bulk, + max_packet_size: 0, + interval_ms: 0, + }; ENDPOINT_COUNT]; + let mut endpoints_out: [EndpointInfo; ENDPOINT_COUNT] = [EndpointInfo { + addr: EndpointAddress::from_parts(0, Direction::In), ep_type: EndpointType::Bulk, max_packet_size: 0, interval_ms: 0, }; ENDPOINT_COUNT]; for i in 0..ENDPOINT_COUNT { - eps[i] = self.endpoints[i].info; + endpoints_in[i] = self.endpoints_in[i].info; + endpoints_out[i] = self.endpoints_out[i].info; } let mut bus = Bus { + _phantom: PhantomData, info: self.info, - endpoints: eps, + endpoints_in, + endpoints_out, delay: McycleDelay::new(sysctl::clocks().cpu0.0), - state: T::state(), + inited: false, }; // Init the usb bus @@ -543,7 +558,6 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { { let r = self.info.regs; // Set endpoint list address - // FIXME: the addr should be 2K bytes aligned unsafe { r.endptlistaddr() .modify(|w| w.set_epbase(DCD_DATA.qhd.as_ptr() as u32 & 0xFFFFF800)) @@ -565,16 +579,16 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { ( bus, Self::ControlPipe { - phantom: PhantomData, + _phantom: PhantomData, max_packet_size: control_max_packet_size as usize, ep_in, ep_out, - state: T::state(), }, ) } } +#[derive(Debug)] pub enum Error { InvalidQtdNum, } @@ -583,9 +597,7 @@ pub(super) struct Info { regs: crate::pac::usb::Usb, } -// TODO: USB STATE? struct State { - #[allow(unused)] waker: AtomicWaker, } @@ -634,7 +646,6 @@ impl crate::interrupt::typelevel::Handler for Interru } pub unsafe fn on_interrupt() { - defmt::info!("USB interrupt"); let r = T::info().regs; // Get triggered interrupts @@ -643,20 +654,23 @@ pub unsafe fn on_interrupt() { // Clear triggered interrupts status bits let triggered_interrupts = status.0 & enabled_interrupts.0; - r.usbsts().write(|w| w.0 = w.0 & (!triggered_interrupts)); + r.usbsts().modify(|w| w.0 = w.0 & (!triggered_interrupts)); + + let status = Usbsts(triggered_interrupts); // Disabled interrupt sources if status.0 == 0 { return; } + // defmt::info!("USB interrupt: {:b}", status.0); // Reset event if status.uri() { // Set IRQ_RESET signal IRQ_RESET.store(true, Ordering::Relaxed); // Wake main thread. Then the reset event will be processed in Bus::poll() - T::state().waker.wake(); + BUS_WAKER.wake(); } // Suspend event @@ -665,12 +679,11 @@ pub unsafe fn on_interrupt() { IRQ_SUSPEND.store(true, Ordering::Relaxed); // Wake main thread. Then the suspend event will be processed in Bus::poll() - T::state().waker.wake(); + BUS_WAKER.wake(); } // Port change event if status.pci() { - defmt::info!("Port change event!"); if r.portsc1().read().ccs() { // Connected } else { @@ -684,24 +697,26 @@ pub unsafe fn on_interrupt() { let ep_status = r.endptstat().read(); // Clear the status by rewrite those bits r.endptstat().modify(|w| w.0 = w.0); + let ep_setup_status = r.endptsetupstat().read(); if ep_setup_status.0 > 0 { - // Setup received, clear setup status first - r.endptsetupstat().modify(|w| w.0 = w.0); - // Get qhd0 - let qhd0 = unsafe { DCD_DATA.qhd[0] }; - // TODO: usbd_event_ep0_setup_complete_handler + EP_OUT_WAKERS[0].wake(); } if ep_status.0 > 0 { // Transfer completed - // TODO: for each endpoint, check endpoint bit + for i in 1..ENDPOINT_COUNT { + if ep_status.erbr() & (1 << i) > 0 { + // OUT endpoint + EP_OUT_WAKERS[i].wake(); + } + if ep_status.etbr() & (1 << i) > 0 { + // IN endpoint + EP_IN_WAKERS[i].wake(); + } + } } - - // TODO: WAKE ENDPOINTS } - - T::state().waker.wake(); } pin_trait!(DmPin, Instance); From 90d9bf4bb6435e24cb2a741ad7578e52f265691a Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Mon, 29 Jul 2024 00:22:41 +0800 Subject: [PATCH 20/43] fix(usb): disable usb reset interrupt in interrupt handler Signed-off-by: HaoboGu --- src/usb/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 5c091fa..8d49d3c 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -648,6 +648,8 @@ impl crate::interrupt::typelevel::Handler for Interru pub unsafe fn on_interrupt() { let r = T::info().regs; + defmt::info!("IRQ"); + // Get triggered interrupts let status = r.usbsts().read(); let enabled_interrupts: hpm_metapac::usb::regs::Usbintr = r.usbintr().read(); @@ -655,7 +657,6 @@ pub unsafe fn on_interrupt() { // Clear triggered interrupts status bits let triggered_interrupts = status.0 & enabled_interrupts.0; r.usbsts().modify(|w| w.0 = w.0 & (!triggered_interrupts)); - let status = Usbsts(triggered_interrupts); // Disabled interrupt sources @@ -669,6 +670,9 @@ pub unsafe fn on_interrupt() { // Set IRQ_RESET signal IRQ_RESET.store(true, Ordering::Relaxed); + r.usbintr().modify(|w| w.set_pce(false)); + r.usbintr().modify(|w| w.set_ure(false)); + // Wake main thread. Then the reset event will be processed in Bus::poll() BUS_WAKER.wake(); } From dcf3ee7f0311a226c06912fb8e8364003f2a00c6 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Tue, 30 Jul 2024 00:44:57 +0800 Subject: [PATCH 21/43] feat(usb): polish reset processing, add more debugging info Signed-off-by: HaoboGu --- Cargo.toml | 1 - examples/hpm5300evk/Cargo.toml | 12 +++++++++++- src/usb/bus.rs | 30 +++++++++++++++++------------- src/usb/control_pipe.rs | 8 +++++++- src/usb/mod.rs | 22 ++++++++++++++-------- 5 files changed, 49 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a9c86cf..91dee54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,6 @@ embedded-hal-async = "1.0.0" chrono = { version = "0.4.38", default-features = false, optional = true } mcan = { version = "0.5.0", optional = true } -xhci = "0.9.2" bitfield-struct = "0.8" [build-dependencies] diff --git a/examples/hpm5300evk/Cargo.toml b/examples/hpm5300evk/Cargo.toml index 496ed19..8788683 100644 --- a/examples/hpm5300evk/Cargo.toml +++ b/examples/hpm5300evk/Cargo.toml @@ -5,7 +5,12 @@ edition = "2021" [dependencies] # hpm5301 is a subset of hpm5361, either is ok if you have the 5301 board. -hpm-hal = { path = "../..", features = ["rt", "embassy", "hpm5361"] } +hpm-hal = { path = "../..", features = [ + "rt", + "embassy", + "hpm5361", + "usb-pin-reuse-hpm5300", +] } panic-halt = "0.2.0" riscv-rt = "0.12.2" @@ -32,6 +37,11 @@ assign-resources = "0.4.1" mcan = "0.5.0" embassy-sync = "0.6.0" +embassy-usb = { version = "0.2.0", features = [ + "defmt", + "max-handler-count-8", + "max-interface-count-8", +] } [profile.release] strip = false # symbols are not flashed to the microcontroller, so don't strip them. diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 956ebff..c471b88 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -10,7 +10,7 @@ use hpm_metapac::usb::regs::*; use riscv::delay::McycleDelay; use super::{init_qhd, Info, Instance, ENDPOINT_COUNT}; -use crate::usb::{reset_dcd_data, EpConfig, BUS_WAKER, IRQ_RESET, IRQ_SUSPEND}; +use crate::usb::{reset_dcd_data, EpConfig, BUS_WAKER, DCD_DATA, IRQ_RESET, IRQ_SUSPEND}; pub struct Bus { pub(crate) _phantom: PhantomData, @@ -43,6 +43,7 @@ impl embassy_usb_driver::Bus for Bus { /// return it. See [`Event`] for the list of events this method should return. async fn poll(&mut self) -> Event { defmt::info!("Bus::poll"); + poll_fn(|cx| { BUS_WAKER.register(cx.waker()); let r = self.info.regs; @@ -53,11 +54,14 @@ impl embassy_usb_driver::Bus for Bus { return Poll::Ready(Event::PowerDetected); } - defmt::info!("WAKED POLL"); if IRQ_RESET.load(Ordering::Acquire) { IRQ_RESET.store(false, Ordering::Relaxed); - // TODO: Clear all existing ep data in regs + // Return setup packet + let setup_packet = unsafe { DCD_DATA.qhd[0].setup_request }; + + // Convert to slice + defmt::trace!("check setup_packet in reset: {:?}", setup_packet); // Set device addr to 0 self.dcd_set_address(0); @@ -77,7 +81,8 @@ impl embassy_usb_driver::Bus for Bus { // Reset bus self.device_bus_reset(64); - // TODO: Reset all eps and wake them all to make sure they are reset + // Set ue, enable usb transfer interrupt + r.usbintr().modify(|w| w.set_ue(true)); defmt::info!("poll: Reset"); return Poll::Ready(Event::Reset); @@ -227,20 +232,19 @@ impl Bus { // This is because the default transfer type is control, according to hpm_sdk, // leaving an un-configured endpoint control will cause undefined behavior // for the data PID tracking on the active endpoint. - for i in 0..ENDPOINT_COUNT { + for i in 1..ENDPOINT_COUNT { r.endptctrl(i as usize).write(|w| { w.set_txt(EndpointType::Bulk as u8); w.set_rxt(EndpointType::Bulk as u8); }); } - // Clear all registers - // TODO: CHECK: In hpm_sdk, are those registers REALLY cleared? - r.endptnak().write_value(Endptnak::default()); - r.endptnaken().write_value(Endptnaken(0)); - r.usbsts().write_value(Usbsts::default()); - r.endptsetupstat().write_value(Endptsetupstat::default()); - r.endptcomplete().write_value(Endptcomplete::default()); + // Clear all registers(by writing 1 to any non-zero bits) + r.endptnak().modify(|w| w.0 = w.0); + r.endptnaken().modify(|w| w.0 = 0); + r.usbsts().modify(|w| w.0 = w.0); + r.endptsetupstat().modify(|w| w.0 = w.0); + r.endptcomplete().modify(|w| w.0 = w.0); while r.endptprime().read().0 != 0 {} @@ -275,7 +279,7 @@ impl Bus { w.set_sts(false); // Parallel transceiver width w.set_ptw(false); - // TODO: Set fullspeed mode + // Forced fullspeed mode // w.set_pfsc(true); }); diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 209c02c..e0d6246 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -24,6 +24,13 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { async fn setup(&mut self) -> [u8; 8] { defmt::info!("ControlPipe::setup"); + + // Return setup packet + let setup_packet = unsafe { DCD_DATA.qhd[0].setup_request }; + + // Convert to slice + defmt::trace!("check setup_packet in setup control pipe: {:?}", setup_packet); + unsafe { init_qhd(&EpConfig { // Must be EndpointType::Control @@ -44,7 +51,6 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { let _ = poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); - // Setup received, clear setup status first if r.endptsetupstat().read().0 != 0 { info!("Got setup packet"); r.endptsetupstat().modify(|w| w.0 = w.0); diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 8d49d3c..b866402 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -57,13 +57,13 @@ pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { DCD_DATA.qhd = [QueueHead::new(); ENDPOINT_COUNT as usize * 2]; DCD_DATA.qtd = [QueueTransferDescriptor::new(); ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize]; + // Set qhd for EP0 DCD_DATA.qhd[0].cap.set_zero_length_termination(true); DCD_DATA.qhd[1].cap.set_zero_length_termination(true); DCD_DATA.qhd[0].cap.set_max_packet_size(ep0_max_packet_size); DCD_DATA.qhd[1].cap.set_max_packet_size(ep0_max_packet_size); - // Set the next pointer INVALID - // TODO: replacement? + // Set the next pointer INVALID(T=1) DCD_DATA.qhd[0].qtd_overlay.next = 1; DCD_DATA.qhd[1].qtd_overlay.next = 1; @@ -74,9 +74,8 @@ pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { pub(crate) unsafe fn init_qhd(ep_config: &EpConfig) { let ep_num = ep_config.ep_addr.index(); let ep_idx = 2 * ep_num + ep_config.ep_addr.is_in() as usize; - // Prepare queue head - DCD_DATA.qhd[ep_idx as usize] = QueueHead::default(); + DCD_DATA.qhd[ep_idx as usize] = QueueHead::new(); DCD_DATA.qhd[ep_idx as usize].cap.set_zero_length_termination(true); DCD_DATA.qhd[ep_idx as usize] .cap @@ -648,14 +647,19 @@ impl crate::interrupt::typelevel::Handler for Interru pub unsafe fn on_interrupt() { let r = T::info().regs; - defmt::info!("IRQ"); - // Get triggered interrupts let status = r.usbsts().read(); - let enabled_interrupts: hpm_metapac::usb::regs::Usbintr = r.usbintr().read(); + let enabled_interrupts = r.usbintr().read(); // Clear triggered interrupts status bits let triggered_interrupts = status.0 & enabled_interrupts.0; + defmt::info!( + "USBIRQ: usbsts: {:b}, usbintr: {:b}, trigger intr: {:b}", + status.0, + enabled_interrupts.0, + triggered_interrupts + ); + r.usbsts().modify(|w| w.0 = w.0 & (!triggered_interrupts)); let status = Usbsts(triggered_interrupts); @@ -670,7 +674,6 @@ pub unsafe fn on_interrupt() { // Set IRQ_RESET signal IRQ_RESET.store(true, Ordering::Relaxed); - r.usbintr().modify(|w| w.set_pce(false)); r.usbintr().modify(|w| w.set_ure(false)); // Wake main thread. Then the reset event will be processed in Bus::poll() @@ -689,6 +692,9 @@ pub unsafe fn on_interrupt() { // Port change event if status.pci() { if r.portsc1().read().ccs() { + r.usbintr().modify(|w| w.set_pce(false)); + // Wake main thread. Then the suspend event will be processed in Bus::poll() + BUS_WAKER.wake(); // Connected } else { // Disconnected From 8b97f6f2e682b388d4f0ccad3120c8114c788338 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Wed, 31 Jul 2024 20:29:18 +0800 Subject: [PATCH 22/43] feat(usb): add generated types for qhdlist Signed-off-by: Haobo Gu --- src/usb/types_v53.rs | 406 ++++++++++++++++++++++++++++++++++++++++++ src/usb/types_v62.rs | 414 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 820 insertions(+) create mode 100644 src/usb/types_v53.rs create mode 100644 src/usb/types_v62.rs diff --git a/src/usb/types_v53.rs b/src/usb/types_v53.rs new file mode 100644 index 0000000..7f35e37 --- /dev/null +++ b/src/usb/types_v53.rs @@ -0,0 +1,406 @@ +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::identity_op)] +#![allow(clippy::unnecessary_cast)] +#![allow(clippy::erasing_op)] + +#[doc = "Queue head block for hpm USB device"] +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Qhd { + ptr: *mut u8, +} +unsafe impl Send for Qhd {} +unsafe impl Sync for Qhd {} +impl Qhd { + #[inline(always)] + pub const unsafe fn from_ptr(ptr: *mut ()) -> Self { + Self { ptr: ptr as _ } + } + #[inline(always)] + pub const fn as_ptr(&self) -> *mut () { + self.ptr as _ + } + #[doc = "Capabilities and characteristics"] + #[inline(always)] + pub const fn cap(self) -> crate::common::Reg { + unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x0usize) as _) } + } + #[doc = "Current dtd address"] + #[inline(always)] + pub const fn cur_dtd(self) -> crate::common::Reg { + unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x04usize) as _) } + } + #[doc = "Next dtd address and termination control"] + #[inline(always)] + pub const fn next_dtd(self) -> crate::common::Reg { + unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x08usize) as _) } + } + #[doc = "Other fields in queue transfer descriptor"] + #[inline(always)] + pub const fn qtd_token(self) -> crate::common::Reg { + unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x0cusize) as _) } + } + #[doc = "Buffer pointer"] + #[inline(always)] + pub const fn buffer(self, n: usize) -> crate::common::Reg { + assert!(n < 5usize); + unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x10usize + n * 4usize) as _) } + } + #[doc = "Current offset in buffer"] + #[inline(always)] + pub const fn current_offset(self) -> crate::common::Reg { + unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x10usize) as _) } + } + #[doc = "Buffer for setup packet"] + #[inline(always)] + pub const fn setup_buffer(self, n: usize) -> crate::common::Reg { + assert!(n < 2usize); + unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x28usize + n * 4usize) as _) } + } +} +#[doc = "List of queue head blocks for hpm USB device"] +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Qhdlist { + ptr: *mut u8, +} +unsafe impl Send for Qhdlist {} +unsafe impl Sync for Qhdlist {} +impl Qhdlist { + #[inline(always)] + pub const unsafe fn from_ptr(ptr: *mut ()) -> Self { + Self { ptr: ptr as _ } + } + #[inline(always)] + pub const fn as_ptr(&self) -> *mut () { + self.ptr as _ + } + #[doc = "Queue head block for hpm USB device"] + #[inline(always)] + pub const fn qhd(self, n: usize) -> Qhd { + assert!(n < 32usize); + unsafe { Qhd::from_ptr(self.ptr.add(0x0usize + n * 48usize) as _) } + } +} +pub mod common { + use core::marker::PhantomData; + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct RW; + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct R; + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct W; + mod sealed { + use super::*; + pub trait Access {} + impl Access for R {} + impl Access for W {} + impl Access for RW {} + } + pub trait Access: sealed::Access + Copy {} + impl Access for R {} + impl Access for W {} + impl Access for RW {} + pub trait Read: Access {} + impl Read for RW {} + impl Read for R {} + pub trait Write: Access {} + impl Write for RW {} + impl Write for W {} + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct Reg { + ptr: *mut u8, + phantom: PhantomData<*mut (T, A)>, + } + unsafe impl Send for Reg {} + unsafe impl Sync for Reg {} + impl Reg { + #[allow(clippy::missing_safety_doc)] + #[inline(always)] + pub const unsafe fn from_ptr(ptr: *mut T) -> Self { + Self { + ptr: ptr as _, + phantom: PhantomData, + } + } + #[inline(always)] + pub const fn as_ptr(&self) -> *mut T { + self.ptr as _ + } + } + impl Reg { + #[inline(always)] + pub fn read(&self) -> T { + unsafe { (self.ptr as *mut T).read_volatile() } + } + } + impl Reg { + #[inline(always)] + pub fn write_value(&self, val: T) { + unsafe { (self.ptr as *mut T).write_volatile(val) } + } + } + impl Reg { + #[inline(always)] + pub fn write(&self, f: impl FnOnce(&mut T) -> R) -> R { + let mut val = Default::default(); + let res = f(&mut val); + self.write_value(val); + res + } + } + impl Reg { + #[inline(always)] + pub fn modify(&self, f: impl FnOnce(&mut T) -> R) -> R { + let mut val = self.read(); + let res = f(&mut val); + self.write_value(val); + res + } + } +} +pub mod regs { + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct Buffer(pub u32); + impl Buffer { + #[doc = "4K aligned buffer pointer"] + #[inline(always)] + pub const fn buffer(&self) -> u32 { + let val = (self.0 >> 12usize) & 0x000f_ffff; + val as u32 + } + #[doc = "4K aligned buffer pointer"] + #[inline(always)] + pub fn set_buffer(&mut self, val: u32) { + self.0 = (self.0 & !(0x000f_ffff << 12usize)) | (((val as u32) & 0x000f_ffff) << 12usize); + } + } + impl Default for Buffer { + #[inline(always)] + fn default() -> Buffer { + Buffer(0) + } + } + #[doc = "Capabilities and characteristics"] + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct Cap(pub u32); + impl Cap { + #[doc = "Interrupt on setup packet"] + #[inline(always)] + pub const fn ios(&self) -> bool { + let val = (self.0 >> 15usize) & 0x01; + val != 0 + } + #[doc = "Interrupt on setup packet"] + #[inline(always)] + pub fn set_ios(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize); + } + #[doc = "Maximum packet size"] + #[inline(always)] + pub const fn max_packet_size(&self) -> u16 { + let val = (self.0 >> 16usize) & 0x07ff; + val as u16 + } + #[doc = "Maximum packet size"] + #[inline(always)] + pub fn set_max_packet_size(&mut self, val: u16) { + self.0 = (self.0 & !(0x07ff << 16usize)) | (((val as u32) & 0x07ff) << 16usize); + } + #[doc = "Zero length termination"] + #[inline(always)] + pub const fn zero_length_termination(&self) -> bool { + let val = (self.0 >> 29usize) & 0x01; + val != 0 + } + #[doc = "Zero length termination"] + #[inline(always)] + pub fn set_zero_length_termination(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize); + } + #[doc = "Isochronous mult"] + #[inline(always)] + pub const fn iso_mult(&self) -> u8 { + let val = (self.0 >> 30usize) & 0x03; + val as u8 + } + #[doc = "Isochronous mult"] + #[inline(always)] + pub fn set_iso_mult(&mut self, val: u8) { + self.0 = (self.0 & !(0x03 << 30usize)) | (((val as u32) & 0x03) << 30usize); + } + } + impl Default for Cap { + #[inline(always)] + fn default() -> Cap { + Cap(0) + } + } + #[doc = "Current dtd address"] + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct CurDtd(pub u32); + impl CurDtd { + #[doc = "32-byte aligned address for current dtd, only bits 5-32 are valid"] + #[inline(always)] + pub const fn cur_dtd_addr(&self) -> u32 { + let val = (self.0 >> 5usize) & 0x07ff_ffff; + val as u32 + } + #[doc = "32-byte aligned address for current dtd, only bits 5-32 are valid"] + #[inline(always)] + pub fn set_cur_dtd_addr(&mut self, val: u32) { + self.0 = (self.0 & !(0x07ff_ffff << 5usize)) | (((val as u32) & 0x07ff_ffff) << 5usize); + } + } + impl Default for CurDtd { + #[inline(always)] + fn default() -> CurDtd { + CurDtd(0) + } + } + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct CurrentOffset(pub u32); + impl CurrentOffset { + #[doc = "Current offset in buffer"] + #[inline(always)] + pub const fn current_offset(&self) -> u16 { + let val = (self.0 >> 0usize) & 0x0fff; + val as u16 + } + #[doc = "Current offset in buffer"] + #[inline(always)] + pub fn set_current_offset(&mut self, val: u16) { + self.0 = (self.0 & !(0x0fff << 0usize)) | (((val as u32) & 0x0fff) << 0usize); + } + } + impl Default for CurrentOffset { + #[inline(always)] + fn default() -> CurrentOffset { + CurrentOffset(0) + } + } + #[doc = "Next dtd address and termination control"] + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct NextDtd(pub u32); + impl NextDtd { + #[doc = "Terminate bit, 1 represents current DTD is the last one"] + #[inline(always)] + pub const fn t(&self) -> bool { + let val = (self.0 >> 0usize) & 0x01; + val != 0 + } + #[doc = "Terminate bit, 1 represents current DTD is the last one"] + #[inline(always)] + pub fn set_t(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize); + } + #[doc = "32-byte aligned address for next dtd, only bits 5-32 are valid"] + #[inline(always)] + pub const fn next_dtd_addr(&self) -> u32 { + let val = (self.0 >> 5usize) & 0x07ff_ffff; + val as u32 + } + #[doc = "32-byte aligned address for next dtd, only bits 5-32 are valid"] + #[inline(always)] + pub fn set_next_dtd_addr(&mut self, val: u32) { + self.0 = (self.0 & !(0x07ff_ffff << 5usize)) | (((val as u32) & 0x07ff_ffff) << 5usize); + } + } + impl Default for NextDtd { + #[inline(always)] + fn default() -> NextDtd { + NextDtd(0) + } + } + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct QtdToken(pub u32); + impl QtdToken { + #[doc = "Status and control"] + #[inline(always)] + pub const fn status(&self) -> u8 { + let val = (self.0 >> 0usize) & 0xff; + val as u8 + } + #[doc = "Status and control"] + #[inline(always)] + pub fn set_status(&mut self, val: u8) { + self.0 = (self.0 & !(0xff << 0usize)) | (((val as u32) & 0xff) << 0usize); + } + #[doc = "Multiplier"] + #[inline(always)] + pub const fn multo(&self) -> u8 { + let val = (self.0 >> 10usize) & 0x03; + val as u8 + } + #[doc = "Multiplier"] + #[inline(always)] + pub fn set_multo(&mut self, val: u8) { + self.0 = (self.0 & !(0x03 << 10usize)) | (((val as u32) & 0x03) << 10usize); + } + #[doc = "Current page"] + #[inline(always)] + pub const fn c_page(&self) -> u8 { + let val = (self.0 >> 12usize) & 0x07; + val as u8 + } + #[doc = "Current page"] + #[inline(always)] + pub fn set_c_page(&mut self, val: u8) { + self.0 = (self.0 & !(0x07 << 12usize)) | (((val as u32) & 0x07) << 12usize); + } + #[doc = "Interrupt on complete"] + #[inline(always)] + pub const fn ioc(&self) -> bool { + let val = (self.0 >> 15usize) & 0x01; + val != 0 + } + #[doc = "Interrupt on complete"] + #[inline(always)] + pub fn set_ioc(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize); + } + #[doc = "Total bytes to transfer"] + #[inline(always)] + pub const fn total_bytes(&self) -> u16 { + let val = (self.0 >> 16usize) & 0x7fff; + val as u16 + } + #[doc = "Total bytes to transfer"] + #[inline(always)] + pub fn set_total_bytes(&mut self, val: u16) { + self.0 = (self.0 & !(0x7fff << 16usize)) | (((val as u32) & 0x7fff) << 16usize); + } + } + impl Default for QtdToken { + #[inline(always)] + fn default() -> QtdToken { + QtdToken(0) + } + } + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct SetupBuffer(pub u32); + impl SetupBuffer { + #[doc = "Buffer for setup packet"] + #[inline(always)] + pub const fn setup_buffer(&self) -> u32 { + let val = (self.0 >> 0usize) & 0xffff_ffff; + val as u32 + } + #[doc = "Buffer for setup packet"] + #[inline(always)] + pub fn set_setup_buffer(&mut self, val: u32) { + self.0 = (self.0 & !(0xffff_ffff << 0usize)) | (((val as u32) & 0xffff_ffff) << 0usize); + } + } + impl Default for SetupBuffer { + #[inline(always)] + fn default() -> SetupBuffer { + SetupBuffer(0) + } + } +} diff --git a/src/usb/types_v62.rs b/src/usb/types_v62.rs new file mode 100644 index 0000000..4aba91f --- /dev/null +++ b/src/usb/types_v62.rs @@ -0,0 +1,414 @@ +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::identity_op)] +#![allow(clippy::unnecessary_cast)] +#![allow(clippy::erasing_op)] + +#[doc = "Queue head block for hpm USB device"] +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Qhd { + ptr: *mut u8, +} +unsafe impl Send for Qhd {} +unsafe impl Sync for Qhd {} +impl Qhd { + #[inline(always)] + pub const unsafe fn from_ptr(ptr: *mut ()) -> Self { + Self { ptr: ptr as _ } + } + #[inline(always)] + pub const fn as_ptr(&self) -> *mut () { + self.ptr as _ + } + #[doc = "Capabilities and characteristics"] + #[inline(always)] + pub const fn cap(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x0usize) as _) } + } + #[doc = "Current dtd address"] + #[inline(always)] + pub const fn cur_dtd(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x04usize) as _) } + } + #[doc = "Next dtd address and termination control"] + #[inline(always)] + pub const fn next_dtd(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x08usize) as _) } + } + #[doc = "Other fields in queue transfer descriptor"] + #[inline(always)] + pub const fn qtd_token(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x0cusize) as _) } + } + #[doc = "Buffer pointer"] + #[inline(always)] + pub const fn buffer(self, n: usize) -> common::Reg { + assert!(n < 5usize); + unsafe { common::Reg::from_ptr(self.ptr.add(0x10usize + n * 4usize) as _) } + } + #[doc = "Current offset in buffer"] + #[inline(always)] + pub const fn current_offset(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x10usize) as _) } + } + #[doc = "Buffer for setup packet"] + #[inline(always)] + pub const fn setup_buffer(self, n: usize) -> common::Reg { + assert!(n < 2usize); + unsafe { common::Reg::from_ptr(self.ptr.add(0x28usize + n * 4usize) as _) } + } +} +#[doc = "List of queue head blocks for hpm USB device"] +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Qhdlist { + ptr: *mut u8, +} +unsafe impl Send for Qhdlist {} +unsafe impl Sync for Qhdlist {} +impl Qhdlist { + #[inline(always)] + pub const unsafe fn from_ptr(ptr: *mut ()) -> Self { + Self { ptr: ptr as _ } + } + #[inline(always)] + pub const fn as_ptr(&self) -> *mut () { + self.ptr as _ + } + #[doc = "Queue head block for hpm USB device"] + #[inline(always)] + pub const fn qhd(self, n: usize) -> Qhd { + assert!(n < 16usize); + unsafe { Qhd::from_ptr(self.ptr.add(0x0usize + n * 48usize) as _) } + } + + // Initialize + #[inline(always)] + pub const fn new(data: [u8; 768]) -> Self { + Self { + ptr: data.as_ptr() as _, + } + } +} +pub mod common { + use core::marker::PhantomData; + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct RW; + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct R; + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct W; + mod sealed { + use super::*; + pub trait Access {} + impl Access for R {} + impl Access for W {} + impl Access for RW {} + } + pub trait Access: sealed::Access + Copy {} + impl Access for R {} + impl Access for W {} + impl Access for RW {} + pub trait Read: Access {} + impl Read for RW {} + impl Read for R {} + pub trait Write: Access {} + impl Write for RW {} + impl Write for W {} + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct Reg { + ptr: *mut u8, + phantom: PhantomData<*mut (T, A)>, + } + unsafe impl Send for Reg {} + unsafe impl Sync for Reg {} + impl Reg { + #[allow(clippy::missing_safety_doc)] + #[inline(always)] + pub const unsafe fn from_ptr(ptr: *mut T) -> Self { + Self { + ptr: ptr as _, + phantom: PhantomData, + } + } + #[inline(always)] + pub const fn as_ptr(&self) -> *mut T { + self.ptr as _ + } + } + impl Reg { + #[inline(always)] + pub fn read(&self) -> T { + unsafe { (self.ptr as *mut T).read_volatile() } + } + } + impl Reg { + #[inline(always)] + pub fn write_value(&self, val: T) { + unsafe { (self.ptr as *mut T).write_volatile(val) } + } + } + impl Reg { + #[inline(always)] + pub fn write(&self, f: impl FnOnce(&mut T) -> R) -> R { + let mut val = Default::default(); + let res = f(&mut val); + self.write_value(val); + res + } + } + impl Reg { + #[inline(always)] + pub fn modify(&self, f: impl FnOnce(&mut T) -> R) -> R { + let mut val = self.read(); + let res = f(&mut val); + self.write_value(val); + res + } + } +} +pub mod regs { + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct Buffer(pub u32); + impl Buffer { + #[doc = "4K aligned buffer pointer"] + #[inline(always)] + pub const fn buffer(&self) -> u32 { + let val = (self.0 >> 12usize) & 0x000f_ffff; + val as u32 + } + #[doc = "4K aligned buffer pointer"] + #[inline(always)] + pub fn set_buffer(&mut self, val: u32) { + self.0 = (self.0 & !(0x000f_ffff << 12usize)) | (((val as u32) & 0x000f_ffff) << 12usize); + } + } + impl Default for Buffer { + #[inline(always)] + fn default() -> Buffer { + Buffer(0) + } + } + #[doc = "Capabilities and characteristics"] + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct Cap(pub u32); + impl Cap { + #[doc = "Interrupt on setup packet"] + #[inline(always)] + pub const fn ios(&self) -> bool { + let val = (self.0 >> 15usize) & 0x01; + val != 0 + } + #[doc = "Interrupt on setup packet"] + #[inline(always)] + pub fn set_ios(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize); + } + #[doc = "Maximum packet size"] + #[inline(always)] + pub const fn max_packet_size(&self) -> u16 { + let val = (self.0 >> 16usize) & 0x07ff; + val as u16 + } + #[doc = "Maximum packet size"] + #[inline(always)] + pub fn set_max_packet_size(&mut self, val: u16) { + self.0 = (self.0 & !(0x07ff << 16usize)) | (((val as u32) & 0x07ff) << 16usize); + } + #[doc = "Zero length termination"] + #[inline(always)] + pub const fn zero_length_termination(&self) -> bool { + let val = (self.0 >> 29usize) & 0x01; + val != 0 + } + #[doc = "Zero length termination"] + #[inline(always)] + pub fn set_zero_length_termination(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize); + } + #[doc = "Isochronous mult"] + #[inline(always)] + pub const fn iso_mult(&self) -> u8 { + let val = (self.0 >> 30usize) & 0x03; + val as u8 + } + #[doc = "Isochronous mult"] + #[inline(always)] + pub fn set_iso_mult(&mut self, val: u8) { + self.0 = (self.0 & !(0x03 << 30usize)) | (((val as u32) & 0x03) << 30usize); + } + } + impl Default for Cap { + #[inline(always)] + fn default() -> Cap { + Cap(0) + } + } + #[doc = "Current dtd address"] + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct CurDtd(pub u32); + impl CurDtd { + #[doc = "32-byte aligned address for current dtd, only bits 5-32 are valid"] + #[inline(always)] + pub const fn cur_dtd_addr(&self) -> u32 { + let val = (self.0 >> 5usize) & 0x07ff_ffff; + val as u32 + } + #[doc = "32-byte aligned address for current dtd, only bits 5-32 are valid"] + #[inline(always)] + pub fn set_cur_dtd_addr(&mut self, val: u32) { + self.0 = (self.0 & !(0x07ff_ffff << 5usize)) | (((val as u32) & 0x07ff_ffff) << 5usize); + } + } + impl Default for CurDtd { + #[inline(always)] + fn default() -> CurDtd { + CurDtd(0) + } + } + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct CurrentOffset(pub u32); + impl CurrentOffset { + #[doc = "Current offset in buffer"] + #[inline(always)] + pub const fn current_offset(&self) -> u16 { + let val = (self.0 >> 0usize) & 0x0fff; + val as u16 + } + #[doc = "Current offset in buffer"] + #[inline(always)] + pub fn set_current_offset(&mut self, val: u16) { + self.0 = (self.0 & !(0x0fff << 0usize)) | (((val as u32) & 0x0fff) << 0usize); + } + } + impl Default for CurrentOffset { + #[inline(always)] + fn default() -> CurrentOffset { + CurrentOffset(0) + } + } + #[doc = "Next dtd address and termination control"] + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct NextDtd(pub u32); + impl NextDtd { + #[doc = "Terminate bit, 1 represents current DTD is the last one"] + #[inline(always)] + pub const fn t(&self) -> bool { + let val = (self.0 >> 0usize) & 0x01; + val != 0 + } + #[doc = "Terminate bit, 1 represents current DTD is the last one"] + #[inline(always)] + pub fn set_t(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize); + } + #[doc = "32-byte aligned address for next dtd, only bits 5-32 are valid"] + #[inline(always)] + pub const fn next_dtd_addr(&self) -> u32 { + let val = (self.0 >> 5usize) & 0x07ff_ffff; + val as u32 + } + #[doc = "32-byte aligned address for next dtd, only bits 5-32 are valid"] + #[inline(always)] + pub fn set_next_dtd_addr(&mut self, val: u32) { + self.0 = (self.0 & !(0x07ff_ffff << 5usize)) | (((val as u32) & 0x07ff_ffff) << 5usize); + } + } + impl Default for NextDtd { + #[inline(always)] + fn default() -> NextDtd { + NextDtd(0) + } + } + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct QtdToken(pub u32); + impl QtdToken { + #[doc = "Status and control"] + #[inline(always)] + pub const fn status(&self) -> u8 { + let val = (self.0 >> 0usize) & 0xff; + val as u8 + } + #[doc = "Status and control"] + #[inline(always)] + pub fn set_status(&mut self, val: u8) { + self.0 = (self.0 & !(0xff << 0usize)) | (((val as u32) & 0xff) << 0usize); + } + #[doc = "Multiplier"] + #[inline(always)] + pub const fn multo(&self) -> u8 { + let val = (self.0 >> 10usize) & 0x03; + val as u8 + } + #[doc = "Multiplier"] + #[inline(always)] + pub fn set_multo(&mut self, val: u8) { + self.0 = (self.0 & !(0x03 << 10usize)) | (((val as u32) & 0x03) << 10usize); + } + #[doc = "Current page"] + #[inline(always)] + pub const fn c_page(&self) -> u8 { + let val = (self.0 >> 12usize) & 0x07; + val as u8 + } + #[doc = "Current page"] + #[inline(always)] + pub fn set_c_page(&mut self, val: u8) { + self.0 = (self.0 & !(0x07 << 12usize)) | (((val as u32) & 0x07) << 12usize); + } + #[doc = "Interrupt on complete"] + #[inline(always)] + pub const fn ioc(&self) -> bool { + let val = (self.0 >> 15usize) & 0x01; + val != 0 + } + #[doc = "Interrupt on complete"] + #[inline(always)] + pub fn set_ioc(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize); + } + #[doc = "Total bytes to transfer"] + #[inline(always)] + pub const fn total_bytes(&self) -> u16 { + let val = (self.0 >> 16usize) & 0x7fff; + val as u16 + } + #[doc = "Total bytes to transfer"] + #[inline(always)] + pub fn set_total_bytes(&mut self, val: u16) { + self.0 = (self.0 & !(0x7fff << 16usize)) | (((val as u32) & 0x7fff) << 16usize); + } + } + impl Default for QtdToken { + #[inline(always)] + fn default() -> QtdToken { + QtdToken(0) + } + } + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct SetupBuffer(pub u32); + impl SetupBuffer { + #[doc = "Buffer for setup packet"] + #[inline(always)] + pub const fn setup_buffer(&self) -> u32 { + let val = (self.0 >> 0usize) & 0xffff_ffff; + val as u32 + } + #[doc = "Buffer for setup packet"] + #[inline(always)] + pub fn set_setup_buffer(&mut self, val: u32) { + self.0 = (self.0 & !(0xffff_ffff << 0usize)) | (((val as u32) & 0xffff_ffff) << 0usize); + } + } + impl Default for SetupBuffer { + #[inline(always)] + fn default() -> SetupBuffer { + SetupBuffer(0) + } + } +} From 365c45c7a2a33e3b6d73de57574087149965fba1 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Thu, 1 Aug 2024 00:02:41 +0800 Subject: [PATCH 23/43] feat(usb): receive setup packet, update generated qhd/qtd types Signed-off-by: HaoboGu --- examples/hpm5300evk/src/bin/usb.rs | 6 +- src/usb/bus.rs | 2 +- src/usb/control_pipe.rs | 31 ++-- src/usb/endpoint.rs | 33 ++-- src/usb/mod.rs | 283 +++++++++++------------------ src/usb/types_v53.rs | 170 +++++++++++++++-- src/usb/types_v53.yaml | 187 +++++++++++++++++++ src/usb/types_v62.rs | 146 +++++++++++++-- src/usb/types_v62.yaml | 187 +++++++++++++++++++ 9 files changed, 807 insertions(+), 238 deletions(-) create mode 100644 src/usb/types_v53.yaml create mode 100644 src/usb/types_v62.yaml diff --git a/examples/hpm5300evk/src/bin/usb.rs b/examples/hpm5300evk/src/bin/usb.rs index 45ace3e..7e16d42 100644 --- a/examples/hpm5300evk/src/bin/usb.rs +++ b/examples/hpm5300evk/src/bin/usb.rs @@ -3,13 +3,13 @@ #![feature(type_alias_impl_trait)] #![feature(abi_riscv_interrupt)] +use defmt::info; use embassy_executor::Spawner; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; -use hal::usb::{UsbDriver, Instance}; use futures_util::future::join; -use defmt::info; +use hal::usb::{Instance, UsbDriver}; use hpm_hal::{bind_interrupts, peripherals}; use {defmt_rtt as _, hpm_hal as hal, riscv_rt as _}; @@ -77,7 +77,7 @@ async fn main(_spawner: Spawner) -> ! { join(usb_fut, echo_fut).await; loop { - embassy_time::Timer::after_secs(1000).await; + embassy_time::Timer::after_millis(500).await; } } diff --git a/src/usb/bus.rs b/src/usb/bus.rs index c471b88..e27e871 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -58,7 +58,7 @@ impl embassy_usb_driver::Bus for Bus { IRQ_RESET.store(false, Ordering::Relaxed); // Return setup packet - let setup_packet = unsafe { DCD_DATA.qhd[0].setup_request }; + let setup_packet = unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() }; // Convert to slice defmt::trace!("check setup_packet in reset: {:?}", setup_packet); diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index e0d6246..b245c3d 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -8,7 +8,7 @@ use hpm_metapac::usb::regs::Endptsetupstat; use super::endpoint::Endpoint; use super::Instance; -use crate::usb::{init_qhd, EpConfig, DCD_DATA, EP_OUT_WAKERS}; +use crate::usb::{DCD_DATA, EP_OUT_WAKERS}; pub struct ControlPipe<'d, T: Instance> { pub(crate) _phantom: PhantomData<&'d mut T>, @@ -26,23 +26,24 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { defmt::info!("ControlPipe::setup"); // Return setup packet - let setup_packet = unsafe { DCD_DATA.qhd[0].setup_request }; + let setup_packet = unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() }; // Convert to slice defmt::trace!("check setup_packet in setup control pipe: {:?}", setup_packet); - unsafe { - init_qhd(&EpConfig { - // Must be EndpointType::Control - transfer: self.ep_out.info.ep_type as u8, - ep_addr: self.ep_out.info.addr, - max_packet_size: self.ep_out.info.max_packet_size, - }) - } + // FIXME: remove it? + // unsafe { + // init_qhd(&EpConfig { + // // Must be EndpointType::Control + // transfer: self.ep_out.info.ep_type as u8, + // ep_addr: self.ep_out.info.addr, + // max_packet_size: self.ep_out.info.max_packet_size, + // }) + // } let r = T::info().regs; - // TODO: 1. Wait for SETUP packet(interrupt here) + // 1. Wait for SETUP packet(interrupt here) // Clear interrupt status r.usbsts().modify(|w| w.set_ui(false)); // Set USB interrupt enable @@ -52,17 +53,13 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { EP_OUT_WAKERS[0].register(cx.waker()); if r.endptsetupstat().read().0 != 0 { - info!("Got setup packet"); r.endptsetupstat().modify(|w| w.0 = w.0); return Poll::Ready(Ok::<(), ()>(())); } - info!("No setup packet yet"); Poll::Pending }) .await; - // .unwrap(); - // while !r.usbsts().read().ui() {} info!("Got setup packet"); // 2. Read setup packet from qhd buffer @@ -71,11 +68,11 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { r.endptsetupstat().write_value(Endptsetupstat::default()); // Return setup packet - let setup_packet = unsafe { DCD_DATA.qhd[0].setup_request }; + let setup_packet = unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() }; // Convert to slice defmt::trace!("setup_packet: {:?}", setup_packet); - setup_packet.0.to_le_bytes() + setup_packet } async fn data_out( diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index f9eeab6..cb8a401 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -4,8 +4,12 @@ use core::task::Poll; use embassy_usb_driver::{EndpointAddress, EndpointIn, EndpointInfo, EndpointOut}; use hpm_metapac::usb::regs::Endptprime; -use crate::usb::{EP_OUT_WAKERS, EP_IN_WAKERS}; -use super::{Error, Info, QueueTransferDescriptor, DCD_DATA, QTD_COUNT_EACH_ENDPOINT}; +#[cfg(hpm53)] +use super::types_v53::Qtd; +#[cfg(hpm62)] +use super::types_v62::Qtd; +use super::{Error, Info, DCD_DATA, QTD_COUNT_EACH_ENDPOINT}; +use crate::usb::{EP_IN_WAKERS, EP_OUT_WAKERS}; pub struct EpConfig { /// Endpoint type @@ -50,13 +54,13 @@ impl Endpoint { // data = core_local_mem_to_sys_address(data); // Add all data to the circular queue - let mut prev_qtd: Option = None; - let mut first_qtd: Option = None; + let mut prev_qtd: Option = None; + let mut first_qtd: Option = None; let mut i = 0; let mut data_offset = 0; let mut remaining_bytes = data.len(); loop { - let mut qtd = unsafe { DCD_DATA.qtd[ep_idx * QTD_COUNT_EACH_ENDPOINT + i] }; + let mut qtd = unsafe { DCD_DATA.qtd_list.qtd(ep_idx * QTD_COUNT_EACH_ENDPOINT + i) }; i += 1; // If the transfer size > 0x4000, then there should be multiple qtds in the linked list @@ -67,7 +71,7 @@ impl Endpoint { remaining_bytes = 0; data.len() }; - + // TODO: use data address for multi-core // Check hpm_sdk: static inline uint32_t core_local_mem_to_sys_address() @@ -76,14 +80,16 @@ impl Endpoint { // Last chunk of the data if remaining_bytes == 0 { - qtd.set_token_int_on_complete(true); + qtd.qtd_token().write(|w| w.set_ioc(true)); } data_offset += transfer_bytes; // Set qtd linked list - if let Some(mut prev_qtd) = prev_qtd { - prev_qtd.next = &qtd as *const _ as u32; + if let Some(prev_qtd) = prev_qtd { + prev_qtd + .next_dtd() + .modify(|w| w.set_next_dtd_addr(&qtd as *const _ as u32 >> 5)); } else { first_qtd = Some(qtd); } @@ -97,7 +103,11 @@ impl Endpoint { // Link qtd to qhd unsafe { - DCD_DATA.qhd[ep_idx].qtd_overlay.next = &(first_qtd.unwrap()) as *const _ as u32; + DCD_DATA + .qhd_list + .qhd(ep_idx) + .next_dtd() + .modify(|w| w.set_next_dtd_addr(&first_qtd.unwrap() as *const _ as u32 >> 5)); } // Start transfer @@ -171,7 +181,8 @@ impl embassy_usb_driver::Endpoint for Endpoint { Poll::Pending } } - }).await; + }) + .await; defmt::info!("endpoint {} enabled", self.info.addr.index()); } } diff --git a/src/usb/mod.rs b/src/usb/mod.rs index b866402..1be566e 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -13,6 +13,11 @@ use embedded_hal::delay::DelayNs; use endpoint::{Endpoint, EpConfig}; use hpm_metapac::usb::regs::Usbsts; use riscv::delay::McycleDelay; +use types::{Qhd, QhdList, Qtd, QtdList}; +#[cfg(hpm53)] +use types_v53 as types; +#[cfg(hpm62)] +use types_v62 as types; use crate::interrupt::typelevel::Interrupt as _; use crate::sysctl; @@ -20,6 +25,10 @@ use crate::sysctl; mod bus; mod control_pipe; mod endpoint; +#[cfg(hpm53)] +mod types_v53; +#[cfg(hpm62)] +mod types_v62; static IRQ_RESET: AtomicBool = AtomicBool::new(false); static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); @@ -38,196 +47,120 @@ const ENDPOINT_COUNT: usize = 16; const QTD_COUNT_EACH_ENDPOINT: usize = 8; const QHD_BUFFER_COUNT: usize = 5; +const QHD_SIZE: usize = 64; +const QTD_SIZE: usize = 32; + +static mut QHD_LIST_DATA: QhdListData = QhdListData([0; QHD_SIZE * ENDPOINT_COUNT * 2]); +static mut QTD_LIST_DATA: QtdListData = QtdListData([0; QTD_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_ENDPOINT]); static mut DCD_DATA: DcdData = DcdData { - qhd: [QueueHead::new(); ENDPOINT_COUNT as usize * 2], - qtd: [QueueTransferDescriptor::new(); ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize], + qhd_list: unsafe { QhdList::from_ptr(QHD_LIST_DATA.0.as_ptr() as *mut _) }, + qtd_list: unsafe { QtdList::from_ptr(QTD_LIST_DATA.0.as_ptr() as *mut _) }, }; #[repr(C, align(2048))] -pub struct DcdData { - /// Queue head - /// NON-CACHABLE - pub(crate) qhd: [QueueHead; ENDPOINT_COUNT as usize * 2], - /// Queue element transfer descriptor - /// NON-CACHABLE - pub(crate) qtd: [QueueTransferDescriptor; ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize], -} - -pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { - DCD_DATA.qhd = [QueueHead::new(); ENDPOINT_COUNT as usize * 2]; - DCD_DATA.qtd = [QueueTransferDescriptor::new(); ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize]; - - // Set qhd for EP0 - DCD_DATA.qhd[0].cap.set_zero_length_termination(true); - DCD_DATA.qhd[1].cap.set_zero_length_termination(true); - DCD_DATA.qhd[0].cap.set_max_packet_size(ep0_max_packet_size); - DCD_DATA.qhd[1].cap.set_max_packet_size(ep0_max_packet_size); +pub struct QhdListData([u8; QHD_SIZE * ENDPOINT_COUNT * 2]); - // Set the next pointer INVALID(T=1) - DCD_DATA.qhd[0].qtd_overlay.next = 1; - DCD_DATA.qhd[1].qtd_overlay.next = 1; +pub struct QtdListData([u8; QTD_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_ENDPOINT]); - // Set for OUT only - DCD_DATA.qhd[0].cap.set_int_on_setup(true); -} - -pub(crate) unsafe fn init_qhd(ep_config: &EpConfig) { - let ep_num = ep_config.ep_addr.index(); - let ep_idx = 2 * ep_num + ep_config.ep_addr.is_in() as usize; - // Prepare queue head - DCD_DATA.qhd[ep_idx as usize] = QueueHead::new(); - DCD_DATA.qhd[ep_idx as usize].cap.set_zero_length_termination(true); - DCD_DATA.qhd[ep_idx as usize] - .cap - .set_max_packet_size(ep_config.max_packet_size & 0x7FF); - // Set next to invalid, T=1 - DCD_DATA.qhd[ep_idx as usize].qtd_overlay.next = 1; - if ep_config.transfer == EndpointType::Isochronous as u8 { - DCD_DATA.qhd[ep_idx as usize] - .cap - .set_iso_mult(((ep_config.max_packet_size >> 11) & 0x3) as u8 + 1); - } - if ep_config.transfer == EndpointType::Control as u8 { - DCD_DATA.qhd[ep_idx as usize].cap.set_int_on_setup(true); - } +pub struct DcdData { + /// List of queue head + pub(crate) qhd_list: QhdList, + /// List of queue transfer descriptor + pub(crate) qtd_list: QtdList, } -impl Default for DcdData { - fn default() -> Self { - Self { - qhd: [QueueHead::default(); ENDPOINT_COUNT as usize * 2], - qtd: [QueueTransferDescriptor::default(); ENDPOINT_COUNT as usize * 2 * 8], +impl Qhd { + pub(crate) fn reset(&mut self) { + self.cap().write(|w| w.0 = 0); + self.cur_dtd().write(|w| w.0 = 0); + self.next_dtd().write(|w| w.0 = 0); + self.qtd_token().write(|w| w.0 = 0); + self.current_offset().write(|w| w.0 = 0); + for buf_idx in 0..5 { + self.buffer(buf_idx).write(|w| w.0 = 0); } + self.setup_buffer(0).write(|w| w.0 = 0); + self.setup_buffer(1).write(|w| w.0 = 0); } -} - -pub(crate) struct QueueHeadV2([u8; 48]); -#[derive(Clone, Copy, Default)] -#[repr(align(32))] -pub(crate) struct QueueHead { - // Capabilities and characteristics - pub(crate) cap: CapabilityAndCharacteristics, - // Current qTD pointer - pub(crate) qtd_addr: u32, - - // Transfer overlay - pub(crate) qtd_overlay: QueueTransferDescriptor, - - pub(crate) setup_request: ControlRequest, - // Due to the fact QHD is 64 bytes aligned but occupies only 48 bytes - // thus there are 16 bytes padding free that we can make use of. - // _reserved: [u8; 16], + pub(crate) fn get_setup_request(&self) -> [u8; 8] { + let mut buf = [0_u8; 8]; + buf[0..4].copy_from_slice(&self.setup_buffer(0).read().0.to_le_bytes()); + buf[4..8].copy_from_slice(&self.setup_buffer(1).read().0.to_le_bytes()); + buf + } } -impl QueueHead { - const fn new() -> Self { - Self { - cap: CapabilityAndCharacteristics::new(), - qtd_addr: 0, - qtd_overlay: QueueTransferDescriptor::new(), - setup_request: ControlRequest::new(), - } +pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { + // Clear all qhd data + for i in 0..ENDPOINT_COUNT as usize * 2 { + DCD_DATA.qhd_list.qhd(i).reset(); } - pub(crate) fn set_next_overlay(&mut self, next: u32) { - self.qtd_overlay.next = next; + // TODO: QTD data + for i in 0..ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize { + DCD_DATA.qtd_list.qtd(i).reset(); } -} -#[bitfield(u64)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(crate) struct ControlRequest { - #[bits(8)] - request_type: u8, - #[bits(8)] - request: u8, - #[bits(16)] - value: u16, - #[bits(16)] - index: u16, - #[bits(16)] - length: u16, -} + // Set qhd for EP0 and EP1 + DCD_DATA.qhd_list.qhd(0).cap().modify(|w| { + w.set_max_packet_size(ep0_max_packet_size); + w.set_zero_length_termination(true); + // IOS is set for control OUT endpoint + w.set_ios(true); + }); + DCD_DATA.qhd_list.qhd(1).cap().modify(|w| { + w.set_max_packet_size(ep0_max_packet_size); + w.set_zero_length_termination(true); + }); -#[bitfield(u32)] -pub(crate) struct CapabilityAndCharacteristics { - #[bits(15)] - /// Number of packets executed per transaction descriptor. - /// - /// - 00: Execute N transactions as demonstrated by the - /// USB variable length protocol where N is computed using - /// Max_packet_length and the Total_bytes field in the dTD - /// - 01: Execute one transaction - /// - 10: Execute two transactions - /// - 11: Execute three transactions - /// - /// Remark: Non-isochronous endpoints must set MULT = 00. - /// - /// Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed. - num_packets_per_td: u16, - - /// Interrupt on setup. - /// - /// This bit is used on control type endpoints to indicate if - /// USBINT is set in response to a setup being received. - #[bits(1)] - int_on_setup: bool, - - #[bits(11)] - max_packet_size: u16, - - #[bits(2)] - _reserved: u8, - - #[bits(1)] - zero_length_termination: bool, - - #[bits(2)] - iso_mult: u8, + // Set the next pointer INVALID(T=1) + DCD_DATA.qhd_list.qhd(0).next_dtd().write(|w| w.set_t(true)); + DCD_DATA.qhd_list.qhd(1).next_dtd().write(|w| w.set_t(true)); } -#[derive(Clone, Copy, Default)] -#[repr(C, align(32))] -pub(crate) struct QueueTransferDescriptor { - // Next point - next: u32, - - token: QueueTransferDescriptorToken, - /// Buffer Page Pointer List - /// - /// Each element in the list is a 4K page aligned, physical memory address. - /// The lower 12 bits in each pointer are reserved (except for the first one) - /// as each memory pointer must reference the start of a 4K page - buffer: [u32; QHD_BUFFER_COUNT], +pub(crate) unsafe fn init_qhd(ep_config: &EpConfig) { + let ep_num = ep_config.ep_addr.index(); + let ep_idx = 2 * ep_num + ep_config.ep_addr.is_in() as usize; + // Prepare queue head + DCD_DATA.qhd_list.qhd(ep_idx).reset(); - /// DCD Area - expected_bytes: u16, + DCD_DATA.qhd_list.qhd(ep_idx).cap().write(|w| { + w.set_max_packet_size(ep_config.max_packet_size); + w.set_zero_length_termination(true); + if ep_config.transfer == EndpointType::Isochronous as u8 { + w.set_iso_mult(((ep_config.max_packet_size >> 11) & 0x3) as u8 + 1); + } + if ep_config.transfer == EndpointType::Control as u8 { + w.set_ios(true); + } + }); - _reserved: [u8; 2], + DCD_DATA.qhd_list.qhd(ep_idx).next_dtd().write(|w| w.set_t(true)); } -impl QueueTransferDescriptor { - const fn new() -> Self { - QueueTransferDescriptor { - next: 0, - token: QueueTransferDescriptorToken::new(), - buffer: [0; QHD_BUFFER_COUNT], - expected_bytes: 0, - _reserved: [0; 2], +impl Qtd { + pub(crate) fn reset(&mut self) { + self.current_offset().write(|w| w.0 = 0); + self.next_dtd().write(|w| w.0 = 0); + self.qtd_token().write(|w| w.0 = 0); + for i in 0..QHD_BUFFER_COUNT { + self.buffer(i).write(|w| w.0 = 0); } + self.expected_bytes().write(|w| w.0 = 0); } pub(crate) fn reinit_with(&mut self, data: &[u8], transfer_bytes: usize) { // Initialize qtd - self.next = 0; - self.token = QueueTransferDescriptorToken::new(); - self.buffer = [0; QHD_BUFFER_COUNT]; - self.expected_bytes = 0; + self.reset(); + + self.qtd_token().modify(|w| { + w.set_total_bytes(transfer_bytes as u16); + w.set_active(true) + }); - self.token.set_active(true); - self.token.set_total_bytes(transfer_bytes as u16); - self.expected_bytes = transfer_bytes as u16; + self.expected_bytes() + .modify(|w| w.set_expected_bytes(transfer_bytes as u16)); // According to the UM, buffer[0] is the start address of the transfer data. // Buffer[0] has two parts: buffer[0] & 0xFFFFF000 is the address, and buffer[0] & 0x00000FFF is the offset. @@ -242,16 +175,16 @@ impl QueueTransferDescriptor { } // Fill data into qtd - self.buffer[0] = data.as_ptr() as u32; + self.buffer(0) + .write(|w| w.set_buffer((data.as_ptr() as u32 & 0xFFFFF000) >> 12)); + self.current_offset() + .write(|w| w.set_current_offset((data.as_ptr() as u32 & 0x00000FFF) as u16)); for i in 1..QHD_BUFFER_COUNT { - // Fill address of next 4K bytes - self.buffer[i] |= (self.buffer[i - 1] & 0xFFFFF000) + 4096; + // Fill address of next 4K bytes, note the addr is already shifted, so we just +1 + let addr = self.buffer(i - 1).read().buffer(); + self.buffer(i).write(|w| w.set_buffer(addr + 1)); } } - - pub(crate) fn set_token_int_on_complete(&mut self, value: bool) { - self.token.set_int_on_complete(value); - } } #[bitfield(u32)] @@ -559,7 +492,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { // Set endpoint list address unsafe { r.endptlistaddr() - .modify(|w| w.set_epbase(DCD_DATA.qhd.as_ptr() as u32 & 0xFFFFF800)) + .modify(|w| w.set_epbase(DCD_DATA.qhd_list.as_ptr() as u32 >> 11)) }; // Clear status @@ -653,14 +586,9 @@ pub unsafe fn on_interrupt() { // Clear triggered interrupts status bits let triggered_interrupts = status.0 & enabled_interrupts.0; - defmt::info!( - "USBIRQ: usbsts: {:b}, usbintr: {:b}, trigger intr: {:b}", - status.0, - enabled_interrupts.0, - triggered_interrupts - ); - - r.usbsts().modify(|w| w.0 = w.0 & (!triggered_interrupts)); + + // TODO: Check all other interrupts are cleared + // r.usbsts().modify(|w| w.0 = w.0 & (!triggered_interrupts)); let status = Usbsts(triggered_interrupts); // Disabled interrupt sources @@ -704,12 +632,15 @@ pub unsafe fn on_interrupt() { // Transfer complete event if status.ui() { defmt::info!("Transfer complete event!"); + + r.usbintr().modify(|w| w.set_ue(false)); let ep_status = r.endptstat().read(); // Clear the status by rewrite those bits r.endptstat().modify(|w| w.0 = w.0); let ep_setup_status = r.endptsetupstat().read(); if ep_setup_status.0 > 0 { + defmt::info!("ep_setup_status: {:b}", ep_setup_status.0); EP_OUT_WAKERS[0].wake(); } diff --git a/src/usb/types_v53.rs b/src/usb/types_v53.rs index 7f35e37..eb490ed 100644 --- a/src/usb/types_v53.rs +++ b/src/usb/types_v53.rs @@ -21,50 +21,50 @@ impl Qhd { } #[doc = "Capabilities and characteristics"] #[inline(always)] - pub const fn cap(self) -> crate::common::Reg { - unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x0usize) as _) } + pub const fn cap(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x0usize) as _) } } #[doc = "Current dtd address"] #[inline(always)] - pub const fn cur_dtd(self) -> crate::common::Reg { - unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x04usize) as _) } + pub const fn cur_dtd(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x04usize) as _) } } #[doc = "Next dtd address and termination control"] #[inline(always)] - pub const fn next_dtd(self) -> crate::common::Reg { - unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x08usize) as _) } + pub const fn next_dtd(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x08usize) as _) } } #[doc = "Other fields in queue transfer descriptor"] #[inline(always)] - pub const fn qtd_token(self) -> crate::common::Reg { - unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x0cusize) as _) } + pub const fn qtd_token(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x0cusize) as _) } } #[doc = "Buffer pointer"] #[inline(always)] - pub const fn buffer(self, n: usize) -> crate::common::Reg { + pub const fn buffer(self, n: usize) -> common::Reg { assert!(n < 5usize); - unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x10usize + n * 4usize) as _) } + unsafe { common::Reg::from_ptr(self.ptr.add(0x10usize + n * 4usize) as _) } } #[doc = "Current offset in buffer"] #[inline(always)] - pub const fn current_offset(self) -> crate::common::Reg { - unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x10usize) as _) } + pub const fn current_offset(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x10usize) as _) } } #[doc = "Buffer for setup packet"] #[inline(always)] - pub const fn setup_buffer(self, n: usize) -> crate::common::Reg { + pub const fn setup_buffer(self, n: usize) -> common::Reg { assert!(n < 2usize); - unsafe { crate::common::Reg::from_ptr(self.ptr.add(0x28usize + n * 4usize) as _) } + unsafe { common::Reg::from_ptr(self.ptr.add(0x28usize + n * 4usize) as _) } } } #[doc = "List of queue head blocks for hpm USB device"] #[derive(Copy, Clone, Eq, PartialEq)] -pub struct Qhdlist { +pub struct QhdList { ptr: *mut u8, } -unsafe impl Send for Qhdlist {} -unsafe impl Sync for Qhdlist {} -impl Qhdlist { +unsafe impl Send for QhdList {} +unsafe impl Sync for QhdList {} +impl QhdList { #[inline(always)] pub const unsafe fn from_ptr(ptr: *mut ()) -> Self { Self { ptr: ptr as _ } @@ -77,7 +77,73 @@ impl Qhdlist { #[inline(always)] pub const fn qhd(self, n: usize) -> Qhd { assert!(n < 32usize); - unsafe { Qhd::from_ptr(self.ptr.add(0x0usize + n * 48usize) as _) } + unsafe { Qhd::from_ptr(self.ptr.add(0x0usize + n * 64usize) as _) } + } +} +#[doc = "Queue transfer descriptor for hpm USB device"] +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Qtd { + ptr: *mut u8, +} +unsafe impl Send for Qtd {} +unsafe impl Sync for Qtd {} +impl Qtd { + #[inline(always)] + pub const unsafe fn from_ptr(ptr: *mut ()) -> Self { + Self { ptr: ptr as _ } + } + #[inline(always)] + pub const fn as_ptr(&self) -> *mut () { + self.ptr as _ + } + #[doc = "Next dtd address and termination control"] + #[inline(always)] + pub const fn next_dtd(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x0usize) as _) } + } + #[doc = "Other fields in queue transfer descriptor"] + #[inline(always)] + pub const fn qtd_token(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x04usize) as _) } + } + #[doc = "Buffer pointer"] + #[inline(always)] + pub const fn buffer(self, n: usize) -> common::Reg { + assert!(n < 5usize); + unsafe { common::Reg::from_ptr(self.ptr.add(0x08usize + n * 4usize) as _) } + } + #[doc = "Current offset in buffer"] + #[inline(always)] + pub const fn current_offset(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x08usize) as _) } + } + #[doc = "Number of bytes expected to transfer"] + #[inline(always)] + pub const fn expected_bytes(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x1cusize) as _) } + } +} +#[doc = "List of queue transfer descriptors for hpm USB device"] +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct QtdList { + ptr: *mut u8, +} +unsafe impl Send for QtdList {} +unsafe impl Sync for QtdList {} +impl QtdList { + #[inline(always)] + pub const unsafe fn from_ptr(ptr: *mut ()) -> Self { + Self { ptr: ptr as _ } + } + #[inline(always)] + pub const fn as_ptr(&self) -> *mut () { + self.ptr as _ + } + #[doc = "Queue transfer descriptor for hpm USB device"] + #[inline(always)] + pub const fn qtd(self, n: usize) -> Qtd { + assert!(n < 256usize); + unsafe { Qtd::from_ptr(self.ptr.add(0x0usize + n * 32usize) as _) } } } pub mod common { @@ -281,6 +347,28 @@ pub mod regs { CurrentOffset(0) } } + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct ExpectedBytes(pub u32); + impl ExpectedBytes { + #[doc = "Number of bytes expected to transfer"] + #[inline(always)] + pub const fn expected_bytes(&self) -> u16 { + let val = (self.0 >> 0usize) & 0xffff; + val as u16 + } + #[doc = "Number of bytes expected to transfer"] + #[inline(always)] + pub fn set_expected_bytes(&mut self, val: u16) { + self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize); + } + } + impl Default for ExpectedBytes { + #[inline(always)] + fn default() -> ExpectedBytes { + ExpectedBytes(0) + } + } #[doc = "Next dtd address and termination control"] #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq)] @@ -330,6 +418,50 @@ pub mod regs { pub fn set_status(&mut self, val: u8) { self.0 = (self.0 & !(0xff << 0usize)) | (((val as u32) & 0xff) << 0usize); } + #[doc = "Transaction error"] + #[inline(always)] + pub const fn transaction_err(&self) -> bool { + let val = (self.0 >> 3usize) & 0x01; + val != 0 + } + #[doc = "Transaction error"] + #[inline(always)] + pub fn set_transaction_err(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize); + } + #[doc = "Buffer error, underrun(IN) or overrun(OUT)"] + #[inline(always)] + pub const fn buffer_err(&self) -> bool { + let val = (self.0 >> 5usize) & 0x01; + val != 0 + } + #[doc = "Buffer error, underrun(IN) or overrun(OUT)"] + #[inline(always)] + pub fn set_buffer_err(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize); + } + #[doc = "Whether current dtd is halted"] + #[inline(always)] + pub const fn halted(&self) -> bool { + let val = (self.0 >> 6usize) & 0x01; + val != 0 + } + #[doc = "Whether current dtd is halted"] + #[inline(always)] + pub fn set_halted(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize); + } + #[doc = "Whether current dtd is active"] + #[inline(always)] + pub const fn active(&self) -> bool { + let val = (self.0 >> 7usize) & 0x01; + val != 0 + } + #[doc = "Whether current dtd is active"] + #[inline(always)] + pub fn set_active(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize); + } #[doc = "Multiplier"] #[inline(always)] pub const fn multo(&self) -> u8 { diff --git a/src/usb/types_v53.yaml b/src/usb/types_v53.yaml new file mode 100644 index 0000000..357177c --- /dev/null +++ b/src/usb/types_v53.yaml @@ -0,0 +1,187 @@ +block/qhd_list: + description: List of queue head blocks for hpm USB device + items: + - name: QHD + description: Queue head block for hpm USB device + byte_offset: 0 + array: + len: 32 + stride: 64 + block: qhd +block/qtd_list: + description: List of queue transfer descriptors for hpm USB device + items: + - name: QTD + description: Queue transfer descriptor for hpm USB device + byte_offset: 0 + array: + len: 256 + stride: 32 + block: qtd +block/qhd: + description: Queue head block for hpm USB device + items: + - name: cap + description: Capabilities and characteristics + byte_offset: 0 + fieldset: CAP + - name: CUR_DTD + description: Current dtd address + byte_offset: 4 + fieldset: CUR_DTD + - name: NEXT_DTD + description: Next dtd address and termination control + byte_offset: 8 + fieldset: NEXT_DTD + - name: QTD_TOKEN + description: Other fields in queue transfer descriptor + byte_offset: 12 + fieldset: QTD_TOKEN + - name: BUFFER + description: Buffer pointer + byte_offset: 16 + fieldset: BUFFER + array: + len: 5 + stride: 4 + - name: CURRENT_OFFSET + description: Current offset in buffer + byte_offset: 16 + fieldset: CURRENT_OFFSET + - name: SETUP_BUFFER + description: Buffer for setup packet + byte_offset: 40 + fieldset: SETUP_BUFFER + array: + len: 2 + stride: 4 +block/qtd: + description: Queue transfer descriptor for hpm USB device + items: + - name: NEXT_DTD + description: Next dtd address and termination control + byte_offset: 0 + fieldset: NEXT_DTD + - name: QTD_TOKEN + description: Other fields in queue transfer descriptor + byte_offset: 4 + fieldset: QTD_TOKEN + - name: BUFFER + description: Buffer pointer + byte_offset: 8 + fieldset: BUFFER + array: + len: 5 + stride: 4 + - name: CURRENT_OFFSET + description: Current offset in buffer + byte_offset: 8 + fieldset: CURRENT_OFFSET + - name: EXPECTED_BYTES + description: Number of bytes expected to transfer + byte_offset: 28 + fieldset: EXPECTED_BYTES +fieldset/CAP: + description: Capabilities and characteristics + fields: + - name: IOS + description: Interrupt on setup packet + bit_offset: 15 + bit_size: 1 + - name: MAX_PACKET_SIZE + description: Maximum packet size + bit_offset: 16 + bit_size: 11 + - name: ZERO_LENGTH_TERMINATION + description: Zero length termination + bit_offset: 29 + bit_size: 1 + - name: ISO_MULT + description: Isochronous mult + bit_offset: 30 + bit_size: 2 +fieldset/CUR_DTD: + description: Current dtd address + fields: + - name: CUR_DTD_ADDR + description: 32-byte aligned address for current dtd, only bits 5-32 are valid + bit_offset: 5 + bit_size: 27 +fieldset/NEXT_DTD: + description: Next dtd address and termination control + fields: + - name: T + description: Terminate bit, 1 represents current DTD is the last one + bit_offset: 0 + bit_size: 1 + - name: NEXT_DTD_ADDR + description: 32-byte aligned address for next dtd, only bits 5-32 are valid + bit_offset: 5 + bit_size: 27 +fieldset/QTD_TOKEN: + descritpion: Other fields in queue transfer descriptor + fields: + - name: STATUS + description: Status and control + bit_offset: 0 + bit_size: 8 + - name: ACTIVE + description: Whether current dtd is active + bit_offset: 7 + bit_size: 1 + - name: HALTED + description: Whether current dtd is halted + bit_offset: 6 + bit_size: 1 + - name: BUFFER_ERR + description: Buffer error, underrun(IN) or overrun(OUT) + bit_offset: 5 + bit_size: 1 + - name: TRANSACTION_ERR + description: Transaction error + bit_offset: 3 + bit_size: 1 + - name: MULTO + description: Multiplier + bit_offset: 10 + bit_size: 2 + - name: C_PAGE + description: Current page + bit_offset: 12 + bit_size: 3 + - name: IOC + description: Interrupt on complete + bit_offset: 15 + bit_size: 1 + - name: TOTAL_BYTES + description: Total bytes to transfer + bit_offset: 16 + bit_size: 15 +fieldset/BUFFER: + descritpion: 4K aligned buffer pointer + fields: + - name: BUFFER + description: 4K aligned buffer pointer + bit_offset: 12 + bit_size: 20 +fieldset/CURRENT_OFFSET: + descritpion: Current offset in buffer + fields: + - name: CURRENT_OFFSET + description: Current offset in buffer + bit_offset: 0 + bit_size: 12 +fieldset/SETUP_BUFFER: + descritpion: Buffer for setup packet + fields: + - name: SETUP_BUFFER + description: Buffer for setup packet + bit_offset: 0 + bit_size: 32 +fieldset/EXPECTED_BYTES: + descritpion: Number of bytes expected to transfer + fields: + - name: EXPECTED_BYTES + description: Number of bytes expected to transfer + bit_offset: 0 + bit_size: 16 \ No newline at end of file diff --git a/src/usb/types_v62.rs b/src/usb/types_v62.rs index 4aba91f..9a4fa81 100644 --- a/src/usb/types_v62.rs +++ b/src/usb/types_v62.rs @@ -59,12 +59,12 @@ impl Qhd { } #[doc = "List of queue head blocks for hpm USB device"] #[derive(Copy, Clone, Eq, PartialEq)] -pub struct Qhdlist { +pub struct QhdList { ptr: *mut u8, } -unsafe impl Send for Qhdlist {} -unsafe impl Sync for Qhdlist {} -impl Qhdlist { +unsafe impl Send for QhdList {} +unsafe impl Sync for QhdList {} +impl QhdList { #[inline(always)] pub const unsafe fn from_ptr(ptr: *mut ()) -> Self { Self { ptr: ptr as _ } @@ -77,15 +77,73 @@ impl Qhdlist { #[inline(always)] pub const fn qhd(self, n: usize) -> Qhd { assert!(n < 16usize); - unsafe { Qhd::from_ptr(self.ptr.add(0x0usize + n * 48usize) as _) } + unsafe { Qhd::from_ptr(self.ptr.add(0x0usize + n * 64usize) as _) } } - - // Initialize +} +#[doc = "Queue transfer descriptor for hpm USB device"] +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Qtd { + ptr: *mut u8, +} +unsafe impl Send for Qtd {} +unsafe impl Sync for Qtd {} +impl Qtd { #[inline(always)] - pub const fn new(data: [u8; 768]) -> Self { - Self { - ptr: data.as_ptr() as _, - } + pub const unsafe fn from_ptr(ptr: *mut ()) -> Self { + Self { ptr: ptr as _ } + } + #[inline(always)] + pub const fn as_ptr(&self) -> *mut () { + self.ptr as _ + } + #[doc = "Next dtd address and termination control"] + #[inline(always)] + pub const fn next_dtd(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x0usize) as _) } + } + #[doc = "Other fields in queue transfer descriptor"] + #[inline(always)] + pub const fn qtd_token(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x04usize) as _) } + } + #[doc = "Buffer pointer"] + #[inline(always)] + pub const fn buffer(self, n: usize) -> common::Reg { + assert!(n < 5usize); + unsafe { common::Reg::from_ptr(self.ptr.add(0x08usize + n * 4usize) as _) } + } + #[doc = "Current offset in buffer"] + #[inline(always)] + pub const fn current_offset(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x08usize) as _) } + } + #[doc = "Number of bytes expected to transfer"] + #[inline(always)] + pub const fn expected_bytes(self) -> common::Reg { + unsafe { common::Reg::from_ptr(self.ptr.add(0x1cusize) as _) } + } +} +#[doc = "List of queue transfer descriptors for hpm USB device"] +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct QtdList { + ptr: *mut u8, +} +unsafe impl Send for QtdList {} +unsafe impl Sync for QtdList {} +impl QtdList { + #[inline(always)] + pub const unsafe fn from_ptr(ptr: *mut ()) -> Self { + Self { ptr: ptr as _ } + } + #[inline(always)] + pub const fn as_ptr(&self) -> *mut () { + self.ptr as _ + } + #[doc = "Queue transfer descriptor for hpm USB device"] + #[inline(always)] + pub const fn qtd(self, n: usize) -> Qtd { + assert!(n < 128usize); + unsafe { Qtd::from_ptr(self.ptr.add(0x0usize + n * 32usize) as _) } } } pub mod common { @@ -289,6 +347,28 @@ pub mod regs { CurrentOffset(0) } } + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct ExpectedBytes(pub u32); + impl ExpectedBytes { + #[doc = "Number of bytes expected to transfer"] + #[inline(always)] + pub const fn expected_bytes(&self) -> u16 { + let val = (self.0 >> 0usize) & 0xffff; + val as u16 + } + #[doc = "Number of bytes expected to transfer"] + #[inline(always)] + pub fn set_expected_bytes(&mut self, val: u16) { + self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize); + } + } + impl Default for ExpectedBytes { + #[inline(always)] + fn default() -> ExpectedBytes { + ExpectedBytes(0) + } + } #[doc = "Next dtd address and termination control"] #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq)] @@ -338,6 +418,50 @@ pub mod regs { pub fn set_status(&mut self, val: u8) { self.0 = (self.0 & !(0xff << 0usize)) | (((val as u32) & 0xff) << 0usize); } + #[doc = "Transaction error"] + #[inline(always)] + pub const fn transaction_err(&self) -> bool { + let val = (self.0 >> 3usize) & 0x01; + val != 0 + } + #[doc = "Transaction error"] + #[inline(always)] + pub fn set_transaction_err(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize); + } + #[doc = "Buffer error, underrun(IN) or overrun(OUT)"] + #[inline(always)] + pub const fn buffer_err(&self) -> bool { + let val = (self.0 >> 5usize) & 0x01; + val != 0 + } + #[doc = "Buffer error, underrun(IN) or overrun(OUT)"] + #[inline(always)] + pub fn set_buffer_err(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize); + } + #[doc = "Whether current dtd is halted"] + #[inline(always)] + pub const fn halted(&self) -> bool { + let val = (self.0 >> 6usize) & 0x01; + val != 0 + } + #[doc = "Whether current dtd is halted"] + #[inline(always)] + pub fn set_halted(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize); + } + #[doc = "Whether current dtd is active"] + #[inline(always)] + pub const fn active(&self) -> bool { + let val = (self.0 >> 7usize) & 0x01; + val != 0 + } + #[doc = "Whether current dtd is active"] + #[inline(always)] + pub fn set_active(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize); + } #[doc = "Multiplier"] #[inline(always)] pub const fn multo(&self) -> u8 { diff --git a/src/usb/types_v62.yaml b/src/usb/types_v62.yaml new file mode 100644 index 0000000..6f9a8dd --- /dev/null +++ b/src/usb/types_v62.yaml @@ -0,0 +1,187 @@ +block/qhd_list: + description: List of queue head blocks for hpm USB device + items: + - name: QHD + description: Queue head block for hpm USB device + byte_offset: 0 + array: + len: 16 + stride: 64 + block: qhd +block/qtd_list: + description: List of queue transfer descriptors for hpm USB device + items: + - name: QTD + description: Queue transfer descriptor for hpm USB device + byte_offset: 0 + array: + len: 128 + stride: 32 + block: qtd +block/qhd: + description: Queue head block for hpm USB device + items: + - name: cap + description: Capabilities and characteristics + byte_offset: 0 + fieldset: CAP + - name: CUR_DTD + description: Current dtd address + byte_offset: 4 + fieldset: CUR_DTD + - name: NEXT_DTD + description: Next dtd address and termination control + byte_offset: 8 + fieldset: NEXT_DTD + - name: QTD_TOKEN + description: Other fields in queue transfer descriptor + byte_offset: 12 + fieldset: QTD_TOKEN + - name: BUFFER + description: Buffer pointer + byte_offset: 16 + fieldset: BUFFER + array: + len: 5 + stride: 4 + - name: CURRENT_OFFSET + description: Current offset in buffer + byte_offset: 16 + fieldset: CURRENT_OFFSET + - name: SETUP_BUFFER + description: Buffer for setup packet + byte_offset: 40 + fieldset: SETUP_BUFFER + array: + len: 2 + stride: 4 +block/qtd: + description: Queue transfer descriptor for hpm USB device + items: + - name: NEXT_DTD + description: Next dtd address and termination control + byte_offset: 0 + fieldset: NEXT_DTD + - name: QTD_TOKEN + description: Other fields in queue transfer descriptor + byte_offset: 4 + fieldset: QTD_TOKEN + - name: BUFFER + description: Buffer pointer + byte_offset: 8 + fieldset: BUFFER + array: + len: 5 + stride: 4 + - name: CURRENT_OFFSET + description: Current offset in buffer + byte_offset: 8 + fieldset: CURRENT_OFFSET + - name: EXPECTED_BYTES + description: Number of bytes expected to transfer + byte_offset: 28 + fieldset: EXPECTED_BYTES +fieldset/CAP: + description: Capabilities and characteristics + fields: + - name: IOS + description: Interrupt on setup packet + bit_offset: 15 + bit_size: 1 + - name: MAX_PACKET_SIZE + description: Maximum packet size + bit_offset: 16 + bit_size: 11 + - name: ZERO_LENGTH_TERMINATION + description: Zero length termination + bit_offset: 29 + bit_size: 1 + - name: ISO_MULT + description: Isochronous mult + bit_offset: 30 + bit_size: 2 +fieldset/CUR_DTD: + description: Current dtd address + fields: + - name: CUR_DTD_ADDR + description: 32-byte aligned address for current dtd, only bits 5-32 are valid + bit_offset: 5 + bit_size: 27 +fieldset/NEXT_DTD: + description: Next dtd address and termination control + fields: + - name: T + description: Terminate bit, 1 represents current DTD is the last one + bit_offset: 0 + bit_size: 1 + - name: NEXT_DTD_ADDR + description: 32-byte aligned address for next dtd, only bits 5-32 are valid + bit_offset: 5 + bit_size: 27 +fieldset/QTD_TOKEN: + descritpion: Other fields in queue transfer descriptor + fields: + - name: STATUS + description: Status and control + bit_offset: 0 + bit_size: 8 + - name: ACTIVE + description: Whether current dtd is active + bit_offset: 7 + bit_size: 1 + - name: HALTED + description: Whether current dtd is halted + bit_offset: 6 + bit_size: 1 + - name: BUFFER_ERR + description: Buffer error, underrun(IN) or overrun(OUT) + bit_offset: 5 + bit_size: 1 + - name: TRANSACTION_ERR + description: Transaction error + bit_offset: 3 + bit_size: 1 + - name: MULTO + description: Multiplier + bit_offset: 10 + bit_size: 2 + - name: C_PAGE + description: Current page + bit_offset: 12 + bit_size: 3 + - name: IOC + description: Interrupt on complete + bit_offset: 15 + bit_size: 1 + - name: TOTAL_BYTES + description: Total bytes to transfer + bit_offset: 16 + bit_size: 15 +fieldset/BUFFER: + descritpion: 4K aligned buffer pointer + fields: + - name: BUFFER + description: 4K aligned buffer pointer + bit_offset: 12 + bit_size: 20 +fieldset/CURRENT_OFFSET: + descritpion: Current offset in buffer + fields: + - name: CURRENT_OFFSET + description: Current offset in buffer + bit_offset: 0 + bit_size: 12 +fieldset/SETUP_BUFFER: + descritpion: Buffer for setup packet + fields: + - name: SETUP_BUFFER + description: Buffer for setup packet + bit_offset: 0 + bit_size: 32 +fieldset/EXPECTED_BYTES: + descritpion: Number of bytes expected to transfer + fields: + - name: EXPECTED_BYTES + description: Number of bytes expected to transfer + bit_offset: 0 + bit_size: 16 \ No newline at end of file From 935b638ee82858d401a6613d244fd6db4f146a07 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Thu, 1 Aug 2024 15:04:42 +0800 Subject: [PATCH 24/43] refactor(usb): clean code, add debugging info, fix qtd alignment Signed-off-by: Haobo Gu --- src/usb/control_pipe.rs | 36 ++++--------- src/usb/endpoint.rs | 11 +++- src/usb/mod.rs | 117 +++++++++------------------------------- 3 files changed, 42 insertions(+), 122 deletions(-) diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index b245c3d..3e5b5c8 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -4,7 +4,6 @@ use core::task::Poll; use defmt::info; use embassy_usb_driver::EndpointError; use futures_util::future::poll_fn; -use hpm_metapac::usb::regs::Endptsetupstat; use super::endpoint::Endpoint; use super::Instance; @@ -25,49 +24,31 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { async fn setup(&mut self) -> [u8; 8] { defmt::info!("ControlPipe::setup"); - // Return setup packet - let setup_packet = unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() }; - - // Convert to slice - defmt::trace!("check setup_packet in setup control pipe: {:?}", setup_packet); - - // FIXME: remove it? - // unsafe { - // init_qhd(&EpConfig { - // // Must be EndpointType::Control - // transfer: self.ep_out.info.ep_type as u8, - // ep_addr: self.ep_out.info.addr, - // max_packet_size: self.ep_out.info.max_packet_size, - // }) - // } - let r = T::info().regs; - // 1. Wait for SETUP packet(interrupt here) - // Clear interrupt status + // Wait for SETUP packet(interrupt here) + // Clear interrupt status and enable USB interrupt first r.usbsts().modify(|w| w.set_ui(false)); - // Set USB interrupt enable r.usbintr().modify(|w| w.set_ue(true)); info!("Waiting for setup packet"); let _ = poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); if r.endptsetupstat().read().0 != 0 { + // See hpm_sdk/middleware/cherryusb/port/hpm/usb_dc_hpm.c: 285L + // Clean endpoint setup status r.endptsetupstat().modify(|w| w.0 = w.0); return Poll::Ready(Ok::<(), ()>(())); } Poll::Pending }) - .await; - info!("Got setup packet"); + .await + .unwrap(); - // 2. Read setup packet from qhd buffer - // See hpm_sdk/middleware/cherryusb/port/hpm/usb_dc_hpm.c: 285L - // TODO: clear setup status, should we clear ALL? - r.endptsetupstat().write_value(Endptsetupstat::default()); + info!("Got setup packet"); - // Return setup packet + // Read setup packet from qhd let setup_packet = unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() }; // Convert to slice @@ -93,6 +74,7 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { _last: bool, ) -> Result<(), embassy_usb_driver::EndpointError> { defmt::info!("ControlPipe::datain"); + // TODO: data_in: it's already chunked by max_packet_size self.ep_in.transfer(data).map_err(|_e| EndpointError::BufferOverflow)?; Ok(()) } diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index cb8a401..d5a9394 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -49,8 +49,14 @@ impl Endpoint { if qtd_num > 8 { return Err(Error::InvalidQtdNum); } - - // Convert data's address, TODO: how to do it in Rust? + defmt::info!( + "Current ep num: {}, ep idx: {}, needed qtd num: {}", + ep_num, + ep_idx, + qtd_num + ); + + // TODO: Convert data's address to coressponding core // data = core_local_mem_to_sys_address(data); // Add all data to the circular queue @@ -76,6 +82,7 @@ impl Endpoint { // Check hpm_sdk: static inline uint32_t core_local_mem_to_sys_address() // Initialize qtd with the data + defmt::info!("data_offset: {}, num transfer_bytes: {}", data_offset, transfer_bytes); qtd.reinit_with(&data[data_offset..], transfer_bytes); // Last chunk of the data diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 1be566e..852250f 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -1,7 +1,6 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; -use bitfield_struct::bitfield; use bus::Bus; use control_pipe::ControlPipe; #[cfg(feature = "usb-pin-reuse-hpm5300")] @@ -47,20 +46,21 @@ const ENDPOINT_COUNT: usize = 16; const QTD_COUNT_EACH_ENDPOINT: usize = 8; const QHD_BUFFER_COUNT: usize = 5; -const QHD_SIZE: usize = 64; -const QTD_SIZE: usize = 32; +const QHD_ITEM_SIZE: usize = 64; +const QTD_ITEM_SIZE: usize = 32; -static mut QHD_LIST_DATA: QhdListData = QhdListData([0; QHD_SIZE * ENDPOINT_COUNT * 2]); -static mut QTD_LIST_DATA: QtdListData = QtdListData([0; QTD_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_ENDPOINT]); +static mut QHD_LIST_DATA: QhdListData = QhdListData([0; QHD_ITEM_SIZE * ENDPOINT_COUNT * 2]); +static mut QTD_LIST_DATA: QtdListData = QtdListData([0; QTD_ITEM_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_ENDPOINT]); static mut DCD_DATA: DcdData = DcdData { qhd_list: unsafe { QhdList::from_ptr(QHD_LIST_DATA.0.as_ptr() as *mut _) }, qtd_list: unsafe { QtdList::from_ptr(QTD_LIST_DATA.0.as_ptr() as *mut _) }, }; #[repr(C, align(2048))] -pub struct QhdListData([u8; QHD_SIZE * ENDPOINT_COUNT * 2]); +pub struct QhdListData([u8; QHD_ITEM_SIZE * ENDPOINT_COUNT * 2]); -pub struct QtdListData([u8; QTD_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_ENDPOINT]); +#[repr(C, align(32))] +pub struct QtdListData([u8; QTD_ITEM_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_ENDPOINT]); pub struct DcdData { /// List of queue head @@ -92,12 +92,10 @@ impl Qhd { } pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { - // Clear all qhd data + // Clear all qhd and qtd data for i in 0..ENDPOINT_COUNT as usize * 2 { DCD_DATA.qhd_list.qhd(i).reset(); } - - // TODO: QTD data for i in 0..ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize { DCD_DATA.qtd_list.qtd(i).reset(); } @@ -170,7 +168,7 @@ impl Qtd { // That's why the buffer[1-4] is filled with a `& 0xFFFFF000`. // To be convenient, if the data length is larger than 4K, we require the data address to be 4K bytes aligned. if transfer_bytes > 0x1000 && data.as_ptr() as u32 % 0x1000 != 0 { - // defmt::error!("The buffer[1-4] must be 4K bytes aligned"); + defmt::error!("The buffer[1-4] must be 4K bytes aligned"); return; } @@ -179,6 +177,12 @@ impl Qtd { .write(|w| w.set_buffer((data.as_ptr() as u32 & 0xFFFFF000) >> 12)); self.current_offset() .write(|w| w.set_current_offset((data.as_ptr() as u32 & 0x00000FFF) as u16)); + defmt::info!( + "data_addr: {:x}, buffer0: {:x}, current_offset: {:x}", + data.as_ptr() as u32, + self.buffer(0).read().buffer(), + self.current_offset().read().0 + ); for i in 1..QHD_BUFFER_COUNT { // Fill address of next 4K bytes, note the addr is already shifted, so we just +1 let addr = self.buffer(i - 1).read().buffer(); @@ -187,34 +191,6 @@ impl Qtd { } } -#[bitfield(u32)] -struct QueueTransferDescriptorToken { - #[bits(3)] - _r1: u8, - #[bits(1)] - xact_err: bool, - #[bits(1)] - _r2: bool, - #[bits(1)] - buffer_err: bool, - #[bits(1)] - halted: bool, - #[bits(1)] - active: bool, - #[bits(2)] - _r3: u8, - #[bits(2)] - iso_mult_override: u8, - #[bits(3)] - _r4: u8, - #[bits(1)] - int_on_complete: bool, - #[bits(15)] - total_bytes: u16, - #[bits(1)] - _r5: bool, -} - #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub(crate) struct EndpointAllocData { pub(crate) info: EndpointInfo, @@ -249,53 +225,6 @@ impl<'d, T: Instance> UsbDriver<'d, T> { #[cfg(feature = "usb-pin-reuse-hpm5300")] dp: impl Peripheral

> + 'd, ) -> Self { unsafe { T::Interrupt::enable() }; - // TODO: Initialization - // - // For HPM5300 series, DP/DM are reused with PA24/PA25. - // To use USB, the func_ctl of PA24/PA25 should be set to ANALOG, - // set IOC of PY00/01/02 aka USB0_ID, USB0_OC, USB0_PWR to USB, - // and config PIOC of PY00/01/02 as well. - // - // C code: - // - // ```c - // void init_usb_pins(void) - // { - // HPM_IOC->PAD[IOC_PAD_PA24].FUNC_CTL = IOC_PAD_FUNC_CTL_ANALOG_MASK; - // HPM_IOC->PAD[IOC_PAD_PA25].FUNC_CTL = IOC_PAD_FUNC_CTL_ANALOG_MASK; - // - // /* USB0_ID */ - // HPM_IOC->PAD[IOC_PAD_PY00].FUNC_CTL = IOC_PY00_FUNC_CTL_USB0_ID; - // /* USB0_OC */ - // HPM_IOC->PAD[IOC_PAD_PY01].FUNC_CTL = IOC_PY01_FUNC_CTL_USB0_OC; - // /* USB0_PWR */ - // HPM_IOC->PAD[IOC_PAD_PY02].FUNC_CTL = IOC_PY02_FUNC_CTL_USB0_PWR; - // - // /* PY port IO needs to configure PIOC as well */ - // HPM_PIOC->PAD[IOC_PAD_PY00].FUNC_CTL = PIOC_PY00_FUNC_CTL_SOC_GPIO_Y_00; - // HPM_PIOC->PAD[IOC_PAD_PY01].FUNC_CTL = PIOC_PY01_FUNC_CTL_SOC_GPIO_Y_01; - // HPM_PIOC->PAD[IOC_PAD_PY02].FUNC_CTL = PIOC_PY02_FUNC_CTL_SOC_GPIO_Y_02; - // } - // ``` - // - // After that, power ctrl polarity of vbus should be set - // - // ```c - // // vbus high level enable - // ptr->OTG_CTRL0 |= USB_OTG_CTRL0_OTG_POWER_MASK_MASK; - // ``` - // - // Then wait for 100ms. - // - // Since QFN48/LQFP64 have no vbus pin, there's an extra step: call `usb_phy_using_internal_vbus` to enable internal vbus - // - // ```c - // static inline void usb_phy_using_internal_vbus(USB_Type *ptr) - // { - // ptr->PHY_CTRL0 |= (USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_MASK | USB_PHY_CTRL0_SESS_VALID_OVERRIDE_MASK) - // | (USB_PHY_CTRL0_VBUS_VALID_OVERRIDE_EN_MASK | USB_PHY_CTRL0_SESS_VALID_OVERRIDE_EN_MASK); - // } - // ``` let r = T::info().regs; @@ -311,9 +240,6 @@ impl<'d, T: Instance> UsbDriver<'d, T> { dm.set_as_analog(); } - // TODO: Set ID/OC/PWR in host mode - // - // Set vbus high level enable let r = T::info().regs; r.otg_ctrl0().modify(|w| w.set_otg_power_mask(true)); @@ -459,6 +385,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { assert_eq!(ep_out.info.addr.index(), 0); assert_eq!(ep_in.info.addr.index(), 0); + // Prepare endpoints info let mut endpoints_in: [EndpointInfo; ENDPOINT_COUNT] = [EndpointInfo { addr: EndpointAddress::from_parts(0, Direction::In), ep_type: EndpointType::Bulk, @@ -471,10 +398,13 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { max_packet_size: 0, interval_ms: 0, }; ENDPOINT_COUNT]; - for i in 0..ENDPOINT_COUNT { + endpoints_in[0] = ep_in.info; + endpoints_out[0] = ep_out.info; + for i in 1..ENDPOINT_COUNT { endpoints_in[i] = self.endpoints_in[i].info; endpoints_out[i] = self.endpoints_out[i].info; } + let mut bus = Bus { _phantom: PhantomData, info: self.info, @@ -484,9 +414,10 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { inited: false, }; - // Init the usb bus + // Init the usb phy and device controller bus.dcd_init(); + // Set ENDPTLISTADDR, enable interrupts { let r = self.info.regs; // Set endpoint list address @@ -496,8 +427,8 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { }; // Clear status + r.usbsts().write(|w| w.0 = 0); // Enable interrupt mask - r.usbsts().write_value(Usbsts::default()); r.usbintr().write(|w| { w.set_ue(true); w.set_uee(true); @@ -506,6 +437,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { }); } + // Start to run usb device bus.dcd_connect(); ( @@ -596,7 +528,6 @@ pub unsafe fn on_interrupt() { return; } - // defmt::info!("USB interrupt: {:b}", status.0); // Reset event if status.uri() { // Set IRQ_RESET signal From 0e2a788e16ffec267976ed84ad66e84470219d2a Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sat, 3 Aug 2024 01:17:32 +0800 Subject: [PATCH 25/43] refactor(usb): add many many debugging logs, but no luck Signed-off-by: HaoboGu --- src/usb/bus.rs | 23 +++--- src/usb/control_pipe.rs | 25 +++++-- src/usb/endpoint.rs | 152 +++++++++++++++++++++++++++++++++------- src/usb/mod.rs | 86 +++++++++++++++++------ 4 files changed, 215 insertions(+), 71 deletions(-) diff --git a/src/usb/bus.rs b/src/usb/bus.rs index e27e871..8d14a75 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -10,7 +10,7 @@ use hpm_metapac::usb::regs::*; use riscv::delay::McycleDelay; use super::{init_qhd, Info, Instance, ENDPOINT_COUNT}; -use crate::usb::{reset_dcd_data, EpConfig, BUS_WAKER, DCD_DATA, IRQ_RESET, IRQ_SUSPEND}; +use crate::usb::{reset_dcd_data, EpConfig, BUS_WAKER, IRQ_RESET, IRQ_SUSPEND}; pub struct Bus { pub(crate) _phantom: PhantomData, @@ -57,12 +57,6 @@ impl embassy_usb_driver::Bus for Bus { if IRQ_RESET.load(Ordering::Acquire) { IRQ_RESET.store(false, Ordering::Relaxed); - // Return setup packet - let setup_packet = unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() }; - - // Convert to slice - defmt::trace!("check setup_packet in reset: {:?}", setup_packet); - // Set device addr to 0 self.dcd_set_address(0); @@ -83,6 +77,7 @@ impl embassy_usb_driver::Bus for Bus { // Set ue, enable usb transfer interrupt r.usbintr().modify(|w| w.set_ue(true)); + r.usbintr().modify(|w| w.set_ure(false)); defmt::info!("poll: Reset"); return Poll::Ready(Event::Reset); @@ -162,8 +157,7 @@ impl Bus { // Enable dp/dm pulldown // In hpm_sdk, this operation is done by `ptr->PHY_CTRL0 &= ~0x001000E0u`. // But there's corresponding bits in register, so we write the register directly here. - let phy_ctrl0 = r.phy_ctrl0().read().0 & (!0x001000E0); - r.phy_ctrl0().write_value(PhyCtrl0(phy_ctrl0)); + r.phy_ctrl0().modify(|w| w.0 = w.0 & (!(0x001000E0))); r.otg_ctrl0().modify(|w| { w.set_otg_utmi_suspendm_sw(false); @@ -248,7 +242,7 @@ impl Bus { while r.endptprime().read().0 != 0 {} - r.endptflush().write_value(Endptflush(0xFFFFFFFF)); + r.endptflush().modify(|w| w.0 = 0xFFFFFFFF); while r.endptflush().read().0 != 0 {} } @@ -267,6 +261,8 @@ impl Bus { // Set mode to device IMMEDIATELY after reset r.usbmode().modify(|w| w.set_cm(0b10)); + assert_eq!(r.usbmode().read().cm(), 0b10); + r.usbmode().modify(|w| { // Set little endian w.set_es(false); @@ -279,7 +275,7 @@ impl Bus { w.set_sts(false); // Parallel transceiver width w.set_ptw(false); - // Forced fullspeed mode + // Forced fullspeed mode, c_sdk commented this line out, use it only when the device runs in full speed mode // w.set_pfsc(true); }); @@ -384,13 +380,12 @@ impl Bus { w.set_txt(0); w.set_txe(true); w.set_txr(true); - // TODO: Better impl? For example, make transfer a bitfield struct - w.0 |= (ep_config.transfer as u32) << 18; + w.set_txt(ep_config.transfer as u8); } else { w.set_rxt(0); w.set_rxe(true); w.set_rxr(true); - w.0 |= (ep_config.transfer as u32) << 2; + w.set_rxt(ep_config.transfer as u8); } }); } diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 3e5b5c8..dbf1996 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -27,14 +27,14 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { let r = T::info().regs; // Wait for SETUP packet(interrupt here) - // Clear interrupt status and enable USB interrupt first - r.usbsts().modify(|w| w.set_ui(false)); + // Clear interrupt status(by writing 1) and enable USB interrupt first + r.usbsts().modify(|w| w.set_ui(true)); + // r.usbsts().modify(|w| w.set_ui(false)); + while r.usbsts().read().ui() {} r.usbintr().modify(|w| w.set_ue(true)); - info!("Waiting for setup packet"); let _ = poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); - - if r.endptsetupstat().read().0 != 0 { + if r.endptsetupstat().read().endptsetupstat() != 0 { // See hpm_sdk/middleware/cherryusb/port/hpm/usb_dc_hpm.c: 285L // Clean endpoint setup status r.endptsetupstat().modify(|w| w.0 = w.0); @@ -51,8 +51,11 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { // Read setup packet from qhd let setup_packet = unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() }; + // Clear interrupt status and enable USB interrupt + r.usbsts().modify(|w| w.set_ui(true)); + while r.usbsts().read().ui() {} + r.usbintr().modify(|w| w.set_ue(true)); // Convert to slice - defmt::trace!("setup_packet: {:?}", setup_packet); setup_packet } @@ -74,8 +77,16 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { _last: bool, ) -> Result<(), embassy_usb_driver::EndpointError> { defmt::info!("ControlPipe::datain"); - // TODO: data_in: it's already chunked by max_packet_size + // if data.len() > 64 { + // panic!("data_in: data.len() > 64"); + // } + // self.ep_in.buffer[0..data.len()].copy_from_slice(data); + // self.ep_in + // .transfer2(data.len()) + // .map_err(|_e| EndpointError::BufferOverflow)?; self.ep_in.transfer(data).map_err(|_e| EndpointError::BufferOverflow)?; + + // self.ep_out.transfer2(0).unwrap(); Ok(()) } diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index d5a9394..de32c3b 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -2,12 +2,7 @@ use core::future::poll_fn; use core::task::Poll; use embassy_usb_driver::{EndpointAddress, EndpointIn, EndpointInfo, EndpointOut}; -use hpm_metapac::usb::regs::Endptprime; -#[cfg(hpm53)] -use super::types_v53::Qtd; -#[cfg(hpm62)] -use super::types_v62::Qtd; use super::{Error, Info, DCD_DATA, QTD_COUNT_EACH_ENDPOINT}; use crate::usb::{EP_IN_WAKERS, EP_OUT_WAKERS}; @@ -22,15 +17,32 @@ pub struct EpConfig { pub struct Endpoint { pub(crate) info: EndpointInfo, pub(crate) usb_info: &'static Info, + pub(crate) buffer: [u8; 64], } impl Endpoint { pub(crate) fn start_transfer(&mut self) { - let ep_idx = self.info.addr.index(); - + let ep_num = self.info.addr.index(); + let ep_idx = 2 * ep_num + self.info.addr.is_in() as usize; let offset = if ep_idx % 2 == 1 { ep_idx / 2 + 16 } else { ep_idx / 2 }; - self.usb_info.regs.endptprime().write_value(Endptprime(1 << offset)); + defmt::info!( + "Start transfer on endpoint {}, dir_in?: {}, offset: {}", + ep_num, + self.info.addr.is_in(), + offset + ); + + // FIXME: Write this reg doesn't work + self.usb_info.regs.endptprime().modify(|w| { + if self.info.addr.is_in() { + w.set_petb(1 << ep_num); + } else { + w.set_perb(1 << ep_num); + } + }); + + while self.usb_info.regs.endptprime().read().0 != 0 {} } pub(crate) fn transfer(&mut self, data: &[u8]) -> Result<(), Error> { @@ -39,6 +51,12 @@ impl Endpoint { let ep_num = self.info.addr.index(); let ep_idx = 2 * ep_num + self.info.addr.is_in() as usize; + defmt::info!( + "=============\nTransfer: ep_num: {}, ep_idx: {}, ep_dir(IN?): {}", + ep_num, + ep_idx, + self.info.addr.is_in() + ); // Setup packet handling using setup lockout mechanism // wait until ENDPTSETUPSTAT before priming data/status in response if ep_num == 0 { @@ -49,24 +67,20 @@ impl Endpoint { if qtd_num > 8 { return Err(Error::InvalidQtdNum); } - defmt::info!( - "Current ep num: {}, ep idx: {}, needed qtd num: {}", - ep_num, - ep_idx, - qtd_num - ); // TODO: Convert data's address to coressponding core // data = core_local_mem_to_sys_address(data); + defmt::info!("Transfered data addr: {:x}, datalen: {}", data.as_ptr(), data.len()); + // Add all data to the circular queue - let mut prev_qtd: Option = None; - let mut first_qtd: Option = None; + let mut prev_qtd: Option = None; + let mut first_qtd: Option = None; let mut i = 0; let mut data_offset = 0; let mut remaining_bytes = data.len(); loop { - let mut qtd = unsafe { DCD_DATA.qtd_list.qtd(ep_idx * QTD_COUNT_EACH_ENDPOINT + i) }; + let qtd_idx = ep_idx * QTD_COUNT_EACH_ENDPOINT + i; i += 1; // If the transfer size > 0x4000, then there should be multiple qtds in the linked list @@ -82,25 +96,41 @@ impl Endpoint { // Check hpm_sdk: static inline uint32_t core_local_mem_to_sys_address() // Initialize qtd with the data - defmt::info!("data_offset: {}, num transfer_bytes: {}", data_offset, transfer_bytes); - qtd.reinit_with(&data[data_offset..], transfer_bytes); + unsafe { + defmt::info!( + "Initializing qtd_idx: {}, addr: {:x}", + qtd_idx, + DCD_DATA.qtd_list.qtd(qtd_idx).as_ptr() as u32 + ); + DCD_DATA + .qtd_list + .qtd(qtd_idx) + .reinit_with(&data[data_offset..], transfer_bytes) + }; // Last chunk of the data if remaining_bytes == 0 { - qtd.qtd_token().write(|w| w.set_ioc(true)); + unsafe { + DCD_DATA.qtd_list.qtd(qtd_idx).qtd_token().modify(|w| w.set_ioc(true)); + }; } data_offset += transfer_bytes; // Set qtd linked list if let Some(prev_qtd) = prev_qtd { - prev_qtd - .next_dtd() - .modify(|w| w.set_next_dtd_addr(&qtd as *const _ as u32 >> 5)); + unsafe { + DCD_DATA + .qtd_list + .qtd(prev_qtd) + .next_dtd() + .modify(|w| w.set_next_dtd_addr(DCD_DATA.qtd_list.qtd(qtd_idx).as_ptr() as u32 >> 5)); + } } else { - first_qtd = Some(qtd); + first_qtd = Some(qtd_idx); } - prev_qtd = Some(qtd); + + prev_qtd = Some(qtd_idx); // Check the remaining_bytes if remaining_bytes == 0 { @@ -109,17 +139,85 @@ impl Endpoint { } // Link qtd to qhd + let first_idx = first_qtd.unwrap(); + unsafe { + defmt::info!( + "Check first qtd idx: {}, addr: {:x} and content: + next_dtd_word: 0x{:x} + total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b} + Buffer0 + offset: {:x} + Buffer1 : {:x} + Buffer2 : {:x} + Buffer3 : {:x} + Buffer4 : {:x}", + first_idx, + DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32, + DCD_DATA.qtd_list.qtd(first_idx).next_dtd().read().0, + DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().total_bytes(), + DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().ioc(), + DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().c_page(), + DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().multo(), + DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().status(), + DCD_DATA.qtd_list.qtd(first_idx).buffer(0).read().0, + DCD_DATA.qtd_list.qtd(first_idx).buffer(1).read().0, + DCD_DATA.qtd_list.qtd(first_idx).buffer(2).read().0, + DCD_DATA.qtd_list.qtd(first_idx).buffer(3).read().0, + DCD_DATA.qtd_list.qtd(first_idx).buffer(4).read().0, + ); + } + unsafe { DCD_DATA .qhd_list .qhd(ep_idx) .next_dtd() - .modify(|w| w.set_next_dtd_addr(&first_qtd.unwrap() as *const _ as u32 >> 5)); + .modify(|w| w.set_next_dtd_addr(DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32 >> 5)); + let qhd = DCD_DATA.qhd_list.qhd(ep_idx); + defmt::info!( + "Check qhd after setting: qhd_idx: {} + 1st word: mult: {}, zlt: {}, mps: {}, ios: {} + 2nd word: {:x} + 3rd word: next_dtd + t: {:x} + total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b}", + ep_idx, + qhd.cap().read().iso_mult(), + qhd.cap().read().zero_length_termination(), + qhd.cap().read().max_packet_size(), + qhd.cap().read().ios(), + qhd.cur_dtd().read().0, + qhd.next_dtd().read().0, + qhd.qtd_token().read().total_bytes(), + qhd.qtd_token().read().ioc(), + qhd.qtd_token().read().c_page(), + qhd.qtd_token().read().multo(), + qhd.qtd_token().read().status(), + ); } // Start transfer self.start_transfer(); + let qhd = unsafe { DCD_DATA.qhd_list.qhd(ep_idx) }; + defmt::info!( + "AFTER TRANS: Check qhd after setting: qhd_idx: {} + 1st word: mult: {}, zlt: {}, mps: {}, ios: {} + 2nd word: {:x} + 3rd word: next_dtd + t: {:x} + total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b}", + ep_idx, + qhd.cap().read().iso_mult(), + qhd.cap().read().zero_length_termination(), + qhd.cap().read().max_packet_size(), + qhd.cap().read().ios(), + qhd.cur_dtd().read().0, + qhd.next_dtd().read().0, + qhd.qtd_token().read().total_bytes(), + qhd.qtd_token().read().ioc(), + qhd.qtd_token().read().c_page(), + qhd.qtd_token().read().multo(), + qhd.qtd_token().read().status(), + ); + Ok(()) } @@ -164,8 +262,8 @@ impl embassy_usb_driver::Endpoint for Endpoint { } async fn wait_enabled(&mut self) { - defmt::info!("Endpoint::wait_enabled"); let i = self.info.addr.index(); + defmt::info!("Endpoint({})::wait_enabled", i); assert!(i != 0); poll_fn(|cx| { // TODO: Simplify the code diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 852250f..5074625 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -100,7 +100,7 @@ pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { DCD_DATA.qtd_list.qtd(i).reset(); } - // Set qhd for EP0 and EP1 + // Set qhd for EP0(qhd0&1) DCD_DATA.qhd_list.qhd(0).cap().modify(|w| { w.set_max_packet_size(ep0_max_packet_size); w.set_zero_length_termination(true); @@ -120,11 +120,12 @@ pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { pub(crate) unsafe fn init_qhd(ep_config: &EpConfig) { let ep_num = ep_config.ep_addr.index(); let ep_idx = 2 * ep_num + ep_config.ep_addr.is_in() as usize; + // Prepare queue head DCD_DATA.qhd_list.qhd(ep_idx).reset(); - DCD_DATA.qhd_list.qhd(ep_idx).cap().write(|w| { - w.set_max_packet_size(ep_config.max_packet_size); + DCD_DATA.qhd_list.qhd(ep_idx).cap().modify(|w| { + w.set_max_packet_size(ep_config.max_packet_size & 0x7FF); w.set_zero_length_termination(true); if ep_config.transfer == EndpointType::Isochronous as u8 { w.set_iso_mult(((ep_config.max_packet_size >> 11) & 0x3) as u8 + 1); @@ -134,7 +135,7 @@ pub(crate) unsafe fn init_qhd(ep_config: &EpConfig) { } }); - DCD_DATA.qhd_list.qhd(ep_idx).next_dtd().write(|w| w.set_t(true)); + DCD_DATA.qhd_list.qhd(ep_idx).next_dtd().modify(|w| w.set_t(true)); } impl Qtd { @@ -154,7 +155,8 @@ impl Qtd { self.qtd_token().modify(|w| { w.set_total_bytes(transfer_bytes as u16); - w.set_active(true) + w.set_active(true); + w.set_ioc(true); }); self.expected_bytes() @@ -172,21 +174,20 @@ impl Qtd { return; } + if transfer_bytes < 0x4000 { + self.next_dtd().modify(|w| w.set_t(true)); + } + // Fill data into qtd self.buffer(0) - .write(|w| w.set_buffer((data.as_ptr() as u32 & 0xFFFFF000) >> 12)); + .modify(|w| w.set_buffer((data.as_ptr() as u32 & 0xFFFFF000) >> 12)); self.current_offset() - .write(|w| w.set_current_offset((data.as_ptr() as u32 & 0x00000FFF) as u16)); - defmt::info!( - "data_addr: {:x}, buffer0: {:x}, current_offset: {:x}", - data.as_ptr() as u32, - self.buffer(0).read().buffer(), - self.current_offset().read().0 - ); + .modify(|w| w.set_current_offset((data.as_ptr() as u32 & 0x00000FFF) as u16)); + for i in 1..QHD_BUFFER_COUNT { // Fill address of next 4K bytes, note the addr is already shifted, so we just +1 let addr = self.buffer(i - 1).read().buffer(); - self.buffer(i).write(|w| w.set_buffer(addr + 1)); + self.buffer(i).modify(|w| w.set_buffer(addr + 1)); } } } @@ -226,6 +227,8 @@ impl<'d, T: Instance> UsbDriver<'d, T> { ) -> Self { unsafe { T::Interrupt::enable() }; + T::add_resource_group(0); + let r = T::info().regs; // Disable dp/dm pulldown @@ -240,8 +243,9 @@ impl<'d, T: Instance> UsbDriver<'d, T> { dm.set_as_analog(); } - // Set vbus high level enable let r = T::info().regs; + + // Set power control polarity, aka vbus high level enable r.otg_ctrl0().modify(|w| w.set_otg_power_mask(true)); // Wait for 100ms @@ -257,8 +261,6 @@ impl<'d, T: Instance> UsbDriver<'d, T> { w.set_sess_valid_override_en(true); }); - T::add_resource_group(0); - // Initialize the bus so that it signals that power is available BUS_WAKER.wake(); @@ -329,6 +331,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { Ok(Endpoint { info: ep, usb_info: self.info, + buffer: [0; 64], }) } @@ -363,6 +366,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { Ok(Endpoint { info: ep, usb_info: self.info, + buffer: [0; 64], }) } @@ -422,12 +426,14 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { let r = self.info.regs; // Set endpoint list address unsafe { + defmt::info!("Setting ENDPTLISTADDR: {:x}", DCD_DATA.qhd_list.as_ptr()); r.endptlistaddr() .modify(|w| w.set_epbase(DCD_DATA.qhd_list.as_ptr() as u32 >> 11)) }; // Clear status - r.usbsts().write(|w| w.0 = 0); + r.usbsts().modify(|w| w.0 = w.0); + // Enable interrupt mask r.usbintr().write(|w| { w.set_ue(true); @@ -528,6 +534,8 @@ pub unsafe fn on_interrupt() { return; } + defmt::info!("GOT IRQ: {:b}", r.usbsts().read().0); + // Reset event if status.uri() { // Set IRQ_RESET signal @@ -562,27 +570,43 @@ pub unsafe fn on_interrupt() { // Transfer complete event if status.ui() { - defmt::info!("Transfer complete event!"); + // Clear endpoint complete status + r.endptcomplete().modify(|w| w.0 = w.0); + // Disable USB transfer interrupt r.usbintr().modify(|w| w.set_ue(false)); let ep_status = r.endptstat().read(); + defmt::info!( + "Transfer complete interrupt: endptstat: {:b}, endptsetupstat: {:b}, endptcomplete: {:b}, endptprime: {:b}, endptflust: {:b}", + r.endptstat().read().0, + r.endptsetupstat().read().0, + r.endptcomplete().read().0, + r.endptprime().read().0, + r.endptflush().read().0, + ); + check_qtd(8); // Clear the status by rewrite those bits r.endptstat().modify(|w| w.0 = w.0); - let ep_setup_status = r.endptsetupstat().read(); - if ep_setup_status.0 > 0 { - defmt::info!("ep_setup_status: {:b}", ep_setup_status.0); + if r.endptsetupstat().read().endptsetupstat() > 0 { + defmt::info!( + "Setup transfer complete: 0b{:b}", + r.endptsetupstat().read().endptsetupstat() + ); EP_OUT_WAKERS[0].wake(); } - if ep_status.0 > 0 { + if r.endptcomplete().read().0 > 0 { + defmt::info!("ep transfer complete: {:b}", r.endptcomplete().read().0); // Transfer completed for i in 1..ENDPOINT_COUNT { if ep_status.erbr() & (1 << i) > 0 { + defmt::info!("wake {} OUT ep", i); // OUT endpoint EP_OUT_WAKERS[i].wake(); } if ep_status.etbr() & (1 << i) > 0 { + defmt::info!("wake {} IN ep", i); // IN endpoint EP_IN_WAKERS[i].wake(); } @@ -593,3 +617,19 @@ pub unsafe fn on_interrupt() { pin_trait!(DmPin, Instance); pin_trait!(DpPin, Instance); + +unsafe fn check_qtd(idx: usize) { + let qtd = DCD_DATA.qtd_list.qtd(idx); + defmt::info!( + "QTD {}: terminate: {}, next_dtd: {:x}, ioc: {}, c_page: {}, active: {}, halted: {}, xfer_err: {}, status: {:b}", + idx, + qtd.next_dtd().read().t(), + qtd.next_dtd().read().next_dtd_addr(), + qtd.qtd_token().read().ioc(), + qtd.qtd_token().read().c_page(), + qtd.qtd_token().read().active(), + qtd.qtd_token().read().halted(), + qtd.qtd_token().read().transaction_err(), + qtd.qtd_token().read().status(), + ); +} From f2db72ace2107f390f1decf6485edb3d41127d4f Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sun, 4 Aug 2024 23:31:45 +0800 Subject: [PATCH 26/43] feat(usb): received set address packet, the returned config desc is still problematic Signed-off-by: HaoboGu --- src/usb/bus.rs | 36 +------------ src/usb/control_pipe.rs | 34 ++++++------ src/usb/endpoint.rs | 116 +++++++++++----------------------------- src/usb/mod.rs | 64 +++++++++------------- 4 files changed, 75 insertions(+), 175 deletions(-) diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 8d14a75..b3869fe 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -160,8 +160,8 @@ impl Bus { r.phy_ctrl0().modify(|w| w.0 = w.0 & (!(0x001000E0))); r.otg_ctrl0().modify(|w| { - w.set_otg_utmi_suspendm_sw(false); w.set_otg_utmi_reset_sw(true); + w.set_otg_utmi_suspendm_sw(false); }); r.phy_ctrl1().modify(|w| { @@ -188,7 +188,7 @@ impl Bus { // OTG utmi clock detection r.phy_status().modify(|w| w.set_utmi_clk_valid(true)); - while r.phy_status().read().utmi_clk_valid() == false {} + while !r.phy_status().read().utmi_clk_valid() {} // Reset and set suspend r.phy_ctrl1().modify(|w| { @@ -500,38 +500,6 @@ impl Bus { } } - // Used in `usb_dc_init` - // pub(crate) fn device_init(&mut self, int_mask: u32) { - // defmt::info!("Bus::device_init"); - // // Clear dcd data first - // unsafe { - // DCD_DATA = DcdData::default(); - // } - - // // Initialize controller - // self.dcd_init(); - - // let r = &self.info.regs; - // // Set endpoint list address - // // TODO: Check if this is correct - // let addr = unsafe { DCD_DATA.qhd.as_ptr() as u32 }; - // r.endptlistaddr().write(|w| w.set_epbase(addr)); - - // // Clear status - // r.usbsts().modify(|w| w.0 = 0); - - // // Enable interrupts - // r.usbintr().modify(|w| w.0 = w.0 | int_mask); - - // // Connect - // r.usbcmd().modify(|w| w.set_rs(true)); - // } - - // pub(crate) fn device_deinit(&mut self) { - // defmt::info!("Bus::device_deinit"); - // self.dcd_deinit(); - // } - pub(crate) fn device_endpoint_close(&mut self, ep_addr: EndpointAddress) { defmt::info!("Bus::device_edpt_close"); self.dcd_endpoint_close(ep_addr); diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index dbf1996..3f71856 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -1,4 +1,4 @@ -use core::marker::PhantomData; +use core::{marker::PhantomData, sync::atomic::Ordering}; use core::task::Poll; use defmt::info; @@ -7,13 +7,13 @@ use futures_util::future::poll_fn; use super::endpoint::Endpoint; use super::Instance; -use crate::usb::{DCD_DATA, EP_OUT_WAKERS}; +use crate::usb::{DCD_DATA, EP_OUT_WAKERS, IRQ_TRANSFER_COMPLETED}; pub struct ControlPipe<'d, T: Instance> { pub(crate) _phantom: PhantomData<&'d mut T>, pub(crate) max_packet_size: usize, - pub(crate) ep_in: Endpoint, - pub(crate) ep_out: Endpoint, + pub(crate) ep_in: Endpoint<'d, T>, + pub(crate) ep_out: Endpoint<'d, T>, } impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { @@ -34,7 +34,8 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { r.usbintr().modify(|w| w.set_ue(true)); let _ = poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); - if r.endptsetupstat().read().endptsetupstat() != 0 { + if IRQ_TRANSFER_COMPLETED.load(Ordering::Acquire) { + IRQ_TRANSFER_COMPLETED.store(false, Ordering::Relaxed); // See hpm_sdk/middleware/cherryusb/port/hpm/usb_dc_hpm.c: 285L // Clean endpoint setup status r.endptsetupstat().modify(|w| w.0 = w.0); @@ -74,19 +75,14 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { &mut self, data: &[u8], _first: bool, - _last: bool, + last: bool, ) -> Result<(), embassy_usb_driver::EndpointError> { defmt::info!("ControlPipe::datain"); - // if data.len() > 64 { - // panic!("data_in: data.len() > 64"); - // } - // self.ep_in.buffer[0..data.len()].copy_from_slice(data); - // self.ep_in - // .transfer2(data.len()) - // .map_err(|_e| EndpointError::BufferOverflow)?; - self.ep_in.transfer(data).map_err(|_e| EndpointError::BufferOverflow)?; - // self.ep_out.transfer2(0).unwrap(); + self.ep_in.transfer(data).map_err(|_e| EndpointError::BufferOverflow)?; + if last { + self.ep_out.transfer(&[]).unwrap(); + } Ok(()) } @@ -95,6 +91,9 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { /// Causes the STATUS packet for the current request to be ACKed. async fn accept(&mut self) { defmt::info!("ControlPipe::accept"); + self.ep_in.transfer(&[]).unwrap(); + + defmt::trace!("control: accept OK"); } async fn reject(&mut self) { @@ -105,14 +104,13 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { } async fn accept_set_address(&mut self, addr: u8) { - defmt::info!("ControlPipe::accept_set_address"); - // Response with STATUS? - // self.ep_in.transfer(&[]); + defmt::info!("ControlPipe::accept_set_address: {}", addr); let r = T::info().regs; r.deviceaddr().modify(|w| { w.set_usbadr(addr); w.set_usbadra(true); }); + self.accept().await; } } diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index de32c3b..b7bbbd5 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -1,10 +1,11 @@ use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; use embassy_usb_driver::{EndpointAddress, EndpointIn, EndpointInfo, EndpointOut}; -use super::{Error, Info, DCD_DATA, QTD_COUNT_EACH_ENDPOINT}; -use crate::usb::{EP_IN_WAKERS, EP_OUT_WAKERS}; +use super::{Error, DCD_DATA, QTD_COUNT_EACH_ENDPOINT}; +use crate::usb::{Instance, EP_IN_WAKERS, EP_OUT_WAKERS}; pub struct EpConfig { /// Endpoint type @@ -14,13 +15,13 @@ pub struct EpConfig { } #[derive(Copy, Clone)] -pub struct Endpoint { +pub struct Endpoint<'d, T: Instance> { + pub(crate) _phantom: PhantomData<&'d mut T>, pub(crate) info: EndpointInfo, - pub(crate) usb_info: &'static Info, pub(crate) buffer: [u8; 64], } -impl Endpoint { +impl<'d, T: Instance> Endpoint<'d, T> { pub(crate) fn start_transfer(&mut self) { let ep_num = self.info.addr.index(); let ep_idx = 2 * ep_num + self.info.addr.is_in() as usize; @@ -33,30 +34,22 @@ impl Endpoint { offset ); - // FIXME: Write this reg doesn't work - self.usb_info.regs.endptprime().modify(|w| { + let r = T::info().regs; + r.endptprime().modify(|w| { if self.info.addr.is_in() { w.set_petb(1 << ep_num); } else { w.set_perb(1 << ep_num); } }); - - while self.usb_info.regs.endptprime().read().0 != 0 {} } pub(crate) fn transfer(&mut self, data: &[u8]) -> Result<(), Error> { - let r = &self.usb_info.regs; + let r = T::info().regs; let ep_num = self.info.addr.index(); let ep_idx = 2 * ep_num + self.info.addr.is_in() as usize; - defmt::info!( - "=============\nTransfer: ep_num: {}, ep_idx: {}, ep_dir(IN?): {}", - ep_num, - ep_idx, - self.info.addr.is_in() - ); // Setup packet handling using setup lockout mechanism // wait until ENDPTSETUPSTAT before priming data/status in response if ep_num == 0 { @@ -71,8 +64,6 @@ impl Endpoint { // TODO: Convert data's address to coressponding core // data = core_local_mem_to_sys_address(data); - defmt::info!("Transfered data addr: {:x}, datalen: {}", data.as_ptr(), data.len()); - // Add all data to the circular queue let mut prev_qtd: Option = None; let mut first_qtd: Option = None; @@ -97,11 +88,6 @@ impl Endpoint { // Initialize qtd with the data unsafe { - defmt::info!( - "Initializing qtd_idx: {}, addr: {:x}", - qtd_idx, - DCD_DATA.qtd_list.qtd(qtd_idx).as_ptr() as u32 - ); DCD_DATA .qtd_list .qtd(qtd_idx) @@ -109,7 +95,7 @@ impl Endpoint { }; // Last chunk of the data - if remaining_bytes == 0 { + if remaining_bytes == 0 && !self.info.addr.is_in() { unsafe { DCD_DATA.qtd_list.qtd(qtd_idx).qtd_token().modify(|w| w.set_ioc(true)); }; @@ -167,24 +153,29 @@ impl Endpoint { } unsafe { - DCD_DATA - .qhd_list - .qhd(ep_idx) - .next_dtd() - .modify(|w| w.set_next_dtd_addr(DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32 >> 5)); - let qhd = DCD_DATA.qhd_list.qhd(ep_idx); + DCD_DATA.qhd_list.qhd(ep_idx).next_dtd().modify(|w| w.set_t(true)); + + DCD_DATA.qhd_list.qhd(ep_idx).next_dtd().modify(|w| { + w.set_next_dtd_addr(DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32 >> 5); + // T **MUST** be set to 0 + w.set_t(false); + }); + let qhd: crate::usb::types_v53::Qhd = DCD_DATA.qhd_list.qhd(ep_idx); + defmt::info!( - "Check qhd after setting: qhd_idx: {} + "ENDPTLISTADDR: {:x} + Check qhd after setting: qhd_idx: {} 1st word: mult: {}, zlt: {}, mps: {}, ios: {} 2nd word: {:x} 3rd word: next_dtd + t: {:x} total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b}", + T::info().regs.endptlistaddr().read().0, ep_idx, qhd.cap().read().iso_mult(), qhd.cap().read().zero_length_termination(), qhd.cap().read().max_packet_size(), qhd.cap().read().ios(), - qhd.cur_dtd().read().0, + qhd.cur_dtd().read().0, // 2nd word qhd.next_dtd().read().0, qhd.qtd_token().read().total_bytes(), qhd.qtd_token().read().ioc(), @@ -197,66 +188,20 @@ impl Endpoint { // Start transfer self.start_transfer(); - let qhd = unsafe { DCD_DATA.qhd_list.qhd(ep_idx) }; - defmt::info!( - "AFTER TRANS: Check qhd after setting: qhd_idx: {} - 1st word: mult: {}, zlt: {}, mps: {}, ios: {} - 2nd word: {:x} - 3rd word: next_dtd + t: {:x} - total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b}", - ep_idx, - qhd.cap().read().iso_mult(), - qhd.cap().read().zero_length_termination(), - qhd.cap().read().max_packet_size(), - qhd.cap().read().ios(), - qhd.cur_dtd().read().0, - qhd.next_dtd().read().0, - qhd.qtd_token().read().total_bytes(), - qhd.qtd_token().read().ioc(), - qhd.qtd_token().read().c_page(), - qhd.qtd_token().read().multo(), - qhd.qtd_token().read().status(), - ); - Ok(()) } pub(crate) fn set_stall(&mut self) { - let r = &self.usb_info.regs; + let r = T::info().regs; if self.info.addr.is_in() { r.endptctrl(self.info.addr.index() as usize).modify(|w| w.set_txs(true)); } else { r.endptctrl(self.info.addr.index() as usize).modify(|w| w.set_rxs(true)); } } - - // pub(crate) fn clean_stall(&mut self) { - // let r = &self.usb_info.regs; - - // r.endptctrl(self.info.addr.index() as usize).modify(|w| { - // if self.info.addr.is_in() { - // // Data toggle also need to be reset - // w.set_txr(true); - // w.set_txs(false); - // } else { - // w.set_rxr(true); - // w.set_rxs(false); - // } - // }); - // } - - // pub(crate) fn check_stall(&self) -> bool { - // let r = &self.usb_info.regs; - - // if self.info.addr.is_in() { - // r.endptctrl(self.info.addr.index() as usize).read().txs() - // } else { - // r.endptctrl(self.info.addr.index() as usize).read().rxs() - // } - // } } -impl embassy_usb_driver::Endpoint for Endpoint { +impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { fn info(&self) -> &embassy_usb_driver::EndpointInfo { &self.info } @@ -266,11 +211,12 @@ impl embassy_usb_driver::Endpoint for Endpoint { defmt::info!("Endpoint({})::wait_enabled", i); assert!(i != 0); poll_fn(|cx| { + let r = T::info().regs; // TODO: Simplify the code if self.info.addr.is_in() { EP_IN_WAKERS[i].register(cx.waker()); // Check if the endpoint is enabled - if self.usb_info.regs.endptctrl(i).read().txe() { + if r.endptctrl(i).read().txe() { defmt::info!("Endpoint::wait_enabled: enabled"); Poll::Ready(()) } else { @@ -279,7 +225,7 @@ impl embassy_usb_driver::Endpoint for Endpoint { } else { EP_OUT_WAKERS[i].register(cx.waker()); // Check if the endpoint is enabled - if self.usb_info.regs.endptctrl(i).read().rxe() { + if r.endptctrl(i).read().rxe() { defmt::info!("Endpoint::wait_enabled: enabled"); Poll::Ready(()) } else { @@ -288,18 +234,18 @@ impl embassy_usb_driver::Endpoint for Endpoint { } }) .await; - defmt::info!("endpoint {} enabled", self.info.addr.index()); + defmt::info!("endpoint {} enabled", i); } } -impl EndpointOut for Endpoint { +impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.transfer(buf).unwrap(); Ok(buf.len()) } } -impl EndpointIn for Endpoint { +impl<'d, T: Instance> EndpointIn for Endpoint<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result<(), embassy_usb_driver::EndpointError> { self.transfer(buf).unwrap(); Ok(()) diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 5074625..807852d 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -13,9 +13,9 @@ use endpoint::{Endpoint, EpConfig}; use hpm_metapac::usb::regs::Usbsts; use riscv::delay::McycleDelay; use types::{Qhd, QhdList, Qtd, QtdList}; -#[cfg(hpm53)] +#[cfg(any(hpm53, hpm68, hpm6e))] use types_v53 as types; -#[cfg(hpm62)] +#[cfg(any(hpm67, hpm63, hpm62))] use types_v62 as types; use crate::interrupt::typelevel::Interrupt as _; @@ -24,9 +24,9 @@ use crate::sysctl; mod bus; mod control_pipe; mod endpoint; -#[cfg(hpm53)] +#[cfg(any(hpm53, hpm68, hpm6e))] mod types_v53; -#[cfg(hpm62)] +#[cfg(any(hpm67, hpm63, hpm62))] mod types_v62; static IRQ_RESET: AtomicBool = AtomicBool::new(false); @@ -113,6 +113,7 @@ pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { }); // Set the next pointer INVALID(T=1) + // TODO: Double check here with c_sdk DCD_DATA.qhd_list.qhd(0).next_dtd().write(|w| w.set_t(true)); DCD_DATA.qhd_list.qhd(1).next_dtd().write(|w| w.set_t(true)); } @@ -156,7 +157,6 @@ impl Qtd { self.qtd_token().modify(|w| { w.set_total_bytes(transfer_bytes as u16); w.set_active(true); - w.set_ioc(true); }); self.expected_bytes() @@ -178,6 +178,12 @@ impl Qtd { self.next_dtd().modify(|w| w.set_t(true)); } + if transfer_bytes == 0 { + self.buffer(0).modify(|w| w.0 = 0); + self.current_offset().modify(|w| w.0 = 0); + return; + } + // Fill data into qtd self.buffer(0) .modify(|w| w.set_buffer((data.as_ptr() as u32 & 0xFFFFF000) >> 12)); @@ -292,9 +298,9 @@ impl<'d, T: Instance> UsbDriver<'d, T> { } impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { - type EndpointOut = Endpoint; + type EndpointOut = Endpoint<'a, T>; - type EndpointIn = Endpoint; + type EndpointIn = Endpoint<'a, T>; type ControlPipe = ControlPipe<'a, T>; @@ -329,8 +335,8 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { self.endpoints_out[ep_idx].used = true; self.endpoints_out[ep_idx].info = ep.clone(); Ok(Endpoint { + _phantom: PhantomData, info: ep, - usb_info: self.info, buffer: [0; 64], }) } @@ -364,8 +370,8 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { self.endpoints_in[ep_idx].used = true; self.endpoints_in[ep_idx].info = ep.clone(); Ok(Endpoint { + _phantom: PhantomData, info: ep, - usb_info: self.info, buffer: [0; 64], }) } @@ -397,7 +403,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { interval_ms: 0, }; ENDPOINT_COUNT]; let mut endpoints_out: [EndpointInfo; ENDPOINT_COUNT] = [EndpointInfo { - addr: EndpointAddress::from_parts(0, Direction::In), + addr: EndpointAddress::from_parts(0, Direction::Out), ep_type: EndpointType::Bulk, max_packet_size: 0, interval_ms: 0, @@ -427,8 +433,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { // Set endpoint list address unsafe { defmt::info!("Setting ENDPTLISTADDR: {:x}", DCD_DATA.qhd_list.as_ptr()); - r.endptlistaddr() - .modify(|w| w.set_epbase(DCD_DATA.qhd_list.as_ptr() as u32 >> 11)) + r.endptlistaddr().modify(|w| w.0 = DCD_DATA.qhd_list.as_ptr() as u32); }; // Clear status @@ -525,17 +530,19 @@ pub unsafe fn on_interrupt() { // Clear triggered interrupts status bits let triggered_interrupts = status.0 & enabled_interrupts.0; - // TODO: Check all other interrupts are cleared - // r.usbsts().modify(|w| w.0 = w.0 & (!triggered_interrupts)); + defmt::info!( + "GOT IRQ: {:b}, triggered: {:b}", + r.usbsts().read().0, + triggered_interrupts + ); let status = Usbsts(triggered_interrupts); + r.usbsts().modify(|w| w.0 = w.0); // Disabled interrupt sources if status.0 == 0 { return; } - defmt::info!("GOT IRQ: {:b}", r.usbsts().read().0); - // Reset event if status.uri() { // Set IRQ_RESET signal @@ -560,10 +567,12 @@ pub unsafe fn on_interrupt() { if status.pci() { if r.portsc1().read().ccs() { r.usbintr().modify(|w| w.set_pce(false)); + defmt::info!("Port change interrupt: connected"); // Wake main thread. Then the suspend event will be processed in Bus::poll() BUS_WAKER.wake(); // Connected } else { + defmt::info!("Port change interrupt: disconnected"); // Disconnected } } @@ -572,7 +581,6 @@ pub unsafe fn on_interrupt() { if status.ui() { // Clear endpoint complete status r.endptcomplete().modify(|w| w.0 = w.0); - // Disable USB transfer interrupt r.usbintr().modify(|w| w.set_ue(false)); let ep_status = r.endptstat().read(); @@ -584,15 +592,11 @@ pub unsafe fn on_interrupt() { r.endptprime().read().0, r.endptflush().read().0, ); - check_qtd(8); // Clear the status by rewrite those bits r.endptstat().modify(|w| w.0 = w.0); if r.endptsetupstat().read().endptsetupstat() > 0 { - defmt::info!( - "Setup transfer complete: 0b{:b}", - r.endptsetupstat().read().endptsetupstat() - ); + IRQ_TRANSFER_COMPLETED.store(true, Ordering::Relaxed); EP_OUT_WAKERS[0].wake(); } @@ -617,19 +621,3 @@ pub unsafe fn on_interrupt() { pin_trait!(DmPin, Instance); pin_trait!(DpPin, Instance); - -unsafe fn check_qtd(idx: usize) { - let qtd = DCD_DATA.qtd_list.qtd(idx); - defmt::info!( - "QTD {}: terminate: {}, next_dtd: {:x}, ioc: {}, c_page: {}, active: {}, halted: {}, xfer_err: {}, status: {:b}", - idx, - qtd.next_dtd().read().t(), - qtd.next_dtd().read().next_dtd_addr(), - qtd.qtd_token().read().ioc(), - qtd.qtd_token().read().c_page(), - qtd.qtd_token().read().active(), - qtd.qtd_token().read().halted(), - qtd.qtd_token().read().transaction_err(), - qtd.qtd_token().read().status(), - ); -} From b911aef95960eae9052e809220171c86b27a3a68 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sun, 4 Aug 2024 23:39:41 +0800 Subject: [PATCH 27/43] refactor(usb): remove unused code Signed-off-by: HaoboGu --- src/usb/endpoint.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index b7bbbd5..f48ea07 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -153,8 +153,6 @@ impl<'d, T: Instance> Endpoint<'d, T> { } unsafe { - DCD_DATA.qhd_list.qhd(ep_idx).next_dtd().modify(|w| w.set_t(true)); - DCD_DATA.qhd_list.qhd(ep_idx).next_dtd().modify(|w| { w.set_next_dtd_addr(DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32 >> 5); // T **MUST** be set to 0 From 77b990178d0cc1802e77bca40c61d444d30d6fa8 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Mon, 5 Aug 2024 00:14:17 +0800 Subject: [PATCH 28/43] refactor(usb): code formatting, set qhd.ios for ep0 Signed-off-by: HaoboGu --- src/usb/control_pipe.rs | 3 ++- src/usb/endpoint.rs | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 3f71856..1972279 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -1,4 +1,5 @@ -use core::{marker::PhantomData, sync::atomic::Ordering}; +use core::marker::PhantomData; +use core::sync::atomic::Ordering; use core::task::Poll; use defmt::info; diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index f48ea07..e586c9b 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -153,6 +153,11 @@ impl<'d, T: Instance> Endpoint<'d, T> { } unsafe { + if ep_num == 0 { + DCD_DATA.qhd_list.qhd(ep_idx).cap().modify(|w| { + w.set_ios(true); + }); + } DCD_DATA.qhd_list.qhd(ep_idx).next_dtd().modify(|w| { w.set_next_dtd_addr(DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32 >> 5); // T **MUST** be set to 0 @@ -164,7 +169,7 @@ impl<'d, T: Instance> Endpoint<'d, T> { "ENDPTLISTADDR: {:x} Check qhd after setting: qhd_idx: {} 1st word: mult: {}, zlt: {}, mps: {}, ios: {} - 2nd word: {:x} + 2nd word: cur dtd: {:x} 3rd word: next_dtd + t: {:x} total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b}", T::info().regs.endptlistaddr().read().0, From 89499c241d626aaceb6e91f225e72d4785293cb6 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Mon, 5 Aug 2024 23:48:07 +0800 Subject: [PATCH 29/43] feat(usb): make usb enumerable Signed-off-by: HaoboGu --- src/usb/bus.rs | 4 +- src/usb/endpoint.rs | 140 ++++++++++++++++++++++++++------------------ src/usb/mod.rs | 9 +-- 3 files changed, 88 insertions(+), 65 deletions(-) diff --git a/src/usb/bus.rs b/src/usb/bus.rs index b3869fe..a19a1c1 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -276,7 +276,9 @@ impl Bus { // Parallel transceiver width w.set_ptw(false); // Forced fullspeed mode, c_sdk commented this line out, use it only when the device runs in full speed mode - // w.set_pfsc(true); + // TODO: Currently, the device can only be recognized at fs mode. + // How to switch to hs mode? + w.set_pfsc(true); }); // Do not use interrupt threshold diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index e586c9b..b8c38be 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -18,21 +18,20 @@ pub struct EpConfig { pub struct Endpoint<'d, T: Instance> { pub(crate) _phantom: PhantomData<&'d mut T>, pub(crate) info: EndpointInfo, - pub(crate) buffer: [u8; 64], } impl<'d, T: Instance> Endpoint<'d, T> { pub(crate) fn start_transfer(&mut self) { let ep_num = self.info.addr.index(); - let ep_idx = 2 * ep_num + self.info.addr.is_in() as usize; - let offset = if ep_idx % 2 == 1 { ep_idx / 2 + 16 } else { ep_idx / 2 }; + // let ep_idx = 2 * ep_num + self.info.addr.is_in() as usize; + // let offset = if ep_idx % 2 == 1 { ep_idx / 2 + 16 } else { ep_idx / 2 }; - defmt::info!( - "Start transfer on endpoint {}, dir_in?: {}, offset: {}", - ep_num, - self.info.addr.is_in(), - offset - ); + // defmt::info!( + // "Start transfer on endpoint {}, dir_in?: {}, offset: {}", + // ep_num, + // self.info.addr.is_in(), + // offset + // ); let r = T::info().regs; r.endptprime().modify(|w| { @@ -126,31 +125,31 @@ impl<'d, T: Instance> Endpoint<'d, T> { // Link qtd to qhd let first_idx = first_qtd.unwrap(); - unsafe { - defmt::info!( - "Check first qtd idx: {}, addr: {:x} and content: - next_dtd_word: 0x{:x} - total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b} - Buffer0 + offset: {:x} - Buffer1 : {:x} - Buffer2 : {:x} - Buffer3 : {:x} - Buffer4 : {:x}", - first_idx, - DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32, - DCD_DATA.qtd_list.qtd(first_idx).next_dtd().read().0, - DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().total_bytes(), - DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().ioc(), - DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().c_page(), - DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().multo(), - DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().status(), - DCD_DATA.qtd_list.qtd(first_idx).buffer(0).read().0, - DCD_DATA.qtd_list.qtd(first_idx).buffer(1).read().0, - DCD_DATA.qtd_list.qtd(first_idx).buffer(2).read().0, - DCD_DATA.qtd_list.qtd(first_idx).buffer(3).read().0, - DCD_DATA.qtd_list.qtd(first_idx).buffer(4).read().0, - ); - } + // unsafe { + // defmt::info!( + // "Check first qtd idx: {}, addr: {:x} and content: + // next_dtd_word: 0x{:x} + // total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b} + // Buffer0 + offset: {:x} + // Buffer1 : {:x} + // Buffer2 : {:x} + // Buffer3 : {:x} + // Buffer4 : {:x}", + // first_idx, + // DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32, + // DCD_DATA.qtd_list.qtd(first_idx).next_dtd().read().0, + // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().total_bytes(), + // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().ioc(), + // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().c_page(), + // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().multo(), + // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().status(), + // DCD_DATA.qtd_list.qtd(first_idx).buffer(0).read().0, + // DCD_DATA.qtd_list.qtd(first_idx).buffer(1).read().0, + // DCD_DATA.qtd_list.qtd(first_idx).buffer(2).read().0, + // DCD_DATA.qtd_list.qtd(first_idx).buffer(3).read().0, + // DCD_DATA.qtd_list.qtd(first_idx).buffer(4).read().0, + // ); + // } unsafe { if ep_num == 0 { @@ -163,29 +162,29 @@ impl<'d, T: Instance> Endpoint<'d, T> { // T **MUST** be set to 0 w.set_t(false); }); - let qhd: crate::usb::types_v53::Qhd = DCD_DATA.qhd_list.qhd(ep_idx); - - defmt::info!( - "ENDPTLISTADDR: {:x} - Check qhd after setting: qhd_idx: {} - 1st word: mult: {}, zlt: {}, mps: {}, ios: {} - 2nd word: cur dtd: {:x} - 3rd word: next_dtd + t: {:x} - total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b}", - T::info().regs.endptlistaddr().read().0, - ep_idx, - qhd.cap().read().iso_mult(), - qhd.cap().read().zero_length_termination(), - qhd.cap().read().max_packet_size(), - qhd.cap().read().ios(), - qhd.cur_dtd().read().0, // 2nd word - qhd.next_dtd().read().0, - qhd.qtd_token().read().total_bytes(), - qhd.qtd_token().read().ioc(), - qhd.qtd_token().read().c_page(), - qhd.qtd_token().read().multo(), - qhd.qtd_token().read().status(), - ); + // let qhd: crate::usb::types_v53::Qhd = DCD_DATA.qhd_list.qhd(ep_idx); + + // defmt::info!( + // "ENDPTLISTADDR: {:x} + // Check qhd after setting: qhd_idx: {} + // 1st word: mult: {}, zlt: {}, mps: {}, ios: {} + // 2nd word: cur dtd: {:x} + // 3rd word: next_dtd + t: {:x} + // total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b}", + // T::info().regs.endptlistaddr().read().0, + // ep_idx, + // qhd.cap().read().iso_mult(), + // qhd.cap().read().zero_length_termination(), + // qhd.cap().read().max_packet_size(), + // qhd.cap().read().ios(), + // qhd.cur_dtd().read().0, // 2nd word + // qhd.next_dtd().read().0, + // qhd.qtd_token().read().total_bytes(), + // qhd.qtd_token().read().ioc(), + // qhd.qtd_token().read().c_page(), + // qhd.qtd_token().read().multo(), + // qhd.qtd_token().read().status(), + // ); } // Start transfer @@ -243,6 +242,19 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { + defmt::info!("EndpointOut::read: data: {=[u8]}", buf); + let ep_num = self.info.addr.index(); + let r = T::info().regs; + poll_fn(|cx| { + EP_OUT_WAKERS[ep_num].register(cx.waker()); + + if r.endptcomplete().read().0 & (1 << ep_num) != 0 { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; self.transfer(buf).unwrap(); Ok(buf.len()) } @@ -250,6 +262,20 @@ impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { impl<'d, T: Instance> EndpointIn for Endpoint<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result<(), embassy_usb_driver::EndpointError> { + defmt::info!("EndpointIn::write: data: {=[u8]}", buf); + let ep_num = self.info.addr.index(); + let offset = ep_num + self.info.addr.is_in() as usize * 16; + let r = T::info().regs; + poll_fn(|cx| { + EP_IN_WAKERS[ep_num].register(cx.waker()); + // It's IN endpoint, so check the bit 16 + offset + if r.endptcomplete().read().0 & (1 << (offset + 16)) != 0 { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; self.transfer(buf).unwrap(); Ok(()) } diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 807852d..12a1751 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -32,7 +32,6 @@ mod types_v62; static IRQ_RESET: AtomicBool = AtomicBool::new(false); static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); static IRQ_TRANSFER_COMPLETED: AtomicBool = AtomicBool::new(false); -static IRQ_PORT_CHANGE: AtomicBool = AtomicBool::new(false); const AW_NEW: AtomicWaker = AtomicWaker::new(); static EP_IN_WAKERS: [AtomicWaker; ENDPOINT_COUNT] = [AW_NEW; ENDPOINT_COUNT]; @@ -337,7 +336,6 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { Ok(Endpoint { _phantom: PhantomData, info: ep, - buffer: [0; 64], }) } @@ -372,7 +370,6 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { Ok(Endpoint { _phantom: PhantomData, info: ep, - buffer: [0; 64], }) } @@ -579,8 +576,6 @@ pub unsafe fn on_interrupt() { // Transfer complete event if status.ui() { - // Clear endpoint complete status - r.endptcomplete().modify(|w| w.0 = w.0); // Disable USB transfer interrupt r.usbintr().modify(|w| w.set_ue(false)); let ep_status = r.endptstat().read(); @@ -592,8 +587,6 @@ pub unsafe fn on_interrupt() { r.endptprime().read().0, r.endptflush().read().0, ); - // Clear the status by rewrite those bits - r.endptstat().modify(|w| w.0 = w.0); if r.endptsetupstat().read().endptsetupstat() > 0 { IRQ_TRANSFER_COMPLETED.store(true, Ordering::Relaxed); @@ -616,6 +609,8 @@ pub unsafe fn on_interrupt() { } } } + // Re-enable USB transfer interrupt + r.usbintr().modify(|w| w.set_ue(true)); } } From e68ca245a723254d991cd201aaa061d719aa849a Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Fri, 16 Aug 2024 23:52:56 +0800 Subject: [PATCH 30/43] feat(usb): improve ep processing, make usb hid & cdc acm receiving work Signed-off-by: HaoboGu --- examples/hpm5300evk/src/bin/usb.rs | 19 +-- examples/hpm5300evk/src/bin/usb_hid.rs | 211 +++++++++++++++++++++++++ src/usb/control_pipe.rs | 30 ++-- src/usb/endpoint.rs | 47 ++++-- src/usb/mod.rs | 126 +++++++++++++-- 5 files changed, 386 insertions(+), 47 deletions(-) create mode 100644 examples/hpm5300evk/src/bin/usb_hid.rs diff --git a/examples/hpm5300evk/src/bin/usb.rs b/examples/hpm5300evk/src/bin/usb.rs index 7e16d42..e5af659 100644 --- a/examples/hpm5300evk/src/bin/usb.rs +++ b/examples/hpm5300evk/src/bin/usb.rs @@ -5,7 +5,7 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use futures_util::future::join; @@ -64,12 +64,13 @@ async fn main(_spawner: Spawner) -> ! { // Do stuff with the class! let echo_fut = async { - loop { - class.wait_connection().await; + // class.wait_connection().await; + let (mut sender, mut reader) = class.split(); + sender.wait_connection().await; + reader.wait_connection().await; info!("Connected"); - let _ = echo(&mut class).await; + let _ = echo(&mut reader, &mut sender).await; info!("Disconnected"); - } }; // Run everything concurrently. @@ -92,13 +93,13 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, UsbDriver<'d, T>>) -> Result<(), Disconnected> { +async fn echo<'d, T: Instance + 'd>(reader: &mut Receiver<'d, UsbDriver<'d, T>>, sender: &mut Sender<'d, UsbDriver<'d, T>>) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { - let n = class.read_packet(&mut buf).await?; + let n = reader.read_packet(&mut buf).await?; let data = &buf[..n]; - // info!("data: {:x}", data); - class.write_packet(data).await?; + info!("data: {:x}", data); + sender.write_packet(data).await?; } } diff --git a/examples/hpm5300evk/src/bin/usb_hid.rs b/examples/hpm5300evk/src/bin/usb_hid.rs new file mode 100644 index 0000000..331d015 --- /dev/null +++ b/examples/hpm5300evk/src/bin/usb_hid.rs @@ -0,0 +1,211 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] +#![feature(abi_riscv_interrupt)] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use defmt::{info, warn}; +use embassy_executor::Spawner; +use embassy_futures::select::select; +use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; +use embassy_usb::control::OutResponse; +use embassy_usb::{Builder, Handler}; +use futures_util::future::join; +use hpm_hal::gpio::{Input, Pull}; +use hpm_hal::{bind_interrupts, peripherals}; +use static_cell::StaticCell; +use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; +use {defmt_rtt as _, hpm_hal as hal, riscv_rt as _}; + +bind_interrupts!(struct Irqs { + USB0 => hal::usb::InterruptHandler; +}); + +static STATE: StaticCell = StaticCell::new(); + +#[embassy_executor::main(entry = "hpm_hal::entry")] +async fn main(_spawner: Spawner) -> ! { + let p = hal::init(Default::default()); + + let usb_driver = hal::usb::UsbDriver::new(p.USB0, p.PA24, p.PA25); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); + static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); + static MSOS_DESC: StaticCell<[u8; 128]> = StaticCell::new(); + static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new(); + + // UsbDevice builder + let mut request_handler = MyRequestHandler {}; + + let mut builder = Builder::new( + usb_driver, + config, + &mut CONFIG_DESC.init([0; 256])[..], + &mut BOS_DESC.init([0; 256])[..], + &mut MSOS_DESC.init([0; 128])[..], + &mut CONTROL_BUF.init([0; 128])[..], + ); + + static DEVICE_HANDLER: StaticCell = StaticCell::new(); + builder.handler(DEVICE_HANDLER.init(MyDeviceHandler::new())); + + // Create classes on the builder. + let config = embassy_usb::class::hid::Config { + report_descriptor: KeyboardReport::desc(), + request_handler: None, + poll_ms: 60, + max_packet_size: 64, + }; + + let state = STATE.init(State::new()); + let mut hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, state, config); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + let mut button = Input::new(p.PA03, Pull::Down); + + // pin!(hid_ready); + let hid_ready = hid.ready(); + select(usb_fut, hid_ready).await; + let usb_fut = usb.run(); + let (reader, mut writer) = hid.split(); + + // Do stuff with the class! + let in_fut = async { + loop { + // info!("Waiting for HIGH on pin 16"); + // button.wait_for_high().await; + // info!("HIGH DETECTED"); + // Create a report with the A key pressed. (no shift modifier) + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + embassy_time::Timer::after_millis(200).await; + // button.wait_for_low().await; + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + embassy_time::Timer::after_secs(1).await; + info!("Wrote report"); + } + }; + + let out_fut = async { + reader.run(false, &mut request_handler).await; + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, join(in_fut, out_fut)).await; + // join(usb_fut, in_fut).await; + defmt::info!("USB task finished"); + loop { + embassy_time::Timer::after_millis(500).await; + } +} + +struct MyRequestHandler {} + +impl RequestHandler for MyRequestHandler { + fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option { + info!("Get report for {:?}", id); + None + } + + fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse { + info!("Set report for {:?}: {=[u8]}", id, data); + OutResponse::Accepted + } + + fn set_idle_ms(&mut self, id: Option, dur: u32) { + info!("Set idle rate for {:?} to {:?}", id, dur); + } + + fn get_idle_ms(&mut self, id: Option) -> Option { + info!("Get idle rate for {:?}", id); + None + } +} + +struct MyDeviceHandler { + configured: AtomicBool, +} + +impl MyDeviceHandler { + fn new() -> Self { + MyDeviceHandler { + configured: AtomicBool::new(false), + } + } +} + +impl Handler for MyDeviceHandler { + fn enabled(&mut self, enabled: bool) { + self.configured.store(false, Ordering::Relaxed); + if enabled { + info!("Device enabled"); + } else { + info!("Device disabled"); + } + } + + fn reset(&mut self) { + self.configured.store(false, Ordering::Relaxed); + info!("Bus reset, the Vbus current limit is 100mA"); + } + + fn addressed(&mut self, addr: u8) { + self.configured.store(false, Ordering::Relaxed); + info!("USB address set to: {}", addr); + } + + fn configured(&mut self, configured: bool) { + self.configured.store(configured, Ordering::Relaxed); + if configured { + info!("Device configured, it may now draw up to the configured current limit from Vbus.") + } else { + info!("Device is no longer configured, the Vbus current limit is 100mA."); + } + } +} + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + defmt::info!("panic: {:?}", defmt::Debug2Format(&info)); + loop {} +} diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 1972279..2c42b24 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -2,13 +2,12 @@ use core::marker::PhantomData; use core::sync::atomic::Ordering; use core::task::Poll; -use defmt::info; use embassy_usb_driver::EndpointError; use futures_util::future::poll_fn; use super::endpoint::Endpoint; use super::Instance; -use crate::usb::{DCD_DATA, EP_OUT_WAKERS, IRQ_TRANSFER_COMPLETED}; +use crate::usb::{DCD_DATA, EP_IN_WAKERS, EP_OUT_WAKERS, IRQ_TRANSFER_COMPLETED}; pub struct ControlPipe<'d, T: Instance> { pub(crate) _phantom: PhantomData<&'d mut T>, @@ -30,7 +29,6 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { // Wait for SETUP packet(interrupt here) // Clear interrupt status(by writing 1) and enable USB interrupt first r.usbsts().modify(|w| w.set_ui(true)); - // r.usbsts().modify(|w| w.set_ui(false)); while r.usbsts().read().ui() {} r.usbintr().modify(|w| w.set_ue(true)); let _ = poll_fn(|cx| { @@ -45,14 +43,13 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { Poll::Pending }) - .await - .unwrap(); - - info!("Got setup packet"); + .await; // Read setup packet from qhd let setup_packet = unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() }; + // info!("Got setup packet: {:x}", setup_packet); + // Clear interrupt status and enable USB interrupt r.usbsts().modify(|w| w.set_ui(true)); while r.usbsts().read().ui() {} @@ -67,7 +64,6 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { _first: bool, _last: bool, ) -> Result { - defmt::info!("ControlPipe::dataout"); self.ep_out.transfer(buf).map_err(|_e| EndpointError::BufferOverflow)?; Ok(buf.len()) } @@ -94,7 +90,23 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { defmt::info!("ControlPipe::accept"); self.ep_in.transfer(&[]).unwrap(); - defmt::trace!("control: accept OK"); + let r = T::info().regs; + // TODO: wait for completion before returning, needed so set_address() doesn't happen early + let _ = poll_fn(|cx| { + EP_IN_WAKERS[0].register(cx.waker()); + if r.endptcomplete().read().etce() & 1 > 0 { + // Clear the flag + r.endptcomplete().modify(|w| w.set_etce(1)); + return Poll::Ready(Ok::<(), ()>(())); + } + + defmt::info!("ControlPipe: accept pending"); + + Poll::Pending + }) + .await + .unwrap(); + defmt::trace!("ControlPipe: accept OK"); } async fn reject(&mut self) { diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index b8c38be..a018bce 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -4,7 +4,7 @@ use core::task::Poll; use embassy_usb_driver::{EndpointAddress, EndpointIn, EndpointInfo, EndpointOut}; -use super::{Error, DCD_DATA, QTD_COUNT_EACH_ENDPOINT}; +use super::{Error, DCD_DATA, QTD_COUNT_EACH_QHD}; use crate::usb::{Instance, EP_IN_WAKERS, EP_OUT_WAKERS}; pub struct EpConfig { @@ -18,6 +18,7 @@ pub struct EpConfig { pub struct Endpoint<'d, T: Instance> { pub(crate) _phantom: PhantomData<&'d mut T>, pub(crate) info: EndpointInfo, + pub(crate) ready: bool, } impl<'d, T: Instance> Endpoint<'d, T> { @@ -70,7 +71,7 @@ impl<'d, T: Instance> Endpoint<'d, T> { let mut data_offset = 0; let mut remaining_bytes = data.len(); loop { - let qtd_idx = ep_idx * QTD_COUNT_EACH_ENDPOINT + i; + let qtd_idx = ep_idx * QTD_COUNT_EACH_QHD + i; i += 1; // If the transfer size > 0x4000, then there should be multiple qtds in the linked list @@ -94,7 +95,7 @@ impl<'d, T: Instance> Endpoint<'d, T> { }; // Last chunk of the data - if remaining_bytes == 0 && !self.info.addr.is_in() { + if remaining_bytes == 0 { unsafe { DCD_DATA.qtd_list.qtd(qtd_idx).qtd_token().modify(|w| w.set_ioc(true)); }; @@ -210,7 +211,7 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { async fn wait_enabled(&mut self) { let i = self.info.addr.index(); - defmt::info!("Endpoint({})::wait_enabled", i); + defmt::info!("Endpoint({})::IN?{}::wait_enabled", i, self.info.addr.is_in()); assert!(i != 0); poll_fn(|cx| { let r = T::info().regs; @@ -219,7 +220,6 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { EP_IN_WAKERS[i].register(cx.waker()); // Check if the endpoint is enabled if r.endptctrl(i).read().txe() { - defmt::info!("Endpoint::wait_enabled: enabled"); Poll::Ready(()) } else { Poll::Pending @@ -228,7 +228,6 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { EP_OUT_WAKERS[i].register(cx.waker()); // Check if the endpoint is enabled if r.endptctrl(i).read().rxe() { - defmt::info!("Endpoint::wait_enabled: enabled"); Poll::Ready(()) } else { Poll::Pending @@ -236,47 +235,67 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { } }) .await; - defmt::info!("endpoint {} enabled", i); + defmt::info!("endpoint {} IN?:{} enabled", i, self.info.addr.is_in()); + self.ready = true; } } impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { - defmt::info!("EndpointOut::read: data: {=[u8]}", buf); + if !self.ready { + defmt::info!( + "EP {}, IN?:{}, not ready to read", + self.info.addr.index(), + self.info.addr.is_in() + ); + return Err(embassy_usb_driver::EndpointError::Disabled); + } let ep_num = self.info.addr.index(); + defmt::info!("EndpointOut::read on EP{}", ep_num); let r = T::info().regs; + self.transfer(buf).unwrap(); poll_fn(|cx| { EP_OUT_WAKERS[ep_num].register(cx.waker()); - if r.endptcomplete().read().0 & (1 << ep_num) != 0 { + if r.endptcomplete().read().erce() & (1 << ep_num) != 0 { + r.endptcomplete().modify(|w| w.set_erce(1 << ep_num)); Poll::Ready(()) } else { Poll::Pending } }) .await; - self.transfer(buf).unwrap(); + defmt::info!("EndpointOut::read: end transfer"); Ok(buf.len()) } } impl<'d, T: Instance> EndpointIn for Endpoint<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result<(), embassy_usb_driver::EndpointError> { - defmt::info!("EndpointIn::write: data: {=[u8]}", buf); + if !self.ready { + defmt::info!( + "EP {}, IN?: {}, not ready to write", + self.info.addr.index(), + self.info.addr.is_in() + ); + return Err(embassy_usb_driver::EndpointError::Disabled); + } let ep_num = self.info.addr.index(); - let offset = ep_num + self.info.addr.is_in() as usize * 16; + defmt::info!("EndpointIn::write: data: {=[u8]}, to EP{}", buf, ep_num); let r = T::info().regs; + self.transfer(buf).unwrap(); poll_fn(|cx| { EP_IN_WAKERS[ep_num].register(cx.waker()); // It's IN endpoint, so check the bit 16 + offset - if r.endptcomplete().read().0 & (1 << (offset + 16)) != 0 { + if r.endptcomplete().read().etce() & (1 << ep_num) != 0 { + r.endptcomplete().modify(|w| w.set_etce(1 << ep_num)); Poll::Ready(()) } else { Poll::Pending } }) .await; - self.transfer(buf).unwrap(); + defmt::info!("EndpointOut::write: transfer finish"); Ok(()) } } diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 12a1751..321780e 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -43,13 +43,13 @@ const ENDPOINT_COUNT: usize = 8; #[cfg(usb_v53)] const ENDPOINT_COUNT: usize = 16; -const QTD_COUNT_EACH_ENDPOINT: usize = 8; +const QTD_COUNT_EACH_QHD: usize = 8; const QHD_BUFFER_COUNT: usize = 5; const QHD_ITEM_SIZE: usize = 64; const QTD_ITEM_SIZE: usize = 32; static mut QHD_LIST_DATA: QhdListData = QhdListData([0; QHD_ITEM_SIZE * ENDPOINT_COUNT * 2]); -static mut QTD_LIST_DATA: QtdListData = QtdListData([0; QTD_ITEM_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_ENDPOINT]); +static mut QTD_LIST_DATA: QtdListData = QtdListData([0; QTD_ITEM_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_QHD]); static mut DCD_DATA: DcdData = DcdData { qhd_list: unsafe { QhdList::from_ptr(QHD_LIST_DATA.0.as_ptr() as *mut _) }, qtd_list: unsafe { QtdList::from_ptr(QTD_LIST_DATA.0.as_ptr() as *mut _) }, @@ -59,7 +59,7 @@ static mut DCD_DATA: DcdData = DcdData { pub struct QhdListData([u8; QHD_ITEM_SIZE * ENDPOINT_COUNT * 2]); #[repr(C, align(32))] -pub struct QtdListData([u8; QTD_ITEM_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_ENDPOINT]); +pub struct QtdListData([u8; QTD_ITEM_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_QHD]); pub struct DcdData { /// List of queue head @@ -95,7 +95,7 @@ pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { for i in 0..ENDPOINT_COUNT as usize * 2 { DCD_DATA.qhd_list.qhd(i).reset(); } - for i in 0..ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_ENDPOINT as usize { + for i in 0..ENDPOINT_COUNT as usize * 2 * QTD_COUNT_EACH_QHD as usize { DCD_DATA.qtd_list.qtd(i).reset(); } @@ -197,7 +197,7 @@ impl Qtd { } } -#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, defmt::Format)] pub(crate) struct EndpointAllocData { pub(crate) info: EndpointInfo, pub(crate) used: bool, @@ -331,11 +331,13 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { max_packet_size, interval_ms, }; + defmt::info!("Allocating ep_out: {:?}", ep); self.endpoints_out[ep_idx].used = true; self.endpoints_out[ep_idx].info = ep.clone(); Ok(Endpoint { _phantom: PhantomData, info: ep, + ready: false, }) } @@ -365,11 +367,13 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { max_packet_size, interval_ms, }; + defmt::info!("Allocating ep_in: {:?}", ep); self.endpoints_in[ep_idx].used = true; self.endpoints_in[ep_idx].info = ep.clone(); Ok(Endpoint { _phantom: PhantomData, info: ep, + ready: false, }) } @@ -527,11 +531,11 @@ pub unsafe fn on_interrupt() { // Clear triggered interrupts status bits let triggered_interrupts = status.0 & enabled_interrupts.0; - defmt::info!( - "GOT IRQ: {:b}, triggered: {:b}", - r.usbsts().read().0, - triggered_interrupts - ); + // defmt::info!( + // "GOT IRQ: {:b}, triggered: {:b}", + // r.usbsts().read().0, + // triggered_interrupts + // ); let status = Usbsts(triggered_interrupts); r.usbsts().modify(|w| w.0 = w.0); @@ -578,7 +582,6 @@ pub unsafe fn on_interrupt() { if status.ui() { // Disable USB transfer interrupt r.usbintr().modify(|w| w.set_ue(false)); - let ep_status = r.endptstat().read(); defmt::info!( "Transfer complete interrupt: endptstat: {:b}, endptsetupstat: {:b}, endptcomplete: {:b}, endptprime: {:b}, endptflust: {:b}", r.endptstat().read().0, @@ -594,15 +597,108 @@ pub unsafe fn on_interrupt() { } if r.endptcomplete().read().0 > 0 { - defmt::info!("ep transfer complete: {:b}", r.endptcomplete().read().0); + // defmt::info!("ep transfer complete: {:b}", r.endptcomplete().read().0); + // 在c_sdk里,直接在中断中处理,所有EP的QTD,包括EP0。 + // 在Rust里,这些处理可以放在对应的EP。 + // 理论上,最合适的逻辑应该是,这里唤醒,然后在control的data_in/data_out,以及端点的write/read里去处理&发送&接受数据。 + // 但是,由于embassy的实现,Read可以等待,write写一次就退出了,不会在write函数中等待 + // 所以,这部分处理先放在这,后面看看怎么解决。 + + // for ep_num in 0..ENDPOINT_COUNT { + // if r.endptcomplete().read().erce() & (1 << ep_num) > 0 { + // let qtd_idx = 2 * ep_num * QTD_COUNT_EACH_QHD; + // // 处理OUT端点 + // let mut qtd = unsafe { DCD_DATA.qtd_list.qtd(qtd_idx) }; + + // // Find the final qtd in the linked list + // let mut do_response = true; + // let mut transfer_len = 0; + // loop { + // if qtd.qtd_token().read().halted() + // || qtd.qtd_token().read().transaction_err() + // || qtd.qtd_token().read().buffer_err() + // { + // defmt::info!("qtd error found: {:b}", qtd.qtd_token().read().0); + // do_response = false; + // break; + // } else if qtd.qtd_token().read().active() { + // do_response = false; + // break; + // } else { + // transfer_len += + // qtd.expected_bytes().read().expected_bytes() - qtd.qtd_token().read().total_bytes() + // } + + // if qtd.next_dtd().read().t() { + // break; + // } else { + // let next_addr = qtd.next_dtd().read().next_dtd_addr() << 5; + // defmt::info!("next_qtd_addr: {:x}", next_addr); + // qtd = Qtd::from_ptr(next_addr as *mut _); + // } + // } + + // if do_response { + // defmt::info!("WAKING EP_OUT_WAKERS[{}]", ep_num); + // EP_OUT_WAKERS[ep_num].wake(); + // // TODO: usbd_event_ep0_out_complete_handler + // // 1. if data_buf_residue != 0, then start reading remaining data + // // 2. if data_buf_residue == 0, received all data at ep0, process setup request first + // // 3. After processing, send NULL to host + // } + // } + // if r.endptcomplete().read().etce() & (1 << ep_num) > 0 { + // let qtd_idx = (2 * ep_num + 1) * QTD_COUNT_EACH_QHD; + // let mut qtd = unsafe { DCD_DATA.qtd_list.qtd(qtd_idx) }; + // // 处理IN端点,下面的代码和OUT端点实际上是一模一样的 + + // // Find the final qtd in the linked list + // let mut do_response = true; + // let mut transfer_len = 0; + // loop { + // if qtd.qtd_token().read().halted() + // || qtd.qtd_token().read().transaction_err() + // || qtd.qtd_token().read().buffer_err() + // { + // defmt::info!("qtd error found: {:b}", qtd.qtd_token().read().0); + // do_response = false; + // break; + // } else if qtd.qtd_token().read().active() { + // do_response = false; + // break; + // } else { + // transfer_len += + // qtd.expected_bytes().read().expected_bytes() - qtd.qtd_token().read().total_bytes() + // } + + // if qtd.next_dtd().read().t() { + // break; + // } else { + // let next_addr = qtd.next_dtd().read().next_dtd_addr() << 5; + // defmt::info!("next_qtd_addr: {:x}", next_addr); + // qtd = Qtd::from_ptr(next_addr as *mut _); + // } + // } + // if do_response { + // defmt::info!("WAKING EP_IN_WAKERS[{}]", ep_num); + // EP_IN_WAKERS[ep_num].wake(); + // // TODO: usbd_event_ep0_in_complete_handler + // // 1. if data_buf_residue != 0, then start sending remaining data + // // 2. if zlp_flag == 1, then send zero length packet + // // 3. if zlp_flag != 1, that means all data has sent completely, then start reading out status + // // -> usbd_ep_start_read(busid, USB_CONTROL_OUT_EP0, NULL, 0); + // } + // } + // } + // Transfer completed - for i in 1..ENDPOINT_COUNT { - if ep_status.erbr() & (1 << i) > 0 { + for i in 0..ENDPOINT_COUNT { + if r.endptcomplete().read().erce() & (1 << i) > 0 { defmt::info!("wake {} OUT ep", i); // OUT endpoint EP_OUT_WAKERS[i].wake(); } - if ep_status.etbr() & (1 << i) > 0 { + if r.endptcomplete().read().etce() & (1 << i) > 0 { defmt::info!("wake {} IN ep", i); // IN endpoint EP_IN_WAKERS[i].wake(); From e6e9dcf3ef5f91717b347537858ae3d02e4fe15b Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sat, 17 Aug 2024 18:26:15 +0800 Subject: [PATCH 31/43] feat(usb): update hid demo Signed-off-by: HaoboGu --- examples/hpm5300evk/src/bin/usb_hid.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/examples/hpm5300evk/src/bin/usb_hid.rs b/examples/hpm5300evk/src/bin/usb_hid.rs index 331d015..c8a1607 100644 --- a/examples/hpm5300evk/src/bin/usb_hid.rs +++ b/examples/hpm5300evk/src/bin/usb_hid.rs @@ -7,7 +7,6 @@ use core::sync::atomic::{AtomicBool, Ordering}; use defmt::{info, warn}; use embassy_executor::Spawner; -use embassy_futures::select::select; use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Handler}; @@ -74,7 +73,7 @@ async fn main(_spawner: Spawner) -> ! { }; let state = STATE.init(State::new()); - let mut hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, state, config); + let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, state, config); // Build the builder. let mut usb = builder.build(); @@ -84,18 +83,13 @@ async fn main(_spawner: Spawner) -> ! { let mut button = Input::new(p.PA03, Pull::Down); - // pin!(hid_ready); - let hid_ready = hid.ready(); - select(usb_fut, hid_ready).await; - let usb_fut = usb.run(); let (reader, mut writer) = hid.split(); // Do stuff with the class! let in_fut = async { loop { - // info!("Waiting for HIGH on pin 16"); - // button.wait_for_high().await; - // info!("HIGH DETECTED"); + info!("Waiting for Button"); + button.wait_for_high().await; // Create a report with the A key pressed. (no shift modifier) let report = KeyboardReport { keycodes: [4, 0, 0, 0, 0, 0], @@ -108,8 +102,8 @@ async fn main(_spawner: Spawner) -> ! { Ok(()) => {} Err(e) => warn!("Failed to send report: {:?}", e), }; - embassy_time::Timer::after_millis(200).await; - // button.wait_for_low().await; + embassy_time::Timer::after_millis(100).await; + button.wait_for_low().await; let report = KeyboardReport { keycodes: [0, 0, 0, 0, 0, 0], leds: 0, @@ -120,8 +114,7 @@ async fn main(_spawner: Spawner) -> ! { Ok(()) => {} Err(e) => warn!("Failed to send report: {:?}", e), }; - embassy_time::Timer::after_secs(1).await; - info!("Wrote report"); + embassy_time::Timer::after_millis(100).await; } }; @@ -132,9 +125,8 @@ async fn main(_spawner: Spawner) -> ! { // Run everything concurrently. // If we had made everything `'static` above instead, we could do this using separate tasks instead. join(usb_fut, join(in_fut, out_fut)).await; - // join(usb_fut, in_fut).await; - defmt::info!("USB task finished"); loop { + defmt::info!("USB task finished"); embassy_time::Timer::after_millis(500).await; } } From 9099c6bf56b0290ab500a5b070a2b26e1e4339d8 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sat, 17 Aug 2024 18:34:57 +0800 Subject: [PATCH 32/43] chore: merge latest master Signed-off-by: HaoboGu --- examples/hpm5300evk/.cargo/config.toml | 17 +++++++++++------ examples/hpm5300evk/src/bin/usb_hid.rs | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/hpm5300evk/.cargo/config.toml b/examples/hpm5300evk/.cargo/config.toml index a7dd49f..4e200fb 100644 --- a/examples/hpm5300evk/.cargo/config.toml +++ b/examples/hpm5300evk/.cargo/config.toml @@ -4,12 +4,17 @@ target = "riscv32imafc-unknown-none-elf" [target.riscv32imafc-unknown-none-elf] # runner = 'riscv64-unknown-elf-gdb -x ../../openocd.gdb' runner = [ - "probe-rs", "run", - "--chip", "HPM5361", - "--chip-description-path", "../../HPMicro.yaml", -# "--chip-description-path", "../../../flash-algo/target/definition.yaml", - "--protocol","jtag", - "--log-format", "{t} {L} {F}:{l} {s}", + "probe-rs", + "run", + "--chip", + "HPM5361", + "--chip-description-path", + "../../HPMicro.yaml", + # "--chip-description-path", "../../../flash-algo/target/definition.yaml", + "--protocol", + "jtag", + "--log-format", + "{t} {L} {F}:{l} {s}", ] rustflags = [ diff --git a/examples/hpm5300evk/src/bin/usb_hid.rs b/examples/hpm5300evk/src/bin/usb_hid.rs index c8a1607..ae4e153 100644 --- a/examples/hpm5300evk/src/bin/usb_hid.rs +++ b/examples/hpm5300evk/src/bin/usb_hid.rs @@ -2,6 +2,7 @@ #![no_std] #![feature(type_alias_impl_trait)] #![feature(abi_riscv_interrupt)] +#![feature(impl_trait_in_assoc_type)] use core::sync::atomic::{AtomicBool, Ordering}; From 78be02e9cc6763159af606f49c1cc7db5eb1a1d9 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sat, 17 Aug 2024 18:45:59 +0800 Subject: [PATCH 33/43] fix(usb): fix ci failure Signed-off-by: HaoboGu --- examples/hpm5300evk/src/bin/usb.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/hpm5300evk/src/bin/usb.rs b/examples/hpm5300evk/src/bin/usb.rs index e5af659..b240cec 100644 --- a/examples/hpm5300evk/src/bin/usb.rs +++ b/examples/hpm5300evk/src/bin/usb.rs @@ -2,6 +2,7 @@ #![no_std] #![feature(type_alias_impl_trait)] #![feature(abi_riscv_interrupt)] +#![feature(impl_trait_in_assoc_type)] use defmt::info; use embassy_executor::Spawner; From 695324664654096f43ec2651568756c8498e40cb Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sat, 17 Aug 2024 22:38:51 +0800 Subject: [PATCH 34/43] feat(usb): make usb working finally Signed-off-by: HaoboGu --- examples/hpm5300evk/src/bin/usb.rs | 26 ++++--- src/usb/control_pipe.rs | 31 +++------ src/usb/endpoint.rs | 108 ++++++++++++++--------------- src/usb/mod.rs | 26 +++---- src/usb/types_v53.rs | 1 + 5 files changed, 87 insertions(+), 105 deletions(-) diff --git a/examples/hpm5300evk/src/bin/usb.rs b/examples/hpm5300evk/src/bin/usb.rs index b240cec..1094811 100644 --- a/examples/hpm5300evk/src/bin/usb.rs +++ b/examples/hpm5300evk/src/bin/usb.rs @@ -55,7 +55,7 @@ async fn main(_spawner: Spawner) -> ! { ); // Create classes on the builder. - let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + let class = CdcAcmClass::new(&mut builder, &mut state, 64); // Build the builder. let mut usb = builder.build(); @@ -65,17 +65,16 @@ async fn main(_spawner: Spawner) -> ! { // Do stuff with the class! let echo_fut = async { - // class.wait_connection().await; - let (mut sender, mut reader) = class.split(); - sender.wait_connection().await; - reader.wait_connection().await; - info!("Connected"); - let _ = echo(&mut reader, &mut sender).await; - info!("Disconnected"); + // class.wait_connection().await; + let (mut sender, mut reader) = class.split(); + sender.wait_connection().await; + reader.wait_connection().await; + info!("Connected"); + let _ = echo(&mut reader, &mut sender).await; + info!("Disconnected"); }; // Run everything concurrently. - // If we had made everything `'static` above instead, we could do this using separate tasks instead. join(usb_fut, echo_fut).await; loop { @@ -94,13 +93,18 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd>(reader: &mut Receiver<'d, UsbDriver<'d, T>>, sender: &mut Sender<'d, UsbDriver<'d, T>>) -> Result<(), Disconnected> { +async fn echo<'d, T: Instance + 'd>( + reader: &mut Receiver<'d, UsbDriver<'d, T>>, + sender: &mut Sender<'d, UsbDriver<'d, T>>, +) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { let n = reader.read_packet(&mut buf).await?; let data = &buf[..n]; - info!("data: {:x}", data); + info!("echo data: {:x}, len: {}", data, n); sender.write_packet(data).await?; + // Clear bufffer + buf = [0; 64]; } } diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 2c42b24..69c038d 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -1,5 +1,4 @@ use core::marker::PhantomData; -use core::sync::atomic::Ordering; use core::task::Poll; use embassy_usb_driver::EndpointError; @@ -7,7 +6,7 @@ use futures_util::future::poll_fn; use super::endpoint::Endpoint; use super::Instance; -use crate::usb::{DCD_DATA, EP_IN_WAKERS, EP_OUT_WAKERS, IRQ_TRANSFER_COMPLETED}; +use crate::usb::{DCD_DATA, EP_IN_WAKERS, EP_OUT_WAKERS}; pub struct ControlPipe<'d, T: Instance> { pub(crate) _phantom: PhantomData<&'d mut T>, @@ -22,8 +21,6 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { } async fn setup(&mut self) -> [u8; 8] { - defmt::info!("ControlPipe::setup"); - let r = T::info().regs; // Wait for SETUP packet(interrupt here) @@ -33,11 +30,10 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { r.usbintr().modify(|w| w.set_ue(true)); let _ = poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); - if IRQ_TRANSFER_COMPLETED.load(Ordering::Acquire) { - IRQ_TRANSFER_COMPLETED.store(false, Ordering::Relaxed); - // See hpm_sdk/middleware/cherryusb/port/hpm/usb_dc_hpm.c: 285L - // Clean endpoint setup status - r.endptsetupstat().modify(|w| w.0 = w.0); + if r.endptsetupstat().read().0 & 1 > 0 { + // Clear the flag + r.endptsetupstat().modify(|w| w.set_endptsetupstat(1)); + r.endptcomplete().modify(|w| w.set_erce(1)); return Poll::Ready(Ok::<(), ()>(())); } @@ -45,17 +41,13 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { }) .await; - // Read setup packet from qhd - let setup_packet = unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() }; - - // info!("Got setup packet: {:x}", setup_packet); - // Clear interrupt status and enable USB interrupt r.usbsts().modify(|w| w.set_ui(true)); while r.usbsts().read().ui() {} r.usbintr().modify(|w| w.set_ue(true)); - // Convert to slice - setup_packet + + // Read setup packet from qhd + unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() } } async fn data_out( @@ -74,8 +66,6 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { _first: bool, last: bool, ) -> Result<(), embassy_usb_driver::EndpointError> { - defmt::info!("ControlPipe::datain"); - self.ep_in.transfer(data).map_err(|_e| EndpointError::BufferOverflow)?; if last { self.ep_out.transfer(&[]).unwrap(); @@ -87,11 +77,9 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { /// /// Causes the STATUS packet for the current request to be ACKed. async fn accept(&mut self) { - defmt::info!("ControlPipe::accept"); self.ep_in.transfer(&[]).unwrap(); let r = T::info().regs; - // TODO: wait for completion before returning, needed so set_address() doesn't happen early let _ = poll_fn(|cx| { EP_IN_WAKERS[0].register(cx.waker()); if r.endptcomplete().read().etce() & 1 > 0 { @@ -100,13 +88,10 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { return Poll::Ready(Ok::<(), ()>(())); } - defmt::info!("ControlPipe: accept pending"); - Poll::Pending }) .await .unwrap(); - defmt::trace!("ControlPipe: accept OK"); } async fn reject(&mut self) { diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index a018bce..687ed6c 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -18,7 +18,6 @@ pub struct EpConfig { pub struct Endpoint<'d, T: Instance> { pub(crate) _phantom: PhantomData<&'d mut T>, pub(crate) info: EndpointInfo, - pub(crate) ready: bool, } impl<'d, T: Instance> Endpoint<'d, T> { @@ -127,29 +126,29 @@ impl<'d, T: Instance> Endpoint<'d, T> { // Link qtd to qhd let first_idx = first_qtd.unwrap(); // unsafe { - // defmt::info!( - // "Check first qtd idx: {}, addr: {:x} and content: - // next_dtd_word: 0x{:x} - // total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b} - // Buffer0 + offset: {:x} - // Buffer1 : {:x} - // Buffer2 : {:x} - // Buffer3 : {:x} - // Buffer4 : {:x}", - // first_idx, - // DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32, - // DCD_DATA.qtd_list.qtd(first_idx).next_dtd().read().0, - // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().total_bytes(), - // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().ioc(), - // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().c_page(), - // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().multo(), - // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().status(), - // DCD_DATA.qtd_list.qtd(first_idx).buffer(0).read().0, - // DCD_DATA.qtd_list.qtd(first_idx).buffer(1).read().0, - // DCD_DATA.qtd_list.qtd(first_idx).buffer(2).read().0, - // DCD_DATA.qtd_list.qtd(first_idx).buffer(3).read().0, - // DCD_DATA.qtd_list.qtd(first_idx).buffer(4).read().0, - // ); + // defmt::info!( + // "Check first qtd idx: {}, addr: {:x} and content: + // next_dtd_word: 0x{:x} + // total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b} + // Buffer0 + offset: {:x} + // Buffer1 : {:x} + // Buffer2 : {:x} + // Buffer3 : {:x} + // Buffer4 : {:x}", + // first_idx, + // DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32, + // DCD_DATA.qtd_list.qtd(first_idx).next_dtd().read().0, + // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().total_bytes(), + // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().ioc(), + // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().c_page(), + // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().multo(), + // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().status(), + // DCD_DATA.qtd_list.qtd(first_idx).buffer(0).read().0, + // DCD_DATA.qtd_list.qtd(first_idx).buffer(1).read().0, + // DCD_DATA.qtd_list.qtd(first_idx).buffer(2).read().0, + // DCD_DATA.qtd_list.qtd(first_idx).buffer(3).read().0, + // DCD_DATA.qtd_list.qtd(first_idx).buffer(4).read().0, + // ); // } unsafe { @@ -163,15 +162,15 @@ impl<'d, T: Instance> Endpoint<'d, T> { // T **MUST** be set to 0 w.set_t(false); }); - // let qhd: crate::usb::types_v53::Qhd = DCD_DATA.qhd_list.qhd(ep_idx); + // let qhd: crate::usb::types_v53::Qhd = DCD_DATA.qhd_list.qhd(ep_idx); // defmt::info!( // "ENDPTLISTADDR: {:x} - // Check qhd after setting: qhd_idx: {} - // 1st word: mult: {}, zlt: {}, mps: {}, ios: {} - // 2nd word: cur dtd: {:x} - // 3rd word: next_dtd + t: {:x} - // total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b}", + // Check qhd after setting: qhd_idx: {} + // 1st word: mult: {}, zlt: {}, mps: {}, ios: {} + // 2nd word: cur dtd: {:x} + // 3rd word: next_dtd + t: {:x} + // total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b}", // T::info().regs.endptlistaddr().read().0, // ep_idx, // qhd.cap().read().iso_mult(), @@ -236,23 +235,14 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { }) .await; defmt::info!("endpoint {} IN?:{} enabled", i, self.info.addr.is_in()); - self.ready = true; } } impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { - if !self.ready { - defmt::info!( - "EP {}, IN?:{}, not ready to read", - self.info.addr.index(), - self.info.addr.is_in() - ); - return Err(embassy_usb_driver::EndpointError::Disabled); - } - let ep_num = self.info.addr.index(); - defmt::info!("EndpointOut::read on EP{}", ep_num); let r = T::info().regs; + + let ep_num = self.info.addr.index(); self.transfer(buf).unwrap(); poll_fn(|cx| { EP_OUT_WAKERS[ep_num].register(cx.waker()); @@ -265,24 +255,19 @@ impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { } }) .await; - defmt::info!("EndpointOut::read: end transfer"); - Ok(buf.len()) + + let ep_num = self.info.addr.index(); + let ep_idx = 2 * ep_num + self.info.addr.is_in() as usize; + let len = unsafe { DCD_DATA.qhd_list.qhd(ep_idx).qtd_token().read().total_bytes() as usize }; + Ok(buf.len() - len) } } impl<'d, T: Instance> EndpointIn for Endpoint<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result<(), embassy_usb_driver::EndpointError> { - if !self.ready { - defmt::info!( - "EP {}, IN?: {}, not ready to write", - self.info.addr.index(), - self.info.addr.is_in() - ); - return Err(embassy_usb_driver::EndpointError::Disabled); - } - let ep_num = self.info.addr.index(); - defmt::info!("EndpointIn::write: data: {=[u8]}, to EP{}", buf, ep_num); let r = T::info().regs; + + let ep_num = self.info.addr.index(); self.transfer(buf).unwrap(); poll_fn(|cx| { EP_IN_WAKERS[ep_num].register(cx.waker()); @@ -295,7 +280,22 @@ impl<'d, T: Instance> EndpointIn for Endpoint<'d, T> { } }) .await; - defmt::info!("EndpointOut::write: transfer finish"); + + // Send zlt(if needed) + if buf.len() == self.info.max_packet_size as usize { + self.transfer(&[]).unwrap(); + poll_fn(|cx| { + EP_IN_WAKERS[ep_num].register(cx.waker()); + if r.endptcomplete().read().etce() & (1 << ep_num) != 0 { + r.endptcomplete().modify(|w| w.set_etce(1 << ep_num)); + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + } + Ok(()) } } diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 321780e..6716c41 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -31,7 +31,6 @@ mod types_v62; static IRQ_RESET: AtomicBool = AtomicBool::new(false); static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); -static IRQ_TRANSFER_COMPLETED: AtomicBool = AtomicBool::new(false); const AW_NEW: AtomicWaker = AtomicWaker::new(); static EP_IN_WAKERS: [AtomicWaker; ENDPOINT_COUNT] = [AW_NEW; ENDPOINT_COUNT]; @@ -112,7 +111,6 @@ pub(crate) unsafe fn reset_dcd_data(ep0_max_packet_size: u16) { }); // Set the next pointer INVALID(T=1) - // TODO: Double check here with c_sdk DCD_DATA.qhd_list.qhd(0).next_dtd().write(|w| w.set_t(true)); DCD_DATA.qhd_list.qhd(1).next_dtd().write(|w| w.set_t(true)); } @@ -337,7 +335,6 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { Ok(Endpoint { _phantom: PhantomData, info: ep, - ready: false, }) } @@ -373,7 +370,6 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { Ok(Endpoint { _phantom: PhantomData, info: ep, - ready: false, }) } @@ -474,13 +470,13 @@ pub(super) struct Info { } struct State { - waker: AtomicWaker, + _waker: AtomicWaker, } impl State { const fn new() -> Self { Self { - waker: AtomicWaker::new(), + _waker: AtomicWaker::new(), } } } @@ -582,17 +578,15 @@ pub unsafe fn on_interrupt() { if status.ui() { // Disable USB transfer interrupt r.usbintr().modify(|w| w.set_ue(false)); - defmt::info!( - "Transfer complete interrupt: endptstat: {:b}, endptsetupstat: {:b}, endptcomplete: {:b}, endptprime: {:b}, endptflust: {:b}", - r.endptstat().read().0, - r.endptsetupstat().read().0, - r.endptcomplete().read().0, - r.endptprime().read().0, - r.endptflush().read().0, - ); + // defmt::info!( + // "Transfer complete interrupt: endptstat: {:b}, endptsetupstat: {:b}, endptcomplete_receive: {:b}, endptcomplete_send: {:b}", + // r.endptstat().read().0, + // r.endptsetupstat().read().0, + // r.endptcomplete().read().erce(), + // r.endptcomplete().read().etce(), + // ); if r.endptsetupstat().read().endptsetupstat() > 0 { - IRQ_TRANSFER_COMPLETED.store(true, Ordering::Relaxed); EP_OUT_WAKERS[0].wake(); } @@ -694,12 +688,10 @@ pub unsafe fn on_interrupt() { // Transfer completed for i in 0..ENDPOINT_COUNT { if r.endptcomplete().read().erce() & (1 << i) > 0 { - defmt::info!("wake {} OUT ep", i); // OUT endpoint EP_OUT_WAKERS[i].wake(); } if r.endptcomplete().read().etce() & (1 << i) > 0 { - defmt::info!("wake {} IN ep", i); // IN endpoint EP_IN_WAKERS[i].wake(); } diff --git a/src/usb/types_v53.rs b/src/usb/types_v53.rs index eb490ed..2f6f559 100644 --- a/src/usb/types_v53.rs +++ b/src/usb/types_v53.rs @@ -2,6 +2,7 @@ #![allow(clippy::identity_op)] #![allow(clippy::unnecessary_cast)] #![allow(clippy::erasing_op)] +#![allow(unused)] #[doc = "Queue head block for hpm USB device"] #[derive(Copy, Clone, Eq, PartialEq)] From f6b95f82e846a3c5cc9bd18d15aa6edfc1758812 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sun, 18 Aug 2024 00:27:00 +0800 Subject: [PATCH 35/43] fix(usb): fix packet missing in release mode by waiting for control transfer completes Signed-off-by: HaoboGu --- src/usb/control_pipe.rs | 42 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 69c038d..7243490 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -56,7 +56,20 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { _first: bool, _last: bool, ) -> Result { + let r = T::info().regs; self.ep_out.transfer(buf).map_err(|_e| EndpointError::BufferOverflow)?; + let _ = poll_fn(|cx| { + EP_OUT_WAKERS[0].register(cx.waker()); + if r.endptcomplete().read().erce() & 1 > 0 { + // Clear the flag + r.endptcomplete().modify(|w| w.set_erce(1)); + return Poll::Ready(Ok::<(), ()>(())); + } + + Poll::Pending + }) + .await + .unwrap(); Ok(buf.len()) } @@ -66,7 +79,28 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { _first: bool, last: bool, ) -> Result<(), embassy_usb_driver::EndpointError> { - self.ep_in.transfer(data).map_err(|_e| EndpointError::BufferOverflow)?; + // defmt::info!( + // "ControlPipe::data_in: len={}, last={}, data={:?}", + // data.len(), + // last, + // data + // ); + let r = T::info().regs; + self.ep_in.transfer(data).unwrap(); + + let _ = poll_fn(|cx| { + EP_IN_WAKERS[0].register(cx.waker()); + if r.endptcomplete().read().etce() & 1 > 0 { + // Clear the flag + r.endptcomplete().modify(|w| w.set_etce(1)); + return Poll::Ready(Ok::<(), ()>(())); + } + + Poll::Pending + }) + .await + .unwrap(); + if last { self.ep_out.transfer(&[]).unwrap(); } @@ -77,9 +111,9 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { /// /// Causes the STATUS packet for the current request to be ACKed. async fn accept(&mut self) { + let r = T::info().regs; self.ep_in.transfer(&[]).unwrap(); - let r = T::info().regs; let _ = poll_fn(|cx| { EP_IN_WAKERS[0].register(cx.waker()); if r.endptcomplete().read().etce() & 1 > 0 { @@ -95,15 +129,13 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { } async fn reject(&mut self) { - defmt::info!("ControlPipe::reject"); + // defmt::info!("ControlPipe::reject"); // Reject, set IN+OUT to stall self.ep_in.set_stall(); self.ep_out.set_stall(); } async fn accept_set_address(&mut self, addr: u8) { - defmt::info!("ControlPipe::accept_set_address: {}", addr); - let r = T::info().regs; r.deviceaddr().modify(|w| { w.set_usbadr(addr); From aa122b52a53abdf9c2ab0648d06afd3adb2e9a31 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sun, 18 Aug 2024 00:30:27 +0800 Subject: [PATCH 36/43] refactor(usb): clean logs Signed-off-by: HaoboGu --- src/usb/bus.rs | 24 ++++++++++++------------ src/usb/endpoint.rs | 5 +---- src/usb/mod.rs | 10 ++++------ 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/usb/bus.rs b/src/usb/bus.rs index a19a1c1..9fee862 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -23,18 +23,12 @@ pub struct Bus { impl embassy_usb_driver::Bus for Bus { /// Enable the USB peripheral. - async fn enable(&mut self) { - // FIXME: dcd init and dcd connect are called when initializing the Bus - // What should be done here? - defmt::info!("Bus::enable"); - } + async fn enable(&mut self) {} /// Disable and powers down the USB peripheral. async fn disable(&mut self) { - defmt::info!("Bus::disable"); - // TODO: dcd deinit or phy deinit? + // defmt::info!("Bus::disable"); self.dcd_deinit(); - // self.phy_deinit(); } /// Wait for a bus-related event. @@ -102,7 +96,12 @@ impl embassy_usb_driver::Bus for Bus { /// Enable or disable an endpoint. fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { - defmt::info!("Bus::endpoint_set_enabled"); + // defmt::info!( + // "Bus::endpoint_set_enabled: ep_num: {}, ep_dir: {}, enabled: {}", + // ep_addr.index(), + // ep_addr.direction(), + // enabled + // ); if enabled { let endpoint_list = if ep_addr.direction() == Direction::In { self.endpoints_in @@ -111,6 +110,7 @@ impl embassy_usb_driver::Bus for Bus { }; let ep_data = endpoint_list[ep_addr.index()]; assert!(ep_data.addr == ep_addr); + // defmt::info!("opening ep: {:?}", ep_data); self.endpoint_open(EpConfig { transfer: ep_data.ep_type as u8, ep_addr, @@ -125,7 +125,7 @@ impl embassy_usb_driver::Bus for Bus { /// /// If the endpoint is an OUT endpoint, it should be prepared to receive data again. fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { - defmt::info!("Bus::endpoint_set_stalled"); + // defmt::info!("Bus::endpoint_set_stalled: {}", stalled); if stalled { self.device_endpoint_stall(ep_addr); } else { @@ -276,7 +276,7 @@ impl Bus { // Parallel transceiver width w.set_ptw(false); // Forced fullspeed mode, c_sdk commented this line out, use it only when the device runs in full speed mode - // TODO: Currently, the device can only be recognized at fs mode. + // TODO: Currently, the device can only be recognized at fs mode. // How to switch to hs mode? w.set_pfsc(true); }); @@ -494,7 +494,7 @@ impl Bus { // } pub(crate) fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { - defmt::info!("Bus::device_bus_reset"); + // defmt::info!("Bus::device_bus_reset"); self.dcd_bus_reset(); unsafe { diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index 687ed6c..16b96c2 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -210,8 +210,6 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { async fn wait_enabled(&mut self) { let i = self.info.addr.index(); - defmt::info!("Endpoint({})::IN?{}::wait_enabled", i, self.info.addr.is_in()); - assert!(i != 0); poll_fn(|cx| { let r = T::info().regs; // TODO: Simplify the code @@ -234,7 +232,6 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { } }) .await; - defmt::info!("endpoint {} IN?:{} enabled", i, self.info.addr.is_in()); } } @@ -271,7 +268,7 @@ impl<'d, T: Instance> EndpointIn for Endpoint<'d, T> { self.transfer(buf).unwrap(); poll_fn(|cx| { EP_IN_WAKERS[ep_num].register(cx.waker()); - // It's IN endpoint, so check the bit 16 + offset + // It's IN endpoint, so check the etce if r.endptcomplete().read().etce() & (1 << ep_num) != 0 { r.endptcomplete().modify(|w| w.set_etce(1 << ep_num)); Poll::Ready(()) diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 6716c41..9f5c5c6 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -329,7 +329,6 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { max_packet_size, interval_ms, }; - defmt::info!("Allocating ep_out: {:?}", ep); self.endpoints_out[ep_idx].used = true; self.endpoints_out[ep_idx].info = ep.clone(); Ok(Endpoint { @@ -364,7 +363,6 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { max_packet_size, interval_ms, }; - defmt::info!("Allocating ep_in: {:?}", ep); self.endpoints_in[ep_idx].used = true; self.endpoints_in[ep_idx].info = ep.clone(); Ok(Endpoint { @@ -429,7 +427,7 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { let r = self.info.regs; // Set endpoint list address unsafe { - defmt::info!("Setting ENDPTLISTADDR: {:x}", DCD_DATA.qhd_list.as_ptr()); + // defmt::info!("Setting ENDPTLISTADDR: {:x}", DCD_DATA.qhd_list.as_ptr()); r.endptlistaddr().modify(|w| w.0 = DCD_DATA.qhd_list.as_ptr() as u32); }; @@ -564,7 +562,7 @@ pub unsafe fn on_interrupt() { if status.pci() { if r.portsc1().read().ccs() { r.usbintr().modify(|w| w.set_pce(false)); - defmt::info!("Port change interrupt: connected"); + // defmt::info!("Port change interrupt: connected"); // Wake main thread. Then the suspend event will be processed in Bus::poll() BUS_WAKER.wake(); // Connected @@ -688,11 +686,11 @@ pub unsafe fn on_interrupt() { // Transfer completed for i in 0..ENDPOINT_COUNT { if r.endptcomplete().read().erce() & (1 << i) > 0 { - // OUT endpoint + // Wake OUT endpoint EP_OUT_WAKERS[i].wake(); } if r.endptcomplete().read().etce() & (1 << i) > 0 { - // IN endpoint + // Wake IN endpoint EP_IN_WAKERS[i].wake(); } } From a6c8279cfaa3a9042a058c21042e2c7e3d049376 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sun, 18 Aug 2024 22:23:52 +0800 Subject: [PATCH 37/43] refactor(usb): clean and refactor usb code Signed-off-by: HaoboGu --- src/usb/bus.rs | 252 ++++++++++++++++------------------------ src/usb/control_pipe.rs | 39 ++++--- src/usb/endpoint.rs | 99 ++++------------ src/usb/mod.rs | 164 +++----------------------- 4 files changed, 167 insertions(+), 387 deletions(-) diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 9fee862..09b6d37 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -3,32 +3,57 @@ use core::marker::PhantomData; use core::sync::atomic::Ordering; use core::task::Poll; -use defmt::error; use embassy_usb_driver::{Direction, EndpointAddress, EndpointInfo, EndpointType, Event, Unsupported}; use embedded_hal::delay::DelayNs; use hpm_metapac::usb::regs::*; use riscv::delay::McycleDelay; -use super::{init_qhd, Info, Instance, ENDPOINT_COUNT}; -use crate::usb::{reset_dcd_data, EpConfig, BUS_WAKER, IRQ_RESET, IRQ_SUSPEND}; +use super::{init_qhd, Instance, ENDPOINT_COUNT}; +use crate::usb::{reset_dcd_data, EpConfig, BUS_WAKER, DCD_DATA, IRQ_RESET, IRQ_SUSPEND}; +/// USB bus pub struct Bus { pub(crate) _phantom: PhantomData, - pub(crate) info: &'static Info, pub(crate) endpoints_out: [EndpointInfo; ENDPOINT_COUNT], pub(crate) endpoints_in: [EndpointInfo; ENDPOINT_COUNT], pub(crate) delay: McycleDelay, pub(crate) inited: bool, } +/// Implement the `embassy_usb_driver::Bus` trait for `Bus`. impl embassy_usb_driver::Bus for Bus { - /// Enable the USB peripheral. - async fn enable(&mut self) {} + /// Enable the USB bus. + async fn enable(&mut self) { + // Init the usb phy and device controller + self.device_init(); + + // Set ENDPTLISTADDR, enable interrupts + { + let r = T::info().regs; + // Set endpoint list address + unsafe { + r.endptlistaddr().modify(|w| w.0 = DCD_DATA.qhd_list.as_ptr() as u32); + }; + + // Clear status + r.usbsts().modify(|w| w.0 = w.0); + + // Enable interrupt mask + r.usbintr().write(|w| { + w.set_ue(true); + w.set_uee(true); + w.set_pce(true); + w.set_ure(true); + }); + } + + // Start to run usb device + self.device_connect(); + } /// Disable and powers down the USB peripheral. async fn disable(&mut self) { - // defmt::info!("Bus::disable"); - self.dcd_deinit(); + self.device_deinit(); } /// Wait for a bus-related event. @@ -36,11 +61,9 @@ impl embassy_usb_driver::Bus for Bus { /// This method should asynchronously wait for an event to happen, then /// return it. See [`Event`] for the list of events this method should return. async fn poll(&mut self) -> Event { - defmt::info!("Bus::poll"); - poll_fn(|cx| { BUS_WAKER.register(cx.waker()); - let r = self.info.regs; + let r = T::info().regs; // TODO: implement VBUS detection. if !self.inited { @@ -48,11 +71,12 @@ impl embassy_usb_driver::Bus for Bus { return Poll::Ready(Event::PowerDetected); } + // RESET event if IRQ_RESET.load(Ordering::Acquire) { IRQ_RESET.store(false, Ordering::Relaxed); // Set device addr to 0 - self.dcd_set_address(0); + self.device_set_address(0); // Set ep0 IN/OUT self.endpoint_open(EpConfig { @@ -69,7 +93,7 @@ impl embassy_usb_driver::Bus for Bus { // Reset bus self.device_bus_reset(64); - // Set ue, enable usb transfer interrupt + // Enable usb transfer interrupt r.usbintr().modify(|w| w.set_ue(true)); r.usbintr().modify(|w| w.set_ure(false)); @@ -77,15 +101,13 @@ impl embassy_usb_driver::Bus for Bus { return Poll::Ready(Event::Reset); } + // SUSPEND event if IRQ_SUSPEND.load(Ordering::Acquire) { IRQ_SUSPEND.store(false, Ordering::Relaxed); - if r.portsc1().read().susp() { // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration. let _device_adr = r.deviceaddr().read().usbadr(); } - - defmt::info!("poll: SUSPEND"); return Poll::Ready(Event::Suspend); } @@ -96,12 +118,6 @@ impl embassy_usb_driver::Bus for Bus { /// Enable or disable an endpoint. fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { - // defmt::info!( - // "Bus::endpoint_set_enabled: ep_num: {}, ep_dir: {}, enabled: {}", - // ep_addr.index(), - // ep_addr.direction(), - // enabled - // ); if enabled { let endpoint_list = if ep_addr.direction() == Direction::In { self.endpoints_in @@ -110,14 +126,13 @@ impl embassy_usb_driver::Bus for Bus { }; let ep_data = endpoint_list[ep_addr.index()]; assert!(ep_data.addr == ep_addr); - // defmt::info!("opening ep: {:?}", ep_data); self.endpoint_open(EpConfig { transfer: ep_data.ep_type as u8, ep_addr, max_packet_size: ep_data.max_packet_size, }); } else { - self.device_endpoint_close(ep_addr); + self.endpoint_close(ep_addr); } } @@ -125,18 +140,22 @@ impl embassy_usb_driver::Bus for Bus { /// /// If the endpoint is an OUT endpoint, it should be prepared to receive data again. fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { - // defmt::info!("Bus::endpoint_set_stalled: {}", stalled); if stalled { - self.device_endpoint_stall(ep_addr); + self.endpoint_stall(ep_addr); } else { - self.device_endpoint_clean_stall(ep_addr); + self.endpoint_clean_stall(ep_addr); } } /// Get whether the STALL condition is set for an endpoint. fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { - defmt::info!("Bus::endpoint_is_stalled"); - self.dcd_endpoint_check_stall(ep_addr) + let r = T::info().regs; + + if ep_addr.is_in() { + r.endptctrl(ep_addr.index() as usize).read().txs() + } else { + r.endptctrl(ep_addr.index() as usize).read().rxs() + } } /// Initiate a remote wakeup of the host by the device. @@ -146,13 +165,14 @@ impl embassy_usb_driver::Bus for Bus { /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support /// remote wakeup or it has not been enabled at creation time. async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { - todo!() + Ok(()) } } impl Bus { - pub(crate) fn phy_init(&mut self) { - let r = &self.info.regs; + /// Initialize USB phy + fn phy_init(&mut self) { + let r = T::info().regs; // Enable dp/dm pulldown // In hpm_sdk, this operation is done by `ptr->PHY_CTRL0 &= ~0x001000E0u`. @@ -197,8 +217,9 @@ impl Bus { }); } - pub(crate) fn phy_deinit(&mut self) { - let r = &self.info.regs; + // Deinitialize USB phy + fn phy_deinit(&mut self) { + let r = T::info().regs; r.otg_ctrl0().modify(|w| { w.set_otg_utmi_suspendm_sw(true); @@ -212,15 +233,15 @@ impl Bus { } /// Get port speed: 00: full speed, 01: low speed, 10: high speed, 11: undefined - /// TODO: Use enum - // pub(crate) fn get_port_speed(&mut self) -> u8 { - // let r = &self.info.regs; + pub(crate) fn get_port_speed(&mut self) -> u8 { + let r = T::info().regs; - // r.portsc1().read().pspd() - // } + r.portsc1().read().pspd() + } - pub(crate) fn dcd_bus_reset(&mut self) { - let r = &self.info.regs; + /// Reset USB bus + fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { + let r = T::info().regs; // For each endpoint, first set the transfer type to ANY type other than control. // This is because the default transfer type is control, according to hpm_sdk, @@ -245,14 +266,19 @@ impl Bus { r.endptflush().modify(|w| w.0 = 0xFFFFFFFF); while r.endptflush().read().0 != 0 {} + + // Reset DCD_DATA + unsafe { + reset_dcd_data(ep0_max_packet_size); + } } /// Initialize USB device controller driver - pub(crate) fn dcd_init(&mut self) { + fn device_init(&mut self) { // Initialize phy first self.phy_init(); - let r = &self.info.regs; + let r = T::info().regs; // Reset controller r.usbcmd().modify(|w| w.set_rst(true)); @@ -292,9 +318,9 @@ impl Bus { }); } - pub(crate) fn dcd_set_address(&mut self, addr: u8) { - let r = &self.info.regs; - + /// Set device address + fn device_set_address(&mut self, addr: u8) { + let r = T::info().regs; r.deviceaddr().modify(|w| { w.set_usbadr(addr); w.set_usbadra(true); @@ -302,8 +328,8 @@ impl Bus { } /// Deinitialize USB device controller driver - pub(crate) fn dcd_deinit(&mut self) { - let r = &self.info.regs; + fn device_deinit(&mut self) { + let r = T::info().regs; // Stop first r.usbcmd().modify(|w| w.set_rs(false)); @@ -322,44 +348,17 @@ impl Bus { } /// Connect by enabling internal pull-up resistor on D+/D- - pub(crate) fn dcd_connect(&mut self) { - let r = &self.info.regs; + fn device_connect(&mut self) { + let r = T::info().regs; r.usbcmd().modify(|w| { w.set_rs(true); }); } - /// Disconnect by disabling internal pull-up resistor on D+/D- - // pub(crate) fn dcd_disconnect(&mut self) { - // let r = &self.info.regs; - - // // Stop - // r.usbcmd().modify(|w| { - // w.set_rs(false); - // }); - - // // Pullup DP to make the phy switch into full speed mode - // r.usbcmd().modify(|w| { - // w.set_rs(true); - // }); - - // // Clear sof flag and wait - // r.usbsts().modify(|w| { - // w.set_sri(true); - // }); - // while r.usbsts().read().sri() == false {} - - // // Disconnect - // r.usbcmd().modify(|w| { - // w.set_rs(false); - // }); - // } - - /// usbd_endpoint_open - pub(crate) fn endpoint_open(&mut self, ep_config: EpConfig) { + /// Open the endpoint + fn endpoint_open(&mut self, ep_config: EpConfig) { if ep_config.ep_addr.index() >= ENDPOINT_COUNT { - error!("Invalid endpoint index"); return; } @@ -367,15 +366,10 @@ impl Bus { unsafe { init_qhd(&ep_config) }; // Open endpoint - self.dcd_endpoint_open(ep_config); - } - - pub(crate) fn dcd_endpoint_open(&mut self, ep_config: EpConfig) { - let r = &self.info.regs; - let ep_num = ep_config.ep_addr.index(); // Enable EP control + let r = T::info().regs; r.endptctrl(ep_num as usize).modify(|w| { // Clear the RXT or TXT bits if ep_config.ep_addr.is_in() { @@ -392,53 +386,9 @@ impl Bus { }); } - // pub(crate) fn endpoint_get_type(&mut self, ep_addr: EndpointAddress) -> u8 { - // let r = &self.info.regs; - - // if ep_addr.is_in() { - // r.endptctrl(ep_addr.index() as usize).read().txt() - // } else { - // r.endptctrl(ep_addr.index() as usize).read().rxt() - // } - // } - - pub(crate) fn device_endpoint_stall(&mut self, ep_addr: EndpointAddress) { - let r = &self.info.regs; - - if ep_addr.is_in() { - r.endptctrl(ep_addr.index() as usize).modify(|w| w.set_txs(true)); - } else { - r.endptctrl(ep_addr.index() as usize).modify(|w| w.set_rxs(true)); - } - } - - pub(crate) fn device_endpoint_clean_stall(&mut self, ep_addr: EndpointAddress) { - let r = &self.info.regs; - - r.endptctrl(ep_addr.index() as usize).modify(|w| { - if ep_addr.is_in() { - // Data toggle also need to be reset - w.set_txr(true); - w.set_txs(false); - } else { - w.set_rxr(true); - w.set_rxs(false); - } - }); - } - - pub(crate) fn dcd_endpoint_check_stall(&mut self, ep_addr: EndpointAddress) -> bool { - let r = &self.info.regs; - - if ep_addr.is_in() { - r.endptctrl(ep_addr.index() as usize).read().txs() - } else { - r.endptctrl(ep_addr.index() as usize).read().rxs() - } - } - - pub(crate) fn dcd_endpoint_close(&mut self, ep_addr: EndpointAddress) { - let r = &self.info.regs; + /// Close the endpoint + fn endpoint_close(&mut self, ep_addr: EndpointAddress) { + let r = T::info().regs; let ep_bit = 1 << ep_addr.index(); @@ -473,6 +423,7 @@ impl Bus { w.set_rxs(false); } }); + // Set transfer type back to ANY type other than control r.endptctrl(ep_addr.index() as usize).write(|w| { if ep_addr.is_in() { @@ -483,27 +434,28 @@ impl Bus { }); } - // pub(crate) fn ep_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { - // let r = &self.info.regs; + fn endpoint_stall(&mut self, ep_addr: EndpointAddress) { + let r = T::info().regs; - // if ep_addr.is_in() { - // r.endptctrl(ep_addr.index() as usize).read().txs() - // } else { - // r.endptctrl(ep_addr.index() as usize).read().rxs() - // } - // } - - pub(crate) fn device_bus_reset(&mut self, ep0_max_packet_size: u16) { - // defmt::info!("Bus::device_bus_reset"); - self.dcd_bus_reset(); - - unsafe { - reset_dcd_data(ep0_max_packet_size); + if ep_addr.is_in() { + r.endptctrl(ep_addr.index() as usize).modify(|w| w.set_txs(true)); + } else { + r.endptctrl(ep_addr.index() as usize).modify(|w| w.set_rxs(true)); } } - pub(crate) fn device_endpoint_close(&mut self, ep_addr: EndpointAddress) { - defmt::info!("Bus::device_edpt_close"); - self.dcd_endpoint_close(ep_addr); + fn endpoint_clean_stall(&mut self, ep_addr: EndpointAddress) { + let r = T::info().regs; + + r.endptctrl(ep_addr.index() as usize).modify(|w| { + if ep_addr.is_in() { + // Data toggle also need to be reset + w.set_txr(true); + w.set_txs(false); + } else { + w.set_rxr(true); + w.set_rxs(false); + } + }); } } diff --git a/src/usb/control_pipe.rs b/src/usb/control_pipe.rs index 7243490..2546833 100644 --- a/src/usb/control_pipe.rs +++ b/src/usb/control_pipe.rs @@ -16,18 +16,20 @@ pub struct ControlPipe<'d, T: Instance> { } impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { + /// Maximum packet size for the control pipe fn max_packet_size(&self) -> usize { self.max_packet_size } + /// Read a single setup packet from the endpoint. async fn setup(&mut self) -> [u8; 8] { let r = T::info().regs; - // Wait for SETUP packet(interrupt here) // Clear interrupt status(by writing 1) and enable USB interrupt first r.usbsts().modify(|w| w.set_ui(true)); while r.usbsts().read().ui() {} r.usbintr().modify(|w| w.set_ue(true)); + // Wait for SETUP packet let _ = poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); if r.endptsetupstat().read().0 & 1 > 0 { @@ -41,7 +43,7 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { }) .await; - // Clear interrupt status and enable USB interrupt + // Clear interrupt status and re-enable USB interrupt r.usbsts().modify(|w| w.set_ui(true)); while r.usbsts().read().ui() {} r.usbintr().modify(|w| w.set_ue(true)); @@ -50,6 +52,10 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { unsafe { DCD_DATA.qhd_list.qhd(0).get_setup_request() } } + /// Read a DATA OUT packet into `buf` in response to a control write request. + /// + /// Must be called after `setup()` for requests with `direction` of `Out` + /// and `length` greater than zero. async fn data_out( &mut self, buf: &mut [u8], @@ -57,7 +63,7 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { _last: bool, ) -> Result { let r = T::info().regs; - self.ep_out.transfer(buf).map_err(|_e| EndpointError::BufferOverflow)?; + self.ep_out.transfer(buf).map_err(|_e| EndpointError::Disabled)?; let _ = poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); if r.endptcomplete().read().erce() & 1 > 0 { @@ -68,23 +74,20 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { Poll::Pending }) - .await - .unwrap(); + .await; + Ok(buf.len()) } + /// Send a DATA IN packet with `data` in response to a control read request. + /// + /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`. async fn data_in( &mut self, data: &[u8], _first: bool, last: bool, ) -> Result<(), embassy_usb_driver::EndpointError> { - // defmt::info!( - // "ControlPipe::data_in: len={}, last={}, data={:?}", - // data.len(), - // last, - // data - // ); let r = T::info().regs; self.ep_in.transfer(data).unwrap(); @@ -98,8 +101,7 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { Poll::Pending }) - .await - .unwrap(); + .await; if last { self.ep_out.transfer(&[]).unwrap(); @@ -124,17 +126,22 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { Poll::Pending }) - .await - .unwrap(); + .await; } + /// Reject a control request. + /// + /// Sets a STALL condition on the pipe to indicate an error. async fn reject(&mut self) { - // defmt::info!("ControlPipe::reject"); // Reject, set IN+OUT to stall self.ep_in.set_stall(); self.ep_out.set_stall(); } + /// Accept SET_ADDRESS control and change bus address. + /// + /// For most drivers this function should firstly call `accept()` and then change the bus address. + /// However, there are peripherals (Synopsys USB OTG) that have reverse order. async fn accept_set_address(&mut self, addr: u8) { let r = T::info().regs; r.deviceaddr().modify(|w| { diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index 16b96c2..33e9d15 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -4,10 +4,10 @@ use core::task::Poll; use embassy_usb_driver::{EndpointAddress, EndpointIn, EndpointInfo, EndpointOut}; -use super::{Error, DCD_DATA, QTD_COUNT_EACH_QHD}; +use super::{DCD_DATA, QTD_COUNT_EACH_QHD}; use crate::usb::{Instance, EP_IN_WAKERS, EP_OUT_WAKERS}; -pub struct EpConfig { +pub(crate) struct EpConfig { /// Endpoint type pub(crate) transfer: u8, pub(crate) ep_addr: EndpointAddress, @@ -23,15 +23,6 @@ pub struct Endpoint<'d, T: Instance> { impl<'d, T: Instance> Endpoint<'d, T> { pub(crate) fn start_transfer(&mut self) { let ep_num = self.info.addr.index(); - // let ep_idx = 2 * ep_num + self.info.addr.is_in() as usize; - // let offset = if ep_idx % 2 == 1 { ep_idx / 2 + 16 } else { ep_idx / 2 }; - - // defmt::info!( - // "Start transfer on endpoint {}, dir_in?: {}, offset: {}", - // ep_num, - // self.info.addr.is_in(), - // offset - // ); let r = T::info().regs; r.endptprime().modify(|w| { @@ -43,7 +34,9 @@ impl<'d, T: Instance> Endpoint<'d, T> { }); } - pub(crate) fn transfer(&mut self, data: &[u8]) -> Result<(), Error> { + /// Schedule the transfer + /// TODO: Add typed error + pub(crate) fn transfer(&mut self, data: &[u8]) -> Result<(), ()> { let r = T::info().regs; let ep_num = self.info.addr.index(); @@ -57,7 +50,7 @@ impl<'d, T: Instance> Endpoint<'d, T> { let qtd_num = (data.len() + 0x3FFF) / 0x4000; if qtd_num > 8 { - return Err(Error::InvalidQtdNum); + return Err(()); } // TODO: Convert data's address to coressponding core @@ -82,7 +75,7 @@ impl<'d, T: Instance> Endpoint<'d, T> { data.len() }; - // TODO: use data address for multi-core + // TODO: Convert data's address to coressponding core // Check hpm_sdk: static inline uint32_t core_local_mem_to_sys_address() // Initialize qtd with the data @@ -125,31 +118,6 @@ impl<'d, T: Instance> Endpoint<'d, T> { // Link qtd to qhd let first_idx = first_qtd.unwrap(); - // unsafe { - // defmt::info!( - // "Check first qtd idx: {}, addr: {:x} and content: - // next_dtd_word: 0x{:x} - // total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b} - // Buffer0 + offset: {:x} - // Buffer1 : {:x} - // Buffer2 : {:x} - // Buffer3 : {:x} - // Buffer4 : {:x}", - // first_idx, - // DCD_DATA.qtd_list.qtd(first_idx).as_ptr() as u32, - // DCD_DATA.qtd_list.qtd(first_idx).next_dtd().read().0, - // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().total_bytes(), - // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().ioc(), - // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().c_page(), - // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().multo(), - // DCD_DATA.qtd_list.qtd(first_idx).qtd_token().read().status(), - // DCD_DATA.qtd_list.qtd(first_idx).buffer(0).read().0, - // DCD_DATA.qtd_list.qtd(first_idx).buffer(1).read().0, - // DCD_DATA.qtd_list.qtd(first_idx).buffer(2).read().0, - // DCD_DATA.qtd_list.qtd(first_idx).buffer(3).read().0, - // DCD_DATA.qtd_list.qtd(first_idx).buffer(4).read().0, - // ); - // } unsafe { if ep_num == 0 { @@ -162,29 +130,6 @@ impl<'d, T: Instance> Endpoint<'d, T> { // T **MUST** be set to 0 w.set_t(false); }); - - // let qhd: crate::usb::types_v53::Qhd = DCD_DATA.qhd_list.qhd(ep_idx); - // defmt::info!( - // "ENDPTLISTADDR: {:x} - // Check qhd after setting: qhd_idx: {} - // 1st word: mult: {}, zlt: {}, mps: {}, ios: {} - // 2nd word: cur dtd: {:x} - // 3rd word: next_dtd + t: {:x} - // total_bytes: {}, ioc: {}, c_page: {}, multO: {}, status: 0b{:b}", - // T::info().regs.endptlistaddr().read().0, - // ep_idx, - // qhd.cap().read().iso_mult(), - // qhd.cap().read().zero_length_termination(), - // qhd.cap().read().max_packet_size(), - // qhd.cap().read().ios(), - // qhd.cur_dtd().read().0, // 2nd word - // qhd.next_dtd().read().0, - // qhd.qtd_token().read().total_bytes(), - // qhd.qtd_token().read().ioc(), - // qhd.qtd_token().read().c_page(), - // qhd.qtd_token().read().multo(), - // qhd.qtd_token().read().status(), - // ); } // Start transfer @@ -204,47 +149,50 @@ impl<'d, T: Instance> Endpoint<'d, T> { } impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { + /// Get the endpoint address fn info(&self) -> &embassy_usb_driver::EndpointInfo { &self.info } + /// Wait for the endpoint to be enabled. async fn wait_enabled(&mut self) { let i = self.info.addr.index(); poll_fn(|cx| { let r = T::info().regs; - // TODO: Simplify the code + // Check if the endpoint is enabled if self.info.addr.is_in() { EP_IN_WAKERS[i].register(cx.waker()); - // Check if the endpoint is enabled if r.endptctrl(i).read().txe() { - Poll::Ready(()) - } else { - Poll::Pending + return Poll::Ready(()); } } else { EP_OUT_WAKERS[i].register(cx.waker()); - // Check if the endpoint is enabled if r.endptctrl(i).read().rxe() { - Poll::Ready(()) - } else { - Poll::Pending + return Poll::Ready(()); } } + Poll::Pending }) .await; } } impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { + /// Read a single packet of data from the endpoint, and return the actual length of + /// the packet. + /// + /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. async fn read(&mut self, buf: &mut [u8]) -> Result { let r = T::info().regs; - let ep_num = self.info.addr.index(); + + // Start read and wait self.transfer(buf).unwrap(); poll_fn(|cx| { EP_OUT_WAKERS[ep_num].register(cx.waker()); if r.endptcomplete().read().erce() & (1 << ep_num) != 0 { + // Clear the flag r.endptcomplete().modify(|w| w.set_erce(1 << ep_num)); Poll::Ready(()) } else { @@ -253,6 +201,7 @@ impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { }) .await; + // Get the actual length of the packet let ep_num = self.info.addr.index(); let ep_idx = 2 * ep_num + self.info.addr.is_in() as usize; let len = unsafe { DCD_DATA.qhd_list.qhd(ep_idx).qtd_token().read().total_bytes() as usize }; @@ -261,10 +210,12 @@ impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { } impl<'d, T: Instance> EndpointIn for Endpoint<'d, T> { + /// Write a single packet of data to the endpoint. async fn write(&mut self, buf: &[u8]) -> Result<(), embassy_usb_driver::EndpointError> { let r = T::info().regs; - let ep_num = self.info.addr.index(); + + // Start write and wait self.transfer(buf).unwrap(); poll_fn(|cx| { EP_IN_WAKERS[ep_num].register(cx.waker()); @@ -278,7 +229,7 @@ impl<'d, T: Instance> EndpointIn for Endpoint<'d, T> { }) .await; - // Send zlt(if needed) + // Send zlt packet(if needed) if buf.len() == self.info.max_packet_size as usize { self.transfer(&[]).unwrap(); poll_fn(|cx| { diff --git a/src/usb/mod.rs b/src/usb/mod.rs index 9f5c5c6..042afde 100644 --- a/src/usb/mod.rs +++ b/src/usb/mod.rs @@ -47,6 +47,7 @@ const QHD_BUFFER_COUNT: usize = 5; const QHD_ITEM_SIZE: usize = 64; const QTD_ITEM_SIZE: usize = 32; +/// FIXME: Better way to handle the ehci data, ref: https://github.com/imxrt-rs/imxrt-usbd/blob/master/src/lib.rs static mut QHD_LIST_DATA: QhdListData = QhdListData([0; QHD_ITEM_SIZE * ENDPOINT_COUNT * 2]); static mut QTD_LIST_DATA: QtdListData = QtdListData([0; QTD_ITEM_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_QHD]); static mut DCD_DATA: DcdData = DcdData { @@ -55,12 +56,12 @@ static mut DCD_DATA: DcdData = DcdData { }; #[repr(C, align(2048))] -pub struct QhdListData([u8; QHD_ITEM_SIZE * ENDPOINT_COUNT * 2]); +pub(crate) struct QhdListData([u8; QHD_ITEM_SIZE * ENDPOINT_COUNT * 2]); #[repr(C, align(32))] -pub struct QtdListData([u8; QTD_ITEM_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_QHD]); +pub(crate) struct QtdListData([u8; QTD_ITEM_SIZE * ENDPOINT_COUNT * 2 * QTD_COUNT_EACH_QHD]); -pub struct DcdData { +pub(crate) struct DcdData { /// List of queue head pub(crate) qhd_list: QhdList, /// List of queue transfer descriptor @@ -167,7 +168,7 @@ impl Qtd { // That's why the buffer[1-4] is filled with a `& 0xFFFFF000`. // To be convenient, if the data length is larger than 4K, we require the data address to be 4K bytes aligned. if transfer_bytes > 0x1000 && data.as_ptr() as u32 % 0x1000 != 0 { - defmt::error!("The buffer[1-4] must be 4K bytes aligned"); + // defmt::error!("The buffer[1-4] must be 4K bytes aligned"); return; } @@ -195,7 +196,8 @@ impl Qtd { } } -#[derive(Copy, Clone, Eq, PartialEq, Debug, defmt::Format)] +/// Endpoint allocation data, used in `UsbDriver` +#[derive(Copy, Clone, Eq, PartialEq, Debug)] pub(crate) struct EndpointAllocData { pub(crate) info: EndpointInfo, pub(crate) used: bool, @@ -217,7 +219,6 @@ impl EndpointAllocData { pub struct UsbDriver<'d, T: Instance> { phantom: PhantomData<&'d mut T>, - info: &'static Info, endpoints_in: [EndpointAllocData; ENDPOINT_COUNT], endpoints_out: [EndpointAllocData; ENDPOINT_COUNT], } @@ -269,7 +270,6 @@ impl<'d, T: Instance> UsbDriver<'d, T> { UsbDriver { phantom: PhantomData, - info: T::info(), endpoints_in: [EndpointAllocData::new(Direction::In); ENDPOINT_COUNT], endpoints_out: [EndpointAllocData::new(Direction::Out); ENDPOINT_COUNT], } @@ -294,6 +294,7 @@ impl<'d, T: Instance> UsbDriver<'d, T> { } } +/// Implement `embassy_usb_driver::Driver` for `UsbDriver` impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { type EndpointOut = Endpoint<'a, T>; @@ -410,42 +411,14 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { endpoints_out[i] = self.endpoints_out[i].info; } - let mut bus = Bus { + let bus = Bus { _phantom: PhantomData, - info: self.info, endpoints_in, endpoints_out, delay: McycleDelay::new(sysctl::clocks().cpu0.0), inited: false, }; - // Init the usb phy and device controller - bus.dcd_init(); - - // Set ENDPTLISTADDR, enable interrupts - { - let r = self.info.regs; - // Set endpoint list address - unsafe { - // defmt::info!("Setting ENDPTLISTADDR: {:x}", DCD_DATA.qhd_list.as_ptr()); - r.endptlistaddr().modify(|w| w.0 = DCD_DATA.qhd_list.as_ptr() as u32); - }; - - // Clear status - r.usbsts().modify(|w| w.0 = w.0); - - // Enable interrupt mask - r.usbintr().write(|w| { - w.set_ue(true); - w.set_uee(true); - w.set_pce(true); - w.set_ure(true); - }); - } - - // Start to run usb device - bus.dcd_connect(); - ( bus, Self::ControlPipe { @@ -458,11 +431,6 @@ impl<'a, T: Instance> Driver<'a> for UsbDriver<'a, T> { } } -#[derive(Debug)] -pub enum Error { - InvalidQtdNum, -} - pub(super) struct Info { regs: crate::pac::usb::Usb, } @@ -515,6 +483,7 @@ impl crate::interrupt::typelevel::Handler for Interru } } +/// USB interrupt handler pub unsafe fn on_interrupt() { let r = T::info().regs; @@ -525,11 +494,6 @@ pub unsafe fn on_interrupt() { // Clear triggered interrupts status bits let triggered_interrupts = status.0 & enabled_interrupts.0; - // defmt::info!( - // "GOT IRQ: {:b}, triggered: {:b}", - // r.usbsts().read().0, - // triggered_interrupts - // ); let status = Usbsts(triggered_interrupts); r.usbsts().modify(|w| w.0 = w.0); @@ -543,9 +507,10 @@ pub unsafe fn on_interrupt() { // Set IRQ_RESET signal IRQ_RESET.store(true, Ordering::Relaxed); + // Disable USB reset interrupt while processing RESET event r.usbintr().modify(|w| w.set_ure(false)); - // Wake main thread. Then the reset event will be processed in Bus::poll() + // Wake USB bus. Then the reset event will be processed in Bus::poll() BUS_WAKER.wake(); } @@ -554,20 +519,18 @@ pub unsafe fn on_interrupt() { // Set IRQ_SUSPEND signal IRQ_SUSPEND.store(true, Ordering::Relaxed); - // Wake main thread. Then the suspend event will be processed in Bus::poll() + // Wake USB bus. Then the suspend event will be processed in Bus::poll() BUS_WAKER.wake(); } // Port change event if status.pci() { if r.portsc1().read().ccs() { + // Connected r.usbintr().modify(|w| w.set_pce(false)); - // defmt::info!("Port change interrupt: connected"); - // Wake main thread. Then the suspend event will be processed in Bus::poll() + // Wake USB bus. Then the event will be processed in Bus::poll() BUS_WAKER.wake(); - // Connected } else { - defmt::info!("Port change interrupt: disconnected"); // Disconnected } } @@ -584,106 +547,13 @@ pub unsafe fn on_interrupt() { // r.endptcomplete().read().etce(), // ); + // If it's a setup packet, wake the EP OUT 0 if r.endptsetupstat().read().endptsetupstat() > 0 { EP_OUT_WAKERS[0].wake(); } + // Transfer completed if r.endptcomplete().read().0 > 0 { - // defmt::info!("ep transfer complete: {:b}", r.endptcomplete().read().0); - // 在c_sdk里,直接在中断中处理,所有EP的QTD,包括EP0。 - // 在Rust里,这些处理可以放在对应的EP。 - // 理论上,最合适的逻辑应该是,这里唤醒,然后在control的data_in/data_out,以及端点的write/read里去处理&发送&接受数据。 - // 但是,由于embassy的实现,Read可以等待,write写一次就退出了,不会在write函数中等待 - // 所以,这部分处理先放在这,后面看看怎么解决。 - - // for ep_num in 0..ENDPOINT_COUNT { - // if r.endptcomplete().read().erce() & (1 << ep_num) > 0 { - // let qtd_idx = 2 * ep_num * QTD_COUNT_EACH_QHD; - // // 处理OUT端点 - // let mut qtd = unsafe { DCD_DATA.qtd_list.qtd(qtd_idx) }; - - // // Find the final qtd in the linked list - // let mut do_response = true; - // let mut transfer_len = 0; - // loop { - // if qtd.qtd_token().read().halted() - // || qtd.qtd_token().read().transaction_err() - // || qtd.qtd_token().read().buffer_err() - // { - // defmt::info!("qtd error found: {:b}", qtd.qtd_token().read().0); - // do_response = false; - // break; - // } else if qtd.qtd_token().read().active() { - // do_response = false; - // break; - // } else { - // transfer_len += - // qtd.expected_bytes().read().expected_bytes() - qtd.qtd_token().read().total_bytes() - // } - - // if qtd.next_dtd().read().t() { - // break; - // } else { - // let next_addr = qtd.next_dtd().read().next_dtd_addr() << 5; - // defmt::info!("next_qtd_addr: {:x}", next_addr); - // qtd = Qtd::from_ptr(next_addr as *mut _); - // } - // } - - // if do_response { - // defmt::info!("WAKING EP_OUT_WAKERS[{}]", ep_num); - // EP_OUT_WAKERS[ep_num].wake(); - // // TODO: usbd_event_ep0_out_complete_handler - // // 1. if data_buf_residue != 0, then start reading remaining data - // // 2. if data_buf_residue == 0, received all data at ep0, process setup request first - // // 3. After processing, send NULL to host - // } - // } - // if r.endptcomplete().read().etce() & (1 << ep_num) > 0 { - // let qtd_idx = (2 * ep_num + 1) * QTD_COUNT_EACH_QHD; - // let mut qtd = unsafe { DCD_DATA.qtd_list.qtd(qtd_idx) }; - // // 处理IN端点,下面的代码和OUT端点实际上是一模一样的 - - // // Find the final qtd in the linked list - // let mut do_response = true; - // let mut transfer_len = 0; - // loop { - // if qtd.qtd_token().read().halted() - // || qtd.qtd_token().read().transaction_err() - // || qtd.qtd_token().read().buffer_err() - // { - // defmt::info!("qtd error found: {:b}", qtd.qtd_token().read().0); - // do_response = false; - // break; - // } else if qtd.qtd_token().read().active() { - // do_response = false; - // break; - // } else { - // transfer_len += - // qtd.expected_bytes().read().expected_bytes() - qtd.qtd_token().read().total_bytes() - // } - - // if qtd.next_dtd().read().t() { - // break; - // } else { - // let next_addr = qtd.next_dtd().read().next_dtd_addr() << 5; - // defmt::info!("next_qtd_addr: {:x}", next_addr); - // qtd = Qtd::from_ptr(next_addr as *mut _); - // } - // } - // if do_response { - // defmt::info!("WAKING EP_IN_WAKERS[{}]", ep_num); - // EP_IN_WAKERS[ep_num].wake(); - // // TODO: usbd_event_ep0_in_complete_handler - // // 1. if data_buf_residue != 0, then start sending remaining data - // // 2. if zlp_flag == 1, then send zero length packet - // // 3. if zlp_flag != 1, that means all data has sent completely, then start reading out status - // // -> usbd_ep_start_read(busid, USB_CONTROL_OUT_EP0, NULL, 0); - // } - // } - // } - - // Transfer completed for i in 0..ENDPOINT_COUNT { if r.endptcomplete().read().erce() & (1 << i) > 0 { // Wake OUT endpoint From 0d328241a1e31f9a17f7d2dd61b6de0afb20cea1 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sun, 18 Aug 2024 22:32:14 +0800 Subject: [PATCH 38/43] chore: clean dependency Signed-off-by: HaoboGu --- Cargo.toml | 2 -- examples/hpm5300evk/.cargo/config.toml | 18 ++++++------------ examples/hpm5300evk/Cargo.toml | 5 +++-- examples/hpm6e00evk/.cargo/config.toml | 4 ++-- src/usb/bus.rs | 1 - 5 files changed, 11 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 91dee54..245e0f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,8 +43,6 @@ embedded-hal-async = "1.0.0" chrono = { version = "0.4.38", default-features = false, optional = true } mcan = { version = "0.5.0", optional = true } -bitfield-struct = "0.8" - [build-dependencies] # hpm-metapac = { path = "../hpm-data/build/hpm-metapac", default-features = false, features = [ # "metadata", diff --git a/examples/hpm5300evk/.cargo/config.toml b/examples/hpm5300evk/.cargo/config.toml index 4e200fb..189833e 100644 --- a/examples/hpm5300evk/.cargo/config.toml +++ b/examples/hpm5300evk/.cargo/config.toml @@ -4,17 +4,11 @@ target = "riscv32imafc-unknown-none-elf" [target.riscv32imafc-unknown-none-elf] # runner = 'riscv64-unknown-elf-gdb -x ../../openocd.gdb' runner = [ - "probe-rs", - "run", - "--chip", - "HPM5361", - "--chip-description-path", - "../../HPMicro.yaml", - # "--chip-description-path", "../../../flash-algo/target/definition.yaml", - "--protocol", - "jtag", - "--log-format", - "{t} {L} {F}:{l} {s}", + "probe-rs", "run", + "--chip", "HPM5361", + "--chip-description-path", "../../HPMicro.yaml", + "--protocol","jtag", + "--log-format", "{t} {L} {F}:{l} {s}", ] rustflags = [ @@ -42,4 +36,4 @@ rustflags = [ build-std = ["core"] [env] -DEFMT_LOG = "info" +DEFMT_LOG = "trace" diff --git a/examples/hpm5300evk/Cargo.toml b/examples/hpm5300evk/Cargo.toml index 8788683..c2fb891 100644 --- a/examples/hpm5300evk/Cargo.toml +++ b/examples/hpm5300evk/Cargo.toml @@ -36,12 +36,13 @@ embedded-hal-bus = "0.2.0" assign-resources = "0.4.1" mcan = "0.5.0" embassy-sync = "0.6.0" - -embassy-usb = { version = "0.2.0", features = [ +embassy-usb = { version = "0.3.0", features = [ "defmt", "max-handler-count-8", "max-interface-count-8", ] } +usbd-hid = "0.8" +static_cell = "2" [profile.release] strip = false # symbols are not flashed to the microcontroller, so don't strip them. diff --git a/examples/hpm6e00evk/.cargo/config.toml b/examples/hpm6e00evk/.cargo/config.toml index c42f603..cef6f2a 100644 --- a/examples/hpm6e00evk/.cargo/config.toml +++ b/examples/hpm6e00evk/.cargo/config.toml @@ -2,8 +2,8 @@ target = "riscv32imafc-unknown-none-elf" [target.riscv32imafc-unknown-none-elf] -runner = 'riscv64-unknown-elf-gdb -x ../../openocd.gdb' -# runner = "probe-rs run --chip HPM6E80 --protocol jtag --chip-description-path ../../HPMicro.yaml" +# runner = 'riscv64-unknown-elf-gdb -x ../../openocd.gdb' +runner = "probe-rs run --chip HPM6E80 --protocol jtag --chip-description-path ../../HPMicro.yaml" rustflags = [ # Target features: diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 09b6d37..8a596e3 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -97,7 +97,6 @@ impl embassy_usb_driver::Bus for Bus { r.usbintr().modify(|w| w.set_ue(true)); r.usbintr().modify(|w| w.set_ure(false)); - defmt::info!("poll: Reset"); return Poll::Ready(Event::Reset); } From db49f4f498f9425111cad3a0bd711f336618880d Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Sun, 18 Aug 2024 22:38:17 +0800 Subject: [PATCH 39/43] feat(usb): add examples for hpm6e00evk Signed-off-by: HaoboGu --- examples/hpm6e00evk/.cargo/config.toml | 2 +- examples/hpm6e00evk/Cargo.toml | 7 + examples/hpm6e00evk/src/bin/usb.rs | 115 ++++++++++++++ examples/hpm6e00evk/src/bin/usb_hid.rs | 204 +++++++++++++++++++++++++ 4 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 examples/hpm6e00evk/src/bin/usb.rs create mode 100644 examples/hpm6e00evk/src/bin/usb_hid.rs diff --git a/examples/hpm6e00evk/.cargo/config.toml b/examples/hpm6e00evk/.cargo/config.toml index cef6f2a..fa83e93 100644 --- a/examples/hpm6e00evk/.cargo/config.toml +++ b/examples/hpm6e00evk/.cargo/config.toml @@ -30,4 +30,4 @@ rustflags = [ build-std = ["core"] [env] -DEFMT_LOG = "info" +DEFMT_LOG = "trace" diff --git a/examples/hpm6e00evk/Cargo.toml b/examples/hpm6e00evk/Cargo.toml index fc51162..e3b1d27 100644 --- a/examples/hpm6e00evk/Cargo.toml +++ b/examples/hpm6e00evk/Cargo.toml @@ -24,11 +24,18 @@ embassy-executor = { version = "0.6.0", features = [ ] } embedded-io = "0.6.1" embedded-graphics = "0.8.1" +embassy-usb = { version = "0.3.0", features = [ + "defmt", + "max-handler-count-8", + "max-interface-count-8", +] } futures-util = { version = "0.3.30", default-features = false } tinygif = "0.0.4" assign-resources = "0.4.1" embedded-hal-bus = "0.2.0" embassy-sync = "0.6.0" +usbd-hid = "0.8" +static_cell = "2" [profile.release] strip = false # symbols are not flashed to the microcontroller, so don't strip them. diff --git a/examples/hpm6e00evk/src/bin/usb.rs b/examples/hpm6e00evk/src/bin/usb.rs new file mode 100644 index 0000000..d293e20 --- /dev/null +++ b/examples/hpm6e00evk/src/bin/usb.rs @@ -0,0 +1,115 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] +#![feature(abi_riscv_interrupt)] +#![feature(impl_trait_in_assoc_type)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures_util::future::join; +use hal::usb::{Instance, UsbDriver}; +use hpm_hal::{bind_interrupts, peripherals}; +use {defmt_rtt as _, hpm_hal as hal}; + +bind_interrupts!(struct Irqs { + USB0 => hal::usb::InterruptHandler; +}); + +#[embassy_executor::main(entry = "hpm_hal::entry")] +async fn main(_spawner: Spawner) -> ! { + let p = hal::init(Default::default()); + + let usb_driver = hal::usb::UsbDriver::new(p.USB0); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + usb_driver, + config, + &mut config_descriptor, + &mut bos_descriptor, + &mut [], // no msos descriptors + &mut control_buf, + ); + + // Create classes on the builder. + let class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + // class.wait_connection().await; + let (mut sender, mut reader) = class.split(); + sender.wait_connection().await; + reader.wait_connection().await; + info!("Connected"); + let _ = echo(&mut reader, &mut sender).await; + info!("Disconnected"); + }; + + // Run everything concurrently. + join(usb_fut, echo_fut).await; + + loop { + embassy_time::Timer::after_millis(500).await; + } +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>( + reader: &mut Receiver<'d, UsbDriver<'d, T>>, + sender: &mut Sender<'d, UsbDriver<'d, T>>, +) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = reader.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("echo data: {:x}, len: {}", data, n); + sender.write_packet(data).await?; + // Clear bufffer + buf = [0; 64]; + } +} + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + defmt::info!("panic: {:?}", defmt::Debug2Format(&info)); + loop {} +} diff --git a/examples/hpm6e00evk/src/bin/usb_hid.rs b/examples/hpm6e00evk/src/bin/usb_hid.rs new file mode 100644 index 0000000..3627c53 --- /dev/null +++ b/examples/hpm6e00evk/src/bin/usb_hid.rs @@ -0,0 +1,204 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] +#![feature(abi_riscv_interrupt)] +#![feature(impl_trait_in_assoc_type)] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use defmt::{info, warn}; +use embassy_executor::Spawner; +use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; +use embassy_usb::control::OutResponse; +use embassy_usb::{Builder, Handler}; +use futures_util::future::join; +use hpm_hal::gpio::{Input, Pull}; +use hpm_hal::{bind_interrupts, peripherals}; +use static_cell::StaticCell; +use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; +use {defmt_rtt as _, hpm_hal as hal}; + +bind_interrupts!(struct Irqs { + USB0 => hal::usb::InterruptHandler; +}); + +static STATE: StaticCell = StaticCell::new(); + +#[embassy_executor::main(entry = "hpm_hal::entry")] +async fn main(_spawner: Spawner) -> ! { + let p = hal::init(Default::default()); + + let usb_driver = hal::usb::UsbDriver::new(p.USB0); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); + static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); + static MSOS_DESC: StaticCell<[u8; 128]> = StaticCell::new(); + static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new(); + + // UsbDevice builder + let mut request_handler = MyRequestHandler {}; + + let mut builder = Builder::new( + usb_driver, + config, + &mut CONFIG_DESC.init([0; 256])[..], + &mut BOS_DESC.init([0; 256])[..], + &mut MSOS_DESC.init([0; 128])[..], + &mut CONTROL_BUF.init([0; 128])[..], + ); + + static DEVICE_HANDLER: StaticCell = StaticCell::new(); + builder.handler(DEVICE_HANDLER.init(MyDeviceHandler::new())); + + // Create classes on the builder. + let config = embassy_usb::class::hid::Config { + report_descriptor: KeyboardReport::desc(), + request_handler: None, + poll_ms: 60, + max_packet_size: 64, + }; + + let state = STATE.init(State::new()); + let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, state, config); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + let mut button = Input::new(p.PA03, Pull::Down); + + let (reader, mut writer) = hid.split(); + + // Do stuff with the class! + let in_fut = async { + loop { + info!("Waiting for Button"); + button.wait_for_high().await; + // Create a report with the A key pressed. (no shift modifier) + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + embassy_time::Timer::after_millis(100).await; + button.wait_for_low().await; + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + embassy_time::Timer::after_millis(100).await; + } + }; + + let out_fut = async { + reader.run(false, &mut request_handler).await; + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, join(in_fut, out_fut)).await; + loop { + defmt::info!("USB task finished"); + embassy_time::Timer::after_millis(500).await; + } +} + +struct MyRequestHandler {} + +impl RequestHandler for MyRequestHandler { + fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option { + info!("Get report for {:?}", id); + None + } + + fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse { + info!("Set report for {:?}: {=[u8]}", id, data); + OutResponse::Accepted + } + + fn set_idle_ms(&mut self, id: Option, dur: u32) { + info!("Set idle rate for {:?} to {:?}", id, dur); + } + + fn get_idle_ms(&mut self, id: Option) -> Option { + info!("Get idle rate for {:?}", id); + None + } +} + +struct MyDeviceHandler { + configured: AtomicBool, +} + +impl MyDeviceHandler { + fn new() -> Self { + MyDeviceHandler { + configured: AtomicBool::new(false), + } + } +} + +impl Handler for MyDeviceHandler { + fn enabled(&mut self, enabled: bool) { + self.configured.store(false, Ordering::Relaxed); + if enabled { + info!("Device enabled"); + } else { + info!("Device disabled"); + } + } + + fn reset(&mut self) { + self.configured.store(false, Ordering::Relaxed); + info!("Bus reset, the Vbus current limit is 100mA"); + } + + fn addressed(&mut self, addr: u8) { + self.configured.store(false, Ordering::Relaxed); + info!("USB address set to: {}", addr); + } + + fn configured(&mut self, configured: bool) { + self.configured.store(configured, Ordering::Relaxed); + if configured { + info!("Device configured, it may now draw up to the configured current limit from Vbus.") + } else { + info!("Device is no longer configured, the Vbus current limit is 100mA."); + } + } +} + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + defmt::info!("panic: {:?}", defmt::Debug2Format(&info)); + loop {} +} From ced6b193cd4e48cba775d1509bb04b03c9651c8d Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Tue, 20 Aug 2024 16:44:14 +0800 Subject: [PATCH 40/43] doc(usb): add usb to readme Signed-off-by: Haobo Gu --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b30d2ac..ca4e6cf 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This crate is a working-in-progress and not ready for use. | HPM6700 | ✓ | ✓ | ✓ | ✓ | ✓+ | ✓+ | ✓+ | ✓+ | ✓+ | | | | | | HPM6300 | ✓ | ✓ | ✓ | ✓ | | | | | | | | | | | HPM6200 | ✓ | | | | | | | | | | | | | -| HPM5300 | ✓ | ✓ | ✓ | ✓ | ✓+ | ✓+ | ✓+ | ✓+ | ✓+ | | ✓ | | ✓ | +| HPM5300 | ✓ | ✓ | ✓ | ✓ | ✓+ | ✓+ | ✓+ | ✓+ | ✓+ | | ✓ | ✓ | ✓ | | HPM6800 | ✓ | | | | | | | | | | | | | | HPM6E00 | ✓ | ✓ | ✓ | ✓ | ✓+ | ✓+ | ✓+ | ✓+ | ✓+ | | | | | @@ -73,6 +73,9 @@ This crate is a working-in-progress and not ready for use. - [x] basic `mcan` wrapper - ~~[ ] async driver~~, better impl it in the App layer, see Mi motor demo - [ ] TSU management +- [x] USB + - [x] Device + - [ ] Host ### Long term Plans From b672c3b5e64d84962830ee36e45ba2367b2fd6d7 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Wed, 21 Aug 2024 10:30:33 +0800 Subject: [PATCH 41/43] feat(usb): add example for hpm67 Signed-off-by: Haobo Gu --- examples/hpm6750evkmini/Cargo.toml | 7 ++ examples/hpm6750evkmini/src/bin/usb.rs | 115 +++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 examples/hpm6750evkmini/src/bin/usb.rs diff --git a/examples/hpm6750evkmini/Cargo.toml b/examples/hpm6750evkmini/Cargo.toml index 2b350e0..b502196 100644 --- a/examples/hpm6750evkmini/Cargo.toml +++ b/examples/hpm6750evkmini/Cargo.toml @@ -32,6 +32,13 @@ futures-util = { version = "0.3.30", default-features = false } assign-resources = "0.4.1" embedded-graphics = "0.8.1" embedded-hal-bus = { version = "0.2.0", features = ["async"] } +embassy-usb = { version = "0.3.0", features = [ + "defmt", + "max-handler-count-8", + "max-interface-count-8", +] } +usbd-hid = "0.8" +static_cell = "2" [profile.release] strip = false # symbols are not flashed to the microcontroller, so don't strip them. diff --git a/examples/hpm6750evkmini/src/bin/usb.rs b/examples/hpm6750evkmini/src/bin/usb.rs new file mode 100644 index 0000000..13fc95a --- /dev/null +++ b/examples/hpm6750evkmini/src/bin/usb.rs @@ -0,0 +1,115 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] +#![feature(abi_riscv_interrupt)] +#![feature(impl_trait_in_assoc_type)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures_util::future::join; +use hal::usb::{Instance, UsbDriver}; +use hpm_hal::{bind_interrupts, peripherals}; +use {defmt_rtt as _, hpm_hal as hal, riscv_rt as _}; + +bind_interrupts!(struct Irqs { + USB0 => hal::usb::InterruptHandler; +}); + +#[embassy_executor::main(entry = "hpm_hal::entry")] +async fn main(_spawner: Spawner) -> ! { + let p = hal::init(Default::default()); + + let usb_driver = hal::usb::UsbDriver::new(p.USB0); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + usb_driver, + config, + &mut config_descriptor, + &mut bos_descriptor, + &mut [], // no msos descriptors + &mut control_buf, + ); + + // Create classes on the builder. + let class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + // class.wait_connection().await; + let (mut sender, mut reader) = class.split(); + sender.wait_connection().await; + reader.wait_connection().await; + info!("Connected"); + let _ = echo(&mut reader, &mut sender).await; + info!("Disconnected"); + }; + + // Run everything concurrently. + join(usb_fut, echo_fut).await; + + loop { + embassy_time::Timer::after_millis(500).await; + } +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>( + reader: &mut Receiver<'d, UsbDriver<'d, T>>, + sender: &mut Sender<'d, UsbDriver<'d, T>>, +) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = reader.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("echo data: {:x}, len: {}", data, n); + sender.write_packet(data).await?; + // Clear bufffer + buf = [0; 64]; + } +} + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + defmt::info!("panic: {:?}", defmt::Debug2Format(&info)); + loop {} +} From 76be50dad450c0e6a9b220bf3e953171ae4e7d6b Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Thu, 22 Aug 2024 11:02:57 +0800 Subject: [PATCH 42/43] feat(usb): enable HS mode, update usb examples Signed-off-by: Haobo Gu --- examples/hpm5300evk/src/bin/usb.rs | 2 +- examples/hpm5300evk/src/bin/usb_hid.rs | 4 ++-- examples/hpm6750evkmini/src/bin/usb.rs | 2 +- examples/hpm6e00evk/src/bin/usb.rs | 2 +- examples/hpm6e00evk/src/bin/usb_hid.rs | 4 ++-- src/usb/bus.rs | 6 ++---- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/examples/hpm5300evk/src/bin/usb.rs b/examples/hpm5300evk/src/bin/usb.rs index 1094811..003e3e9 100644 --- a/examples/hpm5300evk/src/bin/usb.rs +++ b/examples/hpm5300evk/src/bin/usb.rs @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) -> ! { // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); - config.manufacturer = Some("Embassy"); + config.manufacturer = Some("hpm-hal"); config.product = Some("USB-serial example"); config.serial_number = Some("12345678"); diff --git a/examples/hpm5300evk/src/bin/usb_hid.rs b/examples/hpm5300evk/src/bin/usb_hid.rs index ae4e153..0b0fa5f 100644 --- a/examples/hpm5300evk/src/bin/usb_hid.rs +++ b/examples/hpm5300evk/src/bin/usb_hid.rs @@ -32,8 +32,8 @@ async fn main(_spawner: Spawner) -> ! { // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); - config.manufacturer = Some("Embassy"); - config.product = Some("USB-serial example"); + config.manufacturer = Some("hpm-hal"); + config.product = Some("USB-HID example"); config.serial_number = Some("12345678"); // Required for windows compatibility. diff --git a/examples/hpm6750evkmini/src/bin/usb.rs b/examples/hpm6750evkmini/src/bin/usb.rs index 13fc95a..91b32fc 100644 --- a/examples/hpm6750evkmini/src/bin/usb.rs +++ b/examples/hpm6750evkmini/src/bin/usb.rs @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) -> ! { // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); - config.manufacturer = Some("Embassy"); + config.manufacturer = Some("hpm-hal"); config.product = Some("USB-serial example"); config.serial_number = Some("12345678"); diff --git a/examples/hpm6e00evk/src/bin/usb.rs b/examples/hpm6e00evk/src/bin/usb.rs index d293e20..5d96c2e 100644 --- a/examples/hpm6e00evk/src/bin/usb.rs +++ b/examples/hpm6e00evk/src/bin/usb.rs @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) -> ! { // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); - config.manufacturer = Some("Embassy"); + config.manufacturer = Some("hpm-hal"); config.product = Some("USB-serial example"); config.serial_number = Some("12345678"); diff --git a/examples/hpm6e00evk/src/bin/usb_hid.rs b/examples/hpm6e00evk/src/bin/usb_hid.rs index 3627c53..1105322 100644 --- a/examples/hpm6e00evk/src/bin/usb_hid.rs +++ b/examples/hpm6e00evk/src/bin/usb_hid.rs @@ -32,8 +32,8 @@ async fn main(_spawner: Spawner) -> ! { // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); - config.manufacturer = Some("Embassy"); - config.product = Some("USB-serial example"); + config.manufacturer = Some("hpm-hal"); + config.product = Some("USB-HID example"); config.serial_number = Some("12345678"); // Required for windows compatibility. diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 8a596e3..228df47 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -300,10 +300,8 @@ impl Bus { w.set_sts(false); // Parallel transceiver width w.set_ptw(false); - // Forced fullspeed mode, c_sdk commented this line out, use it only when the device runs in full speed mode - // TODO: Currently, the device can only be recognized at fs mode. - // How to switch to hs mode? - w.set_pfsc(true); + // Forced fullspeed mode + // w.set_pfsc(true); }); // Do not use interrupt threshold From 7e7d3778384a71ba1f6495e78daa50171c88e721 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Fri, 23 Aug 2024 11:44:40 +0800 Subject: [PATCH 43/43] fix(usb): check ep status before transfer Signed-off-by: Haobo Gu --- src/usb/bus.rs | 16 +++++++++++++++- src/usb/endpoint.rs | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/usb/bus.rs b/src/usb/bus.rs index 228df47..4ada95a 100644 --- a/src/usb/bus.rs +++ b/src/usb/bus.rs @@ -8,7 +8,7 @@ use embedded_hal::delay::DelayNs; use hpm_metapac::usb::regs::*; use riscv::delay::McycleDelay; -use super::{init_qhd, Instance, ENDPOINT_COUNT}; +use super::{init_qhd, Instance, ENDPOINT_COUNT, EP_IN_WAKERS, EP_OUT_WAKERS}; use crate::usb::{reset_dcd_data, EpConfig, BUS_WAKER, DCD_DATA, IRQ_RESET, IRQ_SUSPEND}; /// USB bus @@ -75,6 +75,12 @@ impl embassy_usb_driver::Bus for Bus { if IRQ_RESET.load(Ordering::Acquire) { IRQ_RESET.store(false, Ordering::Relaxed); + // Disable all endpoints except ep0 + for i in 1..ENDPOINT_COUNT { + self.endpoint_close(EndpointAddress::from_parts(i, Direction::In)); + self.endpoint_close(EndpointAddress::from_parts(i, Direction::Out)); + } + // Set device addr to 0 self.device_set_address(0); @@ -90,6 +96,13 @@ impl embassy_usb_driver::Bus for Bus { max_packet_size: 64, }); + for w in &EP_IN_WAKERS { + w.wake() + } + for w in &EP_OUT_WAKERS { + w.wake() + } + // Reset bus self.device_bus_reset(64); @@ -355,6 +368,7 @@ impl Bus { /// Open the endpoint fn endpoint_open(&mut self, ep_config: EpConfig) { + // defmt::info!("Enabling endpoint: {:?}", ep_config.ep_addr); if ep_config.ep_addr.index() >= ENDPOINT_COUNT { return; } diff --git a/src/usb/endpoint.rs b/src/usb/endpoint.rs index 33e9d15..ebe363d 100644 --- a/src/usb/endpoint.rs +++ b/src/usb/endpoint.rs @@ -146,6 +146,16 @@ impl<'d, T: Instance> Endpoint<'d, T> { r.endptctrl(self.info.addr.index() as usize).modify(|w| w.set_rxs(true)); } } + + pub(crate) fn enabled(&self) -> bool { + let r = T::info().regs; + let ep_num = self.info.addr.index(); + if self.info.addr.is_in() { + r.endptctrl(ep_num).read().txe() + } else { + r.endptctrl(ep_num).read().rxe() + } + } } impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { @@ -156,6 +166,9 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T> { /// Wait for the endpoint to be enabled. async fn wait_enabled(&mut self) { + if self.enabled() { + return; + } let i = self.info.addr.index(); poll_fn(|cx| { let r = T::info().regs; @@ -183,6 +196,9 @@ impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { /// /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. async fn read(&mut self, buf: &mut [u8]) -> Result { + if !self.enabled() { + return Err(embassy_usb_driver::EndpointError::Disabled); + } let r = T::info().regs; let ep_num = self.info.addr.index(); @@ -195,6 +211,8 @@ impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { // Clear the flag r.endptcomplete().modify(|w| w.set_erce(1 << ep_num)); Poll::Ready(()) + } else if !r.endptctrl(ep_num).read().rxe() { + Poll::Ready(()) } else { Poll::Pending } @@ -212,6 +230,9 @@ impl<'d, T: Instance> EndpointOut for Endpoint<'d, T> { impl<'d, T: Instance> EndpointIn for Endpoint<'d, T> { /// Write a single packet of data to the endpoint. async fn write(&mut self, buf: &[u8]) -> Result<(), embassy_usb_driver::EndpointError> { + if !self.enabled() { + return Err(embassy_usb_driver::EndpointError::Disabled); + } let r = T::info().regs; let ep_num = self.info.addr.index(); @@ -223,6 +244,8 @@ impl<'d, T: Instance> EndpointIn for Endpoint<'d, T> { if r.endptcomplete().read().etce() & (1 << ep_num) != 0 { r.endptcomplete().modify(|w| w.set_etce(1 << ep_num)); Poll::Ready(()) + } else if !r.endptctrl(ep_num).read().txe() { + Poll::Ready(()) } else { Poll::Pending }