diff options
Diffstat (limited to 'src/machine/keyctrl.rs')
| -rw-r--r-- | src/machine/keyctrl.rs | 321 |
1 files changed, 224 insertions, 97 deletions
diff --git a/src/machine/keyctrl.rs b/src/machine/keyctrl.rs index 246ac02..40385a2 100644 --- a/src/machine/keyctrl.rs +++ b/src/machine/keyctrl.rs @@ -3,151 +3,238 @@ use crate::machine::device_io::*; use bitflags::bitflags; use core::cmp; use core::cmp::{Eq, PartialEq}; +use core::ffi::c_uchar; use num_enum::{IntoPrimitive, TryFromPrimitive}; // Driver for the PS/2 keybard/mouse controller -// beyound OOStuBS: I'm gonna write full driver for it. - +// beyound OOStuBS: +// The 'gather' field should NEVER have imcomplete state: it should either be a +// valid key, or nothing. +// TODO what if IO ops fails or timeout? // TODO figure out how to abstract the "interrupt control" layer. // Keyboard controller should not be arch dependent #[cfg(target_arch = "x86_64")] use crate::arch::x86_64::interrupt::{pic_8259, pic_8259::PicDeviceInt as PD}; -// this is the driver for keyboard controller not to confuse with the keyboard module. -// The later is an abstraction -// This one serves a the HW driver +use super::key::Modifiers; // TODO -// [functions] -// Keyboard_Controller() -// get_ascii_code() -// key_decoded() -// key_hit() // reboot() // set_led(char led,bool on) // set_repeat_rate(int speed,int delay) pub struct KeyboardController { - code: u8, - prefix: u8, leds: Led, - current: Option<Key>, - last: Option<Key>, - gather: Option<Key>, + keystate: KeyState, + gather: Option<Key>, // if not collected timely it will be overwritten cport: IOPort, dport: IOPort, } -pub enum KeyDelay { - L0 = 0, - L1 = 1, - L2 = 2, - L3 = 3, +struct KeyState { + modi: Modifiers, // active modifiers + prefix: Prefix, // prefix state for some certain modifiers + scan: Option<u8>, } -// x86 and arm has different interrupt controller -#[cfg(target_arch = "x86_64")] -impl KeyboardController { - #[inline(always)] - fn disable_keyboard_int() { - pic_8259::forbid(PD::KEYBOARD); +#[derive(Clone, Copy, Eq, PartialEq)] +enum Prefix { + PREFIX1, + PREFIX2, + NONE, // don't confuse Option None with Prefix enum NONE +} + +impl Prefix { + pub fn try_from_u8(val: u8) -> Option<Prefix> { + match val { + Defs::C_PREFIX1 => Some(Self::PREFIX1), + Defs::C_PREFIX2 => Some(Self::PREFIX2), + _ => None, + } } +} - #[inline(always)] - fn enable_keyboard_int() { - pic_8259::allow(PD::KEYBOARD); +impl KeyState { + pub fn new() -> Self { + return Self { + modi: Modifiers::NONE, + prefix: Prefix::NONE, + scan: None, + }; } - #[inline(always)] - fn toggle_keyboard_int(enable: bool) { - if enable { - Self::enable_keyboard_int() - } else { - Self::disable_keyboard_int() - } + pub fn toggle_capslock(&mut self) { + self.modi.toggle(Modifiers::CAPSLOCK); } - #[inline(always)] - fn is_int_masked() -> bool { - pic_8259::is_masked(PD::KEYBOARD) + pub fn toggle_numlock(&mut self) { + self.modi.toggle(Modifiers::NUMLOCK); + } + + pub fn toggle_scroll_lock(&mut self) { + self.modi.toggle(Modifiers::SCROLL_LOCK); } } impl KeyboardController { pub fn new() -> Self { Self { - code: 0, - prefix: 0, leds: Led::NONE, - last: None, - current: None, - gather: None, + keystate: KeyState::new(), cport: IOPort::new(Defs::CTRL), dport: IOPort::new(Defs::DATA), + gather: None, } } - pub fn fetch_key(&mut self) -> Key { - todo!(); - // this should be called by the interrupt handler. - // 1. read raw keycode from data port - // 2. try to decode and put the result into self.gather. - // TODO consider move the decoding into the epilogue. - } - - // key decoding is stateful, this can't be implemented into Key struct. - pub fn decode_key(&mut self) -> bool { - // try to decode the raw 'code' into self.gather - // return true upon success - // this is a transcription of the C code.... Improve later. - // OOStuBS: The keys that were added in MF II keyboards -- compared to the older AT - // keyboard -- always send one of two possible prefix bytes first. - let done = false; - if self.code == Defs::PREFIX1 || self.code == Defs::PREFIX2 { - self.prefix = self.code; - return false; + // Led and lock state are toggled together, to prevent out-of-sync + // TODO: rollback lock state if setting led fails + fn toggle_lock(&mut self, lock: Modifiers) { + let led = match lock { + Modifiers::SCROLL_LOCK => Some(Led::SCROLL_LOCK), + Modifiers::CAPSLOCK => Some(Led::CAPS_LOCK), + Modifiers::NUMLOCK => Some(Led::NUM_LOCk), + _ => None, + }; + if let Some(led) = led { + self.keystate.modi.toggle(lock); + self.toggle_led(led); } + } - // OOStuBS: Releasing a key is actually only interesting in this implementation - // for the "modifier" keys SHIFT, CTRL and ALT. For the other keys, we - // can ignore the break code. - // ; A Key's break code is identical to its make code with break_bit set - - false + fn toggle_led(&mut self, led: Led) { + self.leds.toggle(led); + todo!("toggle keyboard led: "); } - // block until status.outb becomes 1 - // return false on invalid SR. - // these wait_ functions are unsafe. If used inproperly this becomes a deadloop - // TODO support software timeout - unsafe fn wait_write(&self) { - loop { - let sr = StatusReg::from_bits_truncate(self.cport.inb()); - if sr.contains(StatusReg::OUTB) { - break; + pub fn update_state(&mut self, code: u8) { + // TODO investigate this code pattern: is there much runtime cost?? + self.keystate.scan = Some(code); + if let Some(p) = Prefix::try_from_u8(code) { + self.keystate.prefix = p; + return; + } + if code & Defs::BREAK_BIT == 0 { + if self.press_event() { + self.decode_key(); } + } else { + self.release_event(); } + // the prefix should have been comsumed at this point so clear it + self.keystate.prefix = Prefix::NONE; } - // block until status.inb becomes 0 - unsafe fn wait_read(&self) { - loop { - let sr = StatusReg::from_bits_truncate(self.cport.inb()); - if !sr.contains(StatusReg::INB) { - break; + fn press_event(&mut self) -> bool { + let mut should_decode_ascii = false; + let code = self.keystate.scan.unwrap(); + match code { + Defs::C_SHIFT_L | Defs::C_SHIFT_R => self.keystate.modi.insert(Modifiers::SHIFT), + Defs::C_ALT => match self.keystate.prefix { + Prefix::PREFIX1 => self.keystate.modi.insert(Modifiers::ALT_RIGHT), + _ => self.keystate.modi.insert(Modifiers::ALT_LEFT), + }, + Defs::C_CTRL => match self.keystate.prefix { + Prefix::PREFIX1 => self.keystate.modi.insert(Modifiers::CTRL_RIGHT), + _ => self.keystate.modi.insert(Modifiers::CTRL_LEFT), + }, + Defs::C_CAPSLOCK => self.toggle_lock(Modifiers::CAPSLOCK), + Defs::C_NUM_P => { + if !self.keystate.modi.contains(Modifiers::CTRL_LEFT) { + self.toggle_lock(Modifiers::NUMLOCK); + } + } + Defs::C_SCRLOCK => self.toggle_lock(Modifiers::SCROLL_LOCK), + _ => { + should_decode_ascii = true; } } + should_decode_ascii } - pub fn set_repeat_rate_delay(rate: u8, delay: KeyDelay) { - let rate = cmp::min(rate, 31); - let delay = delay as u8; - let is_masked = Self::is_int_masked(); - // idsable keyboard interrupt - // TODO should have a timeout - Self::disable_keyboard_int(); + fn release_event(&mut self) { + // we only care about release events for shift/alt/ctrl + let code = self.keystate.scan.unwrap() & !Defs::BREAK_BIT; + match code { + Defs::C_SHIFT_L | Defs::C_SHIFT_R => self.keystate.modi.remove(Modifiers::SHIFT), + Defs::C_ALT => match self.keystate.prefix { + Prefix::PREFIX1 => self.keystate.modi.remove(Modifiers::ALT_RIGHT), + _ => self.keystate.modi.remove(Modifiers::ALT_LEFT), + }, + Defs::C_CTRL => match self.keystate.prefix { + Prefix::PREFIX1 => self.keystate.modi.remove(Modifiers::CTRL_RIGHT), + _ => self.keystate.modi.remove(Modifiers::CTRL_LEFT), + }, + _ => {} + } + } - Self::toggle_keyboard_int(is_masked); + #[inline(always)] + pub fn read_status(&self) -> Option<StatusReg> { + // TODO maybe there is a path which leads to invalid SR, e.g. timeout? + Some(StatusReg::from_bits_truncate(self.cport.inb())) + } + + // this should be called by the interrupt handler prologue + pub fn fetch_key(&mut self) { + // I'd like to see if this panics.... + let sr = self.read_status().unwrap(); + // ignore mouse events + if !sr.contains(StatusReg::OUTB) || sr.contains(StatusReg::AUXB) { + return; + } + self.update_state(self.dport.inb()); + } + + // this should be called by the "epilogue" + pub fn consume_key(&mut self) -> Option<Key>{ + let res = self.gather.clone(); + self.gather = None; + return res + } + + pub fn decode_key(&mut self) { + // the decode_key should not be called when there is no scancode. + // mask the breakbit + let s = self.keystate.scan.unwrap(); + let c = s & !Defs::BREAK_BIT; + let m = self.keystate.modi; + let p = self.keystate.prefix; + + if c == 53 && p == Prefix::PREFIX1 { + self.gather = Some(Key { + asc: b'/', + modi: m, + scan: s, + }); + return; + } + + let asc = if m.contains(Modifiers::NUMLOCK) && p == Prefix::NONE && c >= 71 && c <= 83 { + ASC_NUM_TAB[c as usize - 71] + } else if m.contains(Modifiers::ALT_RIGHT) { + ALT_TAB[c as usize] + } else if m.contains(Modifiers::SHIFT) { + SHIFT_TAB[c as usize] + } else if m.contains(Modifiers::CAPSLOCK) { + if (c >= 16 && c <= 26) || (c >= 30 && c <= 40) || (c >= 44 && c <= 50) { + SHIFT_TAB[c as usize] + } else { + NORMAL_TAB[c as usize] + } + } else { + NORMAL_TAB[c as usize] + }; + + self.gather = Some(Key { + asc, + modi: m, + scan: s, + }); + } + + pub fn set_repeat_rate_delay() { + todo!(); } pub fn reboot(&mut self) { @@ -155,8 +242,33 @@ impl KeyboardController { } } -// I think constants are more handy than enum for these... -// Keyboard controller commands +// x86 and arm has different interrupt controller +#[cfg(target_arch = "x86_64")] +impl KeyboardController { + #[inline(always)] + fn disable_keyboard_int() { + pic_8259::forbid(PD::KEYBOARD); + } + + #[inline(always)] + fn enable_keyboard_int() { + pic_8259::allow(PD::KEYBOARD); + } + + #[inline(always)] + fn toggle_keyboard_int(enable: bool) { + if enable { + Self::enable_keyboard_int() + } else { + Self::disable_keyboard_int() + } + } + + #[inline(always)] + fn is_int_masked() -> bool { + pic_8259::is_masked(PD::KEYBOARD) + } +} enum Cmd { // these commands are sent through DATA port @@ -200,7 +312,22 @@ impl Defs { pub const CTRL: u16 = 0x64; pub const DATA: u16 = 0x60; pub const CPU_RESET: u8 = 0xfe; - pub const BREAK_BIT: u8 = 0x80; - pub const PREFIX1: u8 = 0xe0; - pub const PREFIX2: u8 = 0xe1; + pub const BREAK_BIT: u8 = 1 << 7; + // defs of special scan codes. + pub const C_PREFIX1: u8 = 0xe0; + pub const C_PREFIX2: u8 = 0xe1; + pub const C_SHIFT_L: u8 = 0x2a; + pub const C_SHIFT_R: u8 = 0x36; + pub const C_ALT: u8 = 0x38; + pub const C_CTRL: u8 = 0x1d; + pub const C_CAPSLOCK: u8 = 0x3a; + pub const C_SCRLOCK: u8 = 0x46; + pub const C_NUM_P: u8 = 0x45; // NumLock or Pause + pub const C_F1: u8 = 0x3b; + pub const C_DEL: u8 = 0x53; + pub const C_UP: u8 = 0x48; + pub const C_DOWN: u8 = 0x50; + pub const C_LEFT: u8 = 0x4b; + pub const C_RIGHT: u8 = 0x4d; + pub const C_DIV: u8 = 0x8; } |
