usb_device/
endpoint.rs

1use crate::bus::UsbBus;
2use crate::{Result, UsbDirection};
3use core::marker::PhantomData;
4use portable_atomic::{AtomicPtr, Ordering};
5
6/// Trait for endpoint type markers.
7pub trait EndpointDirection {
8    /// Direction value of the marker type.
9    const DIRECTION: UsbDirection;
10}
11
12/// Marker type for OUT endpoints.
13pub struct Out;
14
15impl EndpointDirection for Out {
16    const DIRECTION: UsbDirection = UsbDirection::Out;
17}
18
19/// Marker type for IN endpoints.
20pub struct In;
21
22impl EndpointDirection for In {
23    const DIRECTION: UsbDirection = UsbDirection::In;
24}
25
26/// A host-to-device (OUT) endpoint.
27pub type EndpointOut<'a, B> = Endpoint<'a, B, Out>;
28
29/// A device-to-host (IN) endpoint.
30pub type EndpointIn<'a, B> = Endpoint<'a, B, In>;
31
32/// Isochronous transfers employ one of three synchronization schemes. See USB 2.0 spec 5.12.4.1.
33#[derive(Copy, Clone, Eq, PartialEq, Debug)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub enum IsochronousSynchronizationType {
36    /// Synchronization is not implemented for this endpoint.
37    NoSynchronization,
38    /// Source and Sink sample clocks are free running.
39    Asynchronous,
40    /// Source sample clock is locked to Sink, Sink sample clock is locked to data flow.
41    Adaptive,
42    /// Source and Sink sample clocks are locked to USB SOF.
43    Synchronous,
44}
45
46/// Intended use of an isochronous endpoint, see USB 2.0 spec sections 5.12 and 9.6.6.
47/// Associations between data and feedback endpoints are described in section 9.6.6.
48#[derive(Copy, Clone, Eq, PartialEq, Debug)]
49#[cfg_attr(feature = "defmt", derive(defmt::Format))]
50pub enum IsochronousUsageType {
51    /// Endpoint is used for isochronous data.
52    Data,
53    /// Feedback for synchronization.
54    Feedback,
55    /// Endpoint is data and provides implicit feedback for synchronization.
56    ImplicitFeedbackData,
57}
58
59/// USB endpoint transfer type.
60#[derive(Copy, Clone, Eq, PartialEq, Debug)]
61#[cfg_attr(feature = "defmt", derive(defmt::Format))]
62pub enum EndpointType {
63    /// Control endpoint. Used for device management. Only the host can initiate requests. Usually
64    /// used only endpoint 0.
65    Control,
66    /// Isochronous endpoint. Used for time-critical unreliable data.
67    ///
68    /// See USB 2.0 spec section 5.12 "Special Considerations for Isochronous Transfers"
69    Isochronous {
70        /// Synchronization model used for the data stream that this endpoint relates to.
71        synchronization: IsochronousSynchronizationType,
72        /// Endpoint's role in the synchronization model selected by [Self::Isochronous::synchronization].
73        usage: IsochronousUsageType,
74    },
75    /// Bulk endpoint. Used for large amounts of best-effort reliable data.
76    Bulk,
77    /// Interrupt endpoint. Used for small amounts of time-critical reliable data.
78    Interrupt,
79}
80
81impl EndpointType {
82    /// Format EndpointType for use in bmAttributes transfer type field USB 2.0 spec section 9.6.6
83    pub fn to_bm_attributes(&self) -> u8 {
84        match self {
85            EndpointType::Control => 0b00,
86            EndpointType::Isochronous {
87                synchronization,
88                usage,
89            } => {
90                let sync_bits = match synchronization {
91                    IsochronousSynchronizationType::NoSynchronization => 0b00,
92                    IsochronousSynchronizationType::Asynchronous => 0b01,
93                    IsochronousSynchronizationType::Adaptive => 0b10,
94                    IsochronousSynchronizationType::Synchronous => 0b11,
95                };
96                let usage_bits = match usage {
97                    IsochronousUsageType::Data => 0b00,
98                    IsochronousUsageType::Feedback => 0b01,
99                    IsochronousUsageType::ImplicitFeedbackData => 0b10,
100                };
101                (usage_bits << 4) | (sync_bits << 2) | 0b01
102            }
103            EndpointType::Bulk => 0b10,
104            EndpointType::Interrupt => 0b11,
105        }
106    }
107}
108
109/// Handle for a USB endpoint. The endpoint direction is constrained by the `D` type argument, which
110/// must be either `In` or `Out`.
111pub struct Endpoint<'a, B: UsbBus, D: EndpointDirection> {
112    bus_ptr: &'a AtomicPtr<B>,
113    address: EndpointAddress,
114    ep_type: EndpointType,
115    max_packet_size: u16,
116    interval: u8,
117    _marker: PhantomData<D>,
118}
119
120impl<B: UsbBus, D: EndpointDirection> Endpoint<'_, B, D> {
121    pub(crate) fn new(
122        bus_ptr: &AtomicPtr<B>,
123        address: EndpointAddress,
124        ep_type: EndpointType,
125        max_packet_size: u16,
126        interval: u8,
127    ) -> Endpoint<'_, B, D> {
128        Endpoint {
129            bus_ptr,
130            address,
131            ep_type,
132            max_packet_size,
133            interval,
134            _marker: PhantomData,
135        }
136    }
137
138    fn bus(&self) -> &B {
139        let bus_ptr = self.bus_ptr.load(Ordering::SeqCst);
140        if bus_ptr.is_null() {
141            panic!("UsbBus initialization not complete");
142        }
143
144        unsafe { &*bus_ptr }
145    }
146
147    /// Gets the endpoint address including direction bit.
148    pub fn address(&self) -> EndpointAddress {
149        self.address
150    }
151
152    /// Gets the endpoint transfer type.
153    pub fn ep_type(&self) -> EndpointType {
154        self.ep_type
155    }
156
157    /// Gets the maximum packet size for the endpoint.
158    pub fn max_packet_size(&self) -> u16 {
159        self.max_packet_size
160    }
161
162    /// Gets the poll interval for interrupt endpoints.
163    pub fn interval(&self) -> u8 {
164        self.interval
165    }
166
167    /// Sets the STALL condition for the endpoint.
168    pub fn stall(&self) {
169        self.bus().set_stalled(self.address, true);
170    }
171
172    /// Clears the STALL condition of the endpoint.
173    pub fn unstall(&self) {
174        self.bus().set_stalled(self.address, false);
175    }
176}
177
178impl<B: UsbBus> Endpoint<'_, B, In> {
179    /// Writes a single packet of data to the specified endpoint and returns number of bytes
180    /// actually written. The buffer must not be longer than the `max_packet_size` specified when
181    /// allocating the endpoint.
182    ///
183    /// # Errors
184    ///
185    /// Note: USB bus implementation errors are directly passed through, so be prepared to handle
186    /// other errors as well.
187    ///
188    /// * [`WouldBlock`](crate::UsbError::WouldBlock) - The transmission buffer of the USB
189    ///   peripheral is full and the packet cannot be sent now. A peripheral may or may not support
190    ///   concurrent transmission of packets.
191    /// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The data is longer than the
192    ///   `max_packet_size` specified when allocating the endpoint. This is generally an error in
193    ///   the class implementation.
194    pub fn write(&self, data: &[u8]) -> Result<usize> {
195        self.bus().write(self.address, data)
196    }
197}
198
199impl<B: UsbBus> Endpoint<'_, B, Out> {
200    /// Reads a single packet of data from the specified endpoint and returns the actual length of
201    /// the packet. The buffer should be large enough to fit at least as many bytes as the
202    /// `max_packet_size` specified when allocating the endpoint.
203    ///
204    /// # Errors
205    ///
206    /// Note: USB bus implementation errors are directly passed through, so be prepared to handle
207    /// other errors as well.
208    ///
209    /// * [`WouldBlock`](crate::UsbError::WouldBlock) - There is no packet to be read. Note that
210    ///   this is different from a received zero-length packet, which is valid and significant in
211    ///   USB. A zero-length packet will return `Ok(0)`.
212    /// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The received packet is too long to
213    ///   fit in `data`. This is generally an error in the class implementation.
214    pub fn read(&self, data: &mut [u8]) -> Result<usize> {
215        self.bus().read(self.address, data)
216    }
217}
218
219/// Type-safe endpoint address.
220#[derive(Debug, Clone, Copy, Eq, PartialEq)]
221#[cfg_attr(feature = "defmt", derive(defmt::Format))]
222pub struct EndpointAddress(u8);
223
224impl From<u8> for EndpointAddress {
225    #[inline]
226    fn from(addr: u8) -> EndpointAddress {
227        EndpointAddress(addr)
228    }
229}
230
231impl From<EndpointAddress> for u8 {
232    #[inline]
233    fn from(addr: EndpointAddress) -> u8 {
234        addr.0
235    }
236}
237
238impl EndpointAddress {
239    const INBITS: u8 = UsbDirection::In as u8;
240
241    /// Constructs a new EndpointAddress with the given index and direction.
242    #[inline]
243    pub fn from_parts(index: usize, dir: UsbDirection) -> Self {
244        EndpointAddress(index as u8 | dir as u8)
245    }
246
247    /// Gets the direction part of the address.
248    #[inline]
249    pub fn direction(&self) -> UsbDirection {
250        if (self.0 & Self::INBITS) != 0 {
251            UsbDirection::In
252        } else {
253            UsbDirection::Out
254        }
255    }
256
257    /// Returns true if the direction is IN, otherwise false.
258    #[inline]
259    pub fn is_in(&self) -> bool {
260        (self.0 & Self::INBITS) != 0
261    }
262
263    /// Returns true if the direction is OUT, otherwise false.
264    #[inline]
265    pub fn is_out(&self) -> bool {
266        (self.0 & Self::INBITS) == 0
267    }
268
269    /// Gets the index part of the endpoint address.
270    #[inline]
271    pub fn index(&self) -> usize {
272        (self.0 & !Self::INBITS) as usize
273    }
274}