diff options
Diffstat (limited to 'rsnix/src/ps2.rs')
| -rw-r--r-- | rsnix/src/ps2.rs | 78 |
1 files changed, 59 insertions, 19 deletions
diff --git a/rsnix/src/ps2.rs b/rsnix/src/ps2.rs index 5f4a804..965e4a0 100644 --- a/rsnix/src/ps2.rs +++ b/rsnix/src/ps2.rs | |||
| @@ -169,14 +169,19 @@ pub fn init() { | |||
| 169 | 169 | ||
| 170 | // Enable the keyboard interrupt on the PS/2 controller. | 170 | // Enable the keyboard interrupt on the PS/2 controller. |
| 171 | // Read Configuration Byte (command 0x20), set bit 0 (IRQ1 enable), | 171 | // Read Configuration Byte (command 0x20), set bit 0 (IRQ1 enable), |
| 172 | // write back (command 0x60). | 172 | // write back (command 0x60). Every wait is bounded; a wedged |
| 173 | // controller bails out instead of hanging the boot. | ||
| 173 | unsafe { | 174 | unsafe { |
| 174 | // Tell the keyboard controller we want to read the config byte. | 175 | // Tell the keyboard controller we want to read the config byte. |
| 175 | // Need to wait for the controller to be ready first. | 176 | // Need to wait for the controller to be ready first. |
| 176 | wait_write(); | 177 | if !wait_write() { |
| 178 | return; | ||
| 179 | } | ||
| 177 | core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x20u8, | 180 | core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x20u8, |
| 178 | options(nostack, preserves_flags)); | 181 | options(nostack, preserves_flags)); |
| 179 | wait_read(); | 182 | if !wait_read() { |
| 183 | return; | ||
| 184 | } | ||
| 180 | let mut cfg: u8; | 185 | let mut cfg: u8; |
| 181 | core::arch::asm!("in al, dx", out("al") cfg, in("dx") 0x60u16, | 186 | core::arch::asm!("in al, dx", out("al") cfg, in("dx") 0x60u16, |
| 182 | options(nostack, preserves_flags)); | 187 | options(nostack, preserves_flags)); |
| @@ -184,48 +189,60 @@ pub fn init() { | |||
| 184 | cfg |= 0x01; // enable keyboard interrupt | 189 | cfg |= 0x01; // enable keyboard interrupt |
| 185 | 190 | ||
| 186 | // Write config byte back | 191 | // Write config byte back |
| 187 | wait_write(); | 192 | if !wait_write() { |
| 193 | return; | ||
| 194 | } | ||
| 188 | core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x60u8, | 195 | core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x60u8, |
| 189 | options(nostack, preserves_flags)); | 196 | options(nostack, preserves_flags)); |
| 190 | wait_write(); | 197 | if !wait_write() { |
| 198 | return; | ||
| 199 | } | ||
| 191 | core::arch::asm!("out dx, al", in("dx") 0x60u16, in("al") cfg, | 200 | core::arch::asm!("out dx, al", in("dx") 0x60u16, in("al") cfg, |
| 192 | options(nostack, preserves_flags)); | 201 | options(nostack, preserves_flags)); |
| 193 | } | 202 | } |
| 194 | } | 203 | } |
| 195 | 204 | ||
| 196 | /// Wait until the PS/2 controller is ready to receive a command. | 205 | /// Wait until the PS/2 controller is ready to receive a command. |
| 197 | fn wait_write() { | 206 | /// Bounded: a wedged or missing 8042 must not hang the boot (the classic |
| 207 | /// real-hardware freeze — the controller reports busy forever on some | ||
| 208 | /// machines). Returns false on timeout. | ||
| 209 | fn wait_write() -> bool { | ||
| 198 | unsafe { | 210 | unsafe { |
| 199 | loop { | 211 | for _ in 0..1_000_000 { |
| 200 | let status: u8; | 212 | let status: u8; |
| 201 | core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, | 213 | core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, |
| 202 | options(nostack, preserves_flags)); | 214 | options(nostack, preserves_flags)); |
| 203 | if status & 0x02 == 0 { | 215 | if status & 0x02 == 0 { |
| 204 | // Input buffer empty — controller ready to receive | 216 | // Input buffer empty — controller ready to receive |
| 205 | break; | 217 | return true; |
| 206 | } | 218 | } |
| 207 | } | 219 | } |
| 208 | } | 220 | } |
| 221 | false | ||
| 209 | } | 222 | } |
| 210 | 223 | ||
| 211 | /// Wait until the PS/2 controller has data available to read. | 224 | /// Wait until the PS/2 controller has data available to read. |
| 212 | fn wait_read() { | 225 | /// Bounded like `wait_write`. |
| 226 | fn wait_read() -> bool { | ||
| 213 | unsafe { | 227 | unsafe { |
| 214 | loop { | 228 | for _ in 0..1_000_000 { |
| 215 | let status: u8; | 229 | let status: u8; |
| 216 | core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, | 230 | core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, |
| 217 | options(nostack, preserves_flags)); | 231 | options(nostack, preserves_flags)); |
| 218 | if status & 0x01 != 0 { | 232 | if status & 0x01 != 0 { |
| 219 | // Output buffer full — data ready to read | 233 | // Output buffer full — data ready to read |
| 220 | break; | 234 | return true; |
| 221 | } | 235 | } |
| 222 | } | 236 | } |
| 223 | } | 237 | } |
| 238 | false | ||
| 224 | } | 239 | } |
| 225 | 240 | ||
| 241 | /// Drain any pending bytes. Bounded so a stuck output-full flag (or a | ||
| 242 | /// controller that keeps raising it with no data) cannot hang the boot. | ||
| 226 | fn drain_input() { | 243 | fn drain_input() { |
| 227 | unsafe { | 244 | unsafe { |
| 228 | loop { | 245 | for _ in 0..32 { |
| 229 | let status: u8; | 246 | let status: u8; |
| 230 | core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT, | 247 | core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT, |
| 231 | options(nostack, preserves_flags)); | 248 | options(nostack, preserves_flags)); |
| @@ -268,6 +285,15 @@ fn kbd() -> &'static mut KbdState { | |||
| 268 | unsafe { &mut *core::ptr::addr_of_mut!(KBD) } | 285 | unsafe { &mut *core::ptr::addr_of_mut!(KBD) } |
| 269 | } | 286 | } |
| 270 | 287 | ||
| 288 | /// Feed one scancode-set-1 byte into the shared decoder state machine. | ||
| 289 | /// Used by the PS/2 IRQ path (raw bytes) AND the USB HID keyboard driver | ||
| 290 | /// (which translates HID usage codes to scancode-set-1 make/break bytes so | ||
| 291 | /// the same shift/ctrl/caps decoding applies). Returns a KeyEvent on a | ||
| 292 | /// visible press; releases and modifier-only changes return None. | ||
| 293 | pub(crate) fn feed_scancode(byte: u8) -> Option<KeyEvent> { | ||
| 294 | process_byte(byte) | ||
| 295 | } | ||
| 296 | |||
| 271 | fn process_byte(byte: u8) -> Option<KeyEvent> { | 297 | fn process_byte(byte: u8) -> Option<KeyEvent> { |
| 272 | let state = kbd(); | 298 | let state = kbd(); |
| 273 | 299 | ||
| @@ -466,25 +492,35 @@ pub fn mouse_init() -> bool { | |||
| 466 | drain_input(); | 492 | drain_input(); |
| 467 | unsafe { | 493 | unsafe { |
| 468 | // 1. Enable the auxiliary port itself. | 494 | // 1. Enable the auxiliary port itself. |
| 469 | wait_write(); | 495 | if !wait_write() { |
| 496 | return false; | ||
| 497 | } | ||
| 470 | core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_AUX_ENABLE, | 498 | core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_AUX_ENABLE, |
| 471 | options(nostack, preserves_flags)); | 499 | options(nostack, preserves_flags)); |
| 472 | 500 | ||
| 473 | // 2. Configuration Byte: enable IRQ12 + the aux port clock. Read, | 501 | // 2. Configuration Byte: enable IRQ12 + the aux port clock. Read, |
| 474 | // OR the two bits, write back (preserves the keyboard IRQ bit | 502 | // OR the two bits, write back (preserves the keyboard IRQ bit |
| 475 | // that ps2::init set). | 503 | // that ps2::init set). |
| 476 | wait_write(); | 504 | if !wait_write() { |
| 505 | return false; | ||
| 506 | } | ||
| 477 | core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_READ_CFG, | 507 | core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_READ_CFG, |
| 478 | options(nostack, preserves_flags)); | 508 | options(nostack, preserves_flags)); |
| 479 | wait_read(); | 509 | if !wait_read() { |
| 510 | return false; | ||
| 511 | } | ||
| 480 | let mut cfg: u8; | 512 | let mut cfg: u8; |
| 481 | core::arch::asm!("in al, dx", out("al") cfg, in("dx") DATA_PORT, | 513 | core::arch::asm!("in al, dx", out("al") cfg, in("dx") DATA_PORT, |
| 482 | options(nostack, preserves_flags)); | 514 | options(nostack, preserves_flags)); |
| 483 | cfg |= CFG_AUX_IRQ | CFG_AUX_CLOCK; | 515 | cfg |= CFG_AUX_IRQ | CFG_AUX_CLOCK; |
| 484 | wait_write(); | 516 | if !wait_write() { |
| 517 | return false; | ||
| 518 | } | ||
| 485 | core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_WRITE_CFG, | 519 | core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_WRITE_CFG, |
| 486 | options(nostack, preserves_flags)); | 520 | options(nostack, preserves_flags)); |
| 487 | wait_write(); | 521 | if !wait_write() { |
| 522 | return false; | ||
| 523 | } | ||
| 488 | core::arch::asm!("out dx, al", in("dx") DATA_PORT, in("al") cfg, | 524 | core::arch::asm!("out dx, al", in("dx") DATA_PORT, in("al") cfg, |
| 489 | options(nostack, preserves_flags)); | 525 | options(nostack, preserves_flags)); |
| 490 | 526 | ||
| @@ -504,10 +540,14 @@ pub fn mouse_init() -> bool { | |||
| 504 | /// The wait is bounded so a missing device cannot hang the boot. | 540 | /// The wait is bounded so a missing device cannot hang the boot. |
| 505 | fn aux_command(cmd: u8) -> bool { | 541 | fn aux_command(cmd: u8) -> bool { |
| 506 | unsafe { | 542 | unsafe { |
| 507 | wait_write(); | 543 | if !wait_write() { |
| 544 | return false; | ||
| 545 | } | ||
| 508 | core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_SEND_TO_AUX, | 546 | core::arch::asm!("out dx, al", in("dx") STATUS_PORT, in("al") CMD_SEND_TO_AUX, |
| 509 | options(nostack, preserves_flags)); | 547 | options(nostack, preserves_flags)); |
| 510 | wait_write(); | 548 | if !wait_write() { |
| 549 | return false; | ||
| 550 | } | ||
| 511 | core::arch::asm!("out dx, al", in("dx") DATA_PORT, in("al") cmd, | 551 | core::arch::asm!("out dx, al", in("dx") DATA_PORT, in("al") cmd, |
| 512 | options(nostack, preserves_flags)); | 552 | options(nostack, preserves_flags)); |
| 513 | for _ in 0..100_000 { | 553 | for _ in 0..100_000 { |
