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