crab-usb 0.9.7

A usb host for embedded systems, written in Rust.
Documentation
use alloc::boxed::Box;
#[cfg(any(kmod, umod))]
use alloc::vec::Vec;
use core::{
    any::Any,
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

#[cfg(any(kmod, umod))]
use usb_if::endpoint::{IsoPacketResult, TransferStatus};
use usb_if::{
    descriptor::EndpointType,
    endpoint::{EndpointInfo, RequestId, TransferCompletion, TransferRequest},
    err::TransferError,
};

#[cfg(any(kmod, umod))]
use super::transfer::Transfer;

mod ctrl;

pub(crate) trait EndpointOp: Send + Any + 'static {
    fn submit_request(&mut self, request: TransferRequest) -> Result<RequestId, TransferError>;

    fn reclaim_request(
        &mut self,
        id: RequestId,
    ) -> Option<Result<TransferCompletion, TransferError>>;

    fn register_waker(&self, id: RequestId, cx: &mut Context<'_>);

    fn cancel_request(&mut self, _id: RequestId) -> Result<(), TransferError> {
        Err(TransferError::NotSupported)
    }
}

pub struct Endpoint {
    info: EndpointInfo,
    raw: Box<dyn EndpointOp>,
}

impl Endpoint {
    #[cfg(any(kmod, umod))]
    pub(crate) fn new(info: EndpointInfo, raw: impl EndpointOp) -> Self {
        Self {
            info,
            raw: Box::new(raw),
        }
    }

    pub fn info(&self) -> EndpointInfo {
        self.info
    }

    pub fn submit(&mut self, request: TransferRequest) -> Result<RequestId, TransferError> {
        self.validate_request(&request)?;
        self.raw.submit_request(request)
    }

    pub fn reclaim(&mut self, id: RequestId) -> Result<Option<TransferCompletion>, TransferError> {
        match self.raw.reclaim_request(id) {
            Some(result) => result.map(Some),
            None => Ok(None),
        }
    }

    pub fn poll_request(
        &mut self,
        id: RequestId,
        cx: &mut Context<'_>,
    ) -> Poll<Result<TransferCompletion, TransferError>> {
        match self.raw.reclaim_request(id) {
            Some(res) => Poll::Ready(res),
            None => {
                self.raw.register_waker(id, cx);
                match self.raw.reclaim_request(id) {
                    Some(res) => Poll::Ready(res),
                    None => Poll::Pending,
                }
            }
        }
    }

    pub fn cancel(&mut self, id: RequestId) -> Result<(), TransferError> {
        self.raw.cancel_request(id)
    }

    pub async fn wait(
        &mut self,
        request: TransferRequest,
    ) -> Result<TransferCompletion, TransferError> {
        let id = self.submit(request)?;
        EndpointRequestFuture { id, endpoint: self }.await
    }

    #[allow(unused)]
    pub(crate) fn with_raw_mut<T: EndpointOp, R>(&mut self, f: impl FnOnce(&mut T) -> R) -> R {
        let d = self.raw.as_mut() as &mut dyn Any;
        f(d.downcast_mut::<T>().expect("Endpoint downcast_mut failed"))
    }

    fn validate_request(&self, request: &TransferRequest) -> Result<(), TransferError> {
        let request_type = match request {
            TransferRequest::Control { .. } => EndpointType::Control,
            TransferRequest::Bulk { .. } => EndpointType::Bulk,
            TransferRequest::Interrupt { .. } => EndpointType::Interrupt,
            TransferRequest::Isochronous { .. } => EndpointType::Isochronous,
        };
        if request_type == self.info.transfer_type {
            Ok(())
        } else {
            Err(TransferError::InvalidEndpoint)
        }
    }
}

struct EndpointRequestFuture<'a> {
    id: RequestId,
    endpoint: &'a mut Endpoint,
}

impl Future for EndpointRequestFuture<'_> {
    type Output = Result<TransferCompletion, TransferError>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.get_mut();
        this.endpoint.poll_request(this.id, cx)
    }
}

#[cfg(any(kmod, umod))]
pub(crate) fn transfer_to_completion(id: RequestId, transfer: Transfer) -> TransferCompletion {
    let iso_packets = match &transfer.kind {
        usb_if::endpoint::TransferKind::Isochronous { packet_lengths } => packet_lengths
            .iter()
            .copied()
            .zip(transfer.iso_packet_actual_lengths.iter().copied())
            .map(|(requested_length, actual_length)| IsoPacketResult {
                requested_length,
                actual_length,
                status: TransferStatus::Completed,
            })
            .collect(),
        _ => Vec::new(),
    };

    TransferCompletion {
        request_id: id,
        status: TransferStatus::Completed,
        actual_length: transfer.transfer_len,
        iso_packets,
    }
}