devices/usb/xhci/
usb_hub.rs1use 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
65fn is_event_ring_uninitialized(e: &InterrupterError) -> bool {
67 matches!(e, InterrupterError::AddEvent(EventRingError::Uninitialized))
68}
69
70pub 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 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 pub fn port_id(&self) -> u8 {
101 self.port_id
102 }
103
104 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 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 pub fn backend_device(&self) -> MutexGuard<Option<Arc<Mutex<BackendDeviceType>>>> {
129 self.backend_device.lock()
130 }
131
132 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 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 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 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 pub fn send_device_connected_event(&self) -> std::result::Result<(), InterrupterError> {
197 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 pub fn send_device_disconnected_event(&self) -> std::result::Result<(), InterrupterError> {
212 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
223pub struct UsbHub {
225 ports: Vec<Arc<UsbPort>>,
226}
227
228impl UsbHub {
229 pub fn new(regs: &XhciRegs, interrupter: Arc<Mutex<Interrupter>>) -> UsbHub {
231 let mut ports = Vec::new();
232 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 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 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 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 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}