use crate::pac::udp::csr;
use crate::pac::udp::csr::EPTYPE_A;
use crate::pac::UDP;
use crate::udp::frm_num;
use crate::BorrowUnchecked;
use paste::paste;
use usb_device::{
bus::PollResult,
endpoint::{EndpointAddress, EndpointType},
UsbDirection,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)]
enum NextBank {
Bank0,
Bank1,
}
pub struct Endpoint {
index: u8,
interval: u8,
max_packet_size: u16,
ep_type: EndpointType,
ep_dir: UsbDirection,
next_bank: NextBank,
allocated: bool,
txbanks_free: u8,
stalled: bool,
next_out_zlp: bool,
}
macro_rules! clear_ep {
(
$udp:ident,
$ep:ident,
$epnum:expr
) => {{
$udp.rst_ep.modify(|_, w| w.$ep().set_bit());
while !$udp.rst_ep.read().$ep().bit() && $udp.csr()[$epnum].read().rxbytecnt().bits() != 0 {
}
$udp.rst_ep.modify(|_, w| w.$ep().clear_bit());
}};
}
macro_rules! csr_wait {
(
$field:ident,
set
) => {
paste! {
pub fn [<$field _set>](index: u8) {
UDP::borrow_unchecked(|udp| {
udp.csr()[index as usize]
.modify(|_, w| csr_no_change(w).$field().set_bit());
while !udp.csr()[index as usize].read().$field().bit() {}
});
}
}
};
(
$field:ident,
clear
) => {
paste! {
pub fn [<$field _clear>](index: u8) {
UDP::borrow_unchecked(|udp| {
udp.csr()[index as usize]
.modify(|_, w| csr_no_change(w).$field().clear_bit());
while udp.csr()[index as usize].read().$field().bit() {}
});
}
}
};
(
$field:ident,
$eptype_dir:ident,
eptype
) => {
paste! {
pub fn [<$field _ $eptype_dir>](index: u8) {
UDP::borrow_unchecked(|udp| {
udp.csr()[index as usize]
.modify(|_, w| csr_no_change(w).$field().$eptype_dir());
while !udp.csr()[index as usize].read().$field().[<is_ $eptype_dir>]() {}
});
}
}
};
}
fn csr_clear(index: u8) {
UDP::borrow_unchecked(|udp| {
udp.csr()[index as usize].modify(|_, w| {
csr_no_change(w)
.dir()
.clear_bit()
.epeds()
.clear_bit()
.forcestall()
.clear_bit()
.txpktrdy()
.clear_bit()
.eptype()
.ctrl()
});
loop {
let reg = udp.csr()[index as usize].read();
if !reg.dir().bit()
&& !reg.epeds().bit()
&& !reg.forcestall().bit()
&& !reg.txpktrdy().bit()
&& reg.eptype().is_ctrl()
{
break;
}
}
});
}
fn csr_no_change(w: &mut csr::W) -> &mut csr::W {
w.rx_data_bk0()
.set_bit()
.rx_data_bk1()
.set_bit()
.stallsent()
.set_bit()
.rxsetup()
.set_bit()
.txcomp()
.set_bit()
}
csr_wait!(dir, set);
csr_wait!(epeds, set);
csr_wait!(forcestall, set);
csr_wait!(txpktrdy, set);
csr_wait!(dir, clear);
csr_wait!(epeds, clear);
csr_wait!(forcestall, clear);
csr_wait!(rx_data_bk0, clear);
csr_wait!(rx_data_bk1, clear);
csr_wait!(rxsetup, clear);
csr_wait!(stallsent, clear);
csr_wait!(txcomp, clear);
csr_wait!(txpktrdy, clear);
csr_wait!(eptype, bulk_in, eptype);
csr_wait!(eptype, bulk_out, eptype);
csr_wait!(eptype, ctrl, eptype);
csr_wait!(eptype, int_in, eptype);
csr_wait!(eptype, int_out, eptype);
csr_wait!(eptype, iso_in, eptype);
csr_wait!(eptype, iso_out, eptype);
impl Endpoint {
pub fn new(index: u8) -> Self {
Self {
index,
interval: 1,
max_packet_size: 8,
ep_type: EndpointType::Interrupt,
ep_dir: UsbDirection::Out,
next_bank: NextBank::Bank0,
allocated: false,
txbanks_free: 0,
stalled: false,
next_out_zlp: false,
}
}
pub fn alloc(
&mut self,
ep_type: EndpointType,
ep_dir: UsbDirection,
max_packet_size: u16,
interval: u8,
) -> usb_device::Result<EndpointAddress> {
let address = EndpointAddress::from_parts(self.index as usize, ep_dir);
if ep_type != EndpointType::Control && self.index == 0 {
return Err(usb_device::UsbError::InvalidEndpoint);
}
if self.allocated {
if ep_type == EndpointType::Control {
defmt::trace!(
"{} Endpoint{}::alloc() -> {:?}",
frm_num(),
self.index,
address,
);
return Ok(address);
}
return Err(usb_device::UsbError::InvalidEndpoint);
}
defmt::trace!(
"{} Endpoint{}::alloc({:?}, {:?}, {}, {})",
frm_num(),
self.index,
ep_type,
ep_dir,
max_packet_size,
interval
);
self.reset();
self.max_packet_size = max_packet_size;
if max_packet_size > self.max_packet_size() {
return Err(usb_device::UsbError::EndpointMemoryOverflow);
}
match ep_type {
EndpointType::Bulk => {}
EndpointType::Control => {
if self.dual_bank() {
return Err(usb_device::UsbError::Unsupported);
}
}
EndpointType::Interrupt => {}
EndpointType::Isochronous => {
if !self.dual_bank() {
return Err(usb_device::UsbError::Unsupported);
}
}
}
self.ep_type = ep_type;
self.ep_dir = ep_dir;
self.txbanks_free = if self.dual_bank() { 2 } else { 1 };
self.allocated = true;
self.interval = interval;
defmt::trace!(
"{} Endpoint{}::alloc() -> {:?}",
frm_num(),
self.index,
address,
);
Ok(address)
}
pub fn address(&self) -> EndpointAddress {
EndpointAddress::from_parts(self.index as usize, self.ep_dir)
}
pub fn max_packet_size(&self) -> u16 {
let hardware = match self.index {
0..=3 | 6 | 7 => 64,
4 | 5 => 512, _ => 0, };
core::cmp::min(hardware, self.max_packet_size)
}
fn dual_bank(&self) -> bool {
!matches!(self.index, 0 | 3)
}
fn interrupt(&self) -> bool {
let isr = UDP::borrow_unchecked(|udp| udp.isr.read());
match self.index {
0 => isr.ep0int().bit(),
1 => isr.ep1int().bit(),
2 => isr.ep2int().bit(),
3 => isr.ep3int().bit(),
4 => isr.ep4int().bit(),
5 => isr.ep5int().bit(),
6 => isr.ep6int().bit(),
7 => isr.ep7int().bit(),
_ => false, }
}
fn interrupt_enabled(&self) -> bool {
let imr = UDP::borrow_unchecked(|udp| udp.imr.read());
match self.index {
0 => imr.ep0int().bit(),
1 => imr.ep1int().bit(),
2 => imr.ep2int().bit(),
3 => imr.ep3int().bit(),
4 => imr.ep4int().bit(),
5 => imr.ep5int().bit(),
6 => imr.ep6int().bit(),
7 => imr.ep7int().bit(),
_ => false, }
}
fn interrupt_set(&self, enable: bool) {
unsafe {
UDP::borrow_unchecked(|udp| {
if enable {
match self.index {
0 => udp.ier.write_with_zero(|w| w.ep0int().set_bit()),
1 => udp.ier.write_with_zero(|w| w.ep1int().set_bit()),
2 => udp.ier.write_with_zero(|w| w.ep2int().set_bit()),
3 => udp.ier.write_with_zero(|w| w.ep3int().set_bit()),
4 => udp.ier.write_with_zero(|w| w.ep4int().set_bit()),
5 => udp.ier.write_with_zero(|w| w.ep5int().set_bit()),
6 => udp.ier.write_with_zero(|w| w.ep6int().set_bit()),
7 => udp.ier.write_with_zero(|w| w.ep7int().set_bit()),
_ => {} }
} else {
match self.index {
0 => udp.idr.write_with_zero(|w| w.ep0int().set_bit()),
1 => udp.idr.write_with_zero(|w| w.ep1int().set_bit()),
2 => udp.idr.write_with_zero(|w| w.ep2int().set_bit()),
3 => udp.idr.write_with_zero(|w| w.ep3int().set_bit()),
4 => udp.idr.write_with_zero(|w| w.ep4int().set_bit()),
5 => udp.idr.write_with_zero(|w| w.ep5int().set_bit()),
6 => udp.idr.write_with_zero(|w| w.ep6int().set_bit()),
7 => udp.idr.write_with_zero(|w| w.ep7int().set_bit()),
_ => {} }
}
});
}
}
pub fn interval(&self) -> u8 {
self.interval
}
pub fn stall(&mut self) {
if !self.stalled {
defmt::debug!("{} Endpoint{}::stall()", frm_num(), self.index);
forcestall_set(self.index);
self.stalled = true;
}
}
pub fn unstall(&mut self) {
defmt::trace!("{} Endpoint{}::unstall()", frm_num(), self.index);
forcestall_clear(self.index);
self.stalled = false;
self.txbanks_free = if self.dual_bank() { 2 } else { 1 };
}
pub fn is_stalled(&self) -> bool {
self.stalled
}
pub fn enable(&self) {
if self.allocated {
epeds_set(self.index);
}
}
pub fn disable(&self) {
epeds_clear(self.index);
}
pub fn clear_fifo(&self) {
UDP::borrow_unchecked(|udp| {
match self.index {
0 => clear_ep!(udp, ep0, 0),
1 => clear_ep!(udp, ep1, 1),
2 => clear_ep!(udp, ep2, 2),
3 => clear_ep!(udp, ep3, 3),
4 => clear_ep!(udp, ep4, 4),
5 => clear_ep!(udp, ep5, 5),
6 => clear_ep!(udp, ep6, 6),
7 => clear_ep!(udp, ep7, 7),
_ => {} }
});
}
pub fn reset(&mut self) {
if !self.allocated {
return;
}
csr_clear(self.index);
txpktrdy_set(self.index);
txpktrdy_clear(self.index);
if self.dual_bank() {
txpktrdy_set(self.index);
txpktrdy_clear(self.index);
}
self.clear_fifo();
self.txbanks_free = if self.dual_bank() { 2 } else { 1 };
match self.ep_type {
EndpointType::Bulk => match self.ep_dir {
UsbDirection::In => {
eptype_bulk_in(self.index);
}
UsbDirection::Out => {
eptype_bulk_out(self.index);
}
},
EndpointType::Control => {
eptype_ctrl(self.index);
dir_clear(self.index);
}
EndpointType::Interrupt => match self.ep_dir {
UsbDirection::In => {
eptype_int_in(self.index);
}
UsbDirection::Out => {
eptype_int_out(self.index);
}
},
EndpointType::Isochronous => match self.ep_dir {
UsbDirection::In => {
eptype_iso_in(self.index);
}
UsbDirection::Out => {
eptype_iso_out(self.index);
}
},
}
self.enable();
defmt::trace!(
"{} Endpoint{}::reset() CSR:{:X}",
frm_num(),
self.index,
UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read()).bits()
);
self.interrupt_set(true);
}
pub fn poll(&mut self) -> PollResult {
if !self.allocated {
return PollResult::None;
}
if !self.interrupt_enabled() && self.ep_type != EndpointType::Control {
return PollResult::None;
}
if self.interrupt() {
let csr = UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read());
if csr.stallsent().bit() {
stallsent_clear(self.index);
self.stalled = false;
defmt::debug!(
"{} Endpoint{}::Poll() -> Ack STALL CSR:{:X}",
frm_num(),
self.index,
UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read()).bits(),
);
return PollResult::None;
}
match self.ep_type {
EndpointType::Control => {
if csr.rxsetup().bit() {
defmt::debug!(
"{} Endpoint{}::Poll(Ctrl) -> SETUP CSR:{:X}",
frm_num(),
self.index,
UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read())
.bits()
);
return PollResult::Data {
ep_out: 0,
ep_in_complete: 0,
ep_setup: 1 << self.index,
};
}
if csr.txcomp().bit() {
defmt::debug!(
"{} Endpoint{}::Poll(Ctrl) -> IN CSR:{:X}",
frm_num(),
self.index,
UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read())
.bits()
);
txcomp_clear(self.index);
self.txbanks_free += 1;
return PollResult::Data {
ep_out: 0,
ep_in_complete: 1 << self.index,
ep_setup: 0,
};
}
if csr.rx_data_bk0().bit() {
if csr.rxbytecnt().bits() == 0 {
self.next_out_zlp = true;
rx_data_bk0_clear(self.index);
defmt::debug!(
"{} Endpoint{}::Poll(Ctrl) -> Status OUT CSR:{:X}",
frm_num(),
self.index,
UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read())
.bits()
);
} else {
defmt::debug!(
"{} Endpoint{}::Poll(Ctrl) -> OUT CSR:{:X}",
frm_num(),
self.index,
csr.bits(),
);
}
return PollResult::Data {
ep_out: 1 << self.index,
ep_in_complete: 0,
ep_setup: 0,
};
}
}
EndpointType::Bulk | EndpointType::Interrupt | EndpointType::Isochronous => {
let ep_out = if csr.rx_data_bk0().bit() || csr.rx_data_bk1().bit() {
defmt::trace!(
"{} Endpoint{}::Poll({:?}) -> OUT CSR:{:X}",
frm_num(),
self.index,
self.ep_type,
csr.bits()
);
1 << self.index
} else {
0
};
let ep_in_complete = if csr.txcomp().bit() {
txcomp_clear(self.index);
self.txbanks_free += 1;
defmt::trace!(
"{} Endpoint{}::Poll({:?}) -> IN CSR:{:X}",
frm_num(),
self.index,
self.ep_type,
UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read())
.bits()
);
1 << self.index
} else {
0
};
if (ep_in_complete | ep_out) > 0 {
return PollResult::Data {
ep_out,
ep_in_complete,
ep_setup: 0,
};
}
}
}
}
PollResult::None
}
pub fn write(&mut self, data: &[u8]) -> usb_device::Result<usize> {
if !self.allocated {
return Err(usb_device::UsbError::InvalidEndpoint);
}
let csr = UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read());
if self.txbanks_free == 0 || (self.txbanks_free == 1 && csr.txpktrdy().bit()) {
return Err(usb_device::UsbError::WouldBlock);
}
if data.len() > self.max_packet_size() as usize {
return Err(usb_device::UsbError::EndpointMemoryOverflow);
}
if csr.rx_data_bk0().bit() {
txcomp_clear(self.index);
return Err(usb_device::UsbError::InvalidState);
}
cortex_m::interrupt::free(|_| {
UDP::borrow_unchecked(|udp| {
for byte in data {
unsafe {
udp.fdr[self.index as usize].write_with_zero(|w| w.fifo_data().bits(*byte))
}
}
});
self.txbanks_free -= 1;
txpktrdy_set(self.index);
});
defmt::debug!(
"{} Endpoint{}::write() -> {} CSR:{:X} Banks:{}",
frm_num(),
self.index,
data.len(),
UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read()).bits(),
self.txbanks_free,
);
Ok(data.len())
}
pub fn read(&mut self, data: &mut [u8]) -> usb_device::Result<usize> {
let csr = UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read());
defmt::debug!(
"{} Endpoint{}::read() CSR:{:X}",
frm_num(),
self.index,
csr.bits()
);
if !self.allocated {
return Err(usb_device::UsbError::InvalidEndpoint);
}
if csr.eptype().variant() == Some(EPTYPE_A::CTRL) {
if self.next_out_zlp {
self.next_out_zlp = false;
return Ok(0);
}
if csr.rxsetup().bit() {
let rxbytes = csr.rxbytecnt().bits() as usize;
if rxbytes > data.len() {
rxsetup_clear(self.index);
return Err(usb_device::UsbError::BufferOverflow);
}
if rxbytes != 8 {
rxsetup_clear(self.index);
return Err(usb_device::UsbError::InvalidState);
}
for byte in data.iter_mut().take(rxbytes) {
*byte = UDP::borrow_unchecked(|udp| {
udp.fdr[self.index as usize].read().fifo_data().bits()
});
}
let dir: UsbDirection = data[0].into();
if dir == UsbDirection::In {
dir_set(self.index);
} else {
dir_clear(self.index);
}
rxsetup_clear(self.index);
defmt::debug!(
"{} Endpoint{}::read({}, {:02X}) SETUP {:?} CSR:{:X}",
frm_num(),
self.index,
rxbytes,
&data[0..rxbytes],
dir,
UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read()).bits()
);
return Ok(rxbytes);
}
if csr.rx_data_bk0().bit() {
let rxbytes = csr.rxbytecnt().bits() as usize;
if rxbytes > data.len() {
rx_data_bk0_clear(self.index);
return Err(usb_device::UsbError::BufferOverflow);
}
for byte in data.iter_mut().take(rxbytes) {
*byte = UDP::borrow_unchecked(|udp| {
udp.fdr[self.index as usize].read().fifo_data().bits()
});
}
rx_data_bk0_clear(self.index);
defmt::debug!(
"{} Endpoint{}::read({}, {:02X}) OUT CSR:{:X}",
frm_num(),
self.index,
rxbytes,
&data[0..rxbytes],
UDP::borrow_unchecked(|udp| udp.csr()[self.index as usize].read()).bits()
);
return Ok(rxbytes);
}
return Err(usb_device::UsbError::WouldBlock);
}
match csr.eptype().variant() {
Some(EPTYPE_A::BULK_OUT) | Some(EPTYPE_A::INT_OUT) | Some(EPTYPE_A::ISO_OUT) => {}
_ => {
return Err(usb_device::UsbError::InvalidEndpoint);
}
}
let bank = if csr.rx_data_bk0().bit() && csr.rx_data_bk1().bit() {
self.next_bank
} else if csr.rx_data_bk0().bit() {
NextBank::Bank0
} else if self.dual_bank() && csr.rx_data_bk1().bit() {
NextBank::Bank1
} else {
return Err(usb_device::UsbError::WouldBlock);
};
let rxbytes = csr.rxbytecnt().bits() as usize;
if rxbytes > data.len() {
match bank {
NextBank::Bank0 => {
rx_data_bk0_clear(self.index);
self.next_bank = NextBank::Bank1;
}
NextBank::Bank1 => {
rx_data_bk1_clear(self.index);
self.next_bank = NextBank::Bank0;
}
}
return Err(usb_device::UsbError::BufferOverflow);
}
UDP::borrow_unchecked(|udp| {
for byte in data.iter_mut().take(rxbytes) {
*byte = udp.fdr[self.index as usize].read().fifo_data().bits();
}
});
match bank {
NextBank::Bank0 => {
rx_data_bk0_clear(self.index);
self.next_bank = NextBank::Bank1;
}
NextBank::Bank1 => {
rx_data_bk1_clear(self.index);
self.next_bank = NextBank::Bank0;
}
}
Ok(rxbytes)
}
}