1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
use crate::bus::UsbBus;
use crate::{Result, UsbDirection};
use core::marker::PhantomData;
use core::sync::atomic::{AtomicPtr, Ordering};
/// Trait for endpoint type markers.
pub trait EndpointDirection {
/// Direction value of the marker type.
const DIRECTION: UsbDirection;
}
/// Marker type for OUT endpoints.
pub struct Out;
impl EndpointDirection for Out {
const DIRECTION: UsbDirection = UsbDirection::Out;
}
/// Marker type for IN endpoints.
pub struct In;
impl EndpointDirection for In {
const DIRECTION: UsbDirection = UsbDirection::In;
}
/// A host-to-device (OUT) endpoint.
pub type EndpointOut<'a, B> = Endpoint<'a, B, Out>;
/// A device-to-host (IN) endpoint.
pub type EndpointIn<'a, B> = Endpoint<'a, B, In>;
/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the
/// transfer bmAttributes transfer type bits.
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum EndpointType {
/// Control endpoint. Used for device management. Only the host can initiate requests. Usually
/// used only endpoint 0.
Control = 0b00,
/// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet.
Isochronous = 0b01,
/// Bulk endpoint. Used for large amounts of best-effort reliable data.
Bulk = 0b10,
/// Interrupt endpoint. Used for small amounts of time-critical reliable data.
Interrupt = 0b11,
}
/// Handle for a USB endpoint. The endpoint direction is constrained by the `D` type argument, which
/// must be either `In` or `Out`.
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 }
}
/// Gets the endpoint address including direction bit.
pub fn address(&self) -> EndpointAddress {
self.address
}
/// Gets the endpoint transfer type.
pub fn ep_type(&self) -> EndpointType {
self.ep_type
}
/// Gets the maximum packet size for the endpoint.
pub fn max_packet_size(&self) -> u16 {
self.max_packet_size
}
/// Gets the poll interval for interrupt endpoints.
pub fn interval(&self) -> u8 {
self.interval
}
/// Sets the STALL condition for the endpoint.
pub fn stall(&self) {
self.bus().set_stalled(self.address, true);
}
/// Clears the STALL condition of the endpoint.
pub fn unstall(&self) {
self.bus().set_stalled(self.address, false);
}
}
impl<B: UsbBus> Endpoint<'_, B, In> {
/// Writes a single packet of data to the specified endpoint and returns number of bytes
/// actually written. The buffer must not be longer than the `max_packet_size` specified when
/// allocating the endpoint.
///
/// # Errors
///
/// Note: USB bus implementation errors are directly passed through, so be prepared to handle
/// other errors as well.
///
/// * [`WouldBlock`](crate::UsbError::WouldBlock) - The transmission buffer of the USB
/// peripheral is full and the packet cannot be sent now. A peripheral may or may not support
/// concurrent transmission of packets.
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The data is longer than the
/// `max_packet_size` specified when allocating the endpoint. This is generally an error in
/// the class implementation.
pub fn write(&self, data: &[u8]) -> Result<usize> {
self.bus().write(self.address, data)
}
}
impl<B: UsbBus> Endpoint<'_, B, Out> {
/// Reads a single packet of data from the specified endpoint and returns the actual length of
/// the packet. The buffer should be large enough to fit at least as many bytes as the
/// `max_packet_size` specified when allocating the endpoint.
///
/// # Errors
///
/// Note: USB bus implementation errors are directly passed through, so be prepared to handle
/// other errors as well.
///
/// * [`WouldBlock`](crate::UsbError::WouldBlock) - There is no packet to be read. Note that
/// this is different from a received zero-length packet, which is valid and significant in
/// USB. A zero-length packet will return `Ok(0)`.
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The received packet is too long to
/// fit in `data`. This is generally an error in the class implementation.
pub fn read(&self, data: &mut [u8]) -> Result<usize> {
self.bus().read(self.address, data)
}
}
/// Type-safe endpoint address.
#[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;
/// Constructs a new EndpointAddress with the given index and direction.
#[inline]
pub fn from_parts(index: usize, dir: UsbDirection) -> Self {
EndpointAddress(index as u8 | dir as u8)
}
/// Gets the direction part of the address.
#[inline]
pub fn direction(&self) -> UsbDirection {
if (self.0 & Self::INBITS) != 0 {
UsbDirection::In
} else {
UsbDirection::Out
}
}
/// Returns true if the direction is IN, otherwise false.
#[inline]
pub fn is_in(&self) -> bool {
(self.0 & Self::INBITS) != 0
}
/// Returns true if the direction is OUT, otherwise false.
#[inline]
pub fn is_out(&self) -> bool {
(self.0 & Self::INBITS) == 0
}
/// Gets the index part of the endpoint address.
#[inline]
pub fn index(&self) -> usize {
(self.0 & !Self::INBITS) as usize
}
}