Files
opencoreC/kernel/src/drivers/mouse.rs
T

179 lines
5.2 KiB
Rust

//! PS/2 Mouse Driver
//! Handles 8042 Auxiliary Device communication, 3-byte packet assembly, and coordinate tracking.
use crate::kprintln;
const PS2_DATA_PORT: u16 = 0x60;
const PS2_STATUS_PORT: u16 = 0x64;
const PS2_COMMAND_PORT: u16 = 0x64;
#[inline]
unsafe fn inb(port: u16) -> u8 {
let mut val: u8;
core::arch::asm!("in al, dx", in("dx") port, out("al") val, options(nomem, nostack, preserves_flags));
val
}
#[inline]
unsafe fn outb(port: u16, val: u8) {
core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags));
}
unsafe fn wait_ps2_write() {
let mut timeout = 100000;
while (inb(PS2_STATUS_PORT) & 0x02) != 0 && timeout > 0 {
timeout -= 1;
core::hint::spin_loop();
}
}
unsafe fn wait_ps2_read() {
let mut timeout = 100000;
while (inb(PS2_STATUS_PORT) & 0x01) == 0 && timeout > 0 {
timeout -= 1;
core::hint::spin_loop();
}
}
pub struct MouseState {
pub x: i32,
pub y: i32,
pub buttons: u32, // bit 0 = Left, bit 1 = Right, bit 2 = Middle
packet: [u8; 3],
packet_index: usize,
max_x: i32,
max_y: i32,
}
pub static mut MOUSE_STATE: MouseState = MouseState {
x: 640,
y: 400,
buttons: 0,
packet: [0; 3],
packet_index: 0,
max_x: 1280,
max_y: 800,
};
impl MouseState {
pub fn handle_byte(&mut self, b: u8) {
if self.packet_index == 0 {
// First byte must have bit 3 set (sync bit)
if (b & 0x08) == 0 {
return; // Out of sync, discard
}
}
self.packet[self.packet_index] = b;
self.packet_index += 1;
if self.packet_index == 3 {
self.packet_index = 0;
let flags = self.packet[0];
let raw_dx = self.packet[1];
let raw_dy = self.packet[2];
// Discard invalid overflow packets (bit 6 = X overflow, bit 7 = Y overflow)
if (flags & 0xC0) != 0 {
return;
}
// Buttons: bit 0 = Left, bit 1 = Right, bit 2 = Middle
self.buttons = (flags & 0x07) as u32;
// 8-bit signed two's complement deltas
let mut dx = raw_dx as i8 as i32;
let mut dy = raw_dy as i8 as i32;
// Strict delta clamping to prevent cursor jumps/teleportation at screen edges
if dx > 60 { dx = 60; }
if dx < -60 { dx = -60; }
if dy > 60 { dy = 60; }
if dy < -60 { dy = -60; }
// Smooth pointer scaling (responsive feel)
let step_x = if dx.abs() > 3 { dx * 3 / 2 } else { dx };
let step_y = if dy.abs() > 3 { dy * 3 / 2 } else { dy };
self.x += step_x;
self.y -= step_y; // Invert Y for screen coordinates
// Clamp strictly to screen boundaries
if self.x < 0 { self.x = 0; }
if self.x >= self.max_x { self.x = self.max_x - 1; }
if self.y < 0 { self.y = 0; }
if self.y >= self.max_y { self.y = self.max_y - 1; }
}
}
}
pub fn init(screen_w: i32, screen_h: i32) {
unsafe {
MOUSE_STATE.max_x = screen_w;
MOUSE_STATE.max_y = screen_h;
MOUSE_STATE.x = screen_w / 2;
MOUSE_STATE.y = screen_h / 2;
// 1. Enable auxiliary device (Mouse) in 8042 controller
wait_ps2_write();
outb(PS2_COMMAND_PORT, 0xA8);
// 2. Enable auxiliary device interrupts in 8042 controller configuration byte
wait_ps2_write();
outb(PS2_COMMAND_PORT, 0x20); // Read Controller Command Byte
wait_ps2_read();
let mut status = inb(PS2_DATA_PORT);
status |= 0x02; // Enable IRQ 12 (mouse interrupt)
status &= !0x20; // Clear mouse clock disable
wait_ps2_write();
outb(PS2_COMMAND_PORT, 0x60); // Write Controller Command Byte
wait_ps2_write();
outb(PS2_DATA_PORT, status);
// 3. Set Defaults (command 0xF6) to mouse
wait_ps2_write();
outb(PS2_COMMAND_PORT, 0xD4);
wait_ps2_write();
outb(PS2_DATA_PORT, 0xF6);
wait_ps2_read();
let _ = inb(PS2_DATA_PORT); // ACK (0xFA)
// 4. Enable Data Reporting (command 0xF4) to mouse
wait_ps2_write();
outb(PS2_COMMAND_PORT, 0xD4);
wait_ps2_write();
outb(PS2_DATA_PORT, 0xF4);
wait_ps2_read();
let _ = inb(PS2_DATA_PORT); // ACK (0xFA)
kprintln!("[MOUSE] PS/2 Mouse Driver Initialized (Screen: {}x{}).", screen_w, screen_h);
}
}
#[no_mangle]
pub extern "C" fn mouse_interrupt_handler() {
unsafe {
let status = inb(PS2_COMMAND_PORT);
if (status & 0x01) != 0 {
let data = inb(PS2_DATA_PORT);
if (status & 0x20) != 0 {
MOUSE_STATE.handle_byte(data);
}
}
crate::drivers::timer::send_slave_eoi();
}
}
pub fn get_mouse_info() -> (i32, i32, u32) {
unsafe {
// Drain any pending bytes in 8042 buffer
while (inb(PS2_COMMAND_PORT) & 0x21) == 0x21 {
let data = inb(PS2_DATA_PORT);
MOUSE_STATE.handle_byte(data);
}
(MOUSE_STATE.x, MOUSE_STATE.y, MOUSE_STATE.buttons)
}
}