Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/usb/backend/fido_backend/transfer.rs
5394 views
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 {
40
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.
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
59
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.
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 {
79
fn status(&self) -> TransferStatus {
80
self.status
81
}
82
83
fn actual_length(&self) -> usize {
84
self.actual_length
85
}
86
87
fn buffer(&self) -> &TransferBuffer {
88
&self.buffer
89
}
90
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 {
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
126