devices/usb/xhci/
usb_hub.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::MutexGuard;
7
8use base::info;
9use remain::sorted;
10use sync::Mutex;
11use thiserror::Error;
12use usb_util::DeviceSpeed;
13
14use super::event_ring::Error as EventRingError;
15use super::interrupter::Error as InterrupterError;
16use super::interrupter::Interrupter;
17use super::xhci_backend_device::BackendType;
18use super::xhci_backend_device::XhciBackendDevice;
19use super::xhci_regs::XhciRegs;
20use super::xhci_regs::MAX_PORTS;
21use super::xhci_regs::PORTSC_CONNECT_STATUS_CHANGE;
22use super::xhci_regs::PORTSC_CURRENT_CONNECT_STATUS;
23use super::xhci_regs::PORTSC_PORT_ENABLED;
24use super::xhci_regs::PORTSC_PORT_ENABLED_DISABLED_CHANGE;
25use super::xhci_regs::PORTSC_PORT_SPEED_MASK;
26use super::xhci_regs::PORTSC_PORT_SPEED_SHIFT;
27use super::xhci_regs::USB2_PORTS_END;
28use super::xhci_regs::USB2_PORTS_START;
29use super::xhci_regs::USB3_PORTS_END;
30use super::xhci_regs::USB3_PORTS_START;
31use super::xhci_regs::USB_STS_PORT_CHANGE_DETECT;
32use crate::register_space::Register;
33use crate::usb::backend::device::BackendDeviceType;
34
35#[sorted]
36#[derive(Error, Debug)]
37pub enum Error {
38    #[error("all suitable ports already attached")]
39    AllPortsAttached,
40    #[error("device already detached from port {0}")]
41    AlreadyDetached(u8),
42    #[error("failed to attach device to port {port_id}: {reason}")]
43    Attach {
44        port_id: u8,
45        reason: InterrupterError,
46    },
47    #[error("failed to detach device from port {port_id}: {reason}")]
48    Detach {
49        port_id: u8,
50        reason: InterrupterError,
51    },
52    #[error("device {bus}:{addr}:{vid:04x}:{pid:04x} is not attached")]
53    NoSuchDevice {
54        bus: u8,
55        addr: u8,
56        vid: u16,
57        pid: u16,
58    },
59    #[error("port {0} does not exist")]
60    NoSuchPort(u8),
61}
62
63type Result<T> = std::result::Result<T, Error>;
64
65/// Whether a port status change event failed only because the guest has no event ring yet.
66fn is_event_ring_uninitialized(e: &InterrupterError) -> bool {
67    matches!(e, InterrupterError::AddEvent(EventRingError::Uninitialized))
68}
69
70/// A port on usb hub. It could have a device connected to it.
71pub struct UsbPort {
72    ty: BackendType,
73    port_id: u8,
74    portsc: Register<u32>,
75    usbsts: Register<u32>,
76    interrupter: Arc<Mutex<Interrupter>>,
77    backend_device: Mutex<Option<Arc<Mutex<BackendDeviceType>>>>,
78}
79
80impl UsbPort {
81    /// Create a new usb port that has nothing connected to it.
82    pub fn new(
83        ty: BackendType,
84        port_id: u8,
85        portsc: Register<u32>,
86        usbsts: Register<u32>,
87        interrupter: Arc<Mutex<Interrupter>>,
88    ) -> UsbPort {
89        UsbPort {
90            ty,
91            port_id,
92            portsc,
93            usbsts,
94            interrupter,
95            backend_device: Mutex::new(None),
96        }
97    }
98
99    /// Get the port ID.
100    pub fn port_id(&self) -> u8 {
101        self.port_id
102    }
103
104    /// Detach current connected backend. Returns an error when there is no backend connected.
105    pub fn detach(&self) -> Result<()> {
106        match self.backend_device().take() {
107            Some(backend_device) => {
108                backend_device.lock().stop();
109            }
110            None => {
111                return Err(Error::AlreadyDetached(self.port_id));
112            }
113        };
114        info!("usb_hub: device detached from port {}", self.port_id);
115        self.portsc.clear_bits(PORTSC_PORT_SPEED_MASK);
116        // As in attach(): the port registers already say the device is gone, so a guest that
117        // cannot be signalled yet still sees an empty port once it initializes.
118        match self.send_device_disconnected_event() {
119            Err(ref e) if is_event_ring_uninitialized(e) => Ok(()),
120            other => other.map_err(|reason| Error::Detach {
121                port_id: self.port_id,
122                reason,
123            }),
124        }
125    }
126
127    /// Get current connected backend.
128    pub fn backend_device(&self) -> MutexGuard<Option<Arc<Mutex<BackendDeviceType>>>> {
129        self.backend_device.lock()
130    }
131
132    /// Returns true if a backend device is currently attached to this port.
133    pub fn is_attached(&self) -> bool {
134        self.backend_device().is_some()
135    }
136
137    fn reset(&self) -> std::result::Result<(), InterrupterError> {
138        if self.is_attached() {
139            // This runs as part of the guest driver's own reset sequence, so the event ring is
140            // normally not up yet. See is_event_ring_uninitialized().
141            match self.send_device_connected_event() {
142                Err(ref e) if is_event_ring_uninitialized(e) => {
143                    info!(
144                        "usb_hub: port {} kept its device across the controller reset; the guest \
145                         will pick it up when it scans the ports",
146                        self.port_id
147                    );
148                }
149                other => other?,
150            }
151        }
152        Ok(())
153    }
154
155    fn attach(
156        &self,
157        device: Arc<Mutex<BackendDeviceType>>,
158    ) -> std::result::Result<(), InterrupterError> {
159        info!("usb_hub: backend attached to port {}", self.port_id);
160        let speed = device.lock().get_speed();
161        let mut locked = self.backend_device();
162        assert!(locked.is_none());
163        *locked = Some(device);
164        self.portsc.clear_bits(PORTSC_PORT_SPEED_MASK);
165        // Speed mappings from xHCI spec 7.2.2.1.1 ("Default Speed ID Mapping")
166        let speed_id: u32 = match speed {
167            None => 0,
168            Some(DeviceSpeed::Full) => 1,
169            Some(DeviceSpeed::Low) => 2,
170            Some(DeviceSpeed::High) => 3,
171            Some(DeviceSpeed::Super) => 4,
172            Some(DeviceSpeed::SuperPlus) => 5,
173        };
174        self.portsc.set_bits(speed_id << PORTSC_PORT_SPEED_SHIFT);
175        // A device may be plugged in before the guest driver has initialized, e.g. everything
176        // attached at VM startup. The guest driver programs the event ring segment table late in
177        // its own initialization, well after it resets the controller, so any port change raised
178        // before that point has nowhere to go. It is not lost, though: `PORTSC` is updated before
179        // the event is sent, and the guest scans every port once it finishes initializing.
180        // Treating this as an error kills the whole controller, because the reset path runs
181        // `UsbHub::reset` under a `fail_handle`.
182        match self.send_device_connected_event() {
183            Err(ref e) if is_event_ring_uninitialized(e) => {
184                info!(
185                    "usb_hub: port {} attached before the guest initialized its event ring",
186                    self.port_id
187                );
188                Ok(())
189            }
190            other => other,
191        }
192    }
193
194    /// Inform the guest kernel there is device connected to this port. It combines first few steps
195    /// of USB device initialization process in xHCI spec 4.3.
196    pub fn send_device_connected_event(&self) -> std::result::Result<(), InterrupterError> {
197        // xHCI spec 4.3.
198        self.portsc.set_bits(
199            PORTSC_CURRENT_CONNECT_STATUS
200                | PORTSC_PORT_ENABLED
201                | PORTSC_CONNECT_STATUS_CHANGE
202                | PORTSC_PORT_ENABLED_DISABLED_CHANGE,
203        );
204        self.usbsts.set_bits(USB_STS_PORT_CHANGE_DETECT);
205        self.interrupter
206            .lock()
207            .send_port_status_change_trb(self.port_id)
208    }
209
210    /// Inform the guest kernel that device has been detached.
211    pub fn send_device_disconnected_event(&self) -> std::result::Result<(), InterrupterError> {
212        // xHCI spec 4.3.
213        self.portsc
214            .set_bits(PORTSC_CONNECT_STATUS_CHANGE | PORTSC_PORT_ENABLED_DISABLED_CHANGE);
215        self.portsc.clear_bits(PORTSC_CURRENT_CONNECT_STATUS);
216        self.usbsts.set_bits(USB_STS_PORT_CHANGE_DETECT);
217        self.interrupter
218            .lock()
219            .send_port_status_change_trb(self.port_id)
220    }
221}
222
223/// UsbHub is a set of usb ports.
224pub struct UsbHub {
225    ports: Vec<Arc<UsbPort>>,
226}
227
228impl UsbHub {
229    /// Create usb hub with no device attached.
230    pub fn new(regs: &XhciRegs, interrupter: Arc<Mutex<Interrupter>>) -> UsbHub {
231        let mut ports = Vec::new();
232        // Each port should have a portsc register.
233        assert_eq!(MAX_PORTS as usize, regs.portsc.len());
234
235        for i in USB2_PORTS_START..USB2_PORTS_END {
236            ports.push(Arc::new(UsbPort::new(
237                BackendType::Usb2,
238                i + 1,
239                regs.portsc[i as usize].clone(),
240                regs.usbsts.clone(),
241                interrupter.clone(),
242            )));
243        }
244
245        for i in USB3_PORTS_START..USB3_PORTS_END {
246            ports.push(Arc::new(UsbPort::new(
247                BackendType::Usb3,
248                i + 1,
249                regs.portsc[i as usize].clone(),
250                regs.usbsts.clone(),
251                interrupter.clone(),
252            )));
253        }
254        UsbHub { ports }
255    }
256
257    /// Reset all ports.
258    pub fn reset(&self) -> Result<()> {
259        for p in &self.ports {
260            p.reset().map_err(|reason| Error::Detach {
261                port_id: p.port_id(),
262                reason,
263            })?;
264        }
265        Ok(())
266    }
267
268    /// Get a specific port of the hub.
269    pub fn get_port(&self, port_id: u8) -> Option<Arc<UsbPort>> {
270        if port_id == 0 || port_id > MAX_PORTS {
271            return None;
272        }
273        let port_index = (port_id - 1) as usize;
274        Some(self.ports.get(port_index)?.clone())
275    }
276
277    /// Connect backend to next empty port.
278    pub fn connect_backend(&self, backend: Arc<Mutex<BackendDeviceType>>) -> Result<Arc<UsbPort>> {
279        for port in &self.ports {
280            if port.is_attached() {
281                continue;
282            }
283            if port.ty != backend.lock().get_backend_type() {
284                continue;
285            }
286            let port_id = port.port_id();
287            port.attach(backend)
288                .map_err(|reason| Error::Attach { port_id, reason })?;
289            return Ok(port.clone());
290        }
291        Err(Error::AllPortsAttached)
292    }
293
294    /// Disconnect device from port. Returns false if port id is not valid or could not be
295    /// disonnected.
296    pub fn disconnect_port(&self, port_id: u8) -> Result<()> {
297        match self.get_port(port_id) {
298            Some(port) => port.detach(),
299            None => Err(Error::NoSuchPort(port_id)),
300        }
301    }
302}