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