2020-06-09 16:17:30 +00:00
|
|
|
//! USBD peripheral
|
2020-06-09 09:52:27 +00:00
|
|
|
|
2020-06-09 16:17:30 +00:00
|
|
|
use core::sync::atomic::{self, Ordering};
|
2020-06-09 09:52:27 +00:00
|
|
|
|
|
|
|
use crate::{
|
|
|
|
errata,
|
|
|
|
peripheral::{POWER, USBD},
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Endpoint IN 0
|
|
|
|
pub struct Ep0In {
|
|
|
|
buffer: &'static mut [u8; 64],
|
|
|
|
busy: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ep0In {
|
2020-06-09 16:17:30 +00:00
|
|
|
/// # Safety
|
|
|
|
/// Must be created at most once (singleton)
|
|
|
|
pub(crate) unsafe fn new(buffer: &'static mut [u8; 64]) -> Self {
|
2020-06-09 09:52:27 +00:00
|
|
|
Self {
|
|
|
|
buffer,
|
|
|
|
busy: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 16:17:30 +00:00
|
|
|
/// Starts a data transfer over endpoint 0
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
2020-06-25 17:14:39 +00:00
|
|
|
/// - This function panics if the last transfer was not finished by calling the `end` function
|
|
|
|
/// - This function panics if `bytes` is larger than the maximum packet size (64 bytes)
|
|
|
|
pub fn start(&mut self, bytes: &[u8], usbd: &USBD) {
|
|
|
|
assert!(!self.busy, "EP0IN: last transfer has not completed");
|
|
|
|
assert!(
|
|
|
|
bytes.len() <= self.buffer.len(),
|
|
|
|
"EP0IN: multi-packet data transfers are not supported"
|
|
|
|
);
|
2020-06-09 09:52:27 +00:00
|
|
|
|
|
|
|
let n = bytes.len();
|
|
|
|
self.buffer[..n].copy_from_slice(bytes);
|
|
|
|
|
|
|
|
// use a "shortcut" to issue a status stage after the data transfer is complete
|
|
|
|
usbd.shorts
|
|
|
|
.modify(|_, w| w.ep0datadone_ep0status().set_bit());
|
|
|
|
usbd.epin0
|
|
|
|
.maxcnt
|
|
|
|
.write(|w| unsafe { w.maxcnt().bits(n as u8) });
|
|
|
|
usbd.epin0
|
|
|
|
.ptr
|
|
|
|
.write(|w| unsafe { w.ptr().bits(self.buffer.as_ptr() as u32) });
|
|
|
|
|
|
|
|
self.busy = true;
|
|
|
|
|
2022-01-07 16:24:21 +00:00
|
|
|
defmt::println!("EP0IN: start {}B transfer", n);
|
2020-06-09 09:52:27 +00:00
|
|
|
|
|
|
|
// start DMA transfer
|
|
|
|
dma_start();
|
|
|
|
usbd.tasks_startepin[0].write(|w| w.tasks_startepin().set_bit());
|
|
|
|
}
|
|
|
|
|
2020-06-09 16:17:30 +00:00
|
|
|
/// Completes a data transfer
|
|
|
|
///
|
|
|
|
/// This function must be called after the EP0DATADONE event is raised
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// This function panics if called before `start` or before the EP0DATADONE event is raised by
|
|
|
|
/// the hardware
|
2020-06-09 09:52:27 +00:00
|
|
|
pub fn end(&mut self, usbd: &USBD) {
|
|
|
|
if usbd.events_ep0datadone.read().bits() == 0 {
|
2020-06-09 16:17:30 +00:00
|
|
|
panic!("Ep0In.end called before the EP0DATADONE event was raised");
|
2020-06-09 09:52:27 +00:00
|
|
|
} else {
|
|
|
|
// DMA transfer complete
|
|
|
|
dma_end();
|
|
|
|
usbd.events_ep0datadone.reset();
|
|
|
|
|
|
|
|
self.busy = false;
|
2022-01-07 16:24:21 +00:00
|
|
|
defmt::println!("EP0IN: transfer done");
|
2020-06-09 09:52:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// memory barrier to synchronize the start of a DMA transfer (which will run in parallel) with the
|
|
|
|
// caller's memory operations
|
|
|
|
//
|
|
|
|
// This function call *must* be *followed* by a memory *store* operation. Memory operations that
|
2020-06-25 17:14:39 +00:00
|
|
|
// *precede* this function call will *not* be moved, by the compiler or the instruction pipeline, to
|
2020-06-09 16:17:30 +00:00
|
|
|
// *after* the function call.
|
2020-06-09 09:52:27 +00:00
|
|
|
fn dma_start() {
|
|
|
|
atomic::fence(Ordering::Release);
|
|
|
|
}
|
|
|
|
|
|
|
|
// memory barrier to synchronize the end of a DMA transfer (which ran in parallel) to the caller's
|
|
|
|
// memory operations
|
|
|
|
//
|
|
|
|
// This function call *must* be *preceded* by a memory *load* operation. Memory operations that
|
2020-06-25 17:14:39 +00:00
|
|
|
// *follow* this function call will *not* be moved, by the compiler or the instruction pipeline, to
|
2020-06-09 16:17:30 +00:00
|
|
|
// *before* the function call.
|
2020-06-09 09:52:27 +00:00
|
|
|
fn dma_end() {
|
|
|
|
atomic::fence(Ordering::Acquire);
|
|
|
|
}
|
|
|
|
|
2020-06-09 16:17:30 +00:00
|
|
|
/// Initializes the USBD peripheral
|
2020-06-09 09:52:27 +00:00
|
|
|
// NOTE will be called from user code; at that point the high frequency clock source has already
|
|
|
|
// been configured to use to the external crystal
|
|
|
|
// Reference: section 6.35.4 of the nRF52840 Product Specification
|
|
|
|
pub fn init(power: POWER, usbd: &USBD) {
|
2020-06-09 16:17:30 +00:00
|
|
|
let mut once = true;
|
|
|
|
|
2020-06-25 17:14:39 +00:00
|
|
|
// wait until the USB cable has been connected
|
2020-06-09 09:52:27 +00:00
|
|
|
while power.events_usbdetected.read().bits() == 0 {
|
2020-06-09 16:17:30 +00:00
|
|
|
if once {
|
2022-01-07 16:24:21 +00:00
|
|
|
defmt::println!("waiting for USB connection on port J3");
|
2020-06-09 16:17:30 +00:00
|
|
|
once = false;
|
|
|
|
}
|
2020-06-09 09:52:27 +00:00
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2020-06-09 16:17:30 +00:00
|
|
|
power.events_usbdetected.reset();
|
2020-06-09 09:52:27 +00:00
|
|
|
|
|
|
|
// workaround silicon bug
|
|
|
|
unsafe { errata::e187a() }
|
|
|
|
// enable the USB peripheral
|
|
|
|
usbd.enable.write(|w| w.enable().set_bit());
|
|
|
|
|
|
|
|
// wait for the peripheral to signal it has reached the READY state
|
|
|
|
while usbd.eventcause.read().ready().bit_is_clear() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// write 1 to clear the flag
|
|
|
|
usbd.eventcause.write(|w| w.ready().set_bit());
|
|
|
|
|
|
|
|
// if EVENTCAUSE is all zeroes then also clear the USBEVENT register
|
|
|
|
if usbd.eventcause.read().bits() == 0 {
|
|
|
|
usbd.events_usbevent.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
// complete the silicon bug workaround
|
|
|
|
unsafe { errata::e187b() }
|
|
|
|
|
|
|
|
// also need to wait for the USB power supply regulator to stabilize
|
|
|
|
while power.events_usbpwrrdy.read().bits() == 0 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
power.events_usbpwrrdy.reset();
|
|
|
|
|
|
|
|
// before returning unmask the relevant interrupts
|
|
|
|
usbd.intenset.write(|w| {
|
|
|
|
w.ep0datadone().set_bit();
|
|
|
|
w.ep0setup().set_bit();
|
|
|
|
w.usbreset().set_bit()
|
|
|
|
});
|
|
|
|
|
2020-06-25 17:14:39 +00:00
|
|
|
// enable the D+ line pull-up
|
2020-06-09 09:52:27 +00:00
|
|
|
usbd.usbpullup.write(|w| w.connect().set_bit());
|
|
|
|
}
|
|
|
|
|
2020-06-09 16:17:30 +00:00
|
|
|
/// Stalls endpoint 0
|
2020-06-09 09:52:27 +00:00
|
|
|
pub fn ep0stall(usbd: &USBD) {
|
|
|
|
usbd.tasks_ep0stall.write(|w| w.tasks_ep0stall().set_bit());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// USBD.EVENTS registers mapped to an enum
|
2021-04-14 09:49:34 +00:00
|
|
|
#[derive(Debug, defmt::Format)]
|
2020-06-09 09:52:27 +00:00
|
|
|
pub enum Event {
|
|
|
|
/// `EVENTS_USBRESET` register was active
|
|
|
|
UsbReset,
|
|
|
|
|
|
|
|
/// `EVENTS_EP0DATADONE` register was active
|
|
|
|
UsbEp0DataDone,
|
|
|
|
|
|
|
|
/// `EVENTS_EP0SETUP` register was active
|
|
|
|
UsbEp0Setup,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the next unhandled USB event; returns none if there's no event to handle
|
|
|
|
///
|
|
|
|
/// NOTE this function will clear the corresponding the EVENT register (*) so the caller should
|
|
|
|
/// handle the returned event properly. Expect for USBEVENT and EP0DATADONE
|
|
|
|
pub fn next_event(usbd: &USBD) -> Option<Event> {
|
|
|
|
if usbd.events_usbreset.read().bits() != 0 {
|
|
|
|
usbd.events_usbreset.reset();
|
|
|
|
|
|
|
|
return Some(Event::UsbReset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if usbd.events_ep0datadone.read().bits() != 0 {
|
2020-06-09 16:17:30 +00:00
|
|
|
// this will be cleared by the `Ep0In.end` method
|
2020-06-09 09:52:27 +00:00
|
|
|
// usbd.events_ep0datadone.reset();
|
|
|
|
|
|
|
|
return Some(Event::UsbEp0DataDone);
|
|
|
|
}
|
|
|
|
|
|
|
|
if usbd.events_ep0setup.read().bits() != 0 {
|
|
|
|
usbd.events_ep0setup.reset();
|
|
|
|
|
|
|
|
return Some(Event::UsbEp0Setup);
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2020-07-16 10:06:57 +00:00
|
|
|
/// Reads the BMREQUESTTYPE register and returns the 8-bit BMREQUESTTYPE component of a setup packet
|
|
|
|
pub fn bmrequesttype(usbd: &USBD) -> u8 {
|
|
|
|
// read the 32-bit register and extract the least significant byte
|
|
|
|
// (the alternative is to read the 3 bitfields of the register and merge them into one byte)
|
|
|
|
usbd.bmrequesttype.read().bits() as u8
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads the BREQUEST register and returns the 8-bit BREQUEST component of a setup packet
|
|
|
|
pub fn brequest(usbd: &USBD) -> u8 {
|
|
|
|
usbd.brequest.read().brequest().bits()
|
|
|
|
}
|
|
|
|
|
2020-06-09 16:17:30 +00:00
|
|
|
/// Reads the WLENGTHL and WLENGTHH registers and returns the 16-bit WLENGTH component of a setup packet
|
2020-06-09 09:52:27 +00:00
|
|
|
pub fn wlength(usbd: &USBD) -> u16 {
|
|
|
|
u16::from(usbd.wlengthl.read().wlengthl().bits())
|
|
|
|
| u16::from(usbd.wlengthh.read().wlengthh().bits()) << 8
|
|
|
|
}
|
|
|
|
|
2020-06-09 16:17:30 +00:00
|
|
|
/// Reads the WINDEXL and WINDEXH registers and returns the 16-bit WINDEX component of a setup packet
|
2020-06-09 09:52:27 +00:00
|
|
|
pub fn windex(usbd: &USBD) -> u16 {
|
|
|
|
u16::from(usbd.windexl.read().windexl().bits())
|
|
|
|
| u16::from(usbd.windexh.read().windexh().bits()) << 8
|
|
|
|
}
|
|
|
|
|
2020-06-09 16:17:30 +00:00
|
|
|
/// Reads the WVALUEL and WVALUEH registers and returns the 16-bit WVALUE component of a setup packet
|
2020-06-09 09:52:27 +00:00
|
|
|
pub fn wvalue(usbd: &USBD) -> u16 {
|
|
|
|
u16::from(usbd.wvaluel.read().wvaluel().bits())
|
|
|
|
| u16::from(usbd.wvalueh.read().wvalueh().bits()) << 8
|
|
|
|
}
|