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::interrupter::Error as InterrupterError;
15use super::interrupter::Interrupter;
16use super::xhci_backend_device::BackendType;
17use super::xhci_backend_device::XhciBackendDevice;
18use super::xhci_regs::XhciRegs;
19use super::xhci_regs::MAX_PORTS;
20use super::xhci_regs::PORTSC_CONNECT_STATUS_CHANGE;
21use super::xhci_regs::PORTSC_CURRENT_CONNECT_STATUS;
22use super::xhci_regs::PORTSC_PORT_ENABLED;
23use super::xhci_regs::PORTSC_PORT_ENABLED_DISABLED_CHANGE;
24use super::xhci_regs::PORTSC_PORT_SPEED_MASK;
25use super::xhci_regs::PORTSC_PORT_SPEED_SHIFT;
26use super::xhci_regs::USB2_PORTS_END;
27use super::xhci_regs::USB2_PORTS_START;
28use super::xhci_regs::USB3_PORTS_END;
29use super::xhci_regs::USB3_PORTS_START;
30use super::xhci_regs::USB_STS_PORT_CHANGE_DETECT;
31use crate::register_space::Register;
32use crate::usb::backend::device::BackendDeviceType;
33
34#[sorted]
35#[derive(Error, Debug)]
36pub 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
62type Result<T> = std::result::Result<T, Error>;
63
64pub 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
74impl UsbPort {
75 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
93 pub fn port_id(&self) -> u8 {
95 self.port_id
96 }
97
98 pub fn detach(&self) -> Result<()> {
100 match self.backend_device().take() {
101 Some(backend_device) => {
102 backend_device.lock().stop();
103 }
104 None => {
105 return Err(Error::AlreadyDetached(self.port_id));
106 }
107 };
108 info!("usb_hub: device detached from port {}", self.port_id);
109 self.portsc.clear_bits(PORTSC_PORT_SPEED_MASK);
110 self.send_device_disconnected_event()
111 .map_err(|reason| Error::Detach {
112 port_id: self.port_id,
113 reason,
114 })
115 }
116
117 pub fn backend_device(&self) -> MutexGuard<Option<Arc<Mutex<BackendDeviceType>>>> {
119 self.backend_device.lock()
120 }
121
122 pub fn is_attached(&self) -> bool {
124 self.backend_device().is_some()
125 }
126
127 fn reset(&self) -> std::result::Result<(), InterrupterError> {
128 if self.is_attached() {
129 self.send_device_connected_event()?;
130 }
131 Ok(())
132 }
133
134 fn attach(
135 &self,
136 device: Arc<Mutex<BackendDeviceType>>,
137 ) -> std::result::Result<(), InterrupterError> {
138 info!("usb_hub: backend attached to port {}", self.port_id);
139 let speed = device.lock().get_speed();
140 let mut locked = self.backend_device();
141 assert!(locked.is_none());
142 *locked = Some(device);
143 self.portsc.clear_bits(PORTSC_PORT_SPEED_MASK);
144 let speed_id: u32 = match speed {
146 None => 0,
147 Some(DeviceSpeed::Full) => 1,
148 Some(DeviceSpeed::Low) => 2,
149 Some(DeviceSpeed::High) => 3,
150 Some(DeviceSpeed::Super) => 4,
151 Some(DeviceSpeed::SuperPlus) => 5,
152 };
153 self.portsc.set_bits(speed_id << PORTSC_PORT_SPEED_SHIFT);
154 self.send_device_connected_event()
155 }
156
157 pub fn send_device_connected_event(&self) -> std::result::Result<(), InterrupterError> {
160 self.portsc.set_bits(
162 PORTSC_CURRENT_CONNECT_STATUS
163 | PORTSC_PORT_ENABLED
164 | PORTSC_CONNECT_STATUS_CHANGE
165 | PORTSC_PORT_ENABLED_DISABLED_CHANGE,
166 );
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 pub fn send_device_disconnected_event(&self) -> std::result::Result<(), InterrupterError> {
175 self.portsc
177 .set_bits(PORTSC_CONNECT_STATUS_CHANGE | PORTSC_PORT_ENABLED_DISABLED_CHANGE);
178 self.portsc.clear_bits(PORTSC_CURRENT_CONNECT_STATUS);
179 self.usbsts.set_bits(USB_STS_PORT_CHANGE_DETECT);
180 self.interrupter
181 .lock()
182 .send_port_status_change_trb(self.port_id)
183 }
184}
185
186pub struct UsbHub {
188 ports: Vec<Arc<UsbPort>>,
189}
190
191impl UsbHub {
192 pub fn new(regs: &XhciRegs, interrupter: Arc<Mutex<Interrupter>>) -> UsbHub {
194 let mut ports = Vec::new();
195 assert_eq!(MAX_PORTS as usize, regs.portsc.len());
197
198 for i in USB2_PORTS_START..USB2_PORTS_END {
199 ports.push(Arc::new(UsbPort::new(
200 BackendType::Usb2,
201 i + 1,
202 regs.portsc[i as usize].clone(),
203 regs.usbsts.clone(),
204 interrupter.clone(),
205 )));
206 }
207
208 for i in USB3_PORTS_START..USB3_PORTS_END {
209 ports.push(Arc::new(UsbPort::new(
210 BackendType::Usb3,
211 i + 1,
212 regs.portsc[i as usize].clone(),
213 regs.usbsts.clone(),
214 interrupter.clone(),
215 )));
216 }
217 UsbHub { ports }
218 }
219
220 pub fn reset(&self) -> Result<()> {
222 for p in &self.ports {
223 p.reset().map_err(|reason| Error::Detach {
224 port_id: p.port_id(),
225 reason,
226 })?;
227 }
228 Ok(())
229 }
230
231 pub fn get_port(&self, port_id: u8) -> Option<Arc<UsbPort>> {
233 if port_id == 0 || port_id > MAX_PORTS {
234 return None;
235 }
236 let port_index = (port_id - 1) as usize;
237 Some(self.ports.get(port_index)?.clone())
238 }
239
240 pub fn connect_backend(&self, backend: Arc<Mutex<BackendDeviceType>>) -> Result<Arc<UsbPort>> {
242 for port in &self.ports {
243 if port.is_attached() {
244 continue;
245 }
246 if port.ty != backend.lock().get_backend_type() {
247 continue;
248 }
249 let port_id = port.port_id();
250 port.attach(backend)
251 .map_err(|reason| Error::Attach { port_id, reason })?;
252 return Ok(port.clone());
253 }
254 Err(Error::AllPortsAttached)
255 }
256
257 pub fn disconnect_port(&self, port_id: u8) -> Result<()> {
260 match self.get_port(port_id) {
261 Some(port) => port.detach(),
262 None => Err(Error::NoSuchPort(port_id)),
263 }
264 }
265}