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