Skip to main content

strat9_kernel/hardware/usb/
ehci.rs

1// USB EHCI (Enhanced Host Controller Interface) Driver
2// Reference: EHCI spec 1.0 (USB 2.0)
3//
4// Features:
5// - EHCI controller initialization
6// - Port management
7// - Periodic and asynchronous schedules
8// - High-speed USB 2.0 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;
19
20const EHCI_MMIO_SIZE: usize = 0x1000;
21
22const USBCMD_RUN_STOP: u32 = 1 << 0;
23const USBCMD_HCRST: u32 = 1 << 1;
24const USBCMD_INTE: u32 = 1 << 2;
25const USBCMD_PSE: u32 = 1 << 4;
26const USBCMD_ASE: u32 = 1 << 5;
27
28const USBSTS_INT: u32 = 1 << 0;
29const USBSTS_ERR: u32 = 1 << 1;
30const USBSTS_PCD: u32 = 1 << 2;
31const USBSTS_HCH: u32 = 1 << 12;
32
33const PORTSC_CCS: u32 = 1 << 0;
34const PORTSC_CSC: u32 = 1 << 1;
35const PORTSC_PE: u32 = 1 << 2;
36const PORTSC_PEC: u32 = 1 << 3;
37const PORTSC_OCA: u32 = 1 << 4;
38const PORTSC_OCC: u32 = 1 << 5;
39const PORTSC_FPR: u32 = 1 << 6;
40const PORTSC_SUSP: u32 = 1 << 7;
41const PORTSC_PR: u32 = 1 << 8;
42const PORTSC_PP: u32 = 1 << 12;
43const PORTSC_SPEED_SHIFT: u32 = 26;
44const PORTSC_SPEED_MASK: u32 = 0x03 << PORTSC_SPEED_SHIFT;
45
46const SPEED_FULL: u32 = 0;
47const SPEED_LOW: u32 = 1;
48const SPEED_HIGH: u32 = 2;
49
50#[repr(C)]
51struct EhciCapRegisters {
52    caplength: u8,
53    _reserved: u8,
54    hciversion: u16,
55    hcsparams1: u32,
56    hcsparams2: u32,
57    hccparams: u32,
58}
59
60#[repr(C)]
61struct EhciOpRegisters {
62    usbcmd: u32,
63    usbsts: u32,
64    usbintr: u32,
65    frindex: u32,
66    ctrl_ds_seg: u32,
67    periodic_list_base: u32,
68    async_list_base: u32,
69    _reserved: [u32; 9],
70    config_flag: u32,
71}
72
73#[repr(C)]
74struct EhciPortRegisters {
75    portsc: [u32; 16],
76}
77
78pub struct EhciPort {
79    port_num: usize,
80    enabled: bool,
81    connected: bool,
82    speed: u8,
83}
84
85pub struct EhciController {
86    mmio_base: usize,
87    cap_regs: *const EhciCapRegisters,
88    op_regs: *mut EhciOpRegisters,
89    port_regs: *mut EhciPortRegisters,
90    max_ports: usize,
91    ports: Vec<EhciPort>,
92    periodic_list: *mut u32,
93    periodic_list_phys: u64,
94    async_list: *mut u32,
95    async_list_phys: u64,
96}
97
98unsafe impl Send for EhciController {}
99unsafe impl Sync for EhciController {}
100
101impl EhciController {
102    /// Creates a new instance.
103    pub unsafe fn new(pci_dev: pci::PciDevice) -> Result<Arc<Self>, &'static str> {
104        let bar = match pci_dev.read_bar(0) {
105            Some(Bar::Memory32 { addr, .. }) => addr as u64,
106            _ => return Err("Invalid BAR"),
107        };
108
109        let mmio_base = phys_to_virt(bar) as usize;
110        let cap_regs = mmio_base as *const EhciCapRegisters;
111        let caplength = (*cap_regs).caplength;
112        let op_regs = (mmio_base + caplength as usize) as *mut EhciOpRegisters;
113        let port_regs = (mmio_base + caplength as usize + 0x44) as *mut EhciPortRegisters;
114
115        let max_ports = ((*cap_regs).hcsparams1 as usize) & 0xF;
116
117        let mut controller = Self {
118            mmio_base,
119            cap_regs,
120            op_regs,
121            port_regs,
122            max_ports,
123            ports: Vec::new(),
124            periodic_list: core::ptr::null_mut(),
125            periodic_list_phys: 0,
126            async_list: core::ptr::null_mut(),
127            async_list_phys: 0,
128        };
129
130        controller.init()?;
131        Ok(Arc::new(controller))
132    }
133
134    /// Performs the init operation.
135    fn init(&mut self) -> Result<(), &'static str> {
136        unsafe {
137            let op = &mut *self.op_regs;
138
139            // Stop the controller
140            op.usbcmd &= !USBCMD_RUN_STOP;
141            while op.usbsts & USBSTS_HCH == 0 {
142                core::hint::spin_loop();
143            }
144
145            // Reset the controller
146            op.usbcmd |= USBCMD_HCRST;
147            while op.usbcmd & USBCMD_HCRST != 0 {
148                core::hint::spin_loop();
149            }
150
151            // Initialize ports
152            for i in 0..self.max_ports {
153                let portsc = self.read_portsc(i);
154                self.ports.push(EhciPort {
155                    port_num: i,
156                    enabled: (portsc & PORTSC_PE) != 0,
157                    connected: (portsc & PORTSC_CCS) != 0,
158                    speed: ((portsc >> PORTSC_SPEED_SHIFT) & 0x03) as u8,
159                });
160            }
161
162            // Initialize schedules
163            self.init_schedules()?;
164
165            // Enable interrupts
166            op.usbintr = USBSTS_INT | USBSTS_ERR | USBSTS_PCD;
167
168            // Start the controller
169            op.usbcmd |= USBCMD_RUN_STOP | USBCMD_PSE | USBCMD_ASE | USBCMD_INTE;
170            op.config_flag = 1;
171        }
172        Ok(())
173    }
174
175    /// Initializes schedules.
176    unsafe fn init_schedules(&mut self) -> Result<(), &'static str> {
177        // Allocate periodic list (4KB aligned, 1024 entries)
178        let periodic_frame = allocate_dma_frame().ok_or("Failed to allocate periodic list")?;
179        self.periodic_list_phys = periodic_frame.start_address.as_u64();
180        self.periodic_list = phys_to_virt(self.periodic_list_phys) as *mut u32;
181        core::ptr::write_bytes(self.periodic_list as *mut u8, 0, 4096);
182
183        // Allocate async list (32-byte aligned)
184        let async_frame = allocate_dma_frame().ok_or("Failed to allocate async list")?;
185        self.async_list_phys = async_frame.start_address.as_u64();
186        self.async_list = phys_to_virt(self.async_list_phys) as *mut u32;
187        core::ptr::write_bytes(self.async_list as *mut u8, 0, 4096);
188
189        // Set up async list (empty, points to itself)
190        *self.async_list = (self.async_list_phys as u32) & 0xFFFFFFE0;
191
192        let op = &mut *self.op_regs;
193        op.periodic_list_base = self.periodic_list_phys as u32;
194        op.async_list_base = self.async_list_phys as u32;
195
196        Ok(())
197    }
198
199    /// Reads portsc.
200    unsafe fn read_portsc(&self, port: usize) -> u32 {
201        let portsc_ptr = core::ptr::addr_of!((*self.port_regs).portsc[port]) as *const u32;
202        portsc_ptr.read_volatile()
203    }
204
205    /// Writes portsc.
206    unsafe fn write_portsc(&self, port: usize, val: u32) {
207        let portsc_ptr = core::ptr::addr_of!((*self.port_regs).portsc[port]) as *mut u32;
208        portsc_ptr.write_volatile(val);
209    }
210
211    /// Performs the port count operation.
212    pub fn port_count(&self) -> usize {
213        self.max_ports
214    }
215
216    /// Returns whether port connected.
217    pub fn is_port_connected(&self, port: usize) -> bool {
218        if port >= self.ports.len() {
219            return false;
220        }
221        self.ports[port].connected
222    }
223
224    /// Returns port speed.
225    pub fn get_port_speed(&self, port: usize) -> u8 {
226        if port >= self.ports.len() {
227            return 0;
228        }
229        self.ports[port].speed
230    }
231}
232
233static EHCI_CONTROLLERS: Mutex<Vec<Arc<EhciController>>> = Mutex::new(Vec::new());
234static EHCI_INITIALIZED: AtomicBool = AtomicBool::new(false);
235
236/// Performs the init operation.
237pub fn init() {
238    log::info!("[EHCI] Scanning for EHCI controllers...");
239
240    let candidates = pci::probe_all(ProbeCriteria {
241        vendor_id: None,
242        device_id: None,
243        class_code: Some(0x0C),
244        subclass: Some(0x03),
245        prog_if: Some(0x20),
246    });
247
248    for pci_dev in candidates.into_iter() {
249        log::info!(
250            "EHCI: Found controller at {:?} (VEN:{:04x} DEV:{:04x})",
251            pci_dev.address,
252            pci_dev.vendor_id,
253            pci_dev.device_id
254        );
255
256        pci_dev.enable_bus_master();
257
258        match unsafe { EhciController::new(pci_dev) } {
259            Ok(controller) => {
260                log::info!("[EHCI] Initialized with {} ports", controller.port_count());
261                EHCI_CONTROLLERS.lock().push(controller);
262            }
263            Err(e) => {
264                log::warn!("EHCI: Failed to initialize controller: {}", e);
265            }
266        }
267    }
268
269    EHCI_INITIALIZED.store(true, Ordering::SeqCst);
270    log::info!(
271        "[EHCI] Found {} controller(s)",
272        EHCI_CONTROLLERS.lock().len()
273    );
274}
275
276/// Returns controller.
277pub fn get_controller(index: usize) -> Option<Arc<EhciController>> {
278    EHCI_CONTROLLERS.lock().get(index).cloned()
279}
280
281/// Returns whether available.
282pub fn is_available() -> bool {
283    EHCI_INITIALIZED.load(Ordering::Relaxed)
284}