Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/usb/backend/device_provider.rs
5394 views
1
// Copyright 2023 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::collections::HashMap;
6
use std::fs::File;
7
use std::mem;
8
use std::sync::Arc;
9
use std::time::Duration;
10
11
use anyhow::Context;
12
use base::error;
13
use base::AsRawDescriptor;
14
use base::EventType;
15
use base::RawDescriptor;
16
use base::Tube;
17
use sync::Mutex;
18
use vm_control::UsbControlAttachedDevice;
19
use vm_control::UsbControlCommand;
20
use vm_control::UsbControlResult;
21
use vm_control::USB_CONTROL_MAX_PORTS;
22
23
use crate::usb::backend::device::BackendDevice;
24
use crate::usb::backend::device::DeviceState;
25
use crate::usb::backend::error::Error;
26
use crate::usb::backend::error::Result;
27
use crate::usb::backend::fido_backend::fido_provider::attach_security_key;
28
use crate::usb::backend::host_backend::host_backend_device_provider::attach_host_backend_device;
29
use crate::usb::xhci::usb_hub::UsbHub;
30
use crate::usb::xhci::xhci_backend_device::XhciBackendDevice;
31
use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider;
32
use crate::utils::AsyncJobQueue;
33
use crate::utils::EventHandler;
34
use crate::utils::EventLoop;
35
use crate::utils::FailHandle;
36
37
const SOCKET_TIMEOUT_MS: u64 = 2000;
38
39
/// Device provider is an xhci backend device provider that provides generic semantics to handle
40
/// various types of backend devices and connects them to the xhci layer.
41
pub enum DeviceProvider {
42
// The provider is created but not yet started.
43
Created { control_tube: Mutex<Tube> },
44
// The provider is started on an event loop.
45
Started { inner: Arc<ProviderInner> },
46
// The provider has failed.
47
Failed,
48
}
49
50
impl DeviceProvider {
51
pub fn new() -> Result<(Tube, DeviceProvider)> {
52
let (child_tube, control_tube) = Tube::pair().map_err(Error::CreateControlTube)?;
53
control_tube
54
.set_send_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
55
.map_err(Error::SetupControlTube)?;
56
control_tube
57
.set_recv_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
58
.map_err(Error::SetupControlTube)?;
59
60
let provider = DeviceProvider::Created {
61
control_tube: Mutex::new(child_tube),
62
};
63
Ok((control_tube, provider))
64
}
65
66
fn start_helper(
67
&mut self,
68
fail_handle: Arc<dyn FailHandle>,
69
event_loop: Arc<EventLoop>,
70
hub: Arc<UsbHub>,
71
) -> Result<()> {
72
match mem::replace(self, DeviceProvider::Failed) {
73
DeviceProvider::Created { control_tube } => {
74
let job_queue =
75
AsyncJobQueue::init(&event_loop).map_err(Error::StartAsyncJobQueue)?;
76
let inner = Arc::new(ProviderInner::new(
77
fail_handle,
78
job_queue,
79
event_loop.clone(),
80
control_tube,
81
hub,
82
));
83
let handler: Arc<dyn EventHandler> = inner.clone();
84
event_loop
85
.add_event(
86
&*inner.control_tube.lock(),
87
EventType::Read,
88
Arc::downgrade(&handler),
89
)
90
.map_err(Error::AddToEventLoop)?;
91
*self = DeviceProvider::Started { inner };
92
Ok(())
93
}
94
DeviceProvider::Started { .. } => {
95
error!("Usb device provider has already started");
96
Err(Error::BadBackendProviderState)
97
}
98
DeviceProvider::Failed => {
99
error!("Usb device provider has already failed");
100
Err(Error::BadBackendProviderState)
101
}
102
}
103
}
104
}
105
106
impl XhciBackendDeviceProvider for DeviceProvider {
107
fn start(
108
&mut self,
109
fail_handle: Arc<dyn FailHandle>,
110
event_loop: Arc<EventLoop>,
111
hub: Arc<UsbHub>,
112
) -> Result<()> {
113
self.start_helper(fail_handle, event_loop, hub)
114
}
115
116
fn keep_rds(&self) -> Vec<RawDescriptor> {
117
match self {
118
DeviceProvider::Created { control_tube } => {
119
vec![control_tube.lock().as_raw_descriptor()]
120
}
121
_ => {
122
error!("Trying to get keepfds when DeviceProvider is not in created state");
123
vec![]
124
}
125
}
126
}
127
}
128
129
/// ProviderInner listens to control socket.
130
pub struct ProviderInner {
131
fail_handle: Arc<dyn FailHandle>,
132
job_queue: Arc<AsyncJobQueue>,
133
event_loop: Arc<EventLoop>,
134
control_tube: Mutex<Tube>,
135
usb_hub: Arc<UsbHub>,
136
137
// Map of USB hub port number to per-device context.
138
devices: Mutex<HashMap<u8, DeviceContext>>,
139
}
140
141
struct DeviceContext {
142
event_handler: Arc<dyn EventHandler>,
143
device: Arc<Mutex<dyn BackendDevice>>,
144
}
145
146
impl ProviderInner {
147
fn new(
148
fail_handle: Arc<dyn FailHandle>,
149
job_queue: Arc<AsyncJobQueue>,
150
event_loop: Arc<EventLoop>,
151
control_tube: Mutex<Tube>,
152
usb_hub: Arc<UsbHub>,
153
) -> ProviderInner {
154
ProviderInner {
155
fail_handle,
156
job_queue,
157
event_loop,
158
control_tube,
159
usb_hub,
160
devices: Mutex::new(HashMap::new()),
161
}
162
}
163
164
fn handle_attach_device(&self, usb_file: File) -> UsbControlResult {
165
let (host_device, event_handler) = match attach_host_backend_device(
166
usb_file,
167
DeviceState::new(self.fail_handle.clone(), self.job_queue.clone()),
168
) {
169
Ok((host_device, event_handler)) => (host_device, event_handler),
170
Err(e) => {
171
error!("could not construct USB device from the given file: {}", e);
172
return UsbControlResult::NoSuchDevice;
173
}
174
};
175
176
if let Err(e) = self.event_loop.add_event(
177
&*host_device.lock(),
178
EventType::ReadWrite,
179
Arc::downgrade(&event_handler),
180
) {
181
error!("failed to add USB device to event handler: {}", e);
182
return UsbControlResult::FailedToOpenDevice;
183
}
184
185
// Resetting the device is used to make sure it is in a known state, but it may
186
// still function if the reset fails.
187
if let Err(e) = host_device.lock().reset() {
188
error!("failed to reset device after attach: {:?}", e);
189
}
190
191
let device_ctx = DeviceContext {
192
event_handler,
193
device: host_device.clone(),
194
};
195
196
let port = self.usb_hub.connect_backend(host_device);
197
match port {
198
Ok(port) => {
199
self.devices.lock().insert(port, device_ctx);
200
UsbControlResult::Ok { port }
201
}
202
Err(e) => {
203
error!("failed to connect device to hub: {}", e);
204
UsbControlResult::NoAvailablePort
205
}
206
}
207
}
208
209
fn handle_detach_device(&self, port: u8) -> UsbControlResult {
210
match self.usb_hub.disconnect_port(port) {
211
Ok(()) => {
212
if let Some(device_ctx) = self.devices.lock().remove(&port) {
213
let _ = device_ctx.event_handler.on_event();
214
215
if let Err(e) = device_ctx
216
.device
217
.lock()
218
.detach_event_handler(&self.event_loop)
219
{
220
error!(
221
"failed to remove poll change handler from event loop: {}",
222
e
223
);
224
}
225
}
226
UsbControlResult::Ok { port }
227
}
228
Err(e) => {
229
error!("failed to disconnect device from port {}: {}", port, e);
230
UsbControlResult::NoSuchDevice
231
}
232
}
233
}
234
235
fn handle_attach_security_key(&self, hidraw: File) -> UsbControlResult {
236
let (fido_device, event_handler) = match attach_security_key(
237
hidraw,
238
self.event_loop.clone(),
239
DeviceState::new(self.fail_handle.clone(), self.job_queue.clone()),
240
) {
241
Ok((fido_device, event_handler)) => (fido_device, event_handler),
242
Err(e) => {
243
error!(
244
"could not create a virtual fido device from the given file: {}",
245
e
246
);
247
return UsbControlResult::NoSuchDevice;
248
}
249
};
250
251
if let Err(e) = self.event_loop.add_event(
252
&*fido_device.lock(),
253
EventType::Read,
254
Arc::downgrade(&event_handler),
255
) {
256
error!("failed to add fido device to event handler: {}", e);
257
return UsbControlResult::FailedToOpenDevice;
258
}
259
260
let device_ctx = DeviceContext {
261
event_handler,
262
device: fido_device.clone(),
263
};
264
265
// Reset the device to make sure it's in a usable state.
266
// Resetting it also stops polling on the FD, since we only poll when there is an active
267
// transaction.
268
if let Err(e) = fido_device.lock().reset() {
269
error!("failed to reset fido device after attach: {:?}", e);
270
}
271
272
let port = self.usb_hub.connect_backend(fido_device);
273
match port {
274
Ok(port) => {
275
self.devices.lock().insert(port, device_ctx);
276
UsbControlResult::Ok { port }
277
}
278
Err(e) => {
279
error!("failed to connect device to hub: {}", e);
280
UsbControlResult::NoAvailablePort
281
}
282
}
283
}
284
285
fn handle_list_devices(&self, ports: [u8; USB_CONTROL_MAX_PORTS]) -> UsbControlResult {
286
let mut devices: [UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS] = Default::default();
287
for (result_index, &port_id) in ports.iter().enumerate() {
288
match self.usb_hub.get_port(port_id).and_then(|p| {
289
p.backend_device()
290
.as_ref()
291
.map(|d| d.lock())
292
.map(|d| (d.get_vid(), d.get_pid()))
293
}) {
294
Some((vendor_id, product_id)) => {
295
devices[result_index] = UsbControlAttachedDevice {
296
port: port_id,
297
vendor_id,
298
product_id,
299
}
300
}
301
None => continue,
302
}
303
}
304
UsbControlResult::Devices(devices)
305
}
306
307
fn on_event_helper(&self) -> Result<()> {
308
let tube = self.control_tube.lock();
309
let cmd = tube.recv().map_err(Error::ReadControlTube)?;
310
let result = match cmd {
311
UsbControlCommand::AttachDevice { file } => self.handle_attach_device(file),
312
UsbControlCommand::AttachSecurityKey { file } => self.handle_attach_security_key(file),
313
UsbControlCommand::DetachDevice { port } => self.handle_detach_device(port),
314
UsbControlCommand::ListDevice { ports } => self.handle_list_devices(ports),
315
};
316
tube.send(&result).map_err(Error::WriteControlTube)?;
317
Ok(())
318
}
319
}
320
321
impl EventHandler for ProviderInner {
322
fn on_event(&self) -> anyhow::Result<()> {
323
self.on_event_helper()
324
.context("host backend device provider failed")
325
}
326
}
327
328