diff options
Diffstat (limited to 'src/ps2.rs')
| -rw-r--r-- | src/ps2.rs | 64 |
1 files changed, 56 insertions, 8 deletions
| @@ -158,9 +158,64 @@ static mut KBD: KbdState = KbdState { | |||
| 158 | got_e0: false, | 158 | got_e0: false, |
| 159 | }; | 159 | }; |
| 160 | 160 | ||
| 161 | /// Initialise the PS/2 port for the keyboard — just drain stale bytes. | 161 | /// Initialise the PS/2 port: drain stale bytes + enable keyboard interrupt. |
| 162 | pub fn init() { | 162 | pub fn init() { |
| 163 | drain_input(); | 163 | drain_input(); |
| 164 | |||
| 165 | // Enable the keyboard interrupt on the PS/2 controller. | ||
| 166 | // Read Configuration Byte (command 0x20), set bit 0 (IRQ1 enable), | ||
| 167 | // write back (command 0x60). | ||
| 168 | unsafe { | ||
| 169 | // Tell the keyboard controller we want to read the config byte. | ||
| 170 | // Need to wait for the controller to be ready first. | ||
| 171 | wait_write(); | ||
| 172 | core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x20u8, | ||
| 173 | options(nostack, preserves_flags)); | ||
| 174 | wait_read(); | ||
| 175 | let mut cfg: u8; | ||
| 176 | core::arch::asm!("in al, dx", out("al") cfg, in("dx") 0x60u16, | ||
| 177 | options(nostack, preserves_flags)); | ||
| 178 | |||
| 179 | cfg |= 0x01; // enable keyboard interrupt | ||
| 180 | |||
| 181 | // Write config byte back | ||
| 182 | wait_write(); | ||
| 183 | core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x60u8, | ||
| 184 | options(nostack, preserves_flags)); | ||
| 185 | wait_write(); | ||
| 186 | core::arch::asm!("out dx, al", in("dx") 0x60u16, in("al") cfg, | ||
| 187 | options(nostack, preserves_flags)); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | /// Wait until the PS/2 controller is ready to receive a command. | ||
| 192 | fn wait_write() { | ||
| 193 | unsafe { | ||
| 194 | loop { | ||
| 195 | let status: u8; | ||
| 196 | core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, | ||
| 197 | options(nostack, preserves_flags)); | ||
| 198 | if status & 0x02 == 0 { | ||
| 199 | // Input buffer empty — controller ready to receive | ||
| 200 | break; | ||
| 201 | } | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Wait until the PS/2 controller has data available to read. | ||
| 207 | fn wait_read() { | ||
| 208 | unsafe { | ||
| 209 | loop { | ||
| 210 | let status: u8; | ||
| 211 | core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, | ||
| 212 | options(nostack, preserves_flags)); | ||
| 213 | if status & 0x01 != 0 { | ||
| 214 | // Output buffer full — data ready to read | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 164 | } | 219 | } |
| 165 | 220 | ||
| 166 | fn drain_input() { | 221 | fn drain_input() { |
| @@ -184,18 +239,11 @@ fn drain_input() { | |||
| 184 | /// this function pops and decodes them. | 239 | /// this function pops and decodes them. |
| 185 | pub fn poll_key() -> KeyEvent { | 240 | pub fn poll_key() -> KeyEvent { |
| 186 | loop { | 241 | loop { |
| 187 | // Disable interrupts so the buffer-empty → hlt window is atomic. | ||
| 188 | unsafe { core::arch::asm!("cli", options(nostack, preserves_flags)); } | ||
| 189 | |||
| 190 | if let Some(byte) = crate::interrupts::read_scancode() { | 242 | if let Some(byte) = crate::interrupts::read_scancode() { |
| 191 | unsafe { core::arch::asm!("sti", options(nostack, preserves_flags)); } | ||
| 192 | if let Some(ev) = process_byte(byte) { | 243 | if let Some(ev) = process_byte(byte) { |
| 193 | return ev; | 244 | return ev; |
| 194 | } | 245 | } |
| 195 | } else { | 246 | } else { |
| 196 | // Re-enable and halt. If a keyboard IRQ arrived between the | ||
| 197 | // buffer check and `sti`, the CPU sees a pending interrupt | ||
| 198 | // and `hlt` returns immediately. | ||
| 199 | unsafe { | 247 | unsafe { |
| 200 | core::arch::asm!("sti; hlt", options(nostack, preserves_flags)); | 248 | core::arch::asm!("sti; hlt", options(nostack, preserves_flags)); |
| 201 | } | 249 | } |
