use embassy_usb_driver::{
Direction, EndpointAddress, EndpointType, Speed,
host::{HostError, PipeError},
};
use mpfs_hal::pac;
pub const NUM_ENDPOINTS: usize = 16;
pub const MAX_FIFO_SIZE: u16 = 4096;
pub const OUT_ENDPOINTS_START_ADDR: u16 = 0x0000;
pub const IN_ENDPOINTS_START_ADDR: u16 = 0x8000;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum EndpointState {
Idle,
Rx,
Tx,
TxLast,
TxReadyForNext,
Setup,
RxComplete(Result<usize, PipeError>),
TxComplete,
Disabled,
}
impl Default for EndpointState {
fn default() -> Self {
EndpointState::Disabled
}
}
#[cfg(feature = "alloc")]
mod ep {
extern crate alloc;
use super::{EndpointState, MAX_FIFO_SIZE, default_ep};
use aligned::{A4, Aligned};
use alloc::boxed::Box;
use core::task::Waker;
use mpfs_hal::pac;
#[derive(Debug)]
pub struct EndpointController {
pub ep: pac::mss_usb_ep_t,
pub waker: Option<Waker>,
pub state: EndpointState,
pub buffer: Option<Box<Aligned<A4, [u8; MAX_FIFO_SIZE as usize]>>>,
}
impl EndpointController {
pub fn default() -> Self {
EndpointController {
ep: default_ep(),
waker: None,
state: EndpointState::Disabled,
buffer: None,
}
}
pub fn buffer_addr(&mut self) -> &mut [u8] {
if self.buffer.is_none() {
self.buffer = Some(Box::new(Aligned::<A4, _>([0; MAX_FIFO_SIZE as usize])));
}
self.buffer.as_mut().unwrap().as_mut_slice()
}
}
}
#[cfg(not(feature = "alloc"))]
mod ep {
use super::{EndpointState, default_ep};
use core::task::Waker;
use mpfs_hal::pac;
#[derive(Debug)]
pub struct EndpointController {
pub ep: pac::mss_usb_ep_t,
pub waker: Option<Waker>,
pub state: EndpointState,
}
impl EndpointController {
pub fn default() -> Self {
EndpointController {
ep: default_ep(),
waker: None,
state: EndpointState::Disabled,
}
}
pub fn buffer_addr(&mut self) -> &mut [u8] {
panic!("USB buffers that aren't 4-byte aligned are not supported without alloc");
}
}
}
pub use ep::*;
#[derive(Default, Copy, Clone)]
pub struct EndpointDetails {
pub used: bool,
pub fifo_addr: u16,
pub fifo_size: u16,
}
pub fn alloc_fifo_addr(
max_packet_size: u16,
endpoints: &mut [EndpointDetails],
endpoint_addr: Option<EndpointAddress>,
) -> Result<usize, HostError> {
assert!(
endpoint_addr.is_none(),
"Explicit endpoint address not implemented"
);
if max_packet_size > MAX_FIFO_SIZE {
return Err(HostError::OutOfPipes);
}
let mut fifo_size = max_packet_size.next_power_of_two();
if fifo_size < 1024 {
fifo_size = (fifo_size + 1).next_power_of_two();
}
if let Some(i) = endpoints.iter().position(|x| !x.used) {
let fifo_addr = if i == 0 {
0
} else {
endpoints[i - 1].fifo_addr + endpoints[i - 1].fifo_size
};
if fifo_addr + fifo_size > IN_ENDPOINTS_START_ADDR {
error!(
"FIFO address is out of bounds for out endpoint {:?} with size {:?}",
i,
max_packet_size
);
return Err(HostError::OutOfPipes);
}
endpoints[i] = EndpointDetails {
used: true,
fifo_addr,
fifo_size,
};
Ok(i)
} else {
Err(HostError::OutOfPipes)
}
}
pub fn configure_endpoint_controller(
direction: Direction,
endpoint: usize,
endpoint_type: EndpointType,
max_packet_size: u16,
endpoint_details: EndpointDetails,
speed: Speed,
) -> EndpointController {
let std_max_pkt_sz = if speed == Speed::High {
match endpoint_type {
EndpointType::Bulk => pac::USB_HS_BULK_MAX_PKT_SIZE,
EndpointType::Interrupt => pac::USB_HS_INTERRUPT_MAX_PKT_SIZE,
EndpointType::Isochronous => pac::USB_HS_ISO_MAX_PKT_SIZE,
EndpointType::Control => pac::USB_HS_BULK_MAX_PKT_SIZE, }
} else if speed == Speed::Full {
match endpoint_type {
EndpointType::Bulk => pac::USB_FS_BULK_MAX_PKT_SIZE,
EndpointType::Interrupt => pac::USB_FS_INTERRUPT_MAX_PKT_SIZE,
EndpointType::Isochronous => pac::USB_FS_ISO_MAX_PKT_SIZE,
EndpointType::Control => pac::USB_FS_BULK_MAX_PKT_SIZE,
}
} else {
panic!("Unsupported speed: {:?}", speed);
};
if max_packet_size > std_max_pkt_sz as u16 {
panic!(
"Max packet size is greater than the allowed max packet size for this transfer type {} expected for {:?}, got {}",
std_max_pkt_sz, endpoint_type, max_packet_size
);
}
let fifo_addr = endpoint_details.fifo_addr
+ if direction == Direction::Out {
OUT_ENDPOINTS_START_ADDR
} else {
IN_ENDPOINTS_START_ADDR
};
let dma_channel = endpoint_to_dma_channel(endpoint, direction);
let dma_enable = dma_channel != pac::mss_usb_dma_channel_t_MSS_USB_DMA_CHANNEL_NA;
let dpb_enable = max_packet_size * 2 <= endpoint_details.fifo_size;
let ep = pac::mss_usb_ep_t {
num: endpoint as u32,
xfr_type: mss_transfer_type(endpoint_type),
max_pkt_size: max_packet_size,
fifo_size: endpoint_details.fifo_size,
fifo_addr,
dma_enable: dma_enable as u8,
dpb_enable: dpb_enable as u8,
dma_channel,
..default_ep()
};
EndpointController {
ep,
..EndpointController::default()
}
}
pub fn mss_transfer_type(endpoint_type: EndpointType) -> pac::mss_usb_xfr_type_t {
match endpoint_type {
EndpointType::Bulk => pac::mss_usb_xfr_type_t_MSS_USB_XFR_BULK,
EndpointType::Interrupt => pac::mss_usb_xfr_type_t_MSS_USB_XFR_INTERRUPT,
EndpointType::Isochronous => pac::mss_usb_xfr_type_t_MSS_USB_XFR_ISO,
EndpointType::Control => pac::mss_usb_xfr_type_t_MSS_USB_XFR_CONTROL,
}
}
fn endpoint_to_dma_channel(endpoint: usize, direction: Direction) -> pac::mss_usb_dma_channel_t {
match (endpoint, direction) {
(1, Direction::In) => pac::mss_usb_dma_channel_t_MSS_USB_DMA_CHANNEL1,
(2, Direction::In) => pac::mss_usb_dma_channel_t_MSS_USB_DMA_CHANNEL2,
(1, Direction::Out) => pac::mss_usb_dma_channel_t_MSS_USB_DMA_CHANNEL3,
(2, Direction::Out) => pac::mss_usb_dma_channel_t_MSS_USB_DMA_CHANNEL4,
_ => pac::mss_usb_dma_channel_t_MSS_USB_DMA_CHANNEL_NA,
}
}
pub fn speed_to_mss_value(speed: Speed) -> pac::mss_usb_device_speed_t {
match speed {
Speed::Full => pac::mss_usb_device_speed_t_MSS_USB_DEVICE_FS,
Speed::High => pac::mss_usb_device_speed_t_MSS_USB_DEVICE_HS,
Speed::Low => pac::mss_usb_device_speed_t_MSS_USB_DEVICE_LS,
}
}
pub const fn default_ep() -> pac::mss_usb_ep_t {
pac::mss_usb_ep_t {
num: 0,
xfr_type: pac::mss_usb_xfr_type_t_MSS_USB_XFR_CONTROL,
max_pkt_size: 0,
fifo_size: 0,
fifo_addr: 0,
dma_enable: 0,
dpb_enable: 0,
dma_channel: pac::mss_usb_dma_channel_t_MSS_USB_DMA_CHANNEL_NA,
buf_addr: core::ptr::null_mut(),
txn_length: 0,
xfr_length: 0,
txn_count: 0,
xfr_count: 0,
state: pac::mss_usb_ep_state_t_MSS_USB_EP_VALID,
add_zlp: 0, num_usb_pkt: 1, cep_data_dir: 0,
cep_cmd_addr: core::ptr::null_mut(),
disable_ping: 0,
interval: 0,
stall: 0,
req_pkt_n: 0,
tdev_idx: 0,
}
}