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