Skip to main content

strat9_kernel/hardware/usb/
uhci.rs

1// USB UHCI (Universal Host Controller Interface) Driver
2// Reference: UHCI spec (USB 1.1)
3//
4// Features:
5// - UHCI controller initialization
6// - Port management
7// - Frame list and TD/QH management
8// - Low-speed USB 1.1 support
9
10#![allow(dead_code)]
11
12use crate::{
13    hardware::pci_client::{self as pci, Bar, ProbeCriteria},
14    memory::{allocate_dma_frame, phys_to_virt},
15};
16use alloc::{sync::Arc, vec::Vec};
17use core::sync::atomic::{AtomicBool, Ordering};
18use spin::Mutex;
19use x86_64::instructions::port::Port;
20
21const UHCI_USBCMD: u16 = 0x00;
22const UHCI_USBSTS: u16 = 0x02;
23const UHCI_USBINTR: u16 = 0x04;
24const UHCI_FRNUM: u16 = 0x06;
25const UHCI_FRBASEADDR: u16 = 0x08;
26const UHCI_SOFMOD: u16 = 0x0C;
27const UHCI_PORTSC: u16 = 0x10;
28
29const USBCMD_RUN_STOP: u16 = 1 << 0;
30const USBCMD_HCRESET: u16 = 1 << 1;
31const USBCMD_EGSM: u16 = 1 << 3;
32const USBCMD_FGSM: u16 = 1 << 4;
33const USBCMD_CONFIGURE: u16 = 1 << 6;
34const USBCMD_MAX_PACKET: u16 = 1 << 7;
35
36const USBSTS_USBINT: u16 = 1 << 0;
37const USBSTS_USBERR: u16 = 1 << 1;
38const USBSTS_RD: u16 = 1 << 2;
39const USBSTS_HSE: u16 = 1 << 3;
40const USBSTS_HCPE: u16 = 1 << 4;
41const USBSTS_HCH: u16 = 1 << 5;
42
43const PORTSC_CCS: u16 = 1 << 0;
44const PORTSC_CSC: u16 = 1 << 1;
45const PORTSC_PE: u16 = 1 << 2;
46const PORTSC_PEC: u16 = 1 << 3;
47const PORTSC_LSDA: u16 = 1 << 8;
48const PORTSC_PR: u16 = 1 << 9;
49
50const TD_TOKEN_ACTIVE: u32 = 1 << 23;
51const TD_TOKEN_IOC: u32 = 1 << 24;
52const TD_TOKEN_LS: u32 = 1 << 26;
53const TD_TOKEN_ERRCNT_SHIFT: u32 = 27;
54
55const TD_LINK_PTR_MASK: u32 = 0xFFFFFFF0;
56const TD_LINK_VF: u32 = 1 << 0;
57const TD_LINK_QH: u32 = 1 << 1;
58
59#[repr(C)]
60struct UhciTD {
61    link_ptr: u32,
62    ctrl_status: u32,
63    token: u32,
64    buffer: u32,
65}
66
67#[repr(C)]
68struct UhciQH {
69    head_link: u32,
70    element_link: u32,
71}
72
73pub struct UhciPort {
74    port_num: usize,
75    enabled: bool,
76    connected: bool,
77    low_speed: bool,
78}
79
80pub struct UhciController {
81    io_base: u16,
82    usbcmd: Port<u16>,
83    usbsts: Port<u16>,
84    usbintr: Port<u16>,
85    frnum: Port<u16>,
86    frbaseaddr: Port<u32>,
87    sofmod: Port<u16>,
88    max_ports: usize,
89    ports: Vec<UhciPort>,
90    frame_list: *mut u32,
91    frame_list_phys: u64,
92}
93
94unsafe impl Send for UhciController {}
95unsafe impl Sync for UhciController {}
96
97impl UhciController {
98    /// Creates a new instance.
99    pub unsafe fn new(pci_dev: pci::PciDevice) -> Result<Arc<Self>, &'static str> {
100        let io_base = match pci_dev.read_bar(4) {
101            Some(Bar::Io { port }) => port as u16,
102            _ => return Err("Invalid BAR4"),
103        };
104
105        let mut controller = Self {
106            io_base,
107            usbcmd: Port::new(io_base + UHCI_USBCMD),
108            usbsts: Port::new(io_base + UHCI_USBSTS),
109            usbintr: Port::new(io_base + UHCI_USBINTR),
110            frnum: Port::new(io_base + UHCI_FRNUM),
111            frbaseaddr: Port::new(io_base + UHCI_FRBASEADDR),
112            sofmod: Port::new(io_base + UHCI_SOFMOD),
113            max_ports: 2, // UHCI typically has 2 ports
114            ports: Vec::new(),
115            frame_list: core::ptr::null_mut(),
116            frame_list_phys: 0,
117        };
118
119        controller.init()?;
120        Ok(Arc::new(controller))
121    }
122
123    /// Performs the init operation.
124    fn init(&mut self) -> Result<(), &'static str> {
125        unsafe {
126            let mut cmd = self.usbcmd.read();
127            cmd &= !USBCMD_RUN_STOP;
128            self.usbcmd.write(cmd);
129            let mut timeout = 10000;
130            while self.usbsts.read() & USBSTS_HCH == 0 {
131                core::hint::spin_loop();
132                timeout -= 1;
133                if timeout == 0 {
134                    return Err("UHCI: controller did not halt");
135                }
136            }
137
138            cmd = self.usbcmd.read();
139            cmd |= USBCMD_HCRESET;
140            self.usbcmd.write(cmd);
141            let mut reset_ok = false;
142            for _ in 0..10000 {
143                if self.usbcmd.read() & USBCMD_HCRESET == 0 {
144                    reset_ok = true;
145                    break;
146                }
147                core::hint::spin_loop();
148            }
149            if !reset_ok {
150                return Err("UHCI: controller reset timed out");
151            }
152
153            // Initialize ports
154            for i in 0..self.max_ports {
155                let portsc = self.read_portsc(i);
156                self.ports.push(UhciPort {
157                    port_num: i,
158                    enabled: (portsc & PORTSC_PE) != 0,
159                    connected: (portsc & PORTSC_CCS) != 0,
160                    low_speed: (portsc & PORTSC_LSDA) != 0,
161                });
162            }
163
164            // Initialize frame list
165            self.init_frame_list()?;
166
167            // Enable interrupts
168            self.usbintr
169                .write(USBSTS_USBINT | USBSTS_USBERR | USBSTS_RD);
170
171            // Start the controller
172            cmd = self.usbcmd.read();
173            cmd |= USBCMD_RUN_STOP | USBCMD_CONFIGURE | USBCMD_MAX_PACKET;
174            self.usbcmd.write(cmd);
175        }
176        Ok(())
177    }
178
179    /// Initializes frame list.
180    unsafe fn init_frame_list(&mut self) -> Result<(), &'static str> {
181        // Allocate frame list (4KB aligned, 1024 entries for 1ms frames)
182        let frame = allocate_dma_frame().ok_or("Failed to allocate frame list")?;
183        self.frame_list_phys = frame.start_address.as_u64();
184        self.frame_list = phys_to_virt(self.frame_list_phys) as *mut u32;
185        core::ptr::write_bytes(self.frame_list as *mut u8, 0, 4096);
186
187        // Set up frame list (all entries point to termination)
188        for i in 0..1024 {
189            *self.frame_list.add(i) = 0x0001; // Terminate bit
190        }
191
192        self.frbaseaddr
193            .write((self.frame_list_phys & 0xFFFFF000) as u32);
194
195        Ok(())
196    }
197
198    /// Reads portsc.
199    unsafe fn read_portsc(&self, port: usize) -> u16 {
200        let mut port_reg = Port::new(self.io_base + UHCI_PORTSC + (port as u16) * 2);
201        port_reg.read()
202    }
203
204    /// Writes portsc.
205    unsafe fn write_portsc(&self, port: usize, val: u16) {
206        let mut port_reg = Port::new(self.io_base + UHCI_PORTSC + (port as u16) * 2);
207        port_reg.write(val);
208    }
209
210    /// Performs the port count operation.
211    pub fn port_count(&self) -> usize {
212        self.max_ports
213    }
214
215    /// Returns whether port connected.
216    pub fn is_port_connected(&self, port: usize) -> bool {
217        if port >= self.ports.len() {
218            return false;
219        }
220        self.ports[port].connected
221    }
222
223    /// Returns whether low speed.
224    pub fn is_low_speed(&self, port: usize) -> bool {
225        if port >= self.ports.len() {
226            return false;
227        }
228        self.ports[port].low_speed
229    }
230}
231
232static UHCI_CONTROLLERS: Mutex<Vec<Arc<UhciController>>> = Mutex::new(Vec::new());
233static UHCI_INITIALIZED: AtomicBool = AtomicBool::new(false);
234
235/// Performs the init operation.
236pub fn init() {
237    log::info!("[UHCI] Scanning for UHCI controllers...");
238
239    let candidates = pci::probe_all(ProbeCriteria {
240        vendor_id: None,
241        device_id: None,
242        class_code: Some(0x0C),
243        subclass: Some(0x03),
244        prog_if: Some(0x00),
245    });
246
247    for pci_dev in candidates.into_iter() {
248        log::info!(
249            "UHCI: Found controller at {:?} (VEN:{:04x} DEV:{:04x})",
250            pci_dev.address,
251            pci_dev.vendor_id,
252            pci_dev.device_id
253        );
254
255        pci_dev.enable_bus_master();
256
257        match unsafe { UhciController::new(pci_dev) } {
258            Ok(controller) => {
259                log::info!("[UHCI] Initialized with {} ports", controller.port_count());
260                UHCI_CONTROLLERS.lock().push(controller);
261            }
262            Err(e) => {
263                log::warn!("UHCI: Failed to initialize controller: {}", e);
264            }
265        }
266    }
267
268    UHCI_INITIALIZED.store(true, Ordering::SeqCst);
269    log::info!(
270        "[UHCI] Found {} controller(s)",
271        UHCI_CONTROLLERS.lock().len()
272    );
273}
274
275/// Returns controller.
276pub fn get_controller(index: usize) -> Option<Arc<UhciController>> {
277    UHCI_CONTROLLERS.lock().get(index).cloned()
278}
279
280/// Returns whether available.
281pub fn is_available() -> bool {
282    UHCI_INITIALIZED.load(Ordering::Relaxed)
283}