use core::cell::RefCell;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
use critical_section::Mutex;
use super::{
Channel, ControlEndpoint, DataPid, Dwc2Host, EndpointType, Transaction, TransferError,
DMA_BUFFER_LEN, HCINT_ACK, HCINT_BBERR, HCINT_CHH, HCINT_NAK, HCINT_NYET, HCINT_STALL,
HCINT_TXERR, HCINT_XFRC, MAX_CHANNELS, MAX_CSPLIT_POLLS,
};
use crate::cache::{clean_range, invalidate_range};
use crate::pac::{USB_OTG_GLOBAL, USB_OTG_HOST};
use crate::timer::Timer;
const CSPLIT_RETRY_MICROFRAMES: u8 = 5;
const GINTSTS_SOF: u32 = 1 << 3;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Wait {
None,
Halt,
Frame(u8),
}
struct Slot {
wait: Wait,
ready: Option<u32>,
waker: Option<Waker>,
last_hcint: u32,
seen_hcint: u32,
}
impl Slot {
const fn new() -> Self {
Self {
wait: Wait::None,
ready: None,
waker: None,
last_hcint: 0,
seen_hcint: 0,
}
}
}
static SLOTS: Mutex<RefCell<[Slot; MAX_CHANNELS]>> =
Mutex::new(RefCell::new([const { Slot::new() }; MAX_CHANNELS]));
struct PortSlot {
pending: bool,
waker: Option<Waker>,
}
static PORT: Mutex<RefCell<PortSlot>> = Mutex::new(RefCell::new(PortSlot {
pending: false,
waker: None,
}));
fn set_sof_mask(global: &USB_OTG_GLOBAL, unmask: bool) {
global.gintmsk().modify(|_, w| w.sofm().bit(unmask));
}
fn any_frame_waiter(slots: &[Slot; MAX_CHANNELS]) -> bool {
slots.iter().any(|slot| matches!(slot.wait, Wait::Frame(_)))
}
fn set_channel_interrupt(index: usize, enable: bool) {
let host = unsafe { USB_OTG_HOST::steal() };
unsafe {
host.haintmsk().modify(|r, w| {
let bits = r.haintm().bits();
w.haintm().bits(if enable {
bits | (1 << index)
} else {
bits & !(1 << index)
})
});
}
}
pub fn on_irq() {
let global = unsafe { USB_OTG_GLOBAL::steal() };
let host = unsafe { USB_OTG_HOST::steal() };
let halted = host.haint().read().haint().bits() as u32;
let gintsts = global.gintsts().read();
let sof = gintsts.sof().bit_is_set();
if gintsts.hprtint().bit_is_set() {
host.hprt().write(|w| {
w.ppwr().set_bit();
w.pcdet().set_bit();
w.penchng().set_bit();
w.pocchng().set_bit()
});
critical_section::with(|cs| {
let mut port = PORT.borrow_ref_mut(cs);
port.pending = true;
if let Some(waker) = port.waker.take() {
waker.wake();
}
});
}
if sof {
unsafe {
global.gintsts().write(|w| w.bits(GINTSTS_SOF));
}
}
if halted == 0 && !sof {
return;
}
let microframe = (host.hfnum().read().frnum().bits() & 7) as u8;
critical_section::with(|cs| {
let mut slots = SLOTS.borrow_ref_mut(cs);
for index in 0..MAX_CHANNELS {
let slot = &mut slots[index];
match slot.wait {
Wait::Halt if halted & (1 << index) != 0 => {
let ch = host.host_channel(index);
let hcint = ch.hcint().read().bits();
if hcint & HCINT_CHH == 0 {
continue;
}
unsafe {
ch.hcint().write(|w| w.bits(0xffff_ffff));
}
slot.wait = Wait::None;
slot.ready = Some(hcint);
slot.last_hcint = hcint;
slot.seen_hcint |= hcint;
if let Some(waker) = slot.waker.take() {
waker.wake();
}
}
Wait::Frame(frame) if sof && frame == microframe => {
slot.wait = Wait::None;
slot.ready = Some(0);
if let Some(waker) = slot.waker.take() {
waker.wake();
}
}
_ => {}
}
}
if sof && !any_frame_waiter(&slots) {
set_sof_mask(&global, false);
}
});
}
pub fn last_interrupt(channel: usize) -> u32 {
if channel >= MAX_CHANNELS {
return 0;
}
critical_section::with(|cs| SLOTS.borrow_ref(cs)[channel].last_hcint)
}
pub fn seen_interrupts(channel: usize) -> u32 {
if channel >= MAX_CHANNELS {
return 0;
}
critical_section::with(|cs| SLOTS.borrow_ref(cs)[channel].seen_hcint)
}
fn arm(index: usize, wait: Wait) {
critical_section::with(|cs| {
let mut slots = SLOTS.borrow_ref_mut(cs);
let slot = &mut slots[index];
slot.wait = wait;
slot.ready = None;
slot.waker = None;
let global = unsafe { USB_OTG_GLOBAL::steal() };
set_sof_mask(&global, any_frame_waiter(&slots));
set_channel_interrupt(index, matches!(wait, Wait::Halt));
});
}
fn disarm(index: usize) -> bool {
critical_section::with(|cs| {
let mut slots = SLOTS.borrow_ref_mut(cs);
let slot = &mut slots[index];
let was_waiting = slot.wait != Wait::None;
slot.wait = Wait::None;
slot.ready = None;
slot.waker = None;
if !any_frame_waiter(&slots) {
let global = unsafe { USB_OTG_GLOBAL::steal() };
set_sof_mask(&global, false);
}
set_channel_interrupt(index, false);
was_waiting
})
}
struct SlotWait<'a, 'c> {
channel: &'a Channel<'c>,
abort_on_drop: bool,
timer: &'a Timer,
}
impl Future for SlotWait<'_, '_> {
type Output = u32;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<u32> {
let this = self.get_mut();
critical_section::with(|cs| {
let mut slots = SLOTS.borrow_ref_mut(cs);
let slot = &mut slots[this.channel.index];
if let Some(value) = slot.ready.take() {
this.abort_on_drop = false;
return Poll::Ready(value);
}
slot.waker = Some(cx.waker().clone());
Poll::Pending
})
}
}
impl Drop for SlotWait<'_, '_> {
fn drop(&mut self) {
let abandoned = disarm(self.channel.index);
if abandoned && self.abort_on_drop {
self.channel.abort_channel(self.timer);
}
}
}
struct PortChange;
impl Future for PortChange {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
critical_section::with(|cs| {
let mut port = PORT.borrow_ref_mut(cs);
if core::mem::take(&mut port.pending) {
return Poll::Ready(());
}
port.waker = Some(cx.waker().clone());
Poll::Pending
})
}
}
impl Dwc2Host {
pub async fn wait_for_port_change(&self) {
PortChange.await;
}
pub fn clear_port_change(&self) {
critical_section::with(|cs| {
PORT.borrow_ref_mut(cs).pending = false;
});
}
}
impl Channel<'_> {
async fn start_and_wait(
&self,
endpoint: ControlEndpoint,
txn: &Transaction,
complete_split: bool,
odd_frame: Option<bool>,
timer: &Timer,
) -> u32 {
arm(self.index, Wait::Halt);
let wait = SlotWait {
channel: self,
abort_on_drop: true,
timer,
};
self.start_channel(endpoint, txn, complete_split, odd_frame);
let hcint = wait.await;
self.last_hcint.set(hcint);
hcint
}
async fn wait_for_microframe_async(&self, frame: u8, timer: &Timer) {
if self.current_microframe() == frame {
return;
}
arm(self.index, Wait::Frame(frame));
SlotWait {
channel: self,
abort_on_drop: false,
timer,
}
.await;
}
async fn delay_microframes(&self, microframes: u8, timer: &Timer) {
let target = (self.current_microframe() + microframes) & 7;
self.wait_for_microframe_async(target, timer).await;
}
pub async fn wait_microframes(&self, microframes: u32, timer: &Timer) {
let mut remaining = microframes;
while remaining > 0 {
let step = remaining.min(7) as u8;
self.delay_microframes(step, timer).await;
remaining -= u32::from(step);
}
}
async fn run_transaction_async(
&self,
endpoint: ControlEndpoint,
txn: &Transaction,
timer: &Timer,
) -> Result<usize, TransferError> {
if endpoint.split.is_none() {
let requested = txn.transfer_size as usize;
let odd_frame = if txn.endpoint_type == EndpointType::Interrupt {
let next_frame = (self.current_microframe() + 1) & 7;
self.wait_for_microframe_async(next_frame, timer).await;
Some(next_frame & 1 == 1)
} else {
None
};
let hcint = self
.start_and_wait(endpoint, txn, false, odd_frame, timer)
.await;
return self.interpret_halt(hcint, requested);
}
let periodic = txn.endpoint_type == EndpointType::Interrupt;
let max_packet_size = endpoint.max_packet_size.max(1) as u32;
let mut offset = 0;
let mut pid = txn.pid;
loop {
let this_packet = (txn.transfer_size - offset).min(max_packet_size);
let packet = Transaction {
transfer_size: this_packet,
dma_address: txn.dma_address + offset,
pid,
..*txn
};
let received = if periodic {
self.run_periodic_split_packet_async(endpoint, &packet, timer)
.await? as u32
} else {
self.run_split_packet_async(endpoint, &packet, timer)
.await? as u32
};
offset += received;
pid = pid.toggled();
if received < this_packet || offset >= txn.transfer_size {
break;
}
}
Ok(offset as usize)
}
async fn run_split_packet_async(
&self,
endpoint: ControlEndpoint,
packet: &Transaction,
timer: &Timer,
) -> Result<usize, TransferError> {
let requested = packet.transfer_size as usize;
let hcint = self
.start_and_wait(endpoint, packet, false, None, timer)
.await;
if hcint & HCINT_ACK == 0 {
return self.interpret_halt(hcint, requested);
}
let mut hcint = 0;
for attempt in 0..MAX_CSPLIT_POLLS {
if attempt > 0 {
self.delay_microframes(CSPLIT_RETRY_MICROFRAMES, timer)
.await;
}
hcint = self
.start_and_wait(endpoint, packet, true, None, timer)
.await;
if hcint & (HCINT_NYET | HCINT_NAK) != 0 && hcint & HCINT_XFRC == 0 {
continue;
}
return self.interpret_halt(hcint, requested);
}
self.interpret_halt(hcint, requested)
}
async fn run_periodic_split_packet_async(
&self,
endpoint: ControlEndpoint,
packet: &Transaction,
timer: &Timer,
) -> Result<usize, TransferError> {
let requested = packet.transfer_size as usize;
let mut next_frame = (self.current_microframe() + 1) & 7;
if next_frame == 6 {
next_frame = 7;
}
self.wait_for_microframe_async(next_frame, timer).await;
let hcint = self
.start_and_wait(endpoint, packet, false, Some(next_frame & 1 == 1), timer)
.await;
if hcint & HCINT_ACK == 0 {
return self.interpret_halt(hcint, requested);
}
let mut tries: i32 = if next_frame != 5 { 3 } else { 2 };
next_frame = (next_frame + 2) & 7;
loop {
self.wait_for_microframe_async(next_frame, timer).await;
let hcint = self
.start_and_wait(endpoint, packet, true, Some(next_frame & 1 == 1), timer)
.await;
if hcint & (HCINT_STALL | HCINT_TXERR | HCINT_BBERR | HCINT_XFRC) != 0 {
return self.interpret_halt(hcint, requested);
}
if hcint & (HCINT_NYET | HCINT_ACK) != 0 {
if tries == 0 {
return Err(TransferError::Nak);
}
tries -= 1;
next_frame = (next_frame + 1) & 7;
continue;
}
if hcint & HCINT_NAK != 0 {
return Err(TransferError::Nak);
}
return self.interpret_halt(hcint, requested);
}
}
pub async fn control_setup_async(
&mut self,
endpoint: ControlEndpoint,
setup: &[u8; 8],
timer: &Timer,
) -> Result<(), TransferError> {
self.dma_buffer.0[..8].copy_from_slice(setup);
let address = self.dma_buffer.0.as_ptr() as u32;
clean_range(address, 8);
let txn = Transaction {
endpoint_number: 0,
endpoint_type: EndpointType::Control,
direction_in: false,
pid: DataPid::Setup,
transfer_size: 8,
dma_address: address,
};
self.run_transaction_async(endpoint, &txn, timer).await?;
Ok(())
}
pub async fn control_data_in_async(
&mut self,
endpoint: ControlEndpoint,
buf: &mut [u8],
timer: &Timer,
) -> Result<usize, TransferError> {
debug_assert!(buf.len() <= DMA_BUFFER_LEN);
let address = self.dma_buffer.0.as_ptr() as u32;
let txn = Transaction {
endpoint_number: 0,
endpoint_type: EndpointType::Control,
direction_in: true,
pid: DataPid::Data1,
transfer_size: buf.len() as u32,
dma_address: address,
};
let received = self.run_transaction_async(endpoint, &txn, timer).await?;
invalidate_range(address, buf.len());
buf[..received].copy_from_slice(&self.dma_buffer.0[..received]);
Ok(received)
}
pub async fn control_data_out_async(
&mut self,
endpoint: ControlEndpoint,
data: &[u8],
timer: &Timer,
) -> Result<(), TransferError> {
debug_assert!(data.len() <= DMA_BUFFER_LEN);
self.dma_buffer.0[..data.len()].copy_from_slice(data);
let address = self.dma_buffer.0.as_ptr() as u32;
clean_range(address, data.len());
let txn = Transaction {
endpoint_number: 0,
endpoint_type: EndpointType::Control,
direction_in: false,
pid: DataPid::Data1,
transfer_size: data.len() as u32,
dma_address: address,
};
self.run_transaction_async(endpoint, &txn, timer).await?;
Ok(())
}
pub async fn control_status_in_async(
&mut self,
endpoint: ControlEndpoint,
timer: &Timer,
) -> Result<(), TransferError> {
let address = self.dma_buffer.0.as_ptr() as u32;
let txn = Transaction {
endpoint_number: 0,
endpoint_type: EndpointType::Control,
direction_in: true,
pid: DataPid::Data1,
transfer_size: 0,
dma_address: address,
};
self.run_transaction_async(endpoint, &txn, timer).await?;
Ok(())
}
pub async fn control_status_out_async(
&mut self,
endpoint: ControlEndpoint,
timer: &Timer,
) -> Result<(), TransferError> {
let address = self.dma_buffer.0.as_ptr() as u32;
let txn = Transaction {
endpoint_number: 0,
endpoint_type: EndpointType::Control,
direction_in: false,
pid: DataPid::Data1,
transfer_size: 0,
dma_address: address,
};
self.run_transaction_async(endpoint, &txn, timer).await?;
Ok(())
}
pub async fn interrupt_in_async(
&mut self,
endpoint: ControlEndpoint,
endpoint_number: u8,
data_toggle: &mut bool,
buf: &mut [u8],
timer: &Timer,
) -> Result<usize, TransferError> {
debug_assert!(buf.len() <= DMA_BUFFER_LEN);
let address = self.dma_buffer.0.as_ptr() as u32;
let txn = Transaction {
endpoint_number,
endpoint_type: EndpointType::Interrupt,
direction_in: true,
pid: if *data_toggle {
DataPid::Data1
} else {
DataPid::Data0
},
transfer_size: buf.len() as u32,
dma_address: address,
};
let received = self.run_transaction_async(endpoint, &txn, timer).await?;
*data_toggle = !*data_toggle;
invalidate_range(address, buf.len());
buf[..received].copy_from_slice(&self.dma_buffer.0[..received]);
Ok(received)
}
pub async fn bulk_out_async(
&mut self,
endpoint: ControlEndpoint,
endpoint_number: u8,
data_toggle: &mut bool,
buf: &[u8],
timer: &Timer,
) -> Result<usize, TransferError> {
let address = buf.as_ptr() as u32;
clean_range(address, buf.len());
let txn = Transaction {
endpoint_number,
endpoint_type: EndpointType::Bulk,
direction_in: false,
pid: Channel::toggle_pid(*data_toggle),
transfer_size: buf.len() as u32,
dma_address: address,
};
let sent = self.run_transaction_async(endpoint, &txn, timer).await?;
*data_toggle = self.next_toggle();
Ok(sent)
}
pub async fn bulk_in_async(
&mut self,
endpoint: ControlEndpoint,
endpoint_number: u8,
data_toggle: &mut bool,
buf: &mut [u8],
timer: &Timer,
) -> Result<usize, TransferError> {
let address = buf.as_mut_ptr() as u32;
debug_assert!(
address.is_multiple_of(crate::cache::MIN_CACHE_LINE),
"bulk IN buffer must be cache-line aligned"
);
let max_packet_size = endpoint.max_packet_size.max(1) as u32;
let transfer_size = buf.len() as u32 / max_packet_size * max_packet_size;
let txn = Transaction {
endpoint_number,
endpoint_type: EndpointType::Bulk,
direction_in: true,
pid: Channel::toggle_pid(*data_toggle),
transfer_size,
dma_address: address,
};
let received = self.run_transaction_async(endpoint, &txn, timer).await?;
*data_toggle = self.next_toggle();
invalidate_range(address, buf.len());
Ok(received)
}
}