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::interrupter::Error as InterrupterError;
15use super::interrupter::Interrupter;
16use super::xhci_backend_device::BackendType;
17use super::xhci_backend_device::XhciBackendDevice;
18use super::xhci_regs::XhciRegs;
19use super::xhci_regs::MAX_PORTS;
20use super::xhci_regs::PORTSC_CONNECT_STATUS_CHANGE;
21use super::xhci_regs::PORTSC_CURRENT_CONNECT_STATUS;
22use super::xhci_regs::PORTSC_PORT_ENABLED;
23use super::xhci_regs::PORTSC_PORT_ENABLED_DISABLED_CHANGE;
24use super::xhci_regs::PORTSC_PORT_SPEED_MASK;
25use super::xhci_regs::PORTSC_PORT_SPEED_SHIFT;
26use super::xhci_regs::USB2_PORTS_END;
27use super::xhci_regs::USB2_PORTS_START;
28use super::xhci_regs::USB3_PORTS_END;
29use super::xhci_regs::USB3_PORTS_START;
30use super::xhci_regs::USB_STS_PORT_CHANGE_DETECT;
31use crate::register_space::Register;
32use crate::usb::backend::device::BackendDeviceType;
33
34#[sorted]
35#[derive(Error, Debug)]
36pub enum Error {
37    #[error("all suitable ports already attached")]
38    AllPortsAttached,
39    #[error("device already detached from port {0}")]
40    AlreadyDetached(u8),
41    #[error("failed to attach device to port {port_id}: {reason}")]
42    Attach {
43        port_id: u8,
44        reason: InterrupterError,
45    },
46    #[error("failed to detach device from port {port_id}: {reason}")]
47    Detach {
48        port_id: u8,
49        reason: InterrupterError,
50    },
51    #[error("device {bus}:{addr}:{vid:04x}:{pid:04x} is not attached")]
52    NoSuchDevice {
53        bus: u8,
54        addr: u8,
55        vid: u16,
56        pid: u16,
57    },
58    #[error("port {0} does not exist")]
59    NoSuchPort(u8),
60}
61
62type Result<T> = std::result::Result<T, Error>;
63
64/// A port on usb hub. It could have a device connected to it.
65pub struct UsbPort {
66    ty: BackendType,
67    port_id: u8,
68    portsc: Register<u32>,
69    usbsts: Register<u32>,
70    interrupter: Arc<Mutex<Interrupter>>,
71    backend_device: Mutex<Option<Arc<Mutex<BackendDeviceType>>>>,
72}
73
74impl UsbPort {
75    /// Create a new usb port that has nothing connected to it.
76    pub fn new(
77        ty: BackendType,
78        port_id: u8,
79        portsc: Register<u32>,
80        usbsts: Register<u32>,
81        interrupter: Arc<Mutex<Interrupter>>,
82    ) -> UsbPort {
83        UsbPort {
84            ty,
85            port_id,
86            portsc,
87            usbsts,
88            interrupter,
89            backend_device: Mutex::new(None),
90        }
91    }
92
93    /// Get the port ID.
94    pub fn port_id(&self) -> u8 {
95        self.port_id
96    }
97
98    /// Detach current connected backend. Returns an error when there is no backend connected.
99    pub fn detach(&self) -> Result<()> {
100        match self.backend_device().take() {
101            Some(backend_device) => {
102                backend_device.lock().stop();
103            }
104            None => {
105                return Err(Error::AlreadyDetached(self.port_id));
106            }
107        };
108        info!("usb_hub: device detached from port {}", self.port_id);
109        self.portsc.clear_bits(PORTSC_PORT_SPEED_MASK);
110        self.send_device_disconnected_event()
111            .map_err(|reason| Error::Detach {
112                port_id: self.port_id,
113                reason,
114            })
115    }
116
117    /// Get current connected backend.
118    pub fn backend_device(&self) -> MutexGuard<Option<Arc<Mutex<BackendDeviceType>>>> {
119        self.backend_device.lock()
120    }
121
122    /// Returns true if a backend device is currently attached to this port.
123    pub fn is_attached(&self) -> bool {
124        self.backend_device().is_some()
125    }
126
127    fn reset(&self) -> std::result::Result<(), InterrupterError> {
128        if self.is_attached() {
129            self.send_device_connected_event()?;
130        }
131        Ok(())
132    }
133
134    fn attach(
135        &self,
136        device: Arc<Mutex<BackendDeviceType>>,
137    ) -> std::result::Result<(), InterrupterError> {
138        info!("usb_hub: backend attached to port {}", self.port_id);
139        let speed = device.lock().get_speed();
140        let mut locked = self.backend_device();
141        assert!(locked.is_none());
142        *locked = Some(device);
143        self.portsc.clear_bits(PORTSC_PORT_SPEED_MASK);
144        // Speed mappings from xHCI spec 7.2.2.1.1 ("Default Speed ID Mapping")
145        let speed_id: u32 = match speed {
146            None => 0,
147            Some(DeviceSpeed::Full) => 1,
148            Some(DeviceSpeed::Low) => 2,
149            Some(DeviceSpeed::High) => 3,
150            Some(DeviceSpeed::Super) => 4,
151            Some(DeviceSpeed::SuperPlus) => 5,
152        };
153        self.portsc.set_bits(speed_id << PORTSC_PORT_SPEED_SHIFT);
154        self.send_device_connected_event()
155    }
156
157    /// Inform the guest kernel there is device connected to this port. It combines first few steps
158    /// of USB device initialization process in xHCI spec 4.3.
159    pub fn send_device_connected_event(&self) -> std::result::Result<(), InterrupterError> {
160        // xHCI spec 4.3.
161        self.portsc.set_bits(
162            PORTSC_CURRENT_CONNECT_STATUS
163                | PORTSC_PORT_ENABLED
164                | PORTSC_CONNECT_STATUS_CHANGE
165                | PORTSC_PORT_ENABLED_DISABLED_CHANGE,
166        );
167        self.usbsts.set_bits(USB_STS_PORT_CHANGE_DETECT);
168        self.interrupter
169            .lock()
170            .send_port_status_change_trb(self.port_id)
171    }
172
173    /// Inform the guest kernel that device has been detached.
174    pub fn send_device_disconnected_event(&self) -> std::result::Result<(), InterrupterError> {
175        // xHCI spec 4.3.
176        self.portsc
177            .set_bits(PORTSC_CONNECT_STATUS_CHANGE | PORTSC_PORT_ENABLED_DISABLED_CHANGE);
178        self.portsc.clear_bits(PORTSC_CURRENT_CONNECT_STATUS);
179        self.usbsts.set_bits(USB_STS_PORT_CHANGE_DETECT);
180        self.interrupter
181            .lock()
182            .send_port_status_change_trb(self.port_id)
183    }
184}
185
186/// UsbHub is a set of usb ports.
187pub struct UsbHub {
188    ports: Vec<Arc<UsbPort>>,
189}
190
191impl UsbHub {
192    /// Create usb hub with no device attached.
193    pub fn new(regs: &XhciRegs, interrupter: Arc<Mutex<Interrupter>>) -> UsbHub {
194        let mut ports = Vec::new();
195        // Each port should have a portsc register.
196        assert_eq!(MAX_PORTS as usize, regs.portsc.len());
197
198        for i in USB2_PORTS_START..USB2_PORTS_END {
199            ports.push(Arc::new(UsbPort::new(
200                BackendType::Usb2,
201                i + 1,
202                regs.portsc[i as usize].clone(),
203                regs.usbsts.clone(),
204                interrupter.clone(),
205            )));
206        }
207
208        for i in USB3_PORTS_START..USB3_PORTS_END {
209            ports.push(Arc::new(UsbPort::new(
210                BackendType::Usb3,
211                i + 1,
212                regs.portsc[i as usize].clone(),
213                regs.usbsts.clone(),
214                interrupter.clone(),
215            )));
216        }
217        UsbHub { ports }
218    }
219
220    /// Reset all ports.
221    pub fn reset(&self) -> Result<()> {
222        for p in &self.ports {
223            p.reset().map_err(|reason| Error::Detach {
224                port_id: p.port_id(),
225                reason,
226            })?;
227        }
228        Ok(())
229    }
230
231    /// Get a specific port of the hub.
232    pub fn get_port(&self, port_id: u8) -> Option<Arc<UsbPort>> {
233        if port_id == 0 || port_id > MAX_PORTS {
234            return None;
235        }
236        let port_index = (port_id - 1) as usize;
237        Some(self.ports.get(port_index)?.clone())
238    }
239
240    /// Connect backend to next empty port.
241    pub fn connect_backend(&self, backend: Arc<Mutex<BackendDeviceType>>) -> Result<Arc<UsbPort>> {
242        for port in &self.ports {
243            if port.is_attached() {
244                continue;
245            }
246            if port.ty != backend.lock().get_backend_type() {
247                continue;
248            }
249            let port_id = port.port_id();
250            port.attach(backend)
251                .map_err(|reason| Error::Attach { port_id, reason })?;
252            return Ok(port.clone());
253        }
254        Err(Error::AllPortsAttached)
255    }
256
257    /// Disconnect device from port. Returns false if port id is not valid or could not be
258    /// disonnected.
259    pub fn disconnect_port(&self, port_id: u8) -> Result<()> {
260        match self.get_port(port_id) {
261            Some(port) => port.detach(),
262            None => Err(Error::NoSuchPort(port_id)),
263        }
264    }
265}