diff options
| author | Tianhao Wang <wth@riseup.net> | 2024-02-05 04:18:51 +0100 |
|---|---|---|
| committer | Tianhao Wang <shrik3@mailbox.org> | 2024-06-11 15:13:38 +0200 |
| commit | 959d50d9117a0ed3f280bf7d5bfbffd1fa1a5740 (patch) | |
| tree | cfa2d2cf408ce9597fed3076b76251b8e3e7537d /src | |
| parent | a6b91c56f29257c3e54f0bfb2d99bf07262cb184 (diff) | |
PS/2 Keyboard controller: read and decode key
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 20 | ||||
| -rw-r--r-- | src/machine/key.rs | 51 | ||||
| -rw-r--r-- | src/machine/keyctrl.rs | 321 |
3 files changed, 251 insertions, 141 deletions
@@ -5,11 +5,11 @@ mod arch; mod io; mod machine; +use arch::x86_64::interrupt::pic_8259; +use arch::x86_64::interrupt::pic_8259::PicDeviceInt; use core::panic::PanicInfo; use machine::cgascr::CGAScreen; use machine::interrupt; -use arch::x86_64::interrupt::pic_8259; -use arch::x86_64::interrupt::pic_8259::PicDeviceInt; #[cfg(not(test))] #[panic_handler] @@ -28,9 +28,17 @@ pub extern "C" fn _entry() -> ! { println!(" `-.-' \\ )-`( , o o)"); println!(" `- \\`_`\"'-"); println!("it works!"); - + // testing interrupt/PIC - pic_8259::allow(PicDeviceInt::KEYBOARD); - interrupt::interrupt_enable(); - loop {} + // pic_8259::allow(PicDeviceInt::KEYBOARD); + // interrupt::interrupt_enable(); + // + // busy loop query keyboard + loop { + // let code = io::KBCTL_GLOBAL.lock().simple_read(); + io::KBCTL_GLOBAL.lock().fetch_key(); + if let Some(k) = io::KBCTL_GLOBAL.lock().consume_key() { + println! {"caught key: {:?}", k} + } + } } diff --git a/src/machine/key.rs b/src/machine/key.rs index 04220a6..8d4e5f9 100644 --- a/src/machine/key.rs +++ b/src/machine/key.rs @@ -1,17 +1,14 @@ use bitflags::bitflags; use core::convert; +use core::ffi::c_uchar; +#[derive(Copy, Clone, Debug)] pub struct Key { - asc: u8, - scan: u8, - modi: Modifiers, - rawcode: u8, // this field not necessary, remove after testing + pub asc: c_uchar, + pub scan: u8, + pub modi: Modifiers, } -// Not implementing -// +operator char() -// - impl convert::Into<char> for Key { fn into(self) -> char { self.asc as char @@ -25,6 +22,7 @@ impl convert::Into<u8> for Key { } bitflags! { + #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub struct Modifiers:u8 { const NONE = 0; const SHIFT = 1 << 0; @@ -42,10 +40,9 @@ bitflags! { impl Key { pub fn new() -> Self { Self { - asc: 0, - scan: 0, + asc: 0, // logically scan + modi.shift => asc + scan: 0, // scancode, "raw" modi: Modifiers::NONE, - rawcode: 0, } } pub fn decode(&mut self) { @@ -59,14 +56,6 @@ impl Key { self.scan = 0; } - pub fn set_raw(&mut self, code: u8) { - self.rawcode = code; - } - - pub fn get_raw(self) -> u8 { - self.rawcode - } - // setter and getter for ascii and scancode pub fn set_ascii(&mut self, ascii: u8) { self.asc = ascii; @@ -98,20 +87,6 @@ impl Key { } } -use core::ffi::c_uchar; - -// bit masks for modifier keys -pub enum MbitDefs { - Shift = 0b00000001, - AltLeft = 0b00000010, - AltRight = 0b00000100, - CtrlLeft = 0b00001000, - CtrlRight = 0b00010000, - CapsLock = 0b00100000, - NumLock = 0b01000000, - ScrollLock = 0b10000000, -} - // scan codes of a few specific keys pub enum Scan { F1 = 0x3b, @@ -126,25 +101,25 @@ pub enum Scan { // Decoding tables ... this shit is so ugly, thanks to rust's strong typing system!!! // Also, this is a german layout keyboard // oh btw, the code translation is done by ChatGPT if it's wrong complain to the AI! -const NORMAL_TAB: [c_uchar; 89] = [ +pub const NORMAL_TAB: [c_uchar; 89] = [ 0, 0, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 225, 39, 8, 0, 113, 119, 101, 114, 116, 122, 117, 105, 111, 112, 129, 43, 10, 0, 97, 115, 100, 102, 103, 104, 106, 107, 108, 148, 132, 94, 0, 35, 121, 120, 99, 118, 98, 110, 109, 44, 46, 45, 0, 42, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, ]; -const SHIFT_TAB: [c_uchar; 89] = [ +pub const SHIFT_TAB: [c_uchar; 89] = [ 0, 0, 33, 34, 21, 36, 37, 38, 47, 40, 41, 61, 63, 96, 0, 0, 81, 87, 69, 82, 84, 90, 85, 73, 79, 80, 154, 42, 0, 0, 65, 83, 68, 70, 71, 72, 74, 75, 76, 153, 142, 248, 0, 39, 89, 88, 67, 86, 66, 78, 77, 59, 58, 95, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, ]; -const ALT_TAB: [c_uchar; 89] = [ +pub const ALT_TAB: [c_uchar; 89] = [ 0, 0, 0, 253, 0, 0, 0, 0, 123, 91, 93, 125, 92, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 0, 0, ]; -const ASC_NUM_TAB: [c_uchar; 13] = [55, 56, 57, 45, 52, 53, 54, 43, 49, 50, 51, 48, 44]; -const SCAN_NUM_TAB: [c_uchar; 13] = [8, 9, 10, 53, 5, 6, 7, 27, 2, 3, 4, 11, 51]; +pub const ASC_NUM_TAB: [c_uchar; 13] = [55, 56, 57, 45, 52, 53, 54, 43, 49, 50, 51, 48, 44]; +pub const SCAN_NUM_TAB: [c_uchar; 13] = [8, 9, 10, 53, 5, 6, 7, 27, 2, 3, 4, 11, 51]; 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; } |
