xref: /aosp_15_r20/external/crosvm/devices/src/usb/backend/fido_backend/transfer.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2024 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::sync::Weak;
6 use std::time::Instant;
7 
8 use base::error;
9 use base::Clock;
10 use sync::Mutex;
11 use usb_util::TransferBuffer;
12 use usb_util::TransferStatus;
13 
14 use crate::usb::backend::error::Error as BackendError;
15 use crate::usb::backend::error::Result as BackendResult;
16 use crate::usb::backend::fido_backend::constants::USB_TRANSFER_TIMEOUT_MILLIS;
17 use crate::usb::backend::transfer::BackendTransfer;
18 use crate::usb::backend::transfer::BackendTransferType;
19 use crate::usb::backend::transfer::GenericTransferHandle;
20 
21 /// Implementation of a generic USB transfer for the FIDO backend. It implements common USB
22 /// transfer functionality since it cannot rely on the transfer structures provided by the
23 /// usb_utils crate as the FIDO backend does not use usbdevfs to communicate with the host.
24 pub struct FidoTransfer {
25     /// TransferBuffer structure with either a request or response data from the guest/host.
26     pub buffer: TransferBuffer,
27     /// Status of the transfer, used by the xhci layer for a successful completion.
28     status: TransferStatus,
29     /// Actual length of the transfer, as per USB specs.
30     pub actual_length: usize,
31     /// USB endpoint associated with this transfer.
32     pub endpoint: u8,
33     /// Timestamp of the transfer submission time.
34     submission_time: Instant,
35     /// Callback to be executed once the transfer has completed, to signal the xhci layer.
36     pub callback: Option<Box<dyn Fn(FidoTransfer) + Send + Sync>>,
37 }
38 
39 impl FidoTransfer {
new(endpoint: u8, buffer: TransferBuffer) -> FidoTransfer40     pub fn new(endpoint: u8, buffer: TransferBuffer) -> FidoTransfer {
41         let clock = Clock::new();
42         FidoTransfer {
43             buffer,
44             status: TransferStatus::Error, // Default to error
45             actual_length: 0,
46             endpoint,
47             submission_time: clock.now(),
48             callback: None,
49         }
50     }
51 
52     /// Called when the device is lost and we need to signal to the xhci layer that the transfer
53     /// cannot continue and the device should be detached.
signal_device_lost(&mut self)54     pub fn signal_device_lost(&mut self) {
55         self.status = TransferStatus::NoDevice;
56     }
57 
58     /// Checks if the current transfer should time out or not
timeout_expired(&self) -> bool59     pub fn timeout_expired(&self) -> bool {
60         self.submission_time.elapsed().as_millis() >= USB_TRANSFER_TIMEOUT_MILLIS.into()
61     }
62 
63     /// Finalizes the transfer by setting the right status and then calling the callback to signal
64     /// the xhci layer.
complete_transfer(mut self)65     pub fn complete_transfer(mut self) {
66         // The default status is "Error". Unless it was explicitly set to Cancel or NoDevice,
67         // we can just transition it to Completed instead.
68         if self.status == TransferStatus::Error {
69             self.status = TransferStatus::Completed;
70         }
71 
72         if let Some(cb) = self.callback.take() {
73             cb(self);
74         }
75     }
76 }
77 
78 impl BackendTransfer for FidoTransfer {
status(&self) -> TransferStatus79     fn status(&self) -> TransferStatus {
80         self.status
81     }
82 
actual_length(&self) -> usize83     fn actual_length(&self) -> usize {
84         self.actual_length
85     }
86 
buffer(&self) -> &TransferBuffer87     fn buffer(&self) -> &TransferBuffer {
88         &self.buffer
89     }
90 
set_callback<C: 'static + Fn(BackendTransferType) + Send + Sync>(&mut self, cb: C)91     fn set_callback<C: 'static + Fn(BackendTransferType) + Send + Sync>(&mut self, cb: C) {
92         let callback = move |t: FidoTransfer| cb(BackendTransferType::FidoDevice(t));
93         self.callback = Some(Box::new(callback));
94     }
95 }
96 
97 /// Implementation of a cancel handler for `FidoTransfer`
98 pub struct FidoTransferHandle {
99     pub weak_transfer: Weak<Mutex<Option<FidoTransfer>>>,
100 }
101 
102 impl GenericTransferHandle for FidoTransferHandle {
cancel(&self) -> BackendResult<()>103     fn cancel(&self) -> BackendResult<()> {
104         let rc_transfer = match self.weak_transfer.upgrade() {
105             None => {
106                 return Err(BackendError::TransferHandleAlreadyComplete);
107             }
108             Some(rc_transfer) => rc_transfer,
109         };
110 
111         let mut lock = rc_transfer.lock();
112 
113         let mut transfer = match lock.take() {
114             Some(t) => t,
115             None => {
116                 error!("Transfer has already been lost while being cancelled. Ignore");
117                 return Err(BackendError::TransferHandleAlreadyComplete);
118             }
119         };
120         transfer.status = TransferStatus::Cancelled;
121         *lock = Some(transfer);
122         Ok(())
123     }
124 }
125