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
//! Transfer-related types.
//!
//! Use the methods on an [`Interface`][`super::Interface`] to make individual
//! transfers or obtain a [`Queue`] to manage multiple transfers.

use std::{
    fmt::Display,
    future::Future,
    io,
    marker::PhantomData,
    task::{Context, Poll},
};

use crate::platform;

mod queue;
pub use queue::Queue;

mod buffer;
pub use buffer::{RequestBuffer, ResponseBuffer};

mod control;
#[allow(unused)]
pub(crate) use control::SETUP_PACKET_SIZE;
pub use control::{Control, ControlIn, ControlOut, ControlType, Direction, Recipient};

mod internal;
pub(crate) use internal::{
    notify_completion, PlatformSubmit, PlatformTransfer, TransferHandle, TransferRequest,
};

/// Endpoint type.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub enum EndpointType {
    /// Control endpoint.
    Control = 0,

    /// Isochronous endpoint.
    Isochronous = 1,

    /// Bulk endpoint.
    Bulk = 2,

    /// Interrupt endpoint.
    Interrupt = 3,
}

/// Transfer error.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TransferError {
    /// Transfer was cancelled.
    Cancelled,

    /// Endpoint in a STALL condition.
    ///
    /// This is used by the device to signal that an error occurred. For bulk
    /// and interrupt endpoints, the stall condition can be cleared with
    /// [`Interface::clear_halt`][crate::Interface::clear_halt]. For control
    /// requests, the stall is automatically cleared when another request is
    /// submitted.
    Stall,

    /// Device disconnected.
    Disconnected,

    /// Hardware issue or protocol violation.
    Fault,

    /// Unknown or OS-specific error.
    Unknown,
}

impl Display for TransferError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            TransferError::Cancelled => write!(f, "transfer was cancelled"),
            TransferError::Stall => write!(f, "endpoint STALL condition"),
            TransferError::Disconnected => write!(f, "device disconnected"),
            TransferError::Fault => write!(f, "hardware fault or protocol violation"),
            TransferError::Unknown => write!(f, "unknown error"),
        }
    }
}

impl std::error::Error for TransferError {}

impl From<TransferError> for io::Error {
    fn from(value: TransferError) -> Self {
        match value {
            TransferError::Cancelled => io::Error::new(io::ErrorKind::Interrupted, value),
            TransferError::Stall => io::Error::new(io::ErrorKind::ConnectionReset, value),
            TransferError::Disconnected => io::Error::new(io::ErrorKind::ConnectionAborted, value),
            TransferError::Fault => io::Error::new(io::ErrorKind::Other, value),
            TransferError::Unknown => io::Error::new(io::ErrorKind::Other, value),
        }
    }
}

/// Status and data returned on transfer completion.
///
/// A transfer can return partial data even in the case of failure or
/// cancellation, thus this is a struct containing both `data` and `status`
/// rather than a `Result`. Use [`into_result`][`Completion::into_result`] to
/// ignore a partial transfer and get a `Result`.
#[derive(Debug, Clone)]
#[must_use]
pub struct Completion<T> {
    /// Returned data or buffer to re-use.
    pub data: T,

    /// Indicates successful completion or error.
    pub status: Result<(), TransferError>,
}

impl<T> Completion<T> {
    /// Ignore any partial completion, turning `self` into a `Result` containing
    /// either the completed buffer for a successful transfer or a
    /// `TransferError`.
    pub fn into_result(self) -> Result<T, TransferError> {
        self.status.map(|()| self.data)
    }
}

impl TryFrom<Completion<Vec<u8>>> for Vec<u8> {
    type Error = TransferError;

    fn try_from(c: Completion<Vec<u8>>) -> Result<Self, Self::Error> {
        c.into_result()
    }
}

impl TryFrom<Completion<ResponseBuffer>> for ResponseBuffer {
    type Error = TransferError;

    fn try_from(c: Completion<ResponseBuffer>) -> Result<Self, Self::Error> {
        c.into_result()
    }
}

/// [`Future`] used to await the completion of a transfer.
///
/// Use the methods on [`Interface`][super::Interface] to
/// submit an individual transfer and obtain a `TransferFuture`.
///
/// The transfer is cancelled on drop. The buffer and
/// any partially-completed data are destroyed. This means
/// that `TransferFuture` is not [cancel-safe] and cannot be used
/// in `select!{}`, When racing a `TransferFuture` with a timeout
/// you cannot tell whether data may have been partially transferred on timeout.
/// Use the [`Queue`] interface if these matter for your application.
///
/// [cancel-safe]: https://docs.rs/tokio/latest/tokio/macro.select.html#cancellation-safety
pub struct TransferFuture<D: TransferRequest> {
    transfer: TransferHandle<platform::TransferData>,
    ty: PhantomData<D::Response>,
}

impl<D: TransferRequest> TransferFuture<D> {
    pub(crate) fn new(transfer: TransferHandle<platform::TransferData>) -> TransferFuture<D> {
        TransferFuture {
            transfer,
            ty: PhantomData,
        }
    }
}

impl<D: TransferRequest> Future for TransferFuture<D>
where
    platform::TransferData: PlatformSubmit<D>,
    D::Response: Unpin,
{
    type Output = Completion<D::Response>;

    fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        self.as_mut().transfer.poll_completion::<D>(cx)
    }
}