⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/drivers/
mouse.rs

1//! PS/2 mouse driver.
2//!
3//! Reads 3-byte mouse packets from the PS/2 auxiliary port (x86_64 only).
4//! Maintains absolute cursor position clamped to screen bounds.
5//! On non-x86_64 architectures, all functions are no-op stubs.
6
7use core::sync::atomic::{AtomicBool, AtomicI32, AtomicU16, Ordering};
8
9/// Mouse button state flags.
10pub const BUTTON_LEFT: u8 = 0x01;
11pub const BUTTON_RIGHT: u8 = 0x02;
12pub const BUTTON_MIDDLE: u8 = 0x04;
13
14/// Mouse event produced by the driver.
15#[derive(Debug, Clone, Copy)]
16pub struct MouseEvent {
17    pub dx: i16,
18    pub dy: i16,
19    pub buttons: u8,
20}
21
22// Screen bounds for clamping absolute position
23static SCREEN_WIDTH: AtomicU16 = AtomicU16::new(1280);
24static SCREEN_HEIGHT: AtomicU16 = AtomicU16::new(800);
25
26// Absolute cursor position
27static CURSOR_X: AtomicI32 = AtomicI32::new(640);
28static CURSOR_Y: AtomicI32 = AtomicI32::new(400);
29
30static INITIALIZED: AtomicBool = AtomicBool::new(false);
31
32/// Get the current absolute cursor position.
33pub fn cursor_position() -> (i32, i32) {
34    (
35        CURSOR_X.load(Ordering::Relaxed),
36        CURSOR_Y.load(Ordering::Relaxed),
37    )
38}
39
40/// Set screen bounds for cursor clamping.
41pub fn set_screen_bounds(width: u16, height: u16) {
42    SCREEN_WIDTH.store(width, Ordering::Relaxed);
43    SCREEN_HEIGHT.store(height, Ordering::Relaxed);
44}
45
46/// Check if the mouse driver is initialized.
47pub fn is_initialized() -> bool {
48    INITIALIZED.load(Ordering::Acquire)
49}
50
51// ---------------------------------------------------------------------------
52// x86_64 implementation
53// ---------------------------------------------------------------------------
54
55#[cfg(target_arch = "x86_64")]
56mod x86_64_impl {
57    use core::sync::atomic::AtomicUsize;
58
59    use spin::Mutex;
60
61    use super::*;
62
63    /// Ring buffer for mouse events.
64    const EVENT_BUFFER_SIZE: usize = 64;
65
66    struct MouseBuffer {
67        buf: [MouseEvent; EVENT_BUFFER_SIZE],
68        head: AtomicUsize,
69        tail: AtomicUsize,
70    }
71
72    impl MouseBuffer {
73        const fn new() -> Self {
74            Self {
75                buf: [MouseEvent {
76                    dx: 0,
77                    dy: 0,
78                    buttons: 0,
79                }; EVENT_BUFFER_SIZE],
80                head: AtomicUsize::new(0),
81                tail: AtomicUsize::new(0),
82            }
83        }
84
85        fn push(&mut self, event: MouseEvent) {
86            let head = self.head.load(Ordering::Relaxed);
87            let next = (head + 1) & (EVENT_BUFFER_SIZE - 1);
88            let tail = self.tail.load(Ordering::Acquire);
89            if next == tail {
90                return; // Buffer full
91            }
92            self.buf[head] = event;
93            self.head.store(next, Ordering::Release);
94        }
95
96        fn pop(&self) -> Option<MouseEvent> {
97            let tail = self.tail.load(Ordering::Relaxed);
98            let head = self.head.load(Ordering::Acquire);
99            if tail == head {
100                return None;
101            }
102            let event = self.buf[tail];
103            self.tail
104                .store((tail + 1) & (EVENT_BUFFER_SIZE - 1), Ordering::Release);
105            Some(event)
106        }
107
108        fn has_data(&self) -> bool {
109            self.tail.load(Ordering::Relaxed) != self.head.load(Ordering::Acquire)
110        }
111    }
112
113    // SAFETY: MouseBuffer uses atomic operations for synchronization.
114    // Single producer (poll_mouse) / single consumer (read_event).
115    unsafe impl Send for MouseBuffer {}
116    unsafe impl Sync for MouseBuffer {}
117
118    static MOUSE_BUFFER: Mutex<MouseBuffer> = Mutex::new(MouseBuffer::new());
119
120    /// Packet accumulation state
121    struct PacketState {
122        buf: [u8; 3],
123        idx: usize,
124    }
125
126    static PACKET_STATE: Mutex<PacketState> = Mutex::new(PacketState {
127        buf: [0; 3],
128        idx: 0,
129    });
130
131    /// Wait for the PS/2 controller input buffer to be empty.
132    #[inline]
133    fn wait_input() {
134        for _ in 0..10_000 {
135            // SAFETY: Reading PS/2 status register (port 0x64).
136            let status = unsafe { crate::arch::x86_64::inb(0x64) };
137            if (status & 0x02) == 0 {
138                return;
139            }
140        }
141    }
142
143    /// Wait for PS/2 controller output buffer to have data.
144    #[inline]
145    fn wait_output() -> bool {
146        for _ in 0..10_000 {
147            // SAFETY: Reading PS/2 status register.
148            let status = unsafe { crate::arch::x86_64::inb(0x64) };
149            if (status & 0x01) != 0 {
150                return true;
151            }
152        }
153        false
154    }
155
156    /// Send a command byte to the PS/2 auxiliary device (mouse).
157    fn mouse_write(cmd: u8) {
158        wait_input();
159        // SAFETY: Writing to PS/2 command port.
160        unsafe {
161            crate::arch::x86_64::outb(0x64, 0xD4); // Next byte goes to aux
162                                                   // device
163        }
164        wait_input();
165        // SAFETY: Writing data byte to PS/2 data port.
166        unsafe {
167            crate::arch::x86_64::outb(0x60, cmd);
168        }
169    }
170
171    /// Read a byte from the PS/2 data port (with timeout).
172    fn mouse_read() -> Option<u8> {
173        if wait_output() {
174            // SAFETY: Reading PS/2 data port.
175            Some(unsafe { crate::arch::x86_64::inb(0x60) })
176        } else {
177            None
178        }
179    }
180
181    /// Initialize the PS/2 mouse.
182    pub fn init() {
183        // Enable the auxiliary device (mouse port)
184        wait_input();
185        // SAFETY: Sending PS/2 controller commands.
186        unsafe {
187            crate::arch::x86_64::outb(0x64, 0xA8); // Enable aux port
188        }
189
190        // Get controller configuration byte
191        wait_input();
192        // SAFETY: Reading PS/2 controller configuration via command port.
193        unsafe {
194            crate::arch::x86_64::outb(0x64, 0x20);
195        }
196        let config_byte = if wait_output() {
197            // SAFETY: Reading PS/2 data port for configuration byte.
198            Some(unsafe { crate::arch::x86_64::inb(0x60) })
199        } else {
200            None
201        };
202        if let Some(mut config) = config_byte {
203            // Enable IRQ12 (aux interrupt) and ensure aux clock is enabled
204            config |= 0x02; // Enable IRQ12
205            config &= !0x20; // Disable aux clock inhibit
206
207            wait_input();
208            // SAFETY: Writing PS/2 controller command to set configuration.
209            unsafe {
210                crate::arch::x86_64::outb(0x64, 0x60); // Write config
211            }
212            wait_input();
213            // SAFETY: Writing configuration byte to PS/2 data port.
214            unsafe {
215                crate::arch::x86_64::outb(0x60, config);
216            }
217        }
218
219        // Reset mouse
220        mouse_write(0xFF);
221        let _ = mouse_read(); // ACK
222        let _ = mouse_read(); // Self-test result
223        let _ = mouse_read(); // Device ID
224
225        // Set defaults
226        mouse_write(0xF6);
227        let _ = mouse_read(); // ACK
228
229        // Enable data streaming
230        mouse_write(0xF4);
231        let _ = mouse_read(); // ACK
232
233        INITIALIZED.store(true, Ordering::Release);
234        crate::println!("[MOUSE] PS/2 mouse initialized");
235    }
236
237    /// Process a single byte from the PS/2 auxiliary port.
238    ///
239    /// Called by poll_all() in input_event.rs after reading a byte from
240    /// port 0x60 that was identified as mouse data (status bit 5 set).
241    /// Accumulates bytes into 3-byte packets and pushes complete mouse
242    /// events to the ring buffer.
243    pub fn poll_mouse_byte(byte: u8) {
244        if !INITIALIZED.load(Ordering::Acquire) {
245            return;
246        }
247
248        let mut pkt = PACKET_STATE.lock();
249        let idx = pkt.idx;
250        pkt.buf[idx] = byte;
251        pkt.idx = idx + 1;
252
253        if pkt.idx >= 3 {
254            pkt.idx = 0;
255            let status_byte = pkt.buf[0];
256
257            // Validate: bit 3 must always be set in a standard PS/2 packet
258            if (status_byte & 0x08) == 0 {
259                return; // Resync
260            }
261
262            let buttons = status_byte & 0x07;
263
264            // Reconstruct signed deltas
265            let mut dx = pkt.buf[1] as i16;
266            let mut dy = pkt.buf[2] as i16;
267            if (status_byte & 0x10) != 0 {
268                dx -= 256; // X sign bit
269            }
270            if (status_byte & 0x20) != 0 {
271                dy -= 256; // Y sign bit
272            }
273
274            // PS/2 Y axis is inverted (up = positive)
275            dy = -dy;
276
277            // Update absolute cursor position
278            let sw = SCREEN_WIDTH.load(Ordering::Relaxed) as i32;
279            let sh = SCREEN_HEIGHT.load(Ordering::Relaxed) as i32;
280            let mut cx = CURSOR_X.load(Ordering::Relaxed) + dx as i32;
281            let mut cy = CURSOR_Y.load(Ordering::Relaxed) + dy as i32;
282            cx = cx.clamp(0, sw - 1);
283            cy = cy.clamp(0, sh - 1);
284            CURSOR_X.store(cx, Ordering::Relaxed);
285            CURSOR_Y.store(cy, Ordering::Relaxed);
286
287            let event = MouseEvent { dx, dy, buttons };
288            MOUSE_BUFFER.lock().push(event);
289        }
290    }
291
292    /// Poll for mouse data and push complete packets to the event buffer.
293    ///
294    /// Legacy entry point -- reads one byte from the PS/2 aux port if
295    /// available and feeds it to poll_mouse_byte(). Prefer using the
296    /// centralized polling in input_event::poll_all() instead, which
297    /// correctly distinguishes keyboard vs mouse bytes and drains all
298    /// pending data in a single call.
299    pub fn poll_mouse() {
300        if !INITIALIZED.load(Ordering::Acquire) {
301            return;
302        }
303
304        // Check if aux port has data (status bit 5 = aux data, bit 0 = data ready)
305        // SAFETY: Reading PS/2 status register.
306        let status = unsafe { crate::arch::x86_64::inb(0x64) };
307        if (status & 0x21) != 0x21 {
308            // Bit 0 (data available) + bit 5 (from aux port)
309            return;
310        }
311
312        // SAFETY: Reading PS/2 data port.
313        let byte = unsafe { crate::arch::x86_64::inb(0x60) };
314        poll_mouse_byte(byte);
315    }
316
317    /// Read a mouse event from the buffer.
318    pub fn read_event() -> Option<MouseEvent> {
319        MOUSE_BUFFER.lock().pop()
320    }
321
322    /// Check if mouse buffer has pending events.
323    pub fn has_data() -> bool {
324        MOUSE_BUFFER.lock().has_data()
325    }
326}
327
328#[cfg(target_arch = "x86_64")]
329pub use x86_64_impl::{has_data, init, poll_mouse, poll_mouse_byte, read_event};
330
331// ---------------------------------------------------------------------------
332// Stubs for non-x86_64 architectures
333// ---------------------------------------------------------------------------
334
335#[cfg(not(target_arch = "x86_64"))]
336pub fn init() {}
337
338#[cfg(not(target_arch = "x86_64"))]
339pub fn poll_mouse() {}
340
341#[cfg(not(target_arch = "x86_64"))]
342pub fn poll_mouse_byte(_byte: u8) {}
343
344#[cfg(not(target_arch = "x86_64"))]
345pub fn read_event() -> Option<MouseEvent> {
346    None
347}
348
349#[cfg(not(target_arch = "x86_64"))]
350pub fn has_data() -> bool {
351    false
352}