179 lines
5.2 KiB
Rust
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)
|
|
}
|
|
}
|