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