#[cfg(test)]
mod tests;
use alloc::collections::VecDeque;
use alloc::vec::Vec;
use core::fmt;
use crate::bus::usb::{Completion, DeviceAddress, SetupPacket, Speed, Status, UsbDevice};
use crate::core::error::{Error, Result};
use crate::core::state::{Sink, Source};
use alloc::sync::Weak;
use super::{
DPID_DATA0, Dwc2, PKTSTS_IN_COMPLETE, PKTSTS_IN_DATA, RXSTS_BCNT_SHIFT, RXSTS_DPID_SHIFT,
RXSTS_PKTSTS_SHIFT, RxPacket, State, words_of,
};
pub(super) const DCFG: u64 = 0x800;
pub(super) const DCTL: u64 = 0x804;
pub(super) const DSTS: u64 = 0x808;
pub(super) const DIEPMSK: u64 = 0x810;
pub(super) const DOEPMSK: u64 = 0x814;
pub(super) const DAINT: u64 = 0x818;
pub(super) const DAINTMSK: u64 = 0x81c;
pub(super) const DVBUSDIS: u64 = 0x828;
pub(super) const DVBUSPULSE: u64 = 0x82c;
pub(super) const DIEPEMPMSK: u64 = 0x834;
pub(super) const DIEP_BASE: u64 = 0x900;
pub(super) const DOEP_BASE: u64 = 0xb00;
pub(super) const EP_STRIDE: u64 = 0x20;
pub(super) const DIEPTXF_BASE: u64 = 0x104;
const DCFG_DSPD_MASK: u32 = 0x3;
const DSPD_HIGH: u32 = 0;
const DSPD_FULL_HS_PHY: u32 = 1;
const DSPD_LOW: u32 = 2;
const DSPD_FULL_FS_PHY: u32 = 3;
const DCFG_DAD_SHIFT: u32 = 4;
const DCFG_DAD_MASK: u32 = 0x7f;
const DCFG_WRITABLE: u32 =
DCFG_DSPD_MASK | (1 << 2) | (DCFG_DAD_MASK << DCFG_DAD_SHIFT) | (0x3 << 11);
const DCTL_RWUSIG: u32 = 1 << 0;
pub(super) const DCTL_SDIS: u32 = 1 << 1;
const DCTL_GINSTS: u32 = 1 << 2;
const DCTL_GONSTS: u32 = 1 << 3;
const DCTL_SGINAK: u32 = 1 << 7;
const DCTL_CGINAK: u32 = 1 << 8;
const DCTL_SGONAK: u32 = 1 << 9;
const DCTL_CGONAK: u32 = 1 << 10;
const DCTL_WRITABLE: u32 = DCTL_RWUSIG | DCTL_SDIS | (0x7 << 4) | (1 << 11);
const DCTL_RESET_VALUE: u32 = DCTL_SDIS;
const DSTS_SUSPSTS: u32 = 1 << 0;
const DSTS_ENUMSPD_SHIFT: u32 = 1;
const DSTS_FNSOF_SHIFT: u32 = 8;
const EPCTL_MPSIZ_MASK: u32 = 0x7ff;
const EPCTL_USBAEP: u32 = 1 << 15;
const EPCTL_NAKSTS: u32 = 1 << 17;
const EPCTL_EPTYP_SHIFT: u32 = 18;
const EPCTL_SNPM: u32 = 1 << 20;
const EPCTL_STALL: u32 = 1 << 21;
const EPCTL_TXFNUM_SHIFT: u32 = 22;
const EPCTL_CNAK: u32 = 1 << 26;
const EPCTL_SNAK: u32 = 1 << 27;
const EPCTL_SD0PID: u32 = 1 << 28;
const EPCTL_SODDFRM: u32 = 1 << 29;
const _: () = {
assert!(EPCTL_WRITABLE & (EPCTL_SD0PID | EPCTL_SODDFRM) == 0);
};
const EPCTL_EPDIS: u32 = 1 << 30;
const EPCTL_EPENA: u32 = 1 << 31;
const EPCTL_WRITABLE: u32 = EPCTL_MPSIZ_MASK
| EPCTL_USBAEP
| (0x3 << EPCTL_EPTYP_SHIFT)
| EPCTL_SNPM
| EPCTL_STALL
| (0xf << EPCTL_TXFNUM_SHIFT);
pub(super) const DIEPINT_XFRC: u32 = 1 << 0;
const DIEPINT_EPDISD: u32 = 1 << 1;
const DIEPINT_TOC: u32 = 1 << 3;
pub(super) const DIEPINT_ITTXFE: u32 = 1 << 4;
const DIEPINT_INEPNE: u32 = 1 << 6;
const DIEPINT_TXFE: u32 = 1 << 7;
const DIEPINT_MASK: u32 =
DIEPINT_XFRC | DIEPINT_EPDISD | DIEPINT_TOC | DIEPINT_ITTXFE | DIEPINT_INEPNE | DIEPINT_TXFE;
const DIEPINT_W1C: u32 = DIEPINT_MASK & !DIEPINT_TXFE;
pub(super) const DOEPINT_XFRC: u32 = 1 << 0;
const DOEPINT_EPDISD: u32 = 1 << 1;
pub(super) const DOEPINT_STUP: u32 = 1 << 3;
const DOEPINT_OTEPDIS: u32 = 1 << 4;
const DOEPINT_B2BSTUP: u32 = 1 << 6;
const DOEPINT_MASK: u32 =
DOEPINT_XFRC | DOEPINT_EPDISD | DOEPINT_STUP | DOEPINT_OTEPDIS | DOEPINT_B2BSTUP;
const DTSIZ_XFRSIZ_MASK: u32 = 0x7_ffff;
const DTSIZ0_XFRSIZ_MASK: u32 = 0x7f;
const DTSIZ_PKTCNT_SHIFT: u32 = 19;
const DTSIZ_PKTCNT_MASK: u32 = 0x3ff;
const DTSIZ0_PKTCNT_MASK: u32 = 0x1;
const DTSIZ0_STUPCNT_SHIFT: u32 = 29;
const GINT_GINAKEFF: u32 = 1 << 6;
const GINT_GONAKEFF: u32 = 1 << 7;
pub(super) const GINT_USBRST: u32 = 1 << 12;
pub(super) const GINT_ENUMDNE: u32 = 1 << 13;
pub(super) const GINT_IEPINT: u32 = 1 << 18;
pub(super) const GINT_OEPINT: u32 = 1 << 19;
const GINT_NPTXFE: u32 = 1 << 5;
const PKTSTS_OUT_DATA: u32 = PKTSTS_IN_DATA;
const PKTSTS_OUT_COMPLETE: u32 = PKTSTS_IN_COMPLETE;
const PKTSTS_SETUP_COMPLETE: u32 = 0b0100;
const PKTSTS_SETUP_DATA: u32 = 0b0110;
const RXSTS_FRMNUM_SHIFT: u32 = 21;
pub const MAX_ENDPOINTS: usize = 16;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(super) struct InEndpoint {
ctl: u32,
int: u32,
tsiz: u32,
pub(super) tx: VecDeque<u8>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(super) struct OutEndpoint {
ctl: u32,
int: u32,
tsiz: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct DeviceState {
dcfg: u32,
dctl: u32,
diepmsk: u32,
doepmsk: u32,
daintmsk: u32,
dvbusdis: u32,
dvbuspulse: u32,
diepempmsk: u32,
dieptxf: [u32; MAX_ENDPOINTS],
fnsof: u32,
pub(super) din: [InEndpoint; MAX_ENDPOINTS],
dout: [OutEndpoint; MAX_ENDPOINTS],
}
impl DeviceState {
pub(super) fn reset() -> DeviceState {
DeviceState {
dcfg: 0,
dctl: DCTL_RESET_VALUE,
diepmsk: 0,
doepmsk: 0,
daintmsk: 0,
dvbusdis: 0x0000_17d7,
dvbuspulse: 0x0000_05b8,
diepempmsk: 0,
dieptxf: [0x0200_0400; MAX_ENDPOINTS],
fnsof: 0,
din: core::array::from_fn(|_| InEndpoint::default()),
dout: core::array::from_fn(|_| OutEndpoint::default()),
}
}
fn bus_reset(&mut self) {
self.dcfg &= !(DCFG_DAD_MASK << DCFG_DAD_SHIFT);
self.dctl &= !(DCTL_GINSTS | DCTL_GONSTS);
for ep in &mut self.din {
ep.ctl &= !(EPCTL_EPENA | EPCTL_STALL | EPCTL_NAKSTS);
ep.int = 0;
ep.tsiz = 0;
ep.tx.clear();
}
for ep in &mut self.dout {
ep.ctl &= !(EPCTL_EPENA | EPCTL_STALL | EPCTL_NAKSTS);
ep.int = 0;
ep.tsiz = 0;
}
self.fnsof = 0;
}
fn address(&self) -> DeviceAddress {
DeviceAddress(((self.dcfg >> DCFG_DAD_SHIFT) & DCFG_DAD_MASK) as u8)
}
}
fn ep0_packet_size(ctl: u32) -> u32 {
match ctl & 0x3 {
0 => 64,
1 => 32,
2 => 16,
_ => 8,
}
}
fn packet_size(endpoint: usize, ctl: u32) -> u32 {
if endpoint == 0 {
ep0_packet_size(ctl)
} else {
(ctl & EPCTL_MPSIZ_MASK).max(1)
}
}
fn xfrsiz(endpoint: usize, tsiz: u32) -> u32 {
if endpoint == 0 {
tsiz & DTSIZ0_XFRSIZ_MASK
} else {
tsiz & DTSIZ_XFRSIZ_MASK
}
}
fn pktcnt(endpoint: usize, tsiz: u32) -> u32 {
let mask = if endpoint == 0 {
DTSIZ0_PKTCNT_MASK
} else {
DTSIZ_PKTCNT_MASK
};
(tsiz >> DTSIZ_PKTCNT_SHIFT) & mask
}
fn set_counts(endpoint: usize, tsiz: u32, bytes: u32, packets: u32) -> u32 {
let (xmask, pmask) = if endpoint == 0 {
(DTSIZ0_XFRSIZ_MASK, DTSIZ0_PKTCNT_MASK)
} else {
(DTSIZ_XFRSIZ_MASK, DTSIZ_PKTCNT_MASK)
};
let cleared = tsiz & !(xmask | (pmask << DTSIZ_PKTCNT_SHIFT));
cleared | (bytes & xmask) | ((packets & pmask) << DTSIZ_PKTCNT_SHIFT)
}
pub(super) fn read(core: &Dwc2, state: &State, offset: u64) -> u32 {
let endpoints = usize::from(core.params().endpoints);
let dev = &state.dev;
if (DIEP_BASE..DOEP_BASE).contains(&offset) {
let index = ((offset - DIEP_BASE) / EP_STRIDE) as usize;
if index >= endpoints {
return 0;
}
let ep = &dev.din[index];
return match (offset - DIEP_BASE) % EP_STRIDE {
0x00 => in_ctl(index, ep.ctl),
0x08 => in_int(ep),
0x10 => ep.tsiz,
0x18 => tx_free(core, state, index),
_ => 0,
};
}
if (DOEP_BASE..super::DEVICE_END).contains(&offset) {
let index = ((offset - DOEP_BASE) / EP_STRIDE) as usize;
if index >= endpoints {
return 0;
}
let ep = &dev.dout[index];
return match (offset - DOEP_BASE) % EP_STRIDE {
0x00 => out_ctl(index, ep.ctl, dev.din[0].ctl),
0x08 => ep.int & DOEPINT_MASK,
0x10 => ep.tsiz,
_ => 0,
};
}
match offset {
DCFG => dev.dcfg & DCFG_WRITABLE,
DCTL => dev.dctl & (DCTL_WRITABLE | DCTL_GINSTS | DCTL_GONSTS),
DSTS => {
let mut value = enumerated_speed(core, dev) << DSTS_ENUMSPD_SHIFT;
value |= (dev.fnsof & 0x3fff) << DSTS_FNSOF_SHIFT;
value & !DSTS_SUSPSTS
}
DIEPMSK => dev.diepmsk & DIEPINT_MASK,
DOEPMSK => dev.doepmsk & DOEPINT_MASK,
DAINT => daint(core, state),
DAINTMSK => dev.daintmsk,
DVBUSDIS => dev.dvbusdis & 0xffff,
DVBUSPULSE => dev.dvbuspulse & 0xfff,
DIEPEMPMSK => dev.diepempmsk & 0xffff,
_ => 0,
}
}
pub(super) fn write(core: &Dwc2, state: &mut State, offset: u64, value: u32) -> bool {
let endpoints = usize::from(core.params().endpoints);
if (DIEP_BASE..DOEP_BASE).contains(&offset) {
let index = ((offset - DIEP_BASE) / EP_STRIDE) as usize;
if index < endpoints {
let register = (offset - DIEP_BASE) % EP_STRIDE;
write_in_endpoint(state, index, register, value);
}
return false;
}
if (DOEP_BASE..super::DEVICE_END).contains(&offset) {
let index = ((offset - DOEP_BASE) / EP_STRIDE) as usize;
if index < endpoints {
let register = (offset - DOEP_BASE) % EP_STRIDE;
write_out_endpoint(state, index, register, value);
}
return false;
}
let dev = &mut state.dev;
match offset {
DCFG => dev.dcfg = value & DCFG_WRITABLE,
DCTL => {
let was = dev.dctl;
dev.dctl = (dev.dctl & (DCTL_GINSTS | DCTL_GONSTS)) | (value & DCTL_WRITABLE);
if value & DCTL_SGINAK != 0 {
dev.dctl |= DCTL_GINSTS;
}
if value & DCTL_CGINAK != 0 {
dev.dctl &= !DCTL_GINSTS;
}
if value & DCTL_SGONAK != 0 {
dev.dctl |= DCTL_GONSTS;
}
if value & DCTL_CGONAK != 0 {
dev.dctl &= !DCTL_GONSTS;
}
return (was ^ dev.dctl) & DCTL_SDIS != 0;
}
DIEPMSK => dev.diepmsk = value & DIEPINT_MASK,
DOEPMSK => dev.doepmsk = value & DOEPINT_MASK,
DAINTMSK => dev.daintmsk = value,
DVBUSDIS => dev.dvbusdis = value & 0xffff,
DVBUSPULSE => dev.dvbuspulse = value & 0xfff,
DIEPEMPMSK => dev.diepempmsk = value & 0xffff,
_ => {}
}
false
}
pub(super) fn tx_fifo_register(core: &Dwc2, offset: u64) -> Option<usize> {
let endpoints = u64::from(core.params().endpoints);
if endpoints < 2 || !(DIEPTXF_BASE..DIEPTXF_BASE + (endpoints - 1) * 4).contains(&offset) {
return None;
}
Some(((offset - DIEPTXF_BASE) / 4 + 1) as usize)
}
pub(super) fn read_tx_fifo(state: &State, endpoint: usize) -> u32 {
state.dev.dieptxf.get(endpoint).copied().unwrap_or(0)
}
pub(super) fn write_tx_fifo(state: &mut State, endpoint: usize, value: u32) {
if let Some(slot) = state.dev.dieptxf.get_mut(endpoint) {
*slot = value;
}
}
fn in_ctl(endpoint: usize, ctl: u32) -> u32 {
let mut value = ctl;
if endpoint == 0 {
value |= EPCTL_USBAEP;
value &= !(0x3 << EPCTL_EPTYP_SHIFT);
value = (value & !EPCTL_MPSIZ_MASK) | (ctl & 0x3);
}
value
}
fn out_ctl(endpoint: usize, ctl: u32, diepctl0: u32) -> u32 {
let mut value = ctl;
if endpoint == 0 {
value |= EPCTL_USBAEP;
value &= !(0x3 << EPCTL_EPTYP_SHIFT);
value = (value & !EPCTL_MPSIZ_MASK) | (diepctl0 & 0x3);
value &= !EPCTL_EPDIS;
}
value
}
fn in_int(ep: &InEndpoint) -> u32 {
let mut value = ep.int & DIEPINT_W1C;
if ep.tx.is_empty() {
value |= DIEPINT_TXFE;
}
value
}
fn write_in_endpoint(state: &mut State, index: usize, register: u64, value: u32) {
let ep = &mut state.dev.din[index];
match register {
0x00 => {
let stored = ep.ctl & (EPCTL_EPENA | EPCTL_NAKSTS);
ep.ctl = stored | (value & EPCTL_WRITABLE);
if value & EPCTL_CNAK != 0 {
ep.ctl &= !EPCTL_NAKSTS;
}
if value & EPCTL_SNAK != 0 {
ep.ctl |= EPCTL_NAKSTS;
}
if value & EPCTL_EPENA != 0 {
ep.ctl |= EPCTL_EPENA;
}
if value & EPCTL_EPDIS != 0 {
ep.ctl &= !EPCTL_EPENA;
ep.int |= DIEPINT_EPDISD;
}
}
0x08 => ep.int &= !(value & DIEPINT_W1C),
0x10 => ep.tsiz = value,
_ => {}
}
}
fn write_out_endpoint(state: &mut State, index: usize, register: u64, value: u32) {
let ep = &mut state.dev.dout[index];
match register {
0x00 => {
let stored = ep.ctl & (EPCTL_EPENA | EPCTL_NAKSTS);
ep.ctl = stored | (value & EPCTL_WRITABLE);
if value & EPCTL_CNAK != 0 {
ep.ctl &= !EPCTL_NAKSTS;
}
if value & EPCTL_SNAK != 0 {
ep.ctl |= EPCTL_NAKSTS;
}
if value & EPCTL_EPENA != 0 {
ep.ctl |= EPCTL_EPENA;
}
if value & EPCTL_EPDIS != 0 && index != 0 {
ep.ctl &= !EPCTL_EPENA;
ep.int |= DOEPINT_EPDISD;
}
}
0x08 => ep.int &= !(value & DOEPINT_MASK),
0x10 => ep.tsiz = value,
_ => {}
}
}
pub(super) fn tx_depth(core: &Dwc2, state: &State, endpoint: usize) -> u32 {
let raw = if endpoint == 0 {
state.gnptxfsiz >> super::FIFO_DEPTH_SHIFT
} else {
state.dev.dieptxf.get(endpoint).copied().unwrap_or(0) >> super::FIFO_DEPTH_SHIFT
};
(raw & super::FIFO_DEPTH_MASK).min(core.params().fifo_words)
}
fn tx_free(core: &Dwc2, state: &State, endpoint: usize) -> u32 {
let used = words_of(state.dev.din[endpoint].tx.len());
tx_depth(core, state, endpoint).saturating_sub(used)
}
pub(super) fn push_word(core: &Dwc2, state: &mut State, endpoint: usize, value: u32) {
if endpoint >= usize::from(core.params().endpoints) {
return;
}
if tx_free(core, state, endpoint) == 0 {
return;
}
state.dev.din[endpoint].tx.extend(value.to_le_bytes());
}
fn in_pending(state: &State, index: usize) -> u32 {
let dev = &state.dev;
let mut mask = dev.diepmsk & !DIEPINT_TXFE;
if dev.diepempmsk & (1u32 << index) != 0 {
mask |= DIEPINT_TXFE;
}
in_int(&dev.din[index]) & mask
}
fn out_pending(state: &State, index: usize) -> u32 {
state.dev.dout[index].int & state.dev.doepmsk & DOEPINT_MASK
}
pub(super) fn daint(core: &Dwc2, state: &State) -> u32 {
let mut bits = 0;
for index in 0..usize::from(core.params().endpoints) {
if in_pending(state, index) != 0 {
bits |= 1u32 << index;
}
if out_pending(state, index) != 0 {
bits |= 1u32 << (index + 16);
}
}
bits
}
pub(super) fn gintsts(core: &Dwc2, state: &State) -> u32 {
let mut value = 0;
let pending = daint(core, state) & state.dev.daintmsk;
if pending & 0x0000_ffff != 0 {
value |= GINT_IEPINT;
}
if pending & 0xffff_0000 != 0 {
value |= GINT_OEPINT;
}
if state.dev.dctl & DCTL_GINSTS != 0 {
value |= GINT_GINAKEFF;
}
if state.dev.dctl & DCTL_GONSTS != 0 {
value |= GINT_GONAKEFF;
}
if state.dev.din[0].tx.is_empty() {
value |= GINT_NPTXFE;
}
value
}
fn enumerated_speed(core: &Dwc2, dev: &DeviceState) -> u32 {
match speed_of(core, dev) {
Speed::High => DSPD_HIGH,
Speed::Low => DSPD_LOW,
Speed::Full if core.params().max_speed == Speed::High => DSPD_FULL_HS_PHY,
Speed::Full => DSPD_FULL_FS_PHY,
}
}
fn speed_of(core: &Dwc2, dev: &DeviceState) -> Speed {
let asked = match dev.dcfg & DCFG_DSPD_MASK {
DSPD_HIGH => Speed::High,
DSPD_LOW => Speed::Low,
DSPD_FULL_HS_PHY | DSPD_FULL_FS_PHY => Speed::Full,
_ => Speed::Full,
};
asked.min(core.params().max_speed)
}
pub(super) fn save<S: Sink + ?Sized>(dev: &DeviceState, w: &mut S) -> Result<()> {
w.write_u32(dev.dcfg)?;
w.write_u32(dev.dctl)?;
w.write_u32(dev.diepmsk)?;
w.write_u32(dev.doepmsk)?;
w.write_u32(dev.daintmsk)?;
w.write_u32(dev.dvbusdis)?;
w.write_u32(dev.dvbuspulse)?;
w.write_u32(dev.diepempmsk)?;
w.write_u32(dev.fnsof)?;
w.write_seq_len(MAX_ENDPOINTS as u64)?;
for value in &dev.dieptxf {
w.write_u32(*value)?;
}
w.write_seq_len(MAX_ENDPOINTS as u64)?;
for ep in &dev.din {
w.write_u32(ep.ctl)?;
w.write_u32(ep.int)?;
w.write_u32(ep.tsiz)?;
let staged: Vec<u8> = ep.tx.iter().copied().collect();
w.write_bytes(&staged)?;
}
w.write_seq_len(MAX_ENDPOINTS as u64)?;
for ep in &dev.dout {
w.write_u32(ep.ctl)?;
w.write_u32(ep.int)?;
w.write_u32(ep.tsiz)?;
}
Ok(())
}
pub(super) fn load<'a, S: Source<'a> + ?Sized>(r: &mut S) -> Result<DeviceState> {
let mut dev = DeviceState {
dcfg: r.read_u32()?,
dctl: r.read_u32()?,
diepmsk: r.read_u32()?,
doepmsk: r.read_u32()?,
daintmsk: r.read_u32()?,
dvbusdis: r.read_u32()?,
dvbuspulse: r.read_u32()?,
diepempmsk: r.read_u32()?,
fnsof: r.read_u32()?,
dieptxf: [0; MAX_ENDPOINTS],
din: core::array::from_fn(|_| InEndpoint::default()),
dout: core::array::from_fn(|_| OutEndpoint::default()),
};
let count = r.read_seq_len(4)?;
if count != MAX_ENDPOINTS as u64 {
return Err(Error::State(alloc::format!(
"usb.dwc2: a snapshot with {count} transmit FIFOs, not {MAX_ENDPOINTS}"
)));
}
for slot in &mut dev.dieptxf {
*slot = r.read_u32()?;
}
let count = r.read_seq_len(16)?;
if count != MAX_ENDPOINTS as u64 {
return Err(Error::State(alloc::format!(
"usb.dwc2: a snapshot with {count} IN endpoints, not {MAX_ENDPOINTS}"
)));
}
for ep in &mut dev.din {
ep.ctl = r.read_u32()?;
ep.int = r.read_u32()?;
ep.tsiz = r.read_u32()?;
ep.tx = r.read_bytes()?.iter().copied().collect();
}
let count = r.read_seq_len(12)?;
if count != MAX_ENDPOINTS as u64 {
return Err(Error::State(alloc::format!(
"usb.dwc2: a snapshot with {count} OUT endpoints, not {MAX_ENDPOINTS}"
)));
}
for ep in &mut dev.dout {
ep.ctl = r.read_u32()?;
ep.int = r.read_u32()?;
ep.tsiz = r.read_u32()?;
}
Ok(dev)
}
pub struct Dwc2Gadget {
core: Weak<Dwc2>,
}
impl fmt::Debug for Dwc2Gadget {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Dwc2Gadget")
.field("live", &(self.core.strong_count() > 0))
.finish()
}
}
impl Dwc2Gadget {
#[must_use]
pub(super) fn new(core: Weak<Dwc2>) -> Dwc2Gadget {
Dwc2Gadget { core }
}
}
impl UsbDevice for Dwc2Gadget {
fn speed(&self) -> Speed {
match self.core.upgrade() {
Some(core) => {
let state = core.state.lock();
speed_of(&core, &state.dev)
}
None => Speed::Full,
}
}
fn address(&self) -> DeviceAddress {
match self.core.upgrade() {
Some(core) => {
let state = core.state.lock();
state.dev.address()
}
None => DeviceAddress::DEFAULT,
}
}
fn bus_reset(&self) {
let Some(core) = self.core.upgrade() else {
return;
};
{
let mut state = core.state.lock();
state.dev.bus_reset();
state.rx.clear();
state.gintsts |= GINT_USBRST | GINT_ENUMDNE;
}
core.refresh_irq();
}
fn start_of_frame(&self, frame: u16) {
let Some(core) = self.core.upgrade() else {
return;
};
{
let mut state = core.state.lock();
state.dev.fnsof = u32::from(frame) & 0x3fff;
state.gintsts |= super::GINT_SOF;
}
core.refresh_irq();
}
fn setup(&self, endpoint: u8, packet: SetupPacket) -> Status {
let Some(core) = self.core.upgrade() else {
return Status::NoDevice;
};
let status = {
let mut state = core.state.lock();
accept_setup(&core, &mut state, endpoint, packet)
};
core.refresh_irq();
status
}
fn transfer_in(&self, endpoint: u8, dst: &mut [u8]) -> Completion {
let Some(core) = self.core.upgrade() else {
return Completion::absent();
};
let completion = {
let mut state = core.state.lock();
answer_in(&core, &mut state, endpoint, dst, true)
};
core.refresh_irq();
completion
}
fn transfer_out(&self, endpoint: u8, src: &[u8]) -> Completion {
let Some(core) = self.core.upgrade() else {
return Completion::absent();
};
let completion = {
let mut state = core.state.lock();
accept_out(&core, &mut state, endpoint, src)
};
core.refresh_irq();
completion
}
fn peek_in(&self, endpoint: u8, dst: &mut [u8]) -> Completion {
let Some(core) = self.core.upgrade() else {
return Completion::absent();
};
let mut state = core.state.lock();
answer_in(&core, &mut state, endpoint, dst, false)
}
}
pub(super) fn soft_connected(state: &State) -> bool {
!Dwc2::host_mode(state) && state.dev.dctl & DCTL_SDIS == 0
}
fn accept_setup(core: &Dwc2, state: &mut State, endpoint: u8, packet: SetupPacket) -> Status {
if !soft_connected(state) {
return Status::NoDevice;
}
let index = usize::from(endpoint & 0x0f);
if index >= usize::from(core.params().endpoints) {
return Status::Stall;
}
let depth = core.rx_depth(state);
let need = 1 + words_of(SetupPacket::SIZE as usize) + 1;
if state.rx.words().saturating_add(need) > depth {
return Status::Nak;
}
let frame = state.dev.fnsof & 0xf;
let bytes = packet.encode();
state.rx.queue.push_back(RxPacket {
status: (index as u32)
| ((SetupPacket::SIZE as u32) << RXSTS_BCNT_SHIFT)
| (DPID_DATA0 << RXSTS_DPID_SHIFT)
| (PKTSTS_SETUP_DATA << RXSTS_PKTSTS_SHIFT)
| (frame << RXSTS_FRMNUM_SHIFT),
data: bytes.to_vec(),
});
state.rx.queue.push_back(RxPacket {
status: (index as u32)
| (DPID_DATA0 << RXSTS_DPID_SHIFT)
| (PKTSTS_SETUP_COMPLETE << RXSTS_PKTSTS_SHIFT)
| (frame << RXSTS_FRMNUM_SHIFT),
data: Vec::new(),
});
let ep = &mut state.dev.dout[index];
ep.int |= DOEPINT_STUP;
let stupcnt = (ep.tsiz >> DTSIZ0_STUPCNT_SHIFT) & 0x3;
ep.tsiz = (ep.tsiz & !(0x3 << DTSIZ0_STUPCNT_SHIFT))
| (stupcnt.saturating_sub(1) << DTSIZ0_STUPCNT_SHIFT);
state.dev.din[index].ctl &= !EPCTL_STALL;
state.dev.dout[index].ctl &= !EPCTL_STALL;
Status::Ack
}
fn answer_in(
core: &Dwc2,
state: &mut State,
endpoint: u8,
dst: &mut [u8],
commit: bool,
) -> Completion {
if !soft_connected(state) {
return Completion::absent();
}
let index = usize::from(endpoint & 0x0f);
if index >= usize::from(core.params().endpoints) {
return Completion::stall();
}
let global_nak = state.dev.dctl & DCTL_GINSTS != 0;
let (ctl, tsiz, staged) = {
let ep = &state.dev.din[index];
(ep.ctl, ep.tsiz, ep.tx.len())
};
if ctl & EPCTL_STALL != 0 {
return Completion::stall();
}
if global_nak || ctl & EPCTL_NAKSTS != 0 || ctl & EPCTL_EPENA == 0 {
return Completion::nak();
}
let packets = pktcnt(index, tsiz);
if packets == 0 {
return Completion::nak();
}
let mps = packet_size(index, ctl);
let remaining = xfrsiz(index, tsiz);
let want = mps.min(remaining) as usize;
if staged < want {
if commit {
state.dev.din[index].int |= DIEPINT_ITTXFE;
}
return Completion::nak();
}
if want > dst.len() {
return Completion {
status: Status::Babble,
len: 0,
};
}
let data: Vec<u8> = state.dev.din[index].tx.iter().copied().take(want).collect();
dst[..want].copy_from_slice(&data);
if !commit {
return Completion::ack(want as u64);
}
let ep = &mut state.dev.din[index];
for _ in 0..want {
ep.tx.pop_front();
}
let left = remaining.saturating_sub(want as u32);
let packets = packets.saturating_sub(1);
ep.tsiz = set_counts(index, ep.tsiz, left, packets);
if packets == 0 {
ep.ctl &= !EPCTL_EPENA;
ep.int |= DIEPINT_XFRC;
}
Completion::ack(want as u64)
}
fn accept_out(core: &Dwc2, state: &mut State, endpoint: u8, src: &[u8]) -> Completion {
if !soft_connected(state) {
return Completion::absent();
}
let index = usize::from(endpoint & 0x0f);
if index >= usize::from(core.params().endpoints) {
return Completion::stall();
}
let global_nak = state.dev.dctl & DCTL_GONSTS != 0;
let (ctl, tsiz) = {
let ep = &state.dev.dout[index];
(ep.ctl, ep.tsiz)
};
if ctl & EPCTL_STALL != 0 {
return Completion::stall();
}
if ctl & EPCTL_EPENA == 0 {
if !src.is_empty() {
state.dev.dout[index].int |= DOEPINT_OTEPDIS;
}
return Completion::nak();
}
if global_nak || ctl & EPCTL_NAKSTS != 0 {
return Completion::nak();
}
let packets = pktcnt(index, tsiz);
if packets == 0 {
return Completion::nak();
}
let depth = core.rx_depth(state);
let need = 1 + words_of(src.len()) + 1;
if state.rx.words().saturating_add(need) > depth {
return Completion::nak();
}
let mps = packet_size(index, ctl);
let remaining = xfrsiz(index, tsiz);
let frame = state.dev.fnsof & 0xf;
let taken = src.len().min(remaining as usize);
if taken > 0 {
state.rx.queue.push_back(RxPacket {
status: (index as u32)
| ((taken as u32) << RXSTS_BCNT_SHIFT)
| (DPID_DATA0 << RXSTS_DPID_SHIFT)
| (PKTSTS_OUT_DATA << RXSTS_PKTSTS_SHIFT)
| (frame << RXSTS_FRMNUM_SHIFT),
data: src[..taken].to_vec(),
});
}
let short = (taken as u32) < mps;
let ep = &mut state.dev.dout[index];
let left = remaining.saturating_sub(taken as u32);
let packets = packets.saturating_sub(1);
ep.tsiz = set_counts(index, ep.tsiz, left, packets);
if packets == 0 || short {
ep.ctl &= !EPCTL_EPENA;
ep.int |= DOEPINT_XFRC;
state.rx.queue.push_back(RxPacket {
status: (index as u32)
| (DPID_DATA0 << RXSTS_DPID_SHIFT)
| (PKTSTS_OUT_COMPLETE << RXSTS_PKTSTS_SHIFT)
| (frame << RXSTS_FRMNUM_SHIFT),
data: Vec::new(),
});
}
Completion::ack(taken as u64)
}