diff options
Diffstat (limited to 'rsnix/src/interrupts.rs')
| -rw-r--r-- | rsnix/src/interrupts.rs | 68 |
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. |
| 160 | struct KeyBuf { | 163 | struct 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 | ||
| 166 | unsafe impl Sync for KeyBuf {} | 169 | unsafe impl Sync for ByteRing {} |
| 167 | 170 | ||
| 168 | impl KeyBuf { | 171 | impl 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 | ||
| 208 | static KEYBUF: KeyBuf = KeyBuf::new(); | 211 | static KEYBUF: ByteRing = ByteRing::new(); |
| 212 | static 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`. | ||
| 412 | pub 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 | } |
