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}