summaryrefslogtreecommitdiffstats
path: root/rsnix/src/interrupts.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--rsnix/src/interrupts.rs68
1 files changed, 55 insertions, 13 deletions
diff --git a/rsnix/src/interrupts.rs b/rsnix/src/interrupts.rs
index 6abd54d..7ea0b07 100644
--- a/rsnix/src/interrupts.rs
+++ b/rsnix/src/interrupts.rs
@@ -153,19 +153,22 @@ pub fn uptime_ms() -> u64 {
153} 153}
154 154
155// --------------------------------------------------------------------------- 155// ---------------------------------------------------------------------------
156// Lock-free SPSC ring buffer for keyboard scancodes 156// Lock-free SPSC ring buffers for PS/2 input (keyboard scancodes + mouse
157// bytes). One per device: the ISR routes each data-port byte to the right
158// ring via the controller status register's aux bit (bit 5), so the two
159// devices never steal each other's bytes even when both are pending at once.
157// --------------------------------------------------------------------------- 160// ---------------------------------------------------------------------------
158 161
159/// Single-producer (ISR) / single-consumer (main) lock-free ring buffer. 162/// Single-producer (ISR) / single-consumer (main) lock-free ring buffer.
160struct KeyBuf { 163struct ByteRing {
161 buf: [core::cell::UnsafeCell<u8>; 256], 164 buf: [core::cell::UnsafeCell<u8>; 256],
162 head: AtomicU16, // next slot to read (written by consumer / main) 165 head: AtomicU16, // next slot to read (written by consumer / main)
163 tail: AtomicU16, // next slot to write (written by producer / ISR) 166 tail: AtomicU16, // next slot to write (written by producer / ISR)
164} 167}
165 168
166unsafe impl Sync for KeyBuf {} 169unsafe impl Sync for ByteRing {}
167 170
168impl KeyBuf { 171impl ByteRing {
169 const fn new() -> Self { 172 const fn new() -> Self {
170 // Inline-const array repeat (stable since 1.79): each element is a 173 // Inline-const array repeat (stable since 1.79): each element is a
171 // fresh `UnsafeCell::new(0)` value, so no interior-mutable const. 174 // fresh `UnsafeCell::new(0)` value, so no interior-mutable const.
@@ -205,7 +208,8 @@ impl KeyBuf {
205 } 208 }
206} 209}
207 210
208static KEYBUF: KeyBuf = KeyBuf::new(); 211static KEYBUF: ByteRing = ByteRing::new();
212static MOUSEBUF: ByteRing = ByteRing::new();
209 213
210// --------------------------------------------------------------------------- 214// ---------------------------------------------------------------------------
211// Interrupt stubs (assembly) and dispatch 215// Interrupt stubs (assembly) and dispatch
@@ -240,6 +244,14 @@ core::arch::global_asm!(
240 " push 33", 244 " push 33",
241 " jmp isr_common", 245 " jmp isr_common",
242 "", 246 "",
247 // PS/2 mouse IRQ (IRQ12 → vector 44).
248 ".globl isr_stub_44",
249 ".p2align 4",
250 "isr_stub_44:",
251 " push 0",
252 " push 44",
253 " jmp isr_common",
254 "",
243 // Software yield (vector 0x60 — outside the PIC's 32-47 range, which 255 // Software yield (vector 0x60 — outside the PIC's 32-47 range, which
244 // is why 0x40 was rejected: 0x40 = 64 decimal = slave IRQ 8 slot). 256 // is why 0x40 was rejected: 0x40 = 64 decimal = slave IRQ 8 slot).
245 ".globl isr_stub_60", 257 ".globl isr_stub_60",
@@ -345,13 +357,27 @@ unsafe extern "C" fn isr_dispatch(frame: &IntFrame) -> usize {
345 eoi(0); 357 eoi(0);
346 crate::sched::on_tick(fp) 358 crate::sched::on_tick(fp)
347 } 359 }
348 33 => { 360 33 | 44 => {
349 let byte: u8; 361 // PS/2 data byte. The controller's status register tells us
350 core::arch::asm!("in al, dx", out("al") byte, in("dx") 0x60u16, 362 // which device it came from (bit 5 = auxiliary/mouse), so the
363 // byte is routed to the matching ring — both handlers must
364 // check, because when both devices have data pending the
365 // first IRQ serviced can find the other device's byte in the
366 // output buffer.
367 let status: u8;
368 core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16,
351 options(nostack, preserves_flags)); 369 options(nostack, preserves_flags));
352 // Push scancode to ring buffer (called with interrupts disabled). 370 if status & 0x01 != 0 {
353 KEYBUF.push(byte); 371 let byte: u8;
354 eoi(1); 372 core::arch::asm!("in al, dx", out("al") byte, in("dx") 0x60u16,
373 options(nostack, preserves_flags));
374 if status & 0x20 != 0 {
375 MOUSEBUF.push(byte);
376 } else {
377 KEYBUF.push(byte);
378 }
379 }
380 eoi((frame.vector - 32) as u8);
355 fp 381 fp
356 } 382 }
357 8 => { 383 8 => {
@@ -381,6 +407,12 @@ pub fn read_scancode() -> Option<u8> {
381 KEYBUF.pop() 407 KEYBUF.pop()
382} 408}
383 409
410/// Read one mouse data byte from the interrupt-driven ring buffer
411/// (non-blocking). Packets are assembled in `ps2::try_poll_mouse`.
412pub fn read_mouse_byte() -> Option<u8> {
413 MOUSEBUF.pop()
414}
415
384/// Global tick counter (incremented by timer IRQ, ~100 Hz). u64 so 416/// Global tick counter (incremented by timer IRQ, ~100 Hz). u64 so
385/// uptime never wraps; `uptime_ms` interpolates the PIT counter for the 417/// uptime never wraps; `uptime_ms` interpolates the PIT counter for the
386/// sub-tick fraction. 418/// sub-tick fraction.
@@ -406,6 +438,8 @@ pub fn init() {
406 // Override vectors for interrupt we actually handle. 438 // Override vectors for interrupt we actually handle.
407 idt_set_entry(idt_ptr, 32, isr_stub_32 as *const () as u64, cs, 0x0Eu8, 0); 439 idt_set_entry(idt_ptr, 32, isr_stub_32 as *const () as u64, cs, 0x0Eu8, 0);
408 idt_set_entry(idt_ptr, 33, isr_stub_33 as *const () as u64, cs, 0x0Eu8, 0); 440 idt_set_entry(idt_ptr, 33, isr_stub_33 as *const () as u64, cs, 0x0Eu8, 0);
441 // PS/2 mouse (IRQ12).
442 idt_set_entry(idt_ptr, 44, isr_stub_44 as *const () as u64, cs, 0x0Eu8, 0);
409 // Yield (software interrupt). 443 // Yield (software interrupt).
410 idt_set_entry( 444 idt_set_entry(
411 idt_ptr, 445 idt_ptr,
@@ -439,13 +473,20 @@ pub fn init() {
439 pit_set_rate(); 473 pit_set_rate();
440 PIT_READY.store(true, Ordering::Relaxed); 474 PIT_READY.store(true, Ordering::Relaxed);
441 475
442 // Unmask timer (IRQ0 → bit 0) and keyboard (IRQ1 → bit 1) on master. 476 // Unmask timer (IRQ0), keyboard (IRQ1) and the cascade line (IRQ2)
477 // on the master; unmask IRQ12 (PS/2 mouse) on the slave.
443 let mut mask: u8; 478 let mut mask: u8;
444 asm!("in al, dx", out("al") mask, in("dx") PIC1_DATA, 479 asm!("in al, dx", out("al") mask, in("dx") PIC1_DATA,
445 options(nostack, preserves_flags)); 480 options(nostack, preserves_flags));
446 mask &= !0x03u8; 481 mask &= !0x07u8;
447 asm!("out dx, al", in("dx") PIC1_DATA, in("al") mask, 482 asm!("out dx, al", in("dx") PIC1_DATA, in("al") mask,
448 options(nostack, preserves_flags)); 483 options(nostack, preserves_flags));
484 let mut mask2: u8;
485 asm!("in al, dx", out("al") mask2, in("dx") PIC2_DATA,
486 options(nostack, preserves_flags));
487 mask2 &= !0x10u8; // IRQ12 → bit 4 of the slave PIC
488 asm!("out dx, al", in("dx") PIC2_DATA, in("al") mask2,
489 options(nostack, preserves_flags));
449 490
450 // Enable interrupts. 491 // Enable interrupts.
451 asm!("sti", options(nostack)); 492 asm!("sti", options(nostack));
@@ -469,6 +510,7 @@ unsafe extern "C" {
469 fn isr_stub_14(); 510 fn isr_stub_14();
470 fn isr_stub_32(); 511 fn isr_stub_32();
471 fn isr_stub_33(); 512 fn isr_stub_33();
513 fn isr_stub_44();
472 fn isr_stub_60(); 514 fn isr_stub_60();
473 fn isr_stub_spurious(); 515 fn isr_stub_spurious();
474} 516}