Path: blob/main/devices/src/usb/backend/fido_backend/transfer.rs
5394 views
// Copyright 2024 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::sync::Weak;5use std::time::Instant;67use base::error;8use base::Clock;9use sync::Mutex;10use usb_util::TransferBuffer;11use usb_util::TransferStatus;1213use crate::usb::backend::error::Error as BackendError;14use crate::usb::backend::error::Result as BackendResult;15use crate::usb::backend::fido_backend::constants::USB_TRANSFER_TIMEOUT_MILLIS;16use crate::usb::backend::transfer::BackendTransfer;17use crate::usb::backend::transfer::BackendTransferType;18use crate::usb::backend::transfer::GenericTransferHandle;1920/// Implementation of a generic USB transfer for the FIDO backend. It implements common USB21/// transfer functionality since it cannot rely on the transfer structures provided by the22/// usb_utils crate as the FIDO backend does not use usbdevfs to communicate with the host.23pub struct FidoTransfer {24/// TransferBuffer structure with either a request or response data from the guest/host.25pub buffer: TransferBuffer,26/// Status of the transfer, used by the xhci layer for a successful completion.27status: TransferStatus,28/// Actual length of the transfer, as per USB specs.29pub actual_length: usize,30/// USB endpoint associated with this transfer.31pub endpoint: u8,32/// Timestamp of the transfer submission time.33submission_time: Instant,34/// Callback to be executed once the transfer has completed, to signal the xhci layer.35pub callback: Option<Box<dyn Fn(FidoTransfer) + Send + Sync>>,36}3738impl FidoTransfer {39pub fn new(endpoint: u8, buffer: TransferBuffer) -> FidoTransfer {40let clock = Clock::new();41FidoTransfer {42buffer,43status: TransferStatus::Error, // Default to error44actual_length: 0,45endpoint,46submission_time: clock.now(),47callback: None,48}49}5051/// Called when the device is lost and we need to signal to the xhci layer that the transfer52/// cannot continue and the device should be detached.53pub fn signal_device_lost(&mut self) {54self.status = TransferStatus::NoDevice;55}5657/// Checks if the current transfer should time out or not58pub fn timeout_expired(&self) -> bool {59self.submission_time.elapsed().as_millis() >= USB_TRANSFER_TIMEOUT_MILLIS.into()60}6162/// Finalizes the transfer by setting the right status and then calling the callback to signal63/// the xhci layer.64pub fn complete_transfer(mut self) {65// The default status is "Error". Unless it was explicitly set to Cancel or NoDevice,66// we can just transition it to Completed instead.67if self.status == TransferStatus::Error {68self.status = TransferStatus::Completed;69}7071if let Some(cb) = self.callback.take() {72cb(self);73}74}75}7677impl BackendTransfer for FidoTransfer {78fn status(&self) -> TransferStatus {79self.status80}8182fn actual_length(&self) -> usize {83self.actual_length84}8586fn buffer(&self) -> &TransferBuffer {87&self.buffer88}8990fn set_callback<C: 'static + Fn(BackendTransferType) + Send + Sync>(&mut self, cb: C) {91let callback = move |t: FidoTransfer| cb(BackendTransferType::FidoDevice(t));92self.callback = Some(Box::new(callback));93}94}9596/// Implementation of a cancel handler for `FidoTransfer`97pub struct FidoTransferHandle {98pub weak_transfer: Weak<Mutex<Option<FidoTransfer>>>,99}100101impl GenericTransferHandle for FidoTransferHandle {102fn cancel(&self) -> BackendResult<()> {103let rc_transfer = match self.weak_transfer.upgrade() {104None => {105return Err(BackendError::TransferHandleAlreadyComplete);106}107Some(rc_transfer) => rc_transfer,108};109110let mut lock = rc_transfer.lock();111112let mut transfer = match lock.take() {113Some(t) => t,114None => {115error!("Transfer has already been lost while being cancelled. Ignore");116return Err(BackendError::TransferHandleAlreadyComplete);117}118};119transfer.status = TransferStatus::Cancelled;120*lock = Some(transfer);121Ok(())122}123}124125126