use core::mem::{self, MaybeUninit};
use cortex_m::interrupt::{self, Mutex};
use usb_device::bus::{PollResult, UsbBusAllocator};
use usb_device::endpoint::{EndpointAddress, EndpointType};
use usb_device::{Result, UsbDirection, UsbError};
use crate::endpoint::{calculate_count_rx, Endpoint, EndpointStatus, NUM_ENDPOINTS};
use crate::endpoint_memory::EndpointMemoryAllocator;
use crate::registers::UsbRegisters;
use crate::UsbPeripheral;
pub struct UsbBus<USB> {
peripheral: USB,
regs: Mutex<UsbRegisters<USB>>,
endpoints: [Endpoint<USB>; NUM_ENDPOINTS],
ep_allocator: EndpointMemoryAllocator<USB>,
max_endpoint: usize,
}
impl<USB: UsbPeripheral> UsbBus<USB> {
pub fn new(peripheral: USB) -> UsbBusAllocator<Self> {
USB::enable();
let bus = UsbBus {
peripheral,
regs: Mutex::new(UsbRegisters::new()),
ep_allocator: EndpointMemoryAllocator::new(),
max_endpoint: 0,
endpoints: {
let mut endpoints: [MaybeUninit<Endpoint<USB>>; NUM_ENDPOINTS] =
unsafe { MaybeUninit::uninit().assume_init() };
for i in 0..NUM_ENDPOINTS {
endpoints[i] = MaybeUninit::new(Endpoint::new(i as u8));
}
unsafe { mem::transmute::<_, [Endpoint<USB>; NUM_ENDPOINTS]>(endpoints) }
},
};
UsbBusAllocator::new(bus)
}
pub fn free(self) -> USB {
self.peripheral
}
pub fn force_reenumeration<F: FnOnce()>(&self, disconnect: F) {
interrupt::free(|cs| {
let regs = self.regs.borrow(cs);
let pdwn = regs.cntr.read().pdwn().bit_is_set();
regs.cntr.modify(|_, w| w.pdwn().set_bit());
disconnect();
regs.cntr.modify(|_, w| w.pdwn().bit(pdwn));
});
}
pub fn suspend_low_power_mode(&self) -> bool {
interrupt::free(|cs| {
let regs = self.regs.borrow(cs);
if regs.cntr.read().fsusp().is_suspend() {
regs.cntr.modify(|_, w| w.lpmode().set_bit());
true
} else {
false
}
})
}
pub fn remote_wakeup(&self, resume: bool) {
interrupt::free(|cs| {
self.regs
.borrow(cs)
.cntr
.modify(|r, w| w.resume().bit(resume && r.fsusp().is_suspend()));
})
}
}
impl<USB: UsbPeripheral> usb_device::bus::UsbBus for UsbBus<USB> {
fn alloc_ep(
&mut self,
ep_dir: UsbDirection,
ep_addr: Option<EndpointAddress>,
ep_type: EndpointType,
max_packet_size: u16,
_interval: u8,
) -> Result<EndpointAddress> {
for index in ep_addr.map(|a| a.index()..a.index() + 1).unwrap_or(1..NUM_ENDPOINTS) {
let ep = &mut self.endpoints[index];
match ep.ep_type() {
None => {
ep.set_ep_type(ep_type);
}
Some(t) if t != ep_type => {
continue;
}
_ => {}
};
match ep_dir {
UsbDirection::Out if !ep.is_out_buf_set() => {
let (out_size, size_bits) = calculate_count_rx(max_packet_size as usize)?;
let buffer = self.ep_allocator.allocate_buffer(out_size)?;
ep.set_out_buf(buffer, size_bits);
return Ok(EndpointAddress::from_parts(index, ep_dir));
}
UsbDirection::In if !ep.is_in_buf_set() => {
let size = (max_packet_size as usize + 1) & !0x01;
let buffer = self.ep_allocator.allocate_buffer(size)?;
ep.set_in_buf(buffer);
return Ok(EndpointAddress::from_parts(index, ep_dir));
}
_ => {}
}
}
Err(match ep_addr {
Some(_) => UsbError::InvalidEndpoint,
None => UsbError::EndpointOverflow,
})
}
fn enable(&mut self) {
let mut max = 0;
for (index, ep) in self.endpoints.iter().enumerate() {
if ep.is_out_buf_set() || ep.is_in_buf_set() {
max = index;
}
}
self.max_endpoint = max;
interrupt::free(|cs| {
let regs = self.regs.borrow(cs);
regs.cntr.modify(|_, w| w.pdwn().clear_bit());
USB::startup_delay();
regs.btable.modify(|_, w| w.btable().bits(0));
regs.cntr.modify(|_, w| {
w.fres().clear_bit();
w.resetm().set_bit();
w.suspm().set_bit();
w.wkupm().set_bit();
w.ctrm().set_bit()
});
regs.istr.modify(|_, w| unsafe { w.bits(0) });
if USB::DP_PULL_UP_FEATURE {
regs.bcdr.modify(|_, w| w.dppu().set_bit());
}
});
}
fn reset(&self) {
interrupt::free(|cs| {
let regs = self.regs.borrow(cs);
regs.istr.modify(|_, w| unsafe { w.bits(0) });
regs.daddr.modify(|_, w| w.ef().set_bit().add().bits(0));
for ep in self.endpoints.iter() {
ep.configure(cs);
}
});
}
fn set_device_address(&self, addr: u8) {
interrupt::free(|cs| {
self.regs.borrow(cs).daddr.modify(|_, w| w.add().bits(addr as u8));
});
}
fn poll(&self) -> PollResult {
interrupt::free(|cs| {
let regs = self.regs.borrow(cs);
let istr = regs.istr.read();
if istr.wkup().bit_is_set() {
regs.istr.write(|w| unsafe { w.bits(0xffff) }.wkup().clear_bit());
regs.cntr.modify(|_, w| w.fsusp().clear_bit());
PollResult::Resume
} else if istr.reset().bit_is_set() {
regs.istr.write(|w| unsafe { w.bits(0xffff) }.reset().clear_bit());
PollResult::Reset
} else if istr.susp().bit_is_set() {
regs.istr.write(|w| unsafe { w.bits(0xffff) }.susp().clear_bit());
PollResult::Suspend
} else if istr.ctr().bit_is_set() {
let mut ep_out = 0;
let mut ep_in_complete = 0;
let mut ep_setup = 0;
let mut bit = 1;
for ep in &self.endpoints[0..=self.max_endpoint] {
let v = ep.read_reg();
if v.ctr_rx().bit_is_set() {
ep_out |= bit;
if v.setup().bit_is_set() {
ep_setup |= bit;
}
}
if v.ctr_tx().bit_is_set() {
ep_in_complete |= bit;
ep.clear_ctr_tx(cs);
}
bit <<= 1;
}
PollResult::Data {
ep_out,
ep_in_complete,
ep_setup,
}
} else {
PollResult::None
}
})
}
fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> Result<usize> {
if !ep_addr.is_in() {
return Err(UsbError::InvalidEndpoint);
}
self.endpoints[ep_addr.index()].write(buf)
}
fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> Result<usize> {
if !ep_addr.is_out() {
return Err(UsbError::InvalidEndpoint);
}
self.endpoints[ep_addr.index()].read(buf)
}
fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool) {
interrupt::free(|cs| {
if self.is_stalled(ep_addr) == stalled {
return;
}
let ep = &self.endpoints[ep_addr.index()];
match (stalled, ep_addr.direction()) {
(true, UsbDirection::In) => ep.set_stat_tx(cs, EndpointStatus::Stall),
(true, UsbDirection::Out) => ep.set_stat_rx(cs, EndpointStatus::Stall),
(false, UsbDirection::In) => ep.set_stat_tx(cs, EndpointStatus::Nak),
(false, UsbDirection::Out) => ep.set_stat_rx(cs, EndpointStatus::Valid),
};
});
}
fn is_stalled(&self, ep_addr: EndpointAddress) -> bool {
let ep = &self.endpoints[ep_addr.index()];
let reg_v = ep.read_reg();
let status = match ep_addr.direction() {
UsbDirection::In => reg_v.stat_tx().bits(),
UsbDirection::Out => reg_v.stat_rx().bits(),
};
status == (EndpointStatus::Stall as u8)
}
fn suspend(&self) {
interrupt::free(|cs| {
self.regs.borrow(cs).cntr.modify(|_, w| w.fsusp().set_bit());
});
}
fn resume(&self) {
interrupt::free(|cs| {
self.regs
.borrow(cs)
.cntr
.modify(|_, w| w.fsusp().clear_bit().lpmode().clear_bit());
});
}
}