use core::marker::PhantomData;
use core::sync::atomic::{AtomicPtr, Ordering};
use core::ptr;
use crate::{Result, UsbDirection};
use crate::bus::UsbBus;
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>;
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum EndpointType {
Control = 0b00,
Isochronous = 0b01,
Bulk = 0b10,
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<'a>(
bus_ptr: &'a 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 == ptr::null_mut() {
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)]
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
}
}