use crate::bus::UsbBus;
use crate::{Result, UsbDirection};
use core::marker::PhantomData;
use portable_atomic::{AtomicPtr, Ordering};
pub trait EndpointDirection {
const DIRECTION: UsbDirection;
}
pub struct Out;
impl EndpointDirection for Out {
const DIRECTION: UsbDirection = UsbDirection::Out;
}
pub struct In;
impl EndpointDirection for In {
const DIRECTION: UsbDirection = UsbDirection::In;
}
pub type EndpointOut<'a, B> = Endpoint<'a, B, Out>;
pub type EndpointIn<'a, B> = Endpoint<'a, B, In>;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum IsochronousSynchronizationType {
NoSynchronization,
Asynchronous,
Adaptive,
Synchronous,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum IsochronousUsageType {
Data,
Feedback,
ImplicitFeedbackData,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum EndpointType {
Control,
Isochronous {
synchronization: IsochronousSynchronizationType,
usage: IsochronousUsageType,
},
Bulk,
Interrupt,
}
impl EndpointType {
pub fn to_bm_attributes(&self) -> u8 {
match self {
EndpointType::Control => 0b00,
EndpointType::Isochronous {
synchronization,
usage,
} => {
let sync_bits = match synchronization {
IsochronousSynchronizationType::NoSynchronization => 0b00,
IsochronousSynchronizationType::Asynchronous => 0b01,
IsochronousSynchronizationType::Adaptive => 0b10,
IsochronousSynchronizationType::Synchronous => 0b11,
};
let usage_bits = match usage {
IsochronousUsageType::Data => 0b00,
IsochronousUsageType::Feedback => 0b01,
IsochronousUsageType::ImplicitFeedbackData => 0b10,
};
(usage_bits << 4) | (sync_bits << 2) | 0b01
}
EndpointType::Bulk => 0b10,
EndpointType::Interrupt => 0b11,
}
}
}
pub struct Endpoint<'a, B: UsbBus, D: EndpointDirection> {
bus_ptr: &'a AtomicPtr<B>,
address: EndpointAddress,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
_marker: PhantomData<D>,
}
impl<B: UsbBus, D: EndpointDirection> Endpoint<'_, B, D> {
pub(crate) fn new(
bus_ptr: &AtomicPtr<B>,
address: EndpointAddress,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Endpoint<'_, B, D> {
Endpoint {
bus_ptr,
address,
ep_type,
max_packet_size,
interval,
_marker: PhantomData,
}
}
fn bus(&self) -> &B {
let bus_ptr = self.bus_ptr.load(Ordering::SeqCst);
if bus_ptr.is_null() {
panic!("UsbBus initialization not complete");
}
unsafe { &*bus_ptr }
}
pub fn address(&self) -> EndpointAddress {
self.address
}
pub fn ep_type(&self) -> EndpointType {
self.ep_type
}
pub fn max_packet_size(&self) -> u16 {
self.max_packet_size
}
pub fn interval(&self) -> u8 {
self.interval
}
pub fn stall(&self) {
self.bus().set_stalled(self.address, true);
}
pub fn unstall(&self) {
self.bus().set_stalled(self.address, false);
}
}
impl<B: UsbBus> Endpoint<'_, B, In> {
pub fn write(&self, data: &[u8]) -> Result<usize> {
self.bus().write(self.address, data)
}
}
impl<B: UsbBus> Endpoint<'_, B, Out> {
pub fn read(&self, data: &mut [u8]) -> Result<usize> {
self.bus().read(self.address, data)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointAddress(u8);
impl From<u8> for EndpointAddress {
#[inline]
fn from(addr: u8) -> EndpointAddress {
EndpointAddress(addr)
}
}
impl From<EndpointAddress> for u8 {
#[inline]
fn from(addr: EndpointAddress) -> u8 {
addr.0
}
}
impl EndpointAddress {
const INBITS: u8 = UsbDirection::In as u8;
#[inline]
pub fn from_parts(index: usize, dir: UsbDirection) -> Self {
EndpointAddress(index as u8 | dir as u8)
}
#[inline]
pub fn direction(&self) -> UsbDirection {
if (self.0 & Self::INBITS) != 0 {
UsbDirection::In
} else {
UsbDirection::Out
}
}
#[inline]
pub fn is_in(&self) -> bool {
(self.0 & Self::INBITS) != 0
}
#[inline]
pub fn is_out(&self) -> bool {
(self.0 & Self::INBITS) == 0
}
#[inline]
pub fn index(&self) -> usize {
(self.0 & !Self::INBITS) as usize
}
}