1#![allow(dead_code)]
11
12use crate::{
13 arch::x86_64::io::{inw, outw},
14 hardware::pci_client::{self as pci, Bar, ProbeCriteria},
15 memory::{allocate_zeroed_frame, phys_to_virt},
16 x86_crate_shim::instructions::port::Port,
17};
18use alloc::{sync::Arc, vec::Vec};
19use core::sync::atomic::{AtomicBool, Ordering};
20use spin::Mutex;
21
22const UHCI_USBCMD: u16 = 0x00;
23const UHCI_USBSTS: u16 = 0x02;
24const UHCI_USBINTR: u16 = 0x04;
25const UHCI_FRNUM: u16 = 0x06;
26const UHCI_FRBASEADDR: u16 = 0x08;
27const UHCI_SOFMOD: u16 = 0x0C;
28const UHCI_PORTSC: u16 = 0x10;
29
30const USBCMD_RUN_STOP: u16 = 1 << 0;
31const USBCMD_HCRESET: u16 = 1 << 1;
32const USBCMD_EGSM: u16 = 1 << 3;
33const USBCMD_FGSM: u16 = 1 << 4;
34const USBCMD_CONFIGURE: u16 = 1 << 6;
35const USBCMD_MAX_PACKET: u16 = 1 << 7;
36
37const USBSTS_USBINT: u16 = 1 << 0;
38const USBSTS_USBERR: u16 = 1 << 1;
39const USBSTS_RD: u16 = 1 << 2;
40const USBSTS_HSE: u16 = 1 << 3;
41const USBSTS_HCPE: u16 = 1 << 4;
42const USBSTS_HCH: u16 = 1 << 5;
43
44const PORTSC_CCS: u16 = 1 << 0;
45const PORTSC_CSC: u16 = 1 << 1;
46const PORTSC_PE: u16 = 1 << 2;
47const PORTSC_PEC: u16 = 1 << 3;
48const PORTSC_LSDA: u16 = 1 << 8;
49const PORTSC_PR: u16 = 1 << 9;
50
51const TD_TOKEN_ACTIVE: u32 = 1 << 23;
52const TD_TOKEN_IOC: u32 = 1 << 24;
53const TD_TOKEN_LS: u32 = 1 << 26;
54const TD_TOKEN_ERRCNT_SHIFT: u32 = 27;
55const TD_TOKEN_TOGGLE: u32 = 1 << 19;
56const TD_TOKEN_MAXPKT_SHIFT: u32 = 16;
57const TD_TOKEN_MAXPKT_MASK: u32 = 0x7FF << 16;
58const TD_TOKEN_DEVADDR_SHIFT: u32 = 9;
59const TD_TOKEN_ENDPT_SHIFT: u32 = 6;
60
61const TD_LINK_PTR_MASK: u32 = 0xFFFFFFF0;
62const TD_LINK_VF: u32 = 1 << 0;
63const TD_LINK_QH: u32 = 1 << 1;
64
65#[repr(C)]
66struct UhciTD {
67 link_ptr: u32,
68 ctrl_status: u32,
69 token: u32,
70 buffer: u32,
71}
72
73#[repr(C)]
74struct UhciQH {
75 head_link: u32,
76 element_link: u32,
77}
78
79pub struct UhciPort {
80 port_num: usize,
81 enabled: bool,
82 connected: bool,
83 low_speed: bool,
84}
85
86pub struct UhciController {
87 io_base: u16,
88 usbcmd: Port<u16>,
89 usbsts: Port<u16>,
90 usbintr: Port<u16>,
91 frnum: Port<u16>,
92 frbaseaddr: Port<u32>,
93 sofmod: Port<u16>,
94 max_ports: usize,
95 ports: Vec<UhciPort>,
96 frame_list: *mut u32,
97 frame_list_phys: u64,
98}
99
100unsafe impl Send for UhciController {}
101unsafe impl Sync for UhciController {}
102
103impl UhciController {
104 pub unsafe fn new(pci_dev: pci::PciDevice) -> Result<Arc<Self>, &'static str> {
106 let io_base = match pci_dev.read_bar(4) {
107 Some(Bar::Io { port }) => port as u16,
108 _ => return Err("Invalid BAR4"),
109 };
110
111 let mut controller = Self {
112 io_base,
113 usbcmd: Port::new(io_base + UHCI_USBCMD),
114 usbsts: Port::new(io_base + UHCI_USBSTS),
115 usbintr: Port::new(io_base + UHCI_USBINTR),
116 frnum: Port::new(io_base + UHCI_FRNUM),
117 frbaseaddr: Port::new(io_base + UHCI_FRBASEADDR),
118 sofmod: Port::new(io_base + UHCI_SOFMOD),
119 max_ports: 2, ports: Vec::new(),
121 frame_list: core::ptr::null_mut(),
122 frame_list_phys: 0,
123 };
124
125 controller.init()?;
126 Ok(Arc::new(controller))
127 }
128
129 fn init(&mut self) -> Result<(), &'static str> {
131 unsafe {
132 let mut cmd = self.usbcmd.read();
133 cmd &= !USBCMD_RUN_STOP;
134 self.usbcmd.write(cmd);
135 let mut timeout = 10000;
136 while self.usbsts.read() & USBSTS_HCH == 0 {
137 core::hint::spin_loop();
138 timeout -= 1;
139 if timeout == 0 {
140 return Err("UHCI: controller did not halt");
141 }
142 }
143
144 cmd = self.usbcmd.read();
145 cmd |= USBCMD_HCRESET;
146 self.usbcmd.write(cmd);
147 let mut reset_ok = false;
148 for _ in 0..10000 {
149 if self.usbcmd.read() & USBCMD_HCRESET == 0 {
150 reset_ok = true;
151 break;
152 }
153 core::hint::spin_loop();
154 }
155 if !reset_ok {
156 return Err("UHCI: controller reset timed out");
157 }
158
159 for i in 0..self.max_ports {
161 let portsc = self.read_portsc(i);
162 self.ports.push(UhciPort {
163 port_num: i,
164 enabled: (portsc & PORTSC_PE) != 0,
165 connected: (portsc & PORTSC_CCS) != 0,
166 low_speed: (portsc & PORTSC_LSDA) != 0,
167 });
168 }
169
170 self.init_frame_list()?;
172
173 self.usbintr
175 .write(USBSTS_USBINT | USBSTS_USBERR | USBSTS_RD);
176
177 cmd = self.usbcmd.read();
179 cmd |= USBCMD_RUN_STOP | USBCMD_CONFIGURE | USBCMD_MAX_PACKET;
180 self.usbcmd.write(cmd);
181 }
182 Ok(())
183 }
184
185 unsafe fn init_frame_list(&mut self) -> Result<(), &'static str> {
187 let frame = allocate_zeroed_frame().ok_or("Failed to allocate frame list")?;
189 self.frame_list_phys = frame.start_address.as_u64();
190 self.frame_list = phys_to_virt(self.frame_list_phys) as *mut u32;
191 core::ptr::write_bytes(self.frame_list as *mut u8, 0, 4096);
192
193 for i in 0..1024 {
195 *self.frame_list.add(i) = 0x0001; }
197
198 self.frbaseaddr
199 .write((self.frame_list_phys & 0xFFFFF000) as u32);
200
201 Ok(())
202 }
203
204 unsafe fn read_portsc(&self, port: usize) -> u16 {
206 let mut port_reg = Port::new(self.io_base + UHCI_PORTSC + (port as u16) * 2);
207 port_reg.read()
208 }
209
210 unsafe fn write_portsc(&self, port: usize, val: u16) {
212 let mut port_reg = Port::new(self.io_base + UHCI_PORTSC + (port as u16) * 2);
213 port_reg.write(val);
214 }
215
216 pub fn port_count(&self) -> usize {
218 self.max_ports
219 }
220
221 pub fn is_port_connected(&self, port: usize) -> bool {
223 if port >= self.ports.len() {
224 return false;
225 }
226 self.ports[port].connected
227 }
228
229 pub fn is_low_speed(&self, port: usize) -> bool {
231 if port >= self.ports.len() {
232 return false;
233 }
234 self.ports[port].low_speed
235 }
236
237 unsafe fn reset_port(&self, port: usize) -> bool {
239 let mut portsc = self.read_portsc(port);
240 if portsc & PORTSC_CCS == 0 {
241 return false;
242 }
243
244 self.write_portsc(port, portsc | PORTSC_PR);
246 for _ in 0..10_000u32 {
247 core::hint::spin_loop();
248 }
249 self.write_portsc(port, self.read_portsc(port) & !PORTSC_PR);
250 for _ in 0..10_000u32 {
251 core::hint::spin_loop();
252 }
253
254 for _ in 0..100_000u32 {
256 portsc = self.read_portsc(port);
257 if portsc & PORTSC_PE != 0 {
258 return true;
259 }
260 if portsc & PORTSC_CCS == 0 {
261 return false;
262 }
263 core::hint::spin_loop();
264 }
265 false
266 }
267
268 unsafe fn ctrl_transfer(
273 &self,
274 port: usize,
275 setup_data: &[u8; 8],
276 data_buf: Option<&mut [u8]>,
277 data_len: usize,
278 device_addr: u8,
279 max_packet: u32,
280 low_speed: bool,
281 ) -> Result<usize, &'static str> {
282 let qh_frame = allocate_zeroed_frame().ok_or("UHCI: QH alloc failed")?;
284 let qh_phys = qh_frame.start_address.as_u64();
285 let qh_virt = phys_to_virt(qh_phys) as *mut UhciQH;
286
287 let td_setup_frame = allocate_zeroed_frame().ok_or("UHCI: TD alloc failed")?;
289 let td_setup_phys = td_setup_frame.start_address.as_u64();
290 let td_setup_virt = phys_to_virt(td_setup_phys) as *mut UhciTD;
291
292 let setup_buf_frame = allocate_zeroed_frame().ok_or("UHCI: setup buf alloc failed")?;
294 let setup_buf_phys = setup_buf_frame.start_address.as_u64();
295 let setup_buf_virt = phys_to_virt(setup_buf_phys) as *mut u8;
296 core::ptr::copy_nonoverlapping(setup_data.as_ptr(), setup_buf_virt, 8);
297
298 let dir_in = (setup_data[0] & 0x80) != 0;
299 let has_data = data_buf.is_some();
300
301 let ctrl_base = TD_TOKEN_ACTIVE
318 | (3u32 << TD_TOKEN_ERRCNT_SHIFT)
319 | (if low_speed { TD_TOKEN_LS } else { 0 });
320
321 let setup_token = (8u32 << TD_TOKEN_MAXPKT_SHIFT)
324 | ((device_addr as u32 & 0x7F) << TD_TOKEN_DEVADDR_SHIFT)
325 | (0u32 << TD_TOKEN_ENDPT_SHIFT)
326 | 0x2Du32; (*td_setup_virt).link_ptr = 0;
328 (*td_setup_virt).ctrl_status = ctrl_base;
329 (*td_setup_virt).token = setup_token;
330 (*td_setup_virt).buffer = setup_buf_phys as u32;
331
332 let td_status_frame = allocate_zeroed_frame().ok_or("UHCI: status TD alloc failed")?;
334 let td_status_phys = td_status_frame.start_address.as_u64();
335 let td_status_virt = phys_to_virt(td_status_phys) as *mut UhciTD;
336
337 if has_data && data_len > 0 {
338 let data_buf_frame = allocate_zeroed_frame().ok_or("UHCI: data buf alloc failed")?;
340 let data_buf_phys_addr = data_buf_frame.start_address.as_u64();
341 let data_buf_virt_addr = phys_to_virt(data_buf_phys_addr) as *mut u8;
342
343 if !dir_in {
344 if let Some(ref buf) = data_buf {
345 core::ptr::copy_nonoverlapping(buf.as_ptr(), data_buf_virt_addr, data_len);
346 }
347 }
348
349 let td_data_frame = allocate_zeroed_frame().ok_or("UHCI: data TD alloc failed")?;
351 let td_data_phys = td_data_frame.start_address.as_u64();
352 let td_data_virt = phys_to_virt(td_data_phys) as *mut UhciTD;
353
354 let data_pid: u32 = if dir_in { 0x69 } else { 0xE1 };
355 let data_token = TD_TOKEN_TOGGLE
356 | (((data_len as u32) & 0x7FF) << TD_TOKEN_MAXPKT_SHIFT)
357 | ((device_addr as u32 & 0x7F) << TD_TOKEN_DEVADDR_SHIFT)
358 | (0u32 << TD_TOKEN_ENDPT_SHIFT)
359 | data_pid;
360 (*td_data_virt).link_ptr = 0;
361 (*td_data_virt).ctrl_status = ctrl_base;
362 (*td_data_virt).token = data_token;
363 (*td_data_virt).buffer = data_buf_phys_addr as u32;
364
365 let status_pid: u32 = if dir_in { 0xE1 } else { 0x69 };
367 let status_token = TD_TOKEN_IOC
368 | (0u32 << TD_TOKEN_MAXPKT_SHIFT)
369 | ((device_addr as u32 & 0x7F) << TD_TOKEN_DEVADDR_SHIFT)
370 | (0u32 << TD_TOKEN_ENDPT_SHIFT)
371 | status_pid;
372 (*td_status_virt).link_ptr = 0;
373 (*td_status_virt).ctrl_status = ctrl_base;
374 (*td_status_virt).token = status_token;
375 (*td_status_virt).buffer = 0;
376
377 (*td_setup_virt).link_ptr = (td_data_phys as u32) | TD_LINK_VF;
379 (*td_data_virt).link_ptr = (td_status_phys as u32) | TD_LINK_VF;
380 } else {
381 let status_token = TD_TOKEN_IOC
383 | TD_TOKEN_TOGGLE
384 | (0u32 << TD_TOKEN_MAXPKT_SHIFT)
385 | ((device_addr as u32 & 0x7F) << TD_TOKEN_DEVADDR_SHIFT)
386 | (0u32 << TD_TOKEN_ENDPT_SHIFT)
387 | 0x69u32; (*td_status_virt).link_ptr = 0;
389 (*td_status_virt).ctrl_status = ctrl_base;
390 (*td_status_virt).token = status_token;
391 (*td_status_virt).buffer = 0;
392
393 (*td_setup_virt).link_ptr = (td_status_phys as u32) | TD_LINK_VF;
394 }
395
396 (*qh_virt).head_link = 0x0000_0002; (*qh_virt).element_link = td_setup_phys as u32;
399
400 let status_td_phys = if has_data && data_len > 0 {
402 td_setup_phys + 64 } else {
404 td_setup_phys + 32 };
406
407 let frame_idx = unsafe { inw(self.io_base + UHCI_FRNUM) } as usize % 1024;
409 let old_frame = core::ptr::read_volatile(self.frame_list.add(frame_idx));
410 core::ptr::write_volatile(
411 self.frame_list.add(frame_idx),
412 (qh_phys as u32 & 0xFFFFFFFE) | TD_LINK_QH,
413 );
414
415 let status_td_virt = phys_to_virt(status_td_phys) as *const UhciTD;
417 let mut transferred = 0;
418 for _ in 0..1_000_000u32 {
419 let token = core::ptr::read_volatile(core::ptr::addr_of!((*status_td_virt).token));
420 if token & TD_TOKEN_ACTIVE == 0 {
421 if dir_in && has_data && data_len > 0 {
422 if let Some(buf) = data_buf {
423 let data_td_virt = phys_to_virt(td_setup_phys + 32) as *const UhciTD;
425 let data_buf_ptr = phys_to_virt((*data_td_virt).buffer as u64) as *const u8;
426 core::ptr::copy_nonoverlapping(data_buf_ptr, buf.as_mut_ptr(), data_len);
427 transferred = data_len;
428 }
429 }
430 break;
431 }
432 core::hint::spin_loop();
433 }
434
435 core::ptr::write_volatile(self.frame_list.add(frame_idx), old_frame);
437
438 Ok(transferred)
439 }
440
441 fn enumerate_all_ports(&self) {
443 unsafe {
444 core::arch::asm!("out 0xe9, al", in("al") b'W', options(nomem, nostack));
445 }
446 let mut usb_address: u8 = 1;
447
448 for port in 0..self.max_ports {
449 let portsc = unsafe { self.read_portsc(port) };
450 if portsc & PORTSC_CCS == 0 {
451 continue;
452 }
453
454 log::info!("[UHCI] Port {} connected, resetting...", port);
455
456 if !unsafe { self.reset_port(port) } {
457 log::warn!("[UHCI] Port {} reset failed", port);
458 continue;
459 }
460
461 let low_speed = unsafe { self.is_low_speed(port) };
462 let max_packet: u32 = if low_speed { 8 } else { 64 };
463 log::info!(
464 "[UHCI] Port {} low_speed={} max_pkt={}",
465 port,
466 low_speed,
467 max_packet
468 );
469
470 let addr = usb_address;
472 let ctrl_dev_addr = [0x00u8, 0x05, addr, 0x00, 0x00, 0x00, 0x00, 0x00];
473 if unsafe {
474 self.ctrl_transfer(port, &ctrl_dev_addr, None, 0, 0, max_packet, low_speed)
475 }
476 .is_err()
477 {
478 log::warn!("[UHCI] Port {} set address failed", port);
479 continue;
480 }
481
482 let get_desc_8 = [0x80u8, 0x06, 0x00, 0x01, 0x00, 0x00, 8, 0x00];
484 let mut desc8 = [0u8; 8];
485 if unsafe {
486 self.ctrl_transfer(
487 port,
488 &get_desc_8,
489 Some(&mut desc8),
490 8,
491 addr,
492 max_packet,
493 low_speed,
494 )
495 }
496 .is_err()
497 {
498 log::warn!("[UHCI] Port {} get desc (8) failed", addr);
499 usb_address += 1;
500 continue;
501 }
502
503 let vid = u16::from_le_bytes([desc8[2], desc8[3]]);
504 let pid = u16::from_le_bytes([desc8[4], desc8[5]]);
505 let max_pkt0 = desc8[7] as u16;
506 log::info!(
507 "[UHCI] Port {} device VID={:04x} PID={:04x} max_pkt0={}",
508 port,
509 vid,
510 pid,
511 max_pkt0
512 );
513
514 let get_desc_18 = [0x80u8, 0x06, 0x00, 0x01, 0x00, 0x00, 18, 0x00];
516 let mut dev_desc = [0u8; 18];
517 let _ = unsafe {
518 self.ctrl_transfer(
519 port,
520 &get_desc_18,
521 Some(&mut dev_desc),
522 18,
523 addr,
524 max_pkt0 as u32,
525 low_speed,
526 )
527 };
528
529 let set_config = [0x00u8, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
531 let _ = unsafe {
532 self.ctrl_transfer(port, &set_config, None, 0, addr, max_pkt0 as u32, low_speed)
533 };
534
535 crate::hardware::usb::hid::enumerate_device(port, addr as u8, &dev_desc);
536
537 usb_address += 1;
538 }
539 }
540}
541
542static UHCI_CONTROLLERS: Mutex<Vec<Arc<UhciController>>> = Mutex::new(Vec::new());
543static UHCI_INITIALIZED: AtomicBool = AtomicBool::new(false);
544
545pub fn init() {
547 log::info!("[UHCI] Scanning for UHCI controllers...");
548
549 let candidates = pci::probe_all(ProbeCriteria {
550 vendor_id: None,
551 device_id: None,
552 class_code: Some(0x0C),
553 subclass: Some(0x03),
554 prog_if: Some(0x00),
555 });
556
557 unsafe {
558 core::arch::asm!("out 0xe9, al", in("al") b'q', options(nomem, nostack));
559 core::arch::asm!("out 0xe9, al", in("al") (b'0' + candidates.len() as u8), options(nomem, nostack));
560 core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
561 }
562 for pci_dev in candidates.into_iter() {
563 log::info!(
564 "UHCI: Found controller at {:?} (VEN:{:04x} DEV:{:04x})",
565 pci_dev.address,
566 pci_dev.vendor_id,
567 pci_dev.device_id
568 );
569
570 pci_dev.enable_bus_master();
571
572 match unsafe { UhciController::new(pci_dev) } {
573 Ok(controller) => {
574 log::info!("[UHCI] Initialized with {} ports", controller.port_count());
575 unsafe {
576 core::arch::asm!("out 0xe9, al", in("al") b'Q', options(nomem, nostack));
577 core::arch::asm!("out 0xe9, al", in("al") (b'0' + controller.port_count() as u8), options(nomem, nostack));
578 core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
579 }
580 controller.enumerate_all_ports();
581 UHCI_CONTROLLERS.lock().push(controller);
582 }
583 Err(e) => {
584 log::warn!("UHCI: Failed to initialize controller: {}", e);
585 }
586 }
587 }
588
589 UHCI_INITIALIZED.store(true, Ordering::SeqCst);
590 log::info!(
591 "[UHCI] Found {} controller(s)",
592 UHCI_CONTROLLERS.lock().len()
593 );
594}
595
596pub fn get_controller(index: usize) -> Option<Arc<UhciController>> {
598 UHCI_CONTROLLERS.lock().get(index).cloned()
599}
600
601pub fn is_available() -> bool {
603 UHCI_INITIALIZED.load(Ordering::Relaxed)
604}