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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
use crate::bus::UsbBus;
use crate::{Result, UsbDirection};
use core::marker::PhantomData;
use portable_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>;

/// Isochronous transfers employ one of three synchronization schemes. See USB 2.0 spec 5.12.4.1.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum IsochronousSynchronizationType {
    /// Synchronization is not implemented for this endpoint.
    NoSynchronization,
    /// Source and Sink sample clocks are free running.
    Asynchronous,
    /// Source sample clock is locked to Sink, Sink sample clock is locked to data flow.
    Adaptive,
    /// Source and Sink sample clocks are locked to USB SOF.
    Synchronous,
}

/// Intended use of an isochronous endpoint, see USB 2.0 spec sections 5.12 and 9.6.6.
/// Associations between data and feedback endpoints are described in section 9.6.6.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum IsochronousUsageType {
    /// Endpoint is used for isochronous data.
    Data,
    /// Feedback for synchronization.
    Feedback,
    /// Endpoint is data and provides implicit feedback for synchronization.
    ImplicitFeedbackData,
}

/// USB endpoint transfer type.
#[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,
    /// Isochronous endpoint. Used for time-critical unreliable data.
    ///
    /// See USB 2.0 spec section 5.12 "Special Considerations for Isochronous Transfers"
    Isochronous {
        /// Synchronization model used for the data stream that this endpoint relates to.
        synchronization: IsochronousSynchronizationType,
        /// Endpoint's role in the synchronization model selected by [Self::Isochronous::synchronization].
        usage: IsochronousUsageType,
    },
    /// Bulk endpoint. Used for large amounts of best-effort reliable data.
    Bulk,
    /// Interrupt endpoint. Used for small amounts of time-critical reliable data.
    Interrupt,
}

impl EndpointType {
    /// Format EndpointType for use in bmAttributes transfer type field USB 2.0 spec section 9.6.6
    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,
        }
    }
}

/// 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
    }
}