dev: better key handling

This commit is contained in:
Mark Poliakov 2023-08-04 11:00:31 +03:00
parent ea6684d916
commit 5dd3a252c6
4 changed files with 172 additions and 47 deletions

View File

@ -2,5 +2,12 @@ pub static CODE_SET_1_00: &[u8] = &[
0x00, b'\x1b', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'0', b'-', b'=', b'\x7f',
b'\t', b'q', b'w', b'e', b'r', b't', b'y', b'u', b'i', b'o', b'p', b'[', b']', b'\n', 0x00,
b'a', b's', b'd', b'f', b'g', b'h', b'j', b'k', b'l', b';', b'\'', b'`', 0x00, b'\\', b'z',
b'x', b'c', b'v', b'b', b'n', b'm', b',', b'.', b'/', 0x00,
b'x', b'c', b'v', b'b', b'n', b'm', b',', b'.', b'/', 0x00, b'*', 0x00, b' ',
];
pub static CODE_SET_1_00_SHIFT: &[u8] = &[
0x00, b'\x1b', b'!', b'@', b'#', b'$', b'%', b'^', b'&', b'*', b'(', b')', b'_', b'+', b'\x7f',
b'\t', b'Q', b'W', b'E', b'R', b'T', b'Y', b'U', b'I', b'O', b'P', b'{', b'}', b'\n', 0x00,
b'A', b'S', b'D', b'F', b'G', b'H', b'J', b'K', b'L', b':', b'"', b'~', 0x00, b'|', b'Z', b'X',
b'C', b'V', b'B', b'N', b'M', b'<', b'>', b'?', 0x00,
];

View File

@ -13,19 +13,27 @@ use crate::{
sync::IrqSafeSpinlock,
};
use self::codeset::CODE_SET_1_00;
use self::codeset::{CODE_SET_1_00, CODE_SET_1_00_SHIFT};
mod codeset;
/// PS/2 controller driver
pub struct PS2Controller {
struct Inner {
command: IoPort<u8>,
data: IoPort<u8>,
shift: bool,
ctrl: bool,
tty: Option<&'static dyn TtyDevice<16>>,
}
/// PS/2 controller driver
pub struct PS2Controller {
primary_irq: IrqNumber,
#[allow(unused)]
auxiliary_irq: IrqNumber,
tty: IrqSafeSpinlock<Option<&'static dyn TtyDevice<16>>>,
inner: IrqSafeSpinlock<Inner>,
}
fn translate(codeset: &[u8], key: u8) -> Option<u8> {
@ -40,29 +48,62 @@ fn translate(codeset: &[u8], key: u8) -> Option<u8> {
}
}
impl Inner {
const STATUS_OUTPUT_FULL: u8 = 1 << 0;
const STATUS_INPUT_FULL: u8 = 1 << 1;
fn send_command(&mut self, cmd: u8) {
while self.command.read() & Self::STATUS_INPUT_FULL != 0 {
core::hint::spin_loop();
}
self.command.write(cmd);
}
fn recv_timeout(&mut self, timeout: u64) -> Option<u8> {
let mut counter = 0;
while self.command.read() & Self::STATUS_OUTPUT_FULL == 0 {
counter += 1;
if counter > timeout {
return None;
}
core::hint::spin_loop();
}
Some(self.data.read())
}
fn try_recv(&mut self) -> Option<u8> {
if self.command.read() & Inner::STATUS_OUTPUT_FULL != 0 {
Some(self.data.read())
} else {
None
}
}
}
impl KeyboardDevice for PS2Controller {
fn attach(&self, terminal: &'static dyn TtyDevice<16>) {
self.tty.lock().replace(terminal);
self.inner.lock().tty.replace(terminal);
}
}
impl InterruptSource for PS2Controller {
unsafe fn init_irq(&'static self) -> Result<(), Error> {
let mut inner = self.inner.lock();
let intc = PLATFORM.interrupt_controller();
intc.register_handler(self.primary_irq, self)?;
// Disable PS/2 devices from sending any further data
self.send_command(0xAD);
self.send_command(0xA7);
inner.send_command(0xAD);
inner.send_command(0xA7);
// Flush the buffer
while self.command.read() & Self::STATUS_OUTPUT_FULL != 0 {
self.data.read();
while inner.command.read() & Inner::STATUS_OUTPUT_FULL != 0 {
inner.data.read();
}
// Enable primary port
self.send_command(0xAE);
inner.send_command(0xAE);
intc.enable_irq(self.primary_irq)?;
@ -70,34 +111,103 @@ impl InterruptSource for PS2Controller {
}
fn handle_irq(&self) -> Result<bool, Error> {
let status = self.command.read();
if status & 1 == 0 {
return Ok(false);
}
let key = self.data.read();
if key == 0xE0 {
self.data.read();
infoln!("TODO: handle 0xE0");
return Ok(true);
}
self.data.read();
if key < 128 {
let Some(code) = translate(CODE_SET_1_00, key) else {
return Ok(true);
let mut count = 0;
let mut inner = self.inner.lock();
loop {
let Some(mut scancode) = inner.try_recv() else {
break;
};
let terminal = self.tty.lock();
if let Some(terminal) = &*terminal {
terminal.recv_byte(code);
let e0 = scancode == 0xE0;
if e0 {
scancode = inner.recv_timeout(100000000).unwrap();
}
if !e0 {
match scancode {
// LCtrl pressed
0x1D => {
inner.ctrl = true;
continue;
}
// LCtrl released
0x9D => {
inner.ctrl = false;
continue;
}
// LShift pressed
0x2A => {
inner.shift = true;
continue;
}
// LShift released
0xAA => {
inner.shift = false;
continue;
}
_ => {}
}
}
if scancode > 128 {
continue;
}
let key = match (inner.shift, inner.ctrl, e0) {
// No shift, no ctrl
(false, false, false) => translate(CODE_SET_1_00, scancode).unwrap_or(0),
// Shift, no ctrl
(true, false, false) => translate(CODE_SET_1_00_SHIFT, scancode).unwrap_or(0),
// No shift, ctrl
(false, true, false) => {
let key = translate(CODE_SET_1_00, scancode).unwrap_or(0);
if key == b'c' {
0x3
} else {
0
}
}
// Other keys
_ => 0,
};
if key == 0 {
continue;
}
if let Some(tty) = inner.tty {
tty.recv_byte(key);
}
count += 1;
}
Ok(true)
// let status = self.command.read();
// if status & 1 == 0 {
// return Ok(false);
// }
// let key = self.data.read();
// if key == 0xE0 {
// self.data.read();
// infoln!("TODO: handle 0xE0");
// return Ok(true);
// }
// self.data.read();
// if key < 128 {
// let terminal = self.tty.lock();
// }
// Ok(true)
}
}
@ -108,9 +218,6 @@ impl Device for PS2Controller {
}
impl PS2Controller {
const STATUS_OUTPUT_FULL: u8 = 1 << 0;
const STATUS_INPUT_FULL: u8 = 1 << 1;
/// Constructs a new instance of the device
pub const fn new(
primary_irq: IrqNumber,
@ -118,19 +225,18 @@ impl PS2Controller {
cmd_port: u16,
data_port: u16,
) -> Self {
let inner = Inner {
command: IoPort::new(cmd_port),
data: IoPort::new(data_port),
shift: false,
ctrl: false,
tty: None,
};
Self {
primary_irq,
auxiliary_irq,
command: IoPort::new(cmd_port),
data: IoPort::new(data_port),
tty: IrqSafeSpinlock::new(None),
inner: IrqSafeSpinlock::new(inner),
}
}
fn send_command(&self, cmd: u8) {
while self.command.read() & Self::STATUS_INPUT_FULL != 0 {
core::hint::spin_loop();
}
self.command.write(cmd);
}
}

View File

@ -266,11 +266,23 @@ impl ConsoleState {
let mut flush = false;
match c {
c if c > 127 => {
self.buffer.set_char(
self.cursor_row,
self.cursor_col,
ConsoleChar::from_parts(b'?', self.fg_color, ColorAttribute::Red),
);
self.cursor_col += 1;
}
b'\n' => {
self.cursor_row += 1;
self.cursor_col = 0;
flush = true;
}
b'\x7f' => {
return false;
}
_ => {
self.buffer.set_char(
self.cursor_row,

View File

@ -165,7 +165,7 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
}
// byte == config.chars.interrupt
if byte == b'=' && config.line.contains(TerminalLineOptions::SIGNAL) {
if byte == config.chars.interrupt && config.line.contains(TerminalLineOptions::SIGNAL) {
drop(config);
let pgrp = ring.inner.lock().process_group;
if let Some(pgrp) = pgrp {