strat9_kernel/arch/x86_64/
mouse.rs1use super::io::{inb, outb};
8use core::sync::atomic::{AtomicBool, AtomicI32, AtomicU8, Ordering};
9use spin::Mutex;
10
11const PS2_DATA: u16 = 0x60;
13const PS2_CMD: u16 = 0x64;
14
15const CMD_READ_CFG: u8 = 0x20;
17const CMD_WRITE_CFG: u8 = 0x60;
18const CMD_ENABLE_AUX: u8 = 0xA8;
19const CMD_SEND_TO_MOUSE: u8 = 0xD4;
20
21const MOUSE_RESET: u8 = 0xFF;
23const MOUSE_SET_DEFAULTS: u8 = 0xF6;
24const MOUSE_ENABLE_STREAM: u8 = 0xF4;
25const MOUSE_GET_ID: u8 = 0xF2;
26const MOUSE_SET_SAMPLE_RATE: u8 = 0xF3;
27const MOUSE_ACK: u8 = 0xFA;
28
29const STATUS_OUTPUT_FULL: u8 = 0x01; const STATUS_INPUT_FULL: u8 = 0x02; const EVENT_BUF_SIZE: usize = 64;
35
36struct EventBuffer {
37 buf: [MouseEvent; EVENT_BUF_SIZE],
38 head: usize,
39 tail: usize,
40}
41
42static EVENT_BUF: Mutex<EventBuffer> = Mutex::new(EventBuffer {
43 buf: [MouseEvent {
44 dx: 0,
45 dy: 0,
46 dz: 0,
47 left: false,
48 right: false,
49 middle: false,
50 }; EVENT_BUF_SIZE],
51 head: 0,
52 tail: 0,
53});
54
55static MOUSE_ABS_X: AtomicI32 = AtomicI32::new(0);
57static MOUSE_ABS_Y: AtomicI32 = AtomicI32::new(0);
58static SCREEN_W: AtomicI32 = AtomicI32::new(1280);
60static SCREEN_H: AtomicI32 = AtomicI32::new(800);
61
62static MOUSE_CYCLE: AtomicU8 = AtomicU8::new(0);
65static INTELLIMOUSE: AtomicBool = AtomicBool::new(false);
67pub static MOUSE_READY: AtomicBool = AtomicBool::new(false);
69
70static PACKET_BUF: Mutex<[u8; 4]> = Mutex::new([0u8; 4]);
71
72#[derive(Clone, Copy)]
74pub struct MouseEvent {
75 pub dx: i16,
77 pub dy: i16,
79 pub dz: i8,
81 pub left: bool,
82 pub right: bool,
83 pub middle: bool,
84}
85
86#[inline]
90fn wait_write() {
91 for _ in 0..100_000u32 {
92 if unsafe { inb(PS2_CMD) } & STATUS_INPUT_FULL == 0 {
93 return;
94 }
95 core::hint::spin_loop();
96 }
97}
98
99#[inline]
101fn wait_read() {
102 for _ in 0..100_000u32 {
103 if unsafe { inb(PS2_CMD) } & STATUS_OUTPUT_FULL != 0 {
104 return;
105 }
106 core::hint::spin_loop();
107 }
108}
109
110fn ps2_read() -> u8 {
112 wait_read();
113 unsafe { inb(PS2_DATA) }
114}
115
116fn ps2_write_cmd(cmd: u8) {
118 wait_write();
119 unsafe { outb(PS2_CMD, cmd) };
120}
121
122fn ps2_write_data(data: u8) {
124 wait_write();
125 unsafe { outb(PS2_DATA, data) };
126}
127
128fn mouse_write(data: u8) {
130 ps2_write_cmd(CMD_SEND_TO_MOUSE);
131 ps2_write_data(data);
132}
133
134fn mouse_cmd(cmd: u8) -> bool {
136 mouse_write(cmd);
137 let ack = ps2_read();
138 ack == MOUSE_ACK
139}
140
141fn mouse_cmd_arg(cmd: u8, arg: u8) -> bool {
143 mouse_write(cmd);
144 let ack = ps2_read();
145 if ack != MOUSE_ACK {
146 return false;
147 }
148 mouse_write(arg);
149 let ack2 = ps2_read();
150 ack2 == MOUSE_ACK
151}
152
153fn flush_output() {
155 for _ in 0..16 {
156 if unsafe { inb(PS2_CMD) } & STATUS_OUTPUT_FULL == 0 {
157 break;
158 }
159 unsafe { inb(PS2_DATA) };
160 }
161}
162
163pub fn init() -> bool {
170 flush_output();
171
172 ps2_write_cmd(CMD_ENABLE_AUX);
174
175 ps2_write_cmd(CMD_READ_CFG);
177 let mut cfg = ps2_read();
178 cfg |= 0x02; cfg &= !0x20; ps2_write_cmd(CMD_WRITE_CFG);
181 ps2_write_data(cfg);
182
183 mouse_cmd(MOUSE_RESET);
185 flush_output();
187
188 if !mouse_cmd(MOUSE_SET_DEFAULTS) {
190 crate::serial_println!("[mouse] set_defaults failed");
191 return false;
192 }
193
194 let intellimouse = try_enable_intellimouse();
196 INTELLIMOUSE.store(intellimouse, Ordering::Relaxed);
197 if intellimouse {
198 crate::serial_println!("[mouse] IntelliMouse (scroll wheel) detected");
199 } else {
200 crate::serial_println!("[mouse] Standard PS/2 mouse (3-byte packets)");
201 }
202
203 if !mouse_cmd(MOUSE_ENABLE_STREAM) {
205 crate::serial_println!("[mouse] enable_stream failed");
206 return false;
207 }
208
209 let w = crate::arch::x86_64::vga::width() as i32;
211 let h = crate::arch::x86_64::vga::height() as i32;
212 if w > 0 {
213 SCREEN_W.store(w, Ordering::Relaxed);
214 }
215 if h > 0 {
216 SCREEN_H.store(h, Ordering::Relaxed);
217 }
218
219 MOUSE_READY.store(true, Ordering::Relaxed);
220 crate::serial_println!("[mouse] PS/2 mouse initialized OK");
221 true
222}
223
224fn try_enable_intellimouse() -> bool {
227 mouse_cmd_arg(MOUSE_SET_SAMPLE_RATE, 200);
229 mouse_cmd_arg(MOUSE_SET_SAMPLE_RATE, 100);
230 mouse_cmd_arg(MOUSE_SET_SAMPLE_RATE, 80);
231
232 mouse_write(MOUSE_GET_ID);
234 let ack = ps2_read();
235 if ack != MOUSE_ACK {
236 return false;
237 }
238 let id = ps2_read();
239 id == 0x03
240}
241
242pub fn handle_irq() {
248 let byte = unsafe { inb(PS2_DATA) };
249 let cycle = MOUSE_CYCLE.load(Ordering::Relaxed);
250 let packet_len: u8 = if INTELLIMOUSE.load(Ordering::Relaxed) {
251 4
252 } else {
253 3
254 };
255
256 if cycle == 0 && (byte & 0x08) == 0 {
258 return;
260 }
261
262 {
264 if let Some(mut buf) = PACKET_BUF.try_lock() {
265 buf[cycle as usize] = byte;
266 } else {
267 return; }
269 }
270
271 let next_cycle = cycle + 1;
272 if next_cycle >= packet_len {
273 MOUSE_CYCLE.store(0, Ordering::Relaxed);
275 decode_packet();
276 } else {
277 MOUSE_CYCLE.store(next_cycle, Ordering::Relaxed);
278 }
279}
280
281fn decode_packet() {
283 let buf = {
284 match PACKET_BUF.try_lock() {
285 Some(b) => *b,
286 None => return,
287 }
288 };
289
290 let flags = buf[0];
291 let raw_dx = buf[1] as i16;
292 let raw_dy = buf[2] as i16;
293
294 let dx: i16 = if flags & 0x10 != 0 {
296 raw_dx - 256
297 } else {
298 raw_dx
299 };
300 let dy_ps2: i16 = if flags & 0x20 != 0 {
302 raw_dy - 256
303 } else {
304 raw_dy
305 };
306 let dy = -dy_ps2;
307
308 let dz: i8 = if INTELLIMOUSE.load(Ordering::Relaxed) {
309 let raw = (buf[3] & 0x0F) as i8;
311 if raw & 0x08 != 0 {
312 raw | -16i8
313 } else {
314 raw
315 }
316 } else {
317 0
318 };
319
320 let left = flags & 0x01 != 0;
321 let right = flags & 0x02 != 0;
322 let middle = flags & 0x04 != 0;
323
324 let scr_w = SCREEN_W.load(Ordering::Relaxed);
326 let scr_h = SCREEN_H.load(Ordering::Relaxed);
327 let max_x = if scr_w > 0 { scr_w - 1 } else { 1279 };
328 let max_y = if scr_h > 0 { scr_h - 1 } else { 799 };
329
330 let prev_x = MOUSE_ABS_X.load(Ordering::Relaxed);
331 let prev_y = MOUSE_ABS_Y.load(Ordering::Relaxed);
332 let new_x = (prev_x + dx as i32).clamp(0, max_x);
333 let new_y = (prev_y + dy as i32).clamp(0, max_y);
334 MOUSE_ABS_X.store(new_x, Ordering::Relaxed);
335 MOUSE_ABS_Y.store(new_y, Ordering::Relaxed);
336
337 let event = MouseEvent {
338 dx,
339 dy,
340 dz,
341 left,
342 right,
343 middle,
344 };
345
346 if let Some(mut q) = EVENT_BUF.try_lock() {
348 let tail = q.tail;
349 let next_tail = (tail + 1) % EVENT_BUF_SIZE;
350 if next_tail != q.head {
351 q.buf[tail] = event;
352 q.tail = next_tail;
353 }
354 }
355}
356
357pub fn read_event() -> Option<MouseEvent> {
363 let saved = super::save_flags_and_cli();
365 let result = {
366 let mut q = EVENT_BUF.lock();
367 if q.head == q.tail {
368 None
369 } else {
370 let ev = q.buf[q.head];
371 q.head = (q.head + 1) % EVENT_BUF_SIZE;
372 Some(ev)
373 }
374 };
375 super::restore_flags(saved);
376 result
377}
378
379pub fn has_event() -> bool {
381 let saved = super::save_flags_and_cli();
382 let result = {
383 let q = EVENT_BUF.lock();
384 q.head != q.tail
385 };
386 super::restore_flags(saved);
387 result
388}
389
390pub fn mouse_pos() -> (i32, i32) {
392 (
393 MOUSE_ABS_X.load(Ordering::Relaxed),
394 MOUSE_ABS_Y.load(Ordering::Relaxed),
395 )
396}