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