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