nusb/transfer/
mod.rs

1//! Transfer-related types.
2//!
3//! Use the methods on an [`Interface`][`super::Interface`] to make individual
4//! transfers or obtain a [`Queue`] to manage multiple transfers.
5
6use std::{
7    fmt::Display,
8    future::Future,
9    io,
10    marker::PhantomData,
11    task::{Context, Poll},
12};
13
14use crate::platform;
15
16mod queue;
17pub use queue::Queue;
18
19mod buffer;
20pub use buffer::{RequestBuffer, ResponseBuffer};
21
22mod control;
23#[allow(unused)]
24pub(crate) use control::SETUP_PACKET_SIZE;
25pub use control::{Control, ControlIn, ControlOut, ControlType, Direction, Recipient};
26
27mod internal;
28pub(crate) use internal::{
29    notify_completion, PlatformSubmit, PlatformTransfer, TransferHandle, TransferRequest,
30};
31
32/// Endpoint type.
33#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34#[allow(dead_code)]
35pub enum EndpointType {
36    /// Control endpoint.
37    Control = 0,
38
39    /// Isochronous endpoint.
40    Isochronous = 1,
41
42    /// Bulk endpoint.
43    Bulk = 2,
44
45    /// Interrupt endpoint.
46    Interrupt = 3,
47}
48
49/// Transfer error.
50#[derive(Debug, Copy, Clone, PartialEq, Eq)]
51pub enum TransferError {
52    /// Transfer was cancelled.
53    Cancelled,
54
55    /// Endpoint in a STALL condition.
56    ///
57    /// This is used by the device to signal that an error occurred. For bulk
58    /// and interrupt endpoints, the stall condition can be cleared with
59    /// [`Interface::clear_halt`][crate::Interface::clear_halt]. For control
60    /// requests, the stall is automatically cleared when another request is
61    /// submitted.
62    Stall,
63
64    /// Device disconnected.
65    Disconnected,
66
67    /// Hardware issue or protocol violation.
68    Fault,
69
70    /// Unknown or OS-specific error.
71    Unknown,
72}
73
74impl Display for TransferError {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        match self {
77            TransferError::Cancelled => write!(f, "transfer was cancelled"),
78            TransferError::Stall => write!(f, "endpoint STALL condition"),
79            TransferError::Disconnected => write!(f, "device disconnected"),
80            TransferError::Fault => write!(f, "hardware fault or protocol violation"),
81            TransferError::Unknown => write!(f, "unknown error"),
82        }
83    }
84}
85
86impl std::error::Error for TransferError {}
87
88impl From<TransferError> for io::Error {
89    fn from(value: TransferError) -> Self {
90        match value {
91            TransferError::Cancelled => io::Error::new(io::ErrorKind::Interrupted, value),
92            TransferError::Stall => io::Error::new(io::ErrorKind::ConnectionReset, value),
93            TransferError::Disconnected => io::Error::new(io::ErrorKind::ConnectionAborted, value),
94            TransferError::Fault => io::Error::new(io::ErrorKind::Other, value),
95            TransferError::Unknown => io::Error::new(io::ErrorKind::Other, value),
96        }
97    }
98}
99
100/// Status and data returned on transfer completion.
101///
102/// A transfer can return partial data even in the case of failure or
103/// cancellation, thus this is a struct containing both `data` and `status`
104/// rather than a `Result`. Use [`into_result`][`Completion::into_result`] to
105/// ignore a partial transfer and get a `Result`.
106#[derive(Debug, Clone)]
107#[must_use]
108pub struct Completion<T> {
109    /// Returned data or buffer to re-use.
110    pub data: T,
111
112    /// Indicates successful completion or error.
113    pub status: Result<(), TransferError>,
114}
115
116impl<T> Completion<T> {
117    /// Ignore any partial completion, turning `self` into a `Result` containing
118    /// either the completed buffer for a successful transfer or a
119    /// `TransferError`.
120    pub fn into_result(self) -> Result<T, TransferError> {
121        self.status.map(|()| self.data)
122    }
123}
124
125impl TryFrom<Completion<Vec<u8>>> for Vec<u8> {
126    type Error = TransferError;
127
128    fn try_from(c: Completion<Vec<u8>>) -> Result<Self, Self::Error> {
129        c.into_result()
130    }
131}
132
133impl TryFrom<Completion<ResponseBuffer>> for ResponseBuffer {
134    type Error = TransferError;
135
136    fn try_from(c: Completion<ResponseBuffer>) -> Result<Self, Self::Error> {
137        c.into_result()
138    }
139}
140
141/// [`Future`] used to await the completion of a transfer.
142///
143/// Use the methods on [`Interface`][super::Interface] to
144/// submit an individual transfer and obtain a `TransferFuture`.
145///
146/// The transfer is cancelled on drop. The buffer and
147/// any partially-completed data are destroyed. This means
148/// that `TransferFuture` is not [cancel-safe] and cannot be used
149/// in `select!{}`, When racing a `TransferFuture` with a timeout
150/// you cannot tell whether data may have been partially transferred on timeout.
151/// Use the [`Queue`] interface if these matter for your application.
152///
153/// [cancel-safe]: https://docs.rs/tokio/latest/tokio/macro.select.html#cancellation-safety
154pub struct TransferFuture<D: TransferRequest> {
155    transfer: TransferHandle<platform::TransferData>,
156    ty: PhantomData<D::Response>,
157}
158
159impl<D: TransferRequest> TransferFuture<D> {
160    pub(crate) fn new(transfer: TransferHandle<platform::TransferData>) -> TransferFuture<D> {
161        TransferFuture {
162            transfer,
163            ty: PhantomData,
164        }
165    }
166}
167
168impl<D: TransferRequest> Future for TransferFuture<D>
169where
170    platform::TransferData: PlatformSubmit<D>,
171    D::Response: Unpin,
172{
173    type Output = Completion<D::Response>;
174
175    fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
176        self.as_mut().transfer.poll_completion::<D>(cx)
177    }
178}