devices/usb/backend/
device_provider.rs

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
5use std::collections::HashMap;
6use std::fs::File;
7use std::mem;
8use std::sync::Arc;
9use std::thread;
10use std::time::Duration;
11
12use anyhow::Context;
13use base::error;
14use base::warn;
15use base::AsRawDescriptor;
16use base::EventType;
17use base::RawDescriptor;
18use base::Tube;
19use sync::Mutex;
20use vm_control::UsbControlAttachedDevice;
21use vm_control::UsbControlCommand;
22use vm_control::UsbControlResult;
23use vm_control::USB_CONTROL_MAX_PORTS;
24
25use crate::usb::backend::device::BackendDeviceType;
26use crate::usb::backend::device::DeviceState;
27use crate::usb::backend::error::Error;
28use crate::usb::backend::error::Result;
29use crate::usb::backend::fido_backend::fido_provider::attach_security_key;
30use crate::usb::backend::host_backend::host_backend_device_provider::attach_host_backend_device;
31use crate::usb::backend::utils::UsbUtilEventHandler;
32use crate::usb::xhci::usb_hub::Error as UsbHubError;
33use crate::usb::xhci::usb_hub::UsbHub;
34use crate::usb::xhci::xhci_backend_device::XhciBackendDevice;
35use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider;
36use crate::utils::AsyncJobQueue;
37use crate::utils::EventHandler;
38use crate::utils::EventLoop;
39use crate::utils::FailHandle;
40
41const SOCKET_TIMEOUT_MS: u64 = 2000;
42
43/// Device provider is an xhci backend device provider that provides generic semantics to handle
44/// various types of backend devices and connects them to the xhci layer.
45pub enum DeviceProvider {
46    // The provider is created but not yet started.
47    Created { control_tube: Mutex<Tube> },
48    // The provider is started on an event loop.
49    Started { inner: Arc<ProviderInner> },
50    // The provider has failed.
51    Failed,
52}
53
54impl DeviceProvider {
55    pub fn new() -> Result<(Tube, DeviceProvider)> {
56        let (child_tube, control_tube) = Tube::pair().map_err(Error::CreateControlTube)?;
57        control_tube
58            .set_send_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
59            .map_err(Error::SetupControlTube)?;
60        control_tube
61            .set_recv_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
62            .map_err(Error::SetupControlTube)?;
63
64        let provider = DeviceProvider::Created {
65            control_tube: Mutex::new(child_tube),
66        };
67        Ok((control_tube, provider))
68    }
69
70    fn start_helper(
71        &mut self,
72        fail_handle: Arc<dyn FailHandle>,
73        event_loop: Arc<EventLoop>,
74        hub: Arc<UsbHub>,
75    ) -> Result<()> {
76        match mem::replace(self, DeviceProvider::Failed) {
77            DeviceProvider::Created { control_tube } => {
78                let job_queue =
79                    AsyncJobQueue::init(&event_loop).map_err(Error::StartAsyncJobQueue)?;
80                // The control channel runs on its own event loop, with no fail handle attached,
81                // so a failed controller cannot take `usb list`/`usb detach` down with it --
82                // those two commands are how an exhausted hub gets its ports back, so they must
83                // keep working after a failure. Devices and their transfers stay on the xhci
84                // loop instead, where a failed controller should stop them: an attach attempted
85                // after the failure is still turned away, by add_event.
86                let (control_event_loop, join_handle) =
87                    EventLoop::start("usb_provider".to_string(), None)
88                        .map_err(Error::StartEventLoop)?;
89                let control_event_loop = Arc::new(control_event_loop);
90                let inner = Arc::new(ProviderInner::new(
91                    fail_handle,
92                    job_queue,
93                    event_loop,
94                    control_event_loop.clone(),
95                    join_handle,
96                    control_tube,
97                    hub,
98                ));
99                let handler: Arc<dyn EventHandler> = inner.clone();
100                control_event_loop
101                    .add_event(
102                        &*inner.control_tube.lock(),
103                        EventType::Read,
104                        Arc::downgrade(&handler),
105                    )
106                    .map_err(Error::AddToEventLoop)?;
107                *self = DeviceProvider::Started { inner };
108                Ok(())
109            }
110            DeviceProvider::Started { .. } => {
111                error!("Usb device provider has already started");
112                Err(Error::BadBackendProviderState)
113            }
114            DeviceProvider::Failed => {
115                error!("Usb device provider has already failed");
116                Err(Error::BadBackendProviderState)
117            }
118        }
119    }
120}
121
122impl XhciBackendDeviceProvider for DeviceProvider {
123    fn start(
124        &mut self,
125        fail_handle: Arc<dyn FailHandle>,
126        event_loop: Arc<EventLoop>,
127        hub: Arc<UsbHub>,
128    ) -> Result<()> {
129        self.start_helper(fail_handle, event_loop, hub)
130    }
131
132    fn keep_rds(&self) -> Vec<RawDescriptor> {
133        match self {
134            DeviceProvider::Created { control_tube } => {
135                vec![control_tube.lock().as_raw_descriptor()]
136            }
137            _ => {
138                error!("Trying to get keepfds when DeviceProvider is not in created state");
139                vec![]
140            }
141        }
142    }
143}
144
145/// ProviderInner listens to control socket.
146pub struct ProviderInner {
147    fail_handle: Arc<dyn FailHandle>,
148    job_queue: Arc<AsyncJobQueue>,
149    // The xhci loop, shared with the controller. Attached devices poll here, so that a failed
150    // controller stops them.
151    event_loop: Arc<EventLoop>,
152    // A separate event loop from the xhci one above, so the control channel keeps working
153    // after a controller failure; see start_helper() for why.
154    control_event_loop: Arc<EventLoop>,
155    control_event_loop_join_handle: Option<thread::JoinHandle<()>>,
156    control_tube: Mutex<Tube>,
157    usb_hub: Arc<UsbHub>,
158
159    // Map of USB hub port number to per-device context.
160    devices: Mutex<HashMap<u8, Arc<UsbUtilEventHandler>>>,
161}
162
163impl ProviderInner {
164    fn new(
165        fail_handle: Arc<dyn FailHandle>,
166        job_queue: Arc<AsyncJobQueue>,
167        event_loop: Arc<EventLoop>,
168        control_event_loop: Arc<EventLoop>,
169        control_event_loop_join_handle: thread::JoinHandle<()>,
170        control_tube: Mutex<Tube>,
171        usb_hub: Arc<UsbHub>,
172    ) -> ProviderInner {
173        ProviderInner {
174            fail_handle,
175            job_queue,
176            event_loop,
177            control_event_loop,
178            control_event_loop_join_handle: Some(control_event_loop_join_handle),
179            control_tube,
180            usb_hub,
181            devices: Mutex::new(HashMap::new()),
182        }
183    }
184
185    fn attach_backend(
186        &self,
187        device: Arc<Mutex<BackendDeviceType>>,
188        event_handler: Arc<UsbUtilEventHandler>,
189        event_type: EventType,
190    ) -> UsbControlResult {
191        let hub_port = match self.usb_hub.connect_backend(device.clone()) {
192            Ok(port) => port,
193            Err(e) => {
194                error!("failed to connect device to hub: {}", e);
195                return UsbControlResult::NoAvailablePort;
196            }
197        };
198
199        let port_id = hub_port.port_id();
200        if let Err(e) = event_handler.activate(event_type, Arc::downgrade(&hub_port)) {
201            error!("failed to activate USB event handler: {}", e);
202            let _ = self.usb_hub.disconnect_port(port_id);
203            return UsbControlResult::FailedToOpenDevice;
204        }
205
206        self.devices.lock().insert(port_id, event_handler);
207        UsbControlResult::Ok { port: port_id }
208    }
209
210    fn handle_attach_device(&self, usb_file: File) -> UsbControlResult {
211        let (host_device, event_handler) = match attach_host_backend_device(
212            usb_file,
213            self.event_loop.clone(),
214            DeviceState::new(self.fail_handle.clone(), self.job_queue.clone()),
215        ) {
216            Ok((host_device, event_handler)) => (host_device, event_handler),
217            Err(e) => {
218                error!("could not construct USB device from the given file: {}", e);
219                // The device file itself was valid; constructing state for it is what failed, so
220                // report this as a host-device-init failure rather than an enumeration error.
221                return UsbControlResult::FailedToInitHostDevice;
222            }
223        };
224
225        // Resetting the device is used to make sure it is in a known state, but it may
226        // still function if the reset fails.
227        if let Err(e) = host_device.lock().reset() {
228            error!("failed to reset device during attach: {:?}", e);
229        }
230
231        self.attach_backend(host_device, event_handler, EventType::ReadWrite)
232    }
233
234    fn handle_detach_device(&self, port: u8) -> UsbControlResult {
235        match self.usb_hub.disconnect_port(port) {
236            Ok(()) => {}
237            Err(e @ UsbHubError::Detach { .. }) => {
238                // UsbPort::detach() removes and stops the backend before notifying the guest.
239                // The host-side detach therefore succeeded even if a failed event ring could not
240                // accept the port-status-change event. Continue releasing the host resources.
241                warn!(
242                    "detached device from port {}, but failed to notify the guest: {}",
243                    port, e
244                );
245            }
246            Err(e) => {
247                error!("failed to disconnect device from port {}: {}", port, e);
248                return UsbControlResult::NoSuchDevice;
249            }
250        }
251
252        // The device enters a detaching state in hub.disconnect_port(). Remove the event handler
253        // from the event loop after in-flight transfers have completed and release its host
254        // resources. This is also required when only the guest notification above failed.
255        if let Some(event_handler) = self.devices.lock().remove(&port) {
256            event_handler.signal_detach();
257        }
258        UsbControlResult::Ok { port }
259    }
260
261    fn handle_attach_security_key(&self, hidraw: File) -> UsbControlResult {
262        let (fido_device, event_handler) = match attach_security_key(
263            hidraw,
264            self.event_loop.clone(),
265            DeviceState::new(self.fail_handle.clone(), self.job_queue.clone()),
266        ) {
267            Ok((fido_device, event_handler)) => (fido_device, event_handler),
268            Err(e) => {
269                error!(
270                    "could not create a virtual fido device from the given file: {}",
271                    e
272                );
273                return UsbControlResult::NoSuchDevice;
274            }
275        };
276
277        // Reset the device to make sure it's in a usable state.
278        // Resetting it also stops polling on the FD, since we only poll when there is an active
279        // transaction.
280        if let Err(e) = fido_device.lock().reset() {
281            error!("failed to reset fido device during attach: {:?}", e);
282        }
283
284        self.attach_backend(fido_device, event_handler, EventType::Read)
285    }
286
287    fn handle_list_devices(&self, ports: [u8; USB_CONTROL_MAX_PORTS]) -> UsbControlResult {
288        let mut devices: [UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS] = Default::default();
289        for (result_index, &port_id) in ports.iter().enumerate() {
290            match self.usb_hub.get_port(port_id).and_then(|p| {
291                p.backend_device()
292                    .as_ref()
293                    .map(|d| d.lock())
294                    .map(|d| (d.get_vid(), d.get_pid()))
295            }) {
296                Some((vendor_id, product_id)) => {
297                    devices[result_index] = UsbControlAttachedDevice {
298                        port: port_id,
299                        vendor_id,
300                        product_id,
301                    }
302                }
303                None => continue,
304            }
305        }
306        UsbControlResult::Devices(devices)
307    }
308
309    fn on_event_helper(&self) -> Result<()> {
310        let tube = self.control_tube.lock();
311        let cmd = tube.recv().map_err(Error::ReadControlTube)?;
312        let result = match cmd {
313            UsbControlCommand::AttachDevice { file } => self.handle_attach_device(file),
314            UsbControlCommand::AttachSecurityKey { file } => self.handle_attach_security_key(file),
315            UsbControlCommand::DetachDevice { port } => self.handle_detach_device(port),
316            UsbControlCommand::ListDevice { ports } => self.handle_list_devices(ports),
317        };
318        tube.send(&result).map_err(Error::WriteControlTube)?;
319        Ok(())
320    }
321}
322
323impl EventHandler for ProviderInner {
324    fn on_event(&self) -> anyhow::Result<()> {
325        self.on_event_helper()
326            .context("host backend device provider failed")
327    }
328}
329
330impl Drop for ProviderInner {
331    fn drop(&mut self) {
332        // The loop holds a weak reference to us, so it cannot be what is keeping us alive, and
333        // by the time we get here it has nothing left to dispatch to.
334        self.control_event_loop.stop();
335        if let Some(join_handle) = self.control_event_loop_join_handle.take() {
336            let _ = join_handle.join();
337        }
338    }
339}