devices/usb/backend/
utils.rs

1// Copyright 2019 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::sync::Arc;
6use std::sync::Weak;
7
8use anyhow::Context;
9use base::error;
10use base::EventType;
11use sync::Mutex;
12use usb_util::TransferStatus;
13
14use crate::usb::backend::device::BackendDevice;
15use crate::usb::backend::device::BackendDeviceType;
16use crate::usb::backend::error::Error;
17use crate::usb::backend::error::Result;
18use crate::usb::xhci::usb_hub::UsbPort;
19use crate::usb::xhci::xhci_transfer::XhciTransferState;
20use crate::utils::EventHandler;
21use crate::utils::EventLoop;
22
23#[macro_export]
24/// Allows dispatching a function call to all its enum value implementations.
25/// See `BackendDeviceType` in usb/backend/device.rs for an example usage of it.
26///
27/// # Arguments
28///
29/// * `self` - Replacement for the local `self` reference in the function call.
30/// * `enum` - Enum name that the macro is matching on.
31/// * `types` - Space-separated list of value types of the given enum.
32/// * `func` - Function name that will be called by each match arm.
33/// * `param` - Optional parameters needed for the given function call.
34macro_rules! multi_dispatch {
35    ($self:ident, $enum:ident, $($types:ident )+, $func:ident) => {
36        match $self {
37            $(
38                $enum::$types(device) => device.$func(),
39            )+
40        }
41    };
42    ($self:ident, $enum:ident, $($types:ident )+, $func:ident, $param:expr) => {
43        match $self {
44            $(
45                $enum::$types(device) => device.$func($param),
46            )+
47        }
48    };
49    ($self:ident, $enum:ident, $($types:ident )+, $func:ident, $param1:expr, $param2: expr) => {
50        match $self {
51            $(
52                $enum::$types(device) => device.$func($param1, $param2),
53            )+
54        }
55    };
56    ($self:ident, $enum:ident, $($types:ident )+, $func:ident, $param1:expr, $param2: expr, $param3: expr) => {
57        match $self {
58            $(
59                $enum::$types(device) => device.$func($param1, $param2, $param3),
60            )+
61        }
62    };
63}
64
65pub(crate) use multi_dispatch;
66
67pub(crate) struct UsbUtilEventHandler {
68    pub device: Arc<Mutex<BackendDeviceType>>,
69    pub event_loop: Arc<EventLoop>,
70    pub port: Mutex<Weak<UsbPort>>,
71    pub self_ref: Mutex<Option<Arc<UsbUtilEventHandler>>>,
72}
73
74impl EventHandler for UsbUtilEventHandler {
75    fn on_event(&self) -> anyhow::Result<()> {
76        let (backend_result, is_lost) = {
77            let mut device_locked = self.device.lock();
78            let res = match &mut *device_locked {
79                BackendDeviceType::HostDevice(host_device) => host_device
80                    .device
81                    .lock()
82                    .poll_transfers()
83                    .context("UsbUtilEventHandler poll_transfers failed"),
84                BackendDeviceType::FidoDevice(fido_device) => fido_device
85                    .read_hidraw_file()
86                    .context("FidoDeviceEventHandler failed to read hidraw device"),
87            };
88            (res, device_locked.is_lost())
89        };
90
91        let is_detached = self.port.lock().upgrade().is_none_or(|port| {
92            if is_lost || backend_result.is_err() {
93                let _ = port.detach();
94            }
95            // It might already be manually detached.
96            !port.is_attached()
97        });
98
99        if is_detached {
100            self.signal_detach();
101        }
102
103        if let Err(e) = backend_result {
104            error!("usb: backend event failure: {:#}", e);
105        }
106        Ok(())
107    }
108}
109
110impl UsbUtilEventHandler {
111    pub(crate) fn new(
112        device: Arc<Mutex<BackendDeviceType>>,
113        event_loop: Arc<EventLoop>,
114    ) -> Arc<Self> {
115        Arc::new(Self {
116            device,
117            event_loop,
118            port: Mutex::new(Weak::new()),
119            self_ref: Mutex::new(None),
120        })
121    }
122
123    /// Register this handler with the event loop. After a successful return, call
124    /// finalize_detach() for cleanup.
125    pub fn activate(self: &Arc<Self>, event_type: EventType, port: Weak<UsbPort>) -> Result<()> {
126        *self.port.lock() = port;
127
128        self.event_loop
129            .add_event(
130                &*self.device.lock(),
131                event_type,
132                Arc::downgrade(self) as Weak<dyn EventHandler>,
133            )
134            .map_err(Error::AddToEventLoop)?;
135
136        *self.self_ref.lock() = Some(self.clone());
137        Ok(())
138    }
139
140    /// Signal this handler to perform the host resource cleanup.
141    pub fn signal_detach(&self) {
142        // self_ref acts as our atomic flag. Only the first caller (i.e., manual detach vs unplug)
143        // will succeed.
144        let self_arc = match self.self_ref.lock().take() {
145            Some(arc) => arc,
146            None => return, // Detach process is already running.
147        };
148
149        std::thread::spawn(move || {
150            let mut retries = 0;
151            loop {
152                let can_finalize = {
153                    let mut device = self_arc.device.lock();
154                    if let BackendDeviceType::HostDevice(host_device) = &mut *device {
155                        let _ = host_device.device.lock().poll_transfers();
156                    }
157                    device.can_finalize()
158                };
159
160                if can_finalize {
161                    break;
162                }
163
164                // When a device is unplugged from the host, the in-flight URBs will be aborted and
165                // queued back to the completion list. However, there's a small window where the
166                // completion list is empty because the actual hardware has not responded to the
167                // host kernel. Since there's no way for the user space to wait for the host kernel
168                // to queue them back, we keep polling here.
169                std::thread::sleep(std::time::Duration::from_millis(100));
170                retries += 1;
171
172                // Use a generous 30-second timeout. The guest's StopEndpointCommand typically
173                // times out in 5 seconds. We want to wait much longer to guarantee the host kernel
174                // has time to finish aborting the URBs. In theory, we shouldn't hit this timeout,
175                // because the host driver would also time out around the same time and reset the
176                // HC, making all the in-flight URBs reap-able.
177                if retries > 300 {
178                    error!("Timeout waiting for host device to release all URBs.");
179                    break;
180                }
181            }
182
183            if let Err(e) = self_arc.finalize_detach() {
184                error!("failed to finalize USB detachment: {}", e);
185            }
186        });
187    }
188
189    /// Clean up the resources and remove itself from the event loop.
190    pub(crate) fn finalize_detach(&self) -> Result<()> {
191        let mut device = self.device.lock();
192        if let BackendDeviceType::HostDevice(host_device) = &mut *device {
193            host_device.device.lock().drop_dma_buffer();
194        }
195
196        // Ignore the error, because the event handler might already be detached in the event loop.
197        let _ = device.detach_event_handler(&self.event_loop);
198        Ok(())
199    }
200}
201
202/// Helper function to update xhci_transfer state.
203pub fn update_transfer_state(state: &mut XhciTransferState, status: TransferStatus) -> Result<()> {
204    if status == TransferStatus::Cancelled {
205        *state = XhciTransferState::Cancelled;
206        return Ok(());
207    }
208
209    match *state {
210        XhciTransferState::Cancelling => {
211            *state = XhciTransferState::Cancelled;
212        }
213        XhciTransferState::Submitted { .. } => {
214            *state = XhciTransferState::Completed;
215        }
216        _ => {
217            error!("xhci trasfer state is invalid");
218            *state = XhciTransferState::Completed;
219            return Err(Error::BadXhciTransferState);
220        }
221    }
222    Ok(())
223}