aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTianhao Wang <wth@riseup.net>2024-02-01 23:44:51 +0100
committerTianhao Wang <shrik3@mailbox.org>2024-06-11 15:13:38 +0200
commita6b91c56f29257c3e54f0bfb2d99bf07262cb184 (patch)
tree13c093a1581d9a4c8c1ff34fe35c2b40c21f05b4
parentf857bd1d4f12316bd3434192d41c2489407c11a4 (diff)
PS/2 Keyboard controller: baseline
-rw-r--r--Cargo.toml7
-rw-r--r--README.md6
-rw-r--r--src/io.rs3
-rw-r--r--src/machine/kbd_defs.rs76
-rw-r--r--src/machine/key.rs52
-rw-r--r--src/machine/keyctrl.rs186
-rw-r--r--src/machine/mod.rs3
7 files changed, 236 insertions, 97 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 65a6dd8..c1ce753 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,11 +9,16 @@ edition = "2021"
spin = "0.9.8"
bitflags = "2.4.2"
+
+[dependencies.num_enum]
+version = "0.7.2"
+default-features = false
+
+
[dependencies.lazy_static]
version = "1.4"
features = ["spin_no_std"]
-
[build]
target = "x86_64-rustubs"
diff --git a/README.md b/README.md
index 691a23b..ea8db32 100644
--- a/README.md
+++ b/README.md
@@ -89,3 +89,9 @@ https://rust-lang.github.io/api-guidelines/naming.html
Makefile Cheatsheet:
https://devhints.io/makefile
+
+AT Keyboard Controller:
+https://homepages.cwi.nl/~aeb/linux/kbd/scancodes-8.html
+
+PS/2 Keyboard Controller:
+https://wiki.osdev.org/PS/2_Keyboard
diff --git a/src/io.rs b/src/io.rs
index 417fc1d..664fda2 100644
--- a/src/io.rs
+++ b/src/io.rs
@@ -1,11 +1,12 @@
use crate::machine::cgascr::CGAScreen;
+use crate::machine::keyctrl::KeyboardController;
use core::fmt;
use lazy_static::lazy_static;
use spin::Mutex;
// TODO I want my own locking primitive for practice, instead of stock spin lock
lazy_static! {
- // TODO perhaps remove the 'a lifetime from the struc defs
pub static ref CGASCREEN_GLOBAL: Mutex<CGAScreen> = Mutex::new(CGAScreen::new());
+ pub static ref KBCTL_GLOBAL: Mutex<KeyboardController> = Mutex::new(KeyboardController::new());
}
#[macro_export]
diff --git a/src/machine/kbd_defs.rs b/src/machine/kbd_defs.rs
deleted file mode 100644
index a59d75a..0000000
--- a/src/machine/kbd_defs.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use core::ffi::c_uchar;
-
-// bit masks for modifier keys
-pub enum Mbit {
- 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,
- Del = 0x53,
- Up = 72,
- Down = 80,
- Left = 75,
- Right = 77,
- Div = 8,
-}
-
-// 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] = [
- 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] = [
- 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] = [
- 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];
-
-// I think constants are more handy than enum for these...
-// Keyboard controller commands
-pub const KC_CMD_SET_LED: u8 = 0xed;
-pub const KC_CMD_SET_SPEED: u8 = 0xf3;
-
-// CPU reset .. (reboot)
-pub const KC_CPU_RESET: u8 = 0xfe;
-
-// Status register bits
-pub const KC_SR_OUTB: u8 = 0x01;
-pub const KC_SR_INPB: u8 = 0x02;
-pub const KC_SR_AUXB: u8 = 0x20;
-
-// Keyboard Controller LED bits
-pub const KC_LED_CAPS_LOCK: u8 = 4;
-pub const KC_LED_NUM_LOCK: u8 = 2;
-pub const KC_LED_SCROLL_LOCK: u8 = 1;
-
-// ACK
-pub const KC_REPLY_ACK: u8 = 0xfa;
-
-// some stuffs for decoding
-pub const BREAK_BIT: u8 = 0x80;
-pub const PREFIX1: u8 = 0xe0;
-pub const PREFIX2: u8 = 0xe1;
diff --git a/src/machine/key.rs b/src/machine/key.rs
index 0a09781..04220a6 100644
--- a/src/machine/key.rs
+++ b/src/machine/key.rs
@@ -1,4 +1,3 @@
-use self::super::kbd_defs::*;
use bitflags::bitflags;
use core::convert;
@@ -98,3 +97,54 @@ 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,
+ Del = 0x53,
+ Up = 72,
+ Down = 80,
+ Left = 75,
+ Right = 77,
+ Div = 8,
+}
+
+// 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] = [
+ 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] = [
+ 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] = [
+ 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];
diff --git a/src/machine/keyctrl.rs b/src/machine/keyctrl.rs
index 43f92d1..246ac02 100644
--- a/src/machine/keyctrl.rs
+++ b/src/machine/keyctrl.rs
@@ -1,6 +1,17 @@
-use self::super::kbd_defs::*;
use self::super::key::*;
use crate::machine::device_io::*;
+use bitflags::bitflags;
+use core::cmp;
+use core::cmp::{Eq, PartialEq};
+use num_enum::{IntoPrimitive, TryFromPrimitive};
+
+// Driver for the PS/2 keybard/mouse controller
+// beyound OOStuBS: I'm gonna write full driver for it.
+
+// 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
@@ -19,34 +30,177 @@ use crate::machine::device_io::*;
pub struct KeyboardController {
code: u8,
prefix: u8,
- gather: Key,
- leds: u8,
+ leds: Led,
+ current: Option<Key>,
+ last: Option<Key>,
+ gather: Option<Key>,
+ cport: IOPort,
+ dport: IOPort,
+}
+
+pub enum KeyDelay {
+ L0 = 0,
+ L1 = 1,
+ L2 = 2,
+ L3 = 3,
+}
+
+// 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)
+ }
}
impl KeyboardController {
- const CTRL_PORT:u16 = 0x64;
- const DATA_PORT:u16 = 0x60;
pub fn new() -> Self {
Self {
code: 0,
- prefix: 9,
- gather: Key::new(),
- leds: 0,
+ prefix: 0,
+ leds: Led::NONE,
+ last: None,
+ current: None,
+ gather: None,
+ cport: IOPort::new(Defs::CTRL),
+ dport: IOPort::new(Defs::DATA),
}
}
- pub fn key_hit(&mut self) -> Key {
+ pub fn fetch_key(&mut self) -> Key {
todo!();
- // for debugging only
- let mut invalid: Key = Key::new();
- invalid.set_raw(0xff);
+ // 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.
+ }
- let status = inb(Self::CTRL_PORT);
- return Key::new();
- // TODO here
+ // 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;
+ }
+
+ // 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
}
- pub fn reboot(&mut self) {
+ // 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;
+ }
+ }
+ }
+
+ // 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;
+ }
+ }
+ }
+
+ 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();
+
+ Self::toggle_keyboard_int(is_masked);
+ }
+
+ pub fn reboot(&mut self) {
todo!();
}
}
+
+// I think constants are more handy than enum for these...
+// Keyboard controller commands
+
+enum Cmd {
+ // these commands are sent through DATA port
+ SetLed = 0xed,
+ ScanCode = 0xf0, // Get or set current scancode set
+ SetSpeed = 0xf3,
+}
+
+bitflags! {
+pub struct Led:u8 {
+ const NONE = 0;
+ const SCROLL_LOCK = 1<<0;
+ const NUM_LOCk = 1<<1;
+ const CAPS_LOCK = 1<<2;
+}
+}
+
+bitflags! {
+pub struct StatusReg:u8 {
+ const NONE = 0;
+ const OUTB = 1 << 0; // output buffer full (can read)
+ const INB = 1 << 1; // input buffer full (don't write yet)
+ const SYS = 1 << 2; // System flag, self test success
+ const CMD_DATA = 1 << 3; // 0: last write to input buffer was data (0x60)
+ // 1: last write to input buffer was command (0x64)
+ const NOT_LOCKED = 1 << 4; // Keyboard Lock. 1: not locked 0: locked
+ const AUXB = 1 << 5; // AUXB: aux output buffer full.
+ // on AT, 1: TIMEOUT
+ // on PS/2, 0: keyboard 1: mouse
+ const TIMEOUT = 1 << 6; // 0: OK 1: Timeout.
+ const PARITY_ERR = 1 << 7;
+}
+}
+
+enum Msg {
+ ACK = 0xfa,
+}
+
+pub struct Defs;
+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;
+}
diff --git a/src/machine/mod.rs b/src/machine/mod.rs
index a9cf4cb..78fa73e 100644
--- a/src/machine/mod.rs
+++ b/src/machine/mod.rs
@@ -1,10 +1,9 @@
pub mod cgascr;
+pub mod device_io;
pub mod interrupt;
-mod kbd_defs;
pub mod key;
pub mod keyctrl;
pub mod mem;
pub mod plugbox;
-pub mod device_io;
// TODO: this module *should* be arch independent.