Add basic i8042 keyboard support
This commit is contained in:
parent
3f1bf99b2a
commit
5e3e23bf7c
|
|
@ -117,6 +117,7 @@ name = "aster-framebuffer"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aster-console",
|
||||
"aster-keyboard",
|
||||
"component",
|
||||
"font8x8",
|
||||
"log",
|
||||
|
|
@ -134,6 +135,17 @@ dependencies = [
|
|||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aster-keyboard"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"component",
|
||||
"log",
|
||||
"ostd",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aster-logger"
|
||||
version = "0.1.0"
|
||||
|
|
@ -187,6 +199,7 @@ dependencies = [
|
|||
"aster-console",
|
||||
"aster-framebuffer",
|
||||
"aster-input",
|
||||
"aster-keyboard",
|
||||
"aster-logger",
|
||||
"aster-mlsdisk",
|
||||
"aster-network",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ members = [
|
|||
"kernel/comps/console",
|
||||
"kernel/comps/framebuffer",
|
||||
"kernel/comps/input",
|
||||
"kernel/comps/keyboard",
|
||||
"kernel/comps/network",
|
||||
"kernel/comps/softirq",
|
||||
"kernel/comps/systree",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ framebuffer = { name = "aster-framebuffer" }
|
|||
network = { name = "aster-network" }
|
||||
mlsdisk = { name = "aster-mlsdisk" }
|
||||
systree = { name = "aster-systree" }
|
||||
keyboard = { name = "aster-keyboard" }
|
||||
|
||||
[whitelist]
|
||||
[whitelist.nix.main]
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -180,6 +180,7 @@ OSDK_CRATES := \
|
|||
kernel/comps/console \
|
||||
kernel/comps/framebuffer \
|
||||
kernel/comps/input \
|
||||
kernel/comps/keyboard \
|
||||
kernel/comps/network \
|
||||
kernel/comps/softirq \
|
||||
kernel/comps/systree \
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ aster-time = { path = "comps/time" }
|
|||
aster-virtio = { path = "comps/virtio" }
|
||||
aster-rights = { path = "libs/aster-rights" }
|
||||
aster-systree = { path = "comps/systree" }
|
||||
aster-keyboard = { path = "comps/keyboard" }
|
||||
component = { path = "libs/comp-sys/component" }
|
||||
controlled = { path = "libs/comp-sys/controlled" }
|
||||
osdk-frame-allocator = { path = "../osdk/deps/frame-allocator" }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ edition = "2021"
|
|||
ostd = { path = "../../../ostd" }
|
||||
component = { path = "../../libs/comp-sys/component" }
|
||||
aster-console = { path = "../console" }
|
||||
aster-keyboard ={ path = "../keyboard" }
|
||||
log = "0.4"
|
||||
spin = "0.9.4"
|
||||
font8x8 = { version = "0.2.5", default-features = false, features = [ "unicode" ] }
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
use alloc::{sync::Arc, vec::Vec};
|
||||
|
||||
use aster_console::{AnyConsoleDevice, ConsoleCallback};
|
||||
use aster_keyboard::InputKey;
|
||||
use font8x8::UnicodeFonts;
|
||||
use ostd::{
|
||||
mm::VmReader,
|
||||
sync::{LocalIrqDisabled, SpinLock},
|
||||
Error, Result,
|
||||
};
|
||||
|
|
@ -19,9 +21,9 @@ const FONT_WIDTH: usize = 8;
|
|||
const FONT_HEIGHT: usize = 8;
|
||||
|
||||
/// A text console rendered onto the framebuffer.
|
||||
#[derive(Debug)]
|
||||
pub struct FramebufferConsole {
|
||||
state: SpinLock<ConsoleState, LocalIrqDisabled>,
|
||||
callbacks: SpinLock<Vec<&'static ConsoleCallback>, LocalIrqDisabled>,
|
||||
}
|
||||
|
||||
pub const CONSOLE_NAME: &str = "Framebuffer-Console";
|
||||
|
|
@ -35,6 +37,7 @@ pub(crate) fn init() {
|
|||
};
|
||||
|
||||
FRAMEBUFFER_CONSOLE.call_once(|| Arc::new(FramebufferConsole::new(fb.clone())));
|
||||
aster_keyboard::register_callback(&handle_keyboard_input);
|
||||
}
|
||||
|
||||
impl AnyConsoleDevice for FramebufferConsole {
|
||||
|
|
@ -42,8 +45,8 @@ impl AnyConsoleDevice for FramebufferConsole {
|
|||
self.state.lock().send_buf(buf);
|
||||
}
|
||||
|
||||
fn register_callback(&self, _: &'static ConsoleCallback) {
|
||||
// Unsupported, do nothing.
|
||||
fn register_callback(&self, callback: &'static ConsoleCallback) {
|
||||
self.callbacks.lock().push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +64,7 @@ impl FramebufferConsole {
|
|||
bytes,
|
||||
backend: framebuffer,
|
||||
}),
|
||||
callbacks: SpinLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,6 +122,12 @@ impl FramebufferConsole {
|
|||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for FramebufferConsole {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("FramebufferConsole").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ConsoleState {
|
||||
// FIXME: maybe we should drop the whole `ConsoleState` when it's disabled.
|
||||
|
|
@ -210,3 +220,15 @@ impl ConsoleState {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_keyboard_input(key: InputKey) {
|
||||
let Some(console) = FRAMEBUFFER_CONSOLE.get() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let buffer = key.as_xterm_control_sequence();
|
||||
for callback in console.callbacks.lock().iter() {
|
||||
let reader = VmReader::from(buffer);
|
||||
callback(reader);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "aster-keyboard"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
component = { path = "../../libs/comp-sys/component" }
|
||||
ostd = { path = "../../../ostd" }
|
||||
bitflags = "2.5"
|
||||
log = "0.4"
|
||||
spin = "0.9.4"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Provides i8042 PS/2 Controller I/O port access.
|
||||
//!
|
||||
//! Reference: <https://wiki.osdev.org/I8042_PS/2_Controller>
|
||||
//!
|
||||
|
||||
use bitflags::bitflags;
|
||||
use ostd::{
|
||||
arch::device::io_port::ReadWriteAccess,
|
||||
io::IoPort,
|
||||
sync::{LocalIrqDisabled, SpinLock},
|
||||
};
|
||||
use spin::Once;
|
||||
|
||||
/// The `I8042Controller` singleton.
|
||||
pub(super) static I8042_CONTROLLER: Once<SpinLock<I8042Controller, LocalIrqDisabled>> = Once::new();
|
||||
|
||||
pub(super) fn init() -> Result<(), I8042ControllerError> {
|
||||
let mut controller = I8042Controller::new()?;
|
||||
|
||||
// The steps to initialize the i8042 controller are from:
|
||||
// <https://wiki.osdev.org/I8042_PS/2_Controller#Initialising_the_PS/2_Controller>.
|
||||
|
||||
// Disable devices so that they won't send data at the wrong time and mess up initialization.
|
||||
controller.wait_and_send_command(Command::DisableFirstPort)?;
|
||||
controller.wait_and_send_command(Command::DisableSecondPort)?;
|
||||
|
||||
// Flush the output buffer by reading from the data port and discarding the data.
|
||||
controller.flush_output_buffer();
|
||||
|
||||
// Set the controller configuration byte.
|
||||
let mut config = controller.read_configuration()?;
|
||||
config.remove(
|
||||
Configuration::FIRST_PORT_INTERRUPT_ENABLED
|
||||
| Configuration::FIRST_PORT_TRANSLATION_ENABLED
|
||||
| Configuration::SECOND_PORT_INTERRUPT_ENABLED,
|
||||
);
|
||||
controller.write_configuration(&config)?;
|
||||
|
||||
// Perform controller self-test.
|
||||
controller.wait_and_send_command(Command::TestController)?;
|
||||
let result = controller.wait_and_recv_data()?;
|
||||
if result != 0x55 {
|
||||
// Any value other than 0x55 indicates a self-test fail.
|
||||
return Err(I8042ControllerError::ControllerTestFailed);
|
||||
}
|
||||
// The self-test may reset the controller. Restore the original configuration.
|
||||
controller.write_configuration(&config)?;
|
||||
// The ports may have been enabled if the controller was reset. Flush the output buffer.
|
||||
controller.flush_output_buffer();
|
||||
|
||||
// Determine if there are two channels.
|
||||
controller.wait_and_send_command(Command::EnableSecondPort)?;
|
||||
let has_second_port = config.contains(Configuration::SECOND_PORT_CLOCK_DISABLED)
|
||||
&& !controller
|
||||
.read_configuration()?
|
||||
.contains(Configuration::SECOND_PORT_CLOCK_DISABLED);
|
||||
controller.wait_and_send_command(Command::DisableSecondPort)?;
|
||||
// Flush the output buffer again since we may have enabled the second port.
|
||||
controller.flush_output_buffer();
|
||||
|
||||
// Perform interface tests to the first PS/2 port.
|
||||
controller.wait_and_send_command(Command::TestFirstPort)?;
|
||||
let result = controller.wait_and_recv_data()?;
|
||||
if result != 0x00 {
|
||||
return Err(I8042ControllerError::FirstPortTestFailed);
|
||||
}
|
||||
|
||||
// Perform interface tests to the second PS/2 port (if it exists).
|
||||
if has_second_port {
|
||||
controller.wait_and_send_command(Command::TestSecondPort)?;
|
||||
let result = controller.wait_and_recv_data()?;
|
||||
if result != 0x00 {
|
||||
return Err(I8042ControllerError::SecondPortTestFailed);
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the first PS/2 port.
|
||||
controller.wait_and_send_command(Command::EnableFirstPort)?;
|
||||
if let Err(err) = super::keyboard::init(&mut controller) {
|
||||
log::warn!("i8042 keyboard initialization failed: {:?}", err);
|
||||
controller.wait_and_send_command(Command::DisableFirstPort)?;
|
||||
} else {
|
||||
config.remove(Configuration::FIRST_PORT_CLOCK_DISABLED);
|
||||
config.insert(
|
||||
Configuration::FIRST_PORT_INTERRUPT_ENABLED
|
||||
| Configuration::FIRST_PORT_TRANSLATION_ENABLED,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Add a mouse driver and enable the second PS/2 port (if it exists).
|
||||
|
||||
I8042_CONTROLLER.call_once(|| SpinLock::new(controller));
|
||||
let mut controller = I8042_CONTROLLER.get().unwrap().lock();
|
||||
// Write the new configuration to enable the interrupts after setting up `I8042_CONTROLLER`.
|
||||
controller.write_configuration(&config)?;
|
||||
// Flush the output buffer to ensure that new data can trigger interrupts.
|
||||
controller.flush_output_buffer();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An I8042 PS/2 Controller.
|
||||
pub(super) struct I8042Controller {
|
||||
data_port: IoPort<u8, ReadWriteAccess>,
|
||||
status_or_command_port: IoPort<u8, ReadWriteAccess>,
|
||||
}
|
||||
|
||||
/// The maximum number of times to wait for the i8042 controller to be ready.
|
||||
const MAX_WAITING_COUNT: usize = 64;
|
||||
|
||||
impl I8042Controller {
|
||||
fn new() -> Result<Self, I8042ControllerError> {
|
||||
// TODO: Check the flags in the ACPI table to determine if the PS/2 controller exists. See:
|
||||
// <https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#ia-pc-boot-architecture-flags>.
|
||||
let controller = Self {
|
||||
data_port: IoPort::acquire(0x60).unwrap(),
|
||||
status_or_command_port: IoPort::acquire(0x64).unwrap(),
|
||||
};
|
||||
Ok(controller)
|
||||
}
|
||||
|
||||
fn read_configuration(&mut self) -> Result<Configuration, I8042ControllerError> {
|
||||
self.wait_and_send_command(Command::ReadConfiguration)?;
|
||||
self.wait_and_recv_data()
|
||||
.map(Configuration::from_bits_retain)
|
||||
}
|
||||
|
||||
fn write_configuration(&mut self, config: &Configuration) -> Result<(), I8042ControllerError> {
|
||||
self.wait_and_send_command(Command::WriteConfiguration)?;
|
||||
self.wait_and_send_data(config.bits())
|
||||
}
|
||||
|
||||
fn wait_and_send_command(&mut self, command: Command) -> Result<(), I8042ControllerError> {
|
||||
for _ in 0..MAX_WAITING_COUNT {
|
||||
if self.send_command(command).is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
Err(I8042ControllerError::OutputBusy)
|
||||
}
|
||||
|
||||
fn send_command(&mut self, command: Command) -> Result<(), I8042ControllerError> {
|
||||
if !self.read_status().contains(Status::INPUT_BUFFER_IS_FULL) {
|
||||
self.write_command(command as u8);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(I8042ControllerError::OutputBusy)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_status(&self) -> Status {
|
||||
Status::from_bits_retain(self.status_or_command_port.read())
|
||||
}
|
||||
|
||||
fn write_command(&mut self, command: u8) {
|
||||
self.status_or_command_port.write(command);
|
||||
}
|
||||
|
||||
pub(super) fn wait_and_send_data(&mut self, data: u8) -> Result<(), I8042ControllerError> {
|
||||
for _ in 0..MAX_WAITING_COUNT {
|
||||
if self.send_data(data).is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
Err(I8042ControllerError::OutputBusy)
|
||||
}
|
||||
|
||||
pub(super) fn send_data(&mut self, data: u8) -> Result<(), I8042ControllerError> {
|
||||
if !self.read_status().contains(Status::INPUT_BUFFER_IS_FULL) {
|
||||
self.write_data(data);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(I8042ControllerError::OutputBusy)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_data(&mut self, data: u8) {
|
||||
self.data_port.write(data);
|
||||
}
|
||||
|
||||
pub(super) fn wait_and_recv_data(&mut self) -> Result<u8, I8042ControllerError> {
|
||||
for _ in 0..MAX_WAITING_COUNT {
|
||||
if let Some(data) = self.receive_data() {
|
||||
return Ok(data);
|
||||
}
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
Err(I8042ControllerError::NoInput)
|
||||
}
|
||||
|
||||
pub(super) fn receive_data(&mut self) -> Option<u8> {
|
||||
if self.read_status().contains(Status::OUTPUT_BUFFER_IS_FULL) {
|
||||
Some(self.read_data())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn read_data(&self) -> u8 {
|
||||
self.data_port.read()
|
||||
}
|
||||
|
||||
fn flush_output_buffer(&mut self) {
|
||||
while self.receive_data().is_some() {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when initializing the i8042 controller.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(super) enum I8042ControllerError {
|
||||
ControllerTestFailed,
|
||||
FirstPortTestFailed,
|
||||
SecondPortTestFailed,
|
||||
OutputBusy,
|
||||
NoInput,
|
||||
DeviceResetFailed,
|
||||
DeviceUnknown,
|
||||
DeviceAllocIrqFailed,
|
||||
}
|
||||
|
||||
/// The commands that can be sent to the PS/2 controller.
|
||||
///
|
||||
/// Reference: <https://wiki.osdev.org/I8042_PS/2_Controller#PS/2_Controller_Commands>.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
enum Command {
|
||||
ReadConfiguration = 0x20,
|
||||
WriteConfiguration = 0x60,
|
||||
DisableSecondPort = 0xA7,
|
||||
EnableSecondPort = 0xA8,
|
||||
TestSecondPort = 0xA9,
|
||||
TestController = 0xAA,
|
||||
TestFirstPort = 0xAB,
|
||||
DisableFirstPort = 0xAD,
|
||||
EnableFirstPort = 0xAE,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// The configuration of the PS/2 controller.
|
||||
///
|
||||
/// Reference: <https://wiki.osdev.org/I8042_PS/2_Controller#PS/2_Controller_Configuration_Byte>.
|
||||
struct Configuration: u8 {
|
||||
/// First PS/2 port interrupt (1 = enabled, 0 = disabled)
|
||||
const FIRST_PORT_INTERRUPT_ENABLED = 1 << 0;
|
||||
/// Second PS/2 port interrupt (1 = enabled, 0 = disabled, only if 2 PS/2 ports supported)
|
||||
const SECOND_PORT_INTERRUPT_ENABLED = 1 << 1;
|
||||
/// System Flag (1 = system passed POST, 0 = your OS shouldn't be running)
|
||||
const SYSTEM_POST_PASSED = 1 << 2;
|
||||
/// First PS/2 port clock (1 = disabled, 0 = enabled)
|
||||
const FIRST_PORT_CLOCK_DISABLED = 1 << 4;
|
||||
/// Second PS/2 port clock (1 = disabled, 0 = enabled, only if 2 PS/2 ports supported)
|
||||
const SECOND_PORT_CLOCK_DISABLED = 1 << 5;
|
||||
/// First PS/2 port translation (1 = enabled, 0 = disabled)
|
||||
const FIRST_PORT_TRANSLATION_ENABLED = 1 << 6;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// The status of the i8042 PS/2 controller.
|
||||
///
|
||||
/// Reference: <https://wiki.osdev.org/I8042_PS/2_Controller#Status_Register>.
|
||||
struct Status: u8 {
|
||||
/// Output buffer status (0 = empty, 1 = full)
|
||||
/// Must be set before attempting to read data from port 0x60.
|
||||
const OUTPUT_BUFFER_IS_FULL = 1 << 0;
|
||||
/// Input buffer status (0 = empty, 1 = full)
|
||||
/// Must be clear before attempting to write data to IO port 0x60 or IO port 0x64.
|
||||
const INPUT_BUFFER_IS_FULL = 1 << 1;
|
||||
/// System Flag
|
||||
/// Meant to be cleared on reset and set by firmware (via. PS/2 Controller Configuration Byte)
|
||||
/// if the system passes self tests (POST).
|
||||
const SYSTEM_FLAG = 1 << 2;
|
||||
/// Command or data (0 = data, 1 = command)
|
||||
/// Data written to input buffer is data for PS/2 device or command for controller.
|
||||
const IS_COMMAND = 1 << 3;
|
||||
/// Time-out error (0 = no error, 1 = time-out error)
|
||||
const TIME_OUT_ERROR = 1 << 6;
|
||||
/// Parity error (0 = no error, 1 = parity error)
|
||||
const PARITY_ERROR = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,377 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! The i8042 keyboard driver.
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use ostd::{
|
||||
arch::kernel::{MappedIrqLine, IRQ_CHIP},
|
||||
trap::{irq::IrqLine, TrapFrame},
|
||||
};
|
||||
use spin::Once;
|
||||
|
||||
use super::controller::{I8042Controller, I8042ControllerError, I8042_CONTROLLER};
|
||||
use crate::{InputKey, KEYBOARD_CALLBACKS};
|
||||
|
||||
/// IRQ line for i8042 keyboard.
|
||||
static IRQ_LINE: Once<MappedIrqLine> = Once::new();
|
||||
|
||||
/// ISA interrupt number for i8042 keyboard.
|
||||
const ISA_INTR_NUM: u8 = 1;
|
||||
|
||||
pub(super) fn init(controller: &mut I8042Controller) -> Result<(), I8042ControllerError> {
|
||||
// Reset keyboard device by sending 0xFF (reset command, supported by all PS/2 devices) to port 1
|
||||
// and waiting for a response.
|
||||
controller.wait_and_send_data(0xFF)?;
|
||||
|
||||
// The response should be 0xFA (ACK) and 0xAA (BAT successful), followed by the device PS/2 ID.
|
||||
if controller.wait_and_recv_data()? != 0xFA {
|
||||
return Err(I8042ControllerError::DeviceResetFailed);
|
||||
}
|
||||
// The reset command may take some time to finish. Try again a few times.
|
||||
if (0..5).find_map(|_| controller.wait_and_recv_data().ok()) != Some(0xAA) {
|
||||
return Err(I8042ControllerError::DeviceResetFailed);
|
||||
}
|
||||
// See <https://wiki.osdev.org/I8042_PS/2_Controller#Detecting_PS/2_Device_Types> for a list of IDs.
|
||||
let mut iter = core::iter::from_fn(|| controller.wait_and_recv_data().ok());
|
||||
match (iter.next(), iter.next()) {
|
||||
// Ancient AT keyboard
|
||||
(None, None) => (),
|
||||
// Other devices, including other kinds of keyboards (TODO: Support other kinds of keyboards)
|
||||
_ => return Err(I8042ControllerError::DeviceUnknown),
|
||||
}
|
||||
|
||||
let mut irq_line = IrqLine::alloc()
|
||||
.and_then(|irq_line| {
|
||||
IRQ_CHIP
|
||||
.get()
|
||||
.unwrap()
|
||||
.map_isa_pin_to(irq_line, ISA_INTR_NUM)
|
||||
})
|
||||
.map_err(|_| I8042ControllerError::DeviceAllocIrqFailed)?;
|
||||
irq_line.on_active(handle_keyboard_input);
|
||||
IRQ_LINE.call_once(|| irq_line);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_keyboard_input(_trap_frame: &TrapFrame) {
|
||||
if !I8042_CONTROLLER.is_completed() {
|
||||
return;
|
||||
}
|
||||
|
||||
let key = parse_inputkey();
|
||||
for callback in KEYBOARD_CALLBACKS.lock().iter() {
|
||||
callback(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// A scan code in the Scan Code Set 1.
|
||||
///
|
||||
/// Reference: <https://wiki.osdev.org/PS/2_Keyboard#Scan_Code_Set_1>.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct ScanCode(u8);
|
||||
|
||||
impl ScanCode {
|
||||
fn has_error(&self) -> bool {
|
||||
// Key detection error or internal buffer overrun.
|
||||
self.0 == 0xFF
|
||||
}
|
||||
|
||||
fn is_pressed(&self) -> bool {
|
||||
self.0 & 0x80 == 0
|
||||
}
|
||||
|
||||
fn is_released(&self) -> bool {
|
||||
self.0 & 0x80 != 0
|
||||
}
|
||||
|
||||
fn is_shift(&self) -> bool {
|
||||
let code = self.0 & 0x7F;
|
||||
// Left/right shift codes
|
||||
code == 0x2A || code == 0x36
|
||||
}
|
||||
|
||||
fn is_ctrl(&self) -> bool {
|
||||
let code = self.0 & 0x7F;
|
||||
// Left/right ctrl codes
|
||||
code == 0x1D
|
||||
}
|
||||
|
||||
fn is_caps_lock(&self) -> bool {
|
||||
self.0 == 0x3A
|
||||
}
|
||||
|
||||
fn is_extension(&self) -> bool {
|
||||
self.0 == 0xE0
|
||||
}
|
||||
|
||||
fn plain_map(&self) -> InputKey {
|
||||
match self.0 & 0x7F {
|
||||
0x01 => InputKey::Esc,
|
||||
0x02 => InputKey::One,
|
||||
0x03 => InputKey::Two,
|
||||
0x04 => InputKey::Three,
|
||||
0x05 => InputKey::Four,
|
||||
0x06 => InputKey::Five,
|
||||
0x07 => InputKey::Six,
|
||||
0x08 => InputKey::Seven,
|
||||
0x09 => InputKey::Eight,
|
||||
0x0A => InputKey::Nine,
|
||||
0x0B => InputKey::Zero,
|
||||
0x0C => InputKey::Minus,
|
||||
0x0D => InputKey::Equal,
|
||||
0x0E => InputKey::Del,
|
||||
0x0F => InputKey::Tab,
|
||||
0x10 => InputKey::LowercaseQ,
|
||||
0x11 => InputKey::LowercaseW,
|
||||
0x12 => InputKey::LowercaseE,
|
||||
0x13 => InputKey::LowercaseR,
|
||||
0x14 => InputKey::LowercaseT,
|
||||
0x15 => InputKey::LowercaseY,
|
||||
0x16 => InputKey::LowercaseU,
|
||||
0x17 => InputKey::LowercaseI,
|
||||
0x18 => InputKey::LowercaseO,
|
||||
0x19 => InputKey::LowercaseP,
|
||||
0x1A => InputKey::LeftBracket,
|
||||
0x1B => InputKey::RightBracket,
|
||||
0x1C => InputKey::Cr, // Enter
|
||||
0x1D => InputKey::Ign, // Left Ctrl
|
||||
0x1E => InputKey::LowercaseA,
|
||||
0x1F => InputKey::LowercaseS,
|
||||
0x20 => InputKey::LowercaseD,
|
||||
0x21 => InputKey::LowercaseF,
|
||||
0x22 => InputKey::LowercaseG,
|
||||
0x23 => InputKey::LowercaseH,
|
||||
0x24 => InputKey::LowercaseJ,
|
||||
0x25 => InputKey::LowercaseK,
|
||||
0x26 => InputKey::LowercaseL,
|
||||
0x27 => InputKey::SemiColon,
|
||||
0x28 => InputKey::SingleQuote,
|
||||
0x29 => InputKey::Backtick,
|
||||
0x2A => InputKey::Ign, // Left Shift
|
||||
0x2B => InputKey::BackSlash,
|
||||
0x2C => InputKey::LowercaseZ,
|
||||
0x2D => InputKey::LowercaseX,
|
||||
0x2E => InputKey::LowercaseC,
|
||||
0x2F => InputKey::LowercaseV,
|
||||
0x30 => InputKey::LowercaseB,
|
||||
0x31 => InputKey::LowercaseN,
|
||||
0x32 => InputKey::LowercaseM,
|
||||
0x33 => InputKey::Comma,
|
||||
0x34 => InputKey::Period,
|
||||
0x35 => InputKey::ForwardSlash,
|
||||
0x36 => InputKey::Ign, // Right Shift
|
||||
0x37 => InputKey::Asterisk, // Keypad-* or (*/PrtScn) on a 83/84-key keyboard
|
||||
0x38 => InputKey::Ign, // Left Alt
|
||||
0x39 => InputKey::Space,
|
||||
0x3A => InputKey::Ign, // CapsLock
|
||||
0x3B => InputKey::F1,
|
||||
0x3C => InputKey::F2,
|
||||
0x3D => InputKey::F3,
|
||||
0x3E => InputKey::F4,
|
||||
0x3F => InputKey::F5,
|
||||
0x40 => InputKey::F6,
|
||||
0x41 => InputKey::F7,
|
||||
0x42 => InputKey::F8,
|
||||
0x43 => InputKey::F9,
|
||||
0x44 => InputKey::F10,
|
||||
0x45 => InputKey::Ign, // NumLock
|
||||
0x46 => InputKey::Ign, // ScrollLock
|
||||
0x47 => InputKey::Home, // Keypad-7 or Home
|
||||
0x48 => InputKey::UpArrow, // Keypad-8 or Up
|
||||
0x49 => InputKey::PageUp, // Keypad-9 or PageUp
|
||||
0x4A => InputKey::Minus, // Keypad--
|
||||
0x4B => InputKey::LeftArrow, // Keypad-4 or Left
|
||||
0x4C => InputKey::Five, // Keypad-5
|
||||
0x4D => InputKey::RightArrow, // Keypad-6 or Right
|
||||
0x4E => InputKey::Plus, // Keypad-+
|
||||
0x4F => InputKey::End, // Keypad-1 or End
|
||||
0x50 => InputKey::DownArrow, // Keypad-2 or Down
|
||||
0x51 => InputKey::PageDown, // Keypad-3 or PageDown
|
||||
0x52 => InputKey::Insert, // Keypad-0 or Insert
|
||||
0x53 => InputKey::Delete, // Keypad-. or Del
|
||||
0x57 => InputKey::F11,
|
||||
0x58 => InputKey::F12,
|
||||
_ => InputKey::Ign,
|
||||
}
|
||||
}
|
||||
|
||||
fn shift_map(&self) -> InputKey {
|
||||
match self.0 & 0x7F {
|
||||
0x01 => InputKey::Esc,
|
||||
0x02 => InputKey::Exclamation,
|
||||
0x03 => InputKey::At,
|
||||
0x04 => InputKey::Hash,
|
||||
0x05 => InputKey::Dollar,
|
||||
0x06 => InputKey::Percent,
|
||||
0x07 => InputKey::Caret,
|
||||
0x08 => InputKey::Ampersand,
|
||||
0x09 => InputKey::Asterisk,
|
||||
0x0A => InputKey::LeftParen,
|
||||
0x0B => InputKey::RightParen,
|
||||
0x0C => InputKey::Underscore,
|
||||
0x0D => InputKey::Plus,
|
||||
0x0E => InputKey::Del,
|
||||
0x0F => InputKey::Tab,
|
||||
0x10 => InputKey::UppercaseQ,
|
||||
0x11 => InputKey::UppercaseW,
|
||||
0x12 => InputKey::UppercaseE,
|
||||
0x13 => InputKey::UppercaseR,
|
||||
0x14 => InputKey::UppercaseT,
|
||||
0x15 => InputKey::UppercaseY,
|
||||
0x16 => InputKey::UppercaseU,
|
||||
0x17 => InputKey::UppercaseI,
|
||||
0x18 => InputKey::UppercaseO,
|
||||
0x19 => InputKey::UppercaseP,
|
||||
0x1A => InputKey::LeftBrace,
|
||||
0x1B => InputKey::RightBrace,
|
||||
0x1C => InputKey::Cr,
|
||||
0x1E => InputKey::UppercaseA,
|
||||
0x1F => InputKey::UppercaseS,
|
||||
0x20 => InputKey::UppercaseD,
|
||||
0x21 => InputKey::UppercaseF,
|
||||
0x22 => InputKey::UppercaseG,
|
||||
0x23 => InputKey::UppercaseH,
|
||||
0x24 => InputKey::UppercaseJ,
|
||||
0x25 => InputKey::UppercaseK,
|
||||
0x26 => InputKey::UppercaseL,
|
||||
0x27 => InputKey::Colon,
|
||||
0x28 => InputKey::DoubleQuote,
|
||||
0x29 => InputKey::Tilde,
|
||||
0x2B => InputKey::Pipe,
|
||||
0x2C => InputKey::UppercaseZ,
|
||||
0x2D => InputKey::UppercaseX,
|
||||
0x2E => InputKey::UppercaseC,
|
||||
0x2F => InputKey::UppercaseV,
|
||||
0x30 => InputKey::UppercaseB,
|
||||
0x31 => InputKey::UppercaseN,
|
||||
0x32 => InputKey::UppercaseM,
|
||||
0x33 => InputKey::LessThan,
|
||||
0x34 => InputKey::GreaterThan,
|
||||
0x35 => InputKey::Question,
|
||||
0x39 => InputKey::Space,
|
||||
_ => InputKey::Ign,
|
||||
}
|
||||
}
|
||||
|
||||
fn ctrl_map(&self) -> InputKey {
|
||||
match self.0 & 0x7F {
|
||||
0x02 => InputKey::One,
|
||||
0x03 => InputKey::Nul,
|
||||
0x04 => InputKey::Esc,
|
||||
0x05 => InputKey::Fs,
|
||||
0x06 => InputKey::Gs,
|
||||
0x07 => InputKey::Rs,
|
||||
0x08 => InputKey::Us,
|
||||
0x09 => InputKey::Del,
|
||||
0x0A => InputKey::Nine,
|
||||
0x0B => InputKey::Zero,
|
||||
0x0C => InputKey::Us,
|
||||
0x0D => InputKey::Equal,
|
||||
0x0E => InputKey::Bs,
|
||||
0x10 => InputKey::Dc1,
|
||||
0x11 => InputKey::Etb,
|
||||
0x12 => InputKey::Enq,
|
||||
0x13 => InputKey::Dc2,
|
||||
0x14 => InputKey::Dc4,
|
||||
0x15 => InputKey::Em,
|
||||
0x16 => InputKey::Nak,
|
||||
0x17 => InputKey::Tab,
|
||||
0x18 => InputKey::Si,
|
||||
0x19 => InputKey::Dle,
|
||||
0x1A => InputKey::Esc,
|
||||
0x1B => InputKey::Gs,
|
||||
0x1C => InputKey::Cr,
|
||||
0x1E => InputKey::Soh,
|
||||
0x1F => InputKey::Dc3,
|
||||
0x20 => InputKey::Eot,
|
||||
0x21 => InputKey::Ack,
|
||||
0x22 => InputKey::Bel,
|
||||
0x23 => InputKey::Bs,
|
||||
0x24 => InputKey::Lf,
|
||||
0x25 => InputKey::Vt,
|
||||
0x26 => InputKey::Ff,
|
||||
0x27 => InputKey::SemiColon,
|
||||
0x28 => InputKey::SingleQuote,
|
||||
0x29 => InputKey::Backtick,
|
||||
0x2B => InputKey::Fs,
|
||||
0x2C => InputKey::Sub,
|
||||
0x2D => InputKey::Can,
|
||||
0x2E => InputKey::Etx,
|
||||
0x2F => InputKey::Syn,
|
||||
0x30 => InputKey::Stx,
|
||||
0x31 => InputKey::So,
|
||||
0x32 => InputKey::Cr,
|
||||
0x33 => InputKey::Comma,
|
||||
0x34 => InputKey::Period,
|
||||
0x35 => InputKey::Us,
|
||||
_ => InputKey::Ign,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_inputkey() -> InputKey {
|
||||
static CAPS_LOCK: AtomicBool = AtomicBool::new(false); // CapsLock key state
|
||||
static SHIFT_KEY: AtomicBool = AtomicBool::new(false); // Shift key pressed
|
||||
static CTRL_KEY: AtomicBool = AtomicBool::new(false); // Ctrl key pressed
|
||||
|
||||
let Some(data) = I8042_CONTROLLER.get().unwrap().lock().receive_data() else {
|
||||
log::warn!("i8042 keyboard has no input data");
|
||||
return InputKey::Ign;
|
||||
};
|
||||
|
||||
let code = ScanCode(data);
|
||||
if code.has_error() {
|
||||
log::warn!("i8042 keyboard key detection error or internal buffer overrun");
|
||||
return InputKey::Ign;
|
||||
}
|
||||
|
||||
// TODO: Handle the scancodes with extended byte (0xE0). It generates two
|
||||
// different interrupts: the first containing 0xE0, the second containing
|
||||
// the scancode.
|
||||
if code.is_extension() {
|
||||
return InputKey::Ign;
|
||||
}
|
||||
|
||||
// Handle the Ctrl key, holds the state.
|
||||
if code.is_ctrl() {
|
||||
if code.is_pressed() {
|
||||
CTRL_KEY.store(true, Ordering::Relaxed);
|
||||
} else {
|
||||
CTRL_KEY.store(false, Ordering::Relaxed);
|
||||
}
|
||||
return InputKey::Ign;
|
||||
}
|
||||
|
||||
// Handle the Shift key, holds the state.
|
||||
if code.is_shift() {
|
||||
if code.is_pressed() {
|
||||
SHIFT_KEY.store(true, Ordering::Relaxed);
|
||||
} else {
|
||||
SHIFT_KEY.store(false, Ordering::Relaxed);
|
||||
}
|
||||
return InputKey::Ign;
|
||||
}
|
||||
|
||||
// Ignore other release events.
|
||||
if code.is_released() {
|
||||
return InputKey::Ign;
|
||||
}
|
||||
|
||||
if code.is_caps_lock() {
|
||||
CAPS_LOCK.fetch_xor(true, Ordering::Relaxed);
|
||||
return InputKey::Ign;
|
||||
}
|
||||
|
||||
let ctrl_key = CTRL_KEY.load(Ordering::Relaxed);
|
||||
let shift_key = SHIFT_KEY.load(Ordering::Relaxed);
|
||||
let caps_lock = CAPS_LOCK.load(Ordering::Relaxed);
|
||||
if ctrl_key {
|
||||
code.ctrl_map()
|
||||
} else if shift_key ^ caps_lock {
|
||||
code.shift_map()
|
||||
} else {
|
||||
code.plain_map()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
mod controller;
|
||||
mod keyboard;
|
||||
|
||||
pub(crate) fn init() {
|
||||
if let Err(err) = controller::init() {
|
||||
log::warn!("i8042 controller initialization failed: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Handle keyboard input.
|
||||
#![no_std]
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
|
||||
use component::{init_component, ComponentInitError};
|
||||
use ostd::sync::{LocalIrqDisabled, SpinLock};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod i8042_chip;
|
||||
|
||||
static KEYBOARD_CALLBACKS: SpinLock<Vec<Box<KeyboardCallback>>, LocalIrqDisabled> =
|
||||
SpinLock::new(Vec::new());
|
||||
|
||||
#[init_component]
|
||||
fn init() -> Result<(), ComponentInitError> {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
i8042_chip::init();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The callback function for keyboard.
|
||||
pub type KeyboardCallback = dyn Fn(InputKey) + Send + Sync;
|
||||
|
||||
pub fn register_callback(callback: &'static KeyboardCallback) {
|
||||
KEYBOARD_CALLBACKS.lock().push(Box::new(callback));
|
||||
}
|
||||
|
||||
/// Define unified keycodes for different types of keyboards.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum InputKey {
|
||||
Ign, // Ignore this key
|
||||
// Control characters
|
||||
Nul, // Ctrl + @, null
|
||||
Soh, // Ctrl + A, start of heading
|
||||
Stx, // Ctrl + B, start of text
|
||||
Etx, // Ctrl + C, end of text
|
||||
Eot, // Ctrl + D, end of transmission
|
||||
Enq, // Ctrl + E, enquiry
|
||||
Ack, // Ctrl + F, acknowledge
|
||||
Bel, // Ctrl + G, bell
|
||||
Bs, // Ctrl + H, backspace,
|
||||
Tab, // Ctrl + I, horizontal tab
|
||||
Lf, // Ctrl + J, NL line feed, new line
|
||||
Vt, // Ctrl + K, vertical tab
|
||||
Ff, // Ctrl + L, NP form feed, new page
|
||||
Cr, // Ctrl + M, carriage return
|
||||
So, // Ctrl + N, shift out
|
||||
Si, // Ctrl + O, shift in
|
||||
Dle, // Ctrl + P, data link escape
|
||||
Dc1, // Ctrl + Q, device control 1
|
||||
Dc2, // Ctrl + R, device control 2
|
||||
Dc3, // Ctrl + S, device control 3
|
||||
Dc4, // Ctrl + T, device control 4
|
||||
Nak, // Ctrl + U, negative acknowledge
|
||||
Syn, // Ctrl + V, synchronous idle
|
||||
Etb, // Ctrl + W, end of trans. block
|
||||
Can, // Ctrl + X, cancel
|
||||
Em, // Ctrl + Y, end of medium
|
||||
Sub, // Ctrl + Z, substitute
|
||||
Esc, // Ctrl + [, escape
|
||||
Fs, // Ctrl + \, file separator
|
||||
Gs, // Ctrl + ], group separator
|
||||
Rs, // Ctrl + ^, record separator
|
||||
Us, // Ctrl + _, unit separator
|
||||
Space, // ' '
|
||||
Exclamation, // '!'
|
||||
DoubleQuote, // '"'
|
||||
Hash, // '#'
|
||||
Dollar, // '$'
|
||||
Percent, // '%'
|
||||
Ampersand, // '&'
|
||||
SingleQuote, // '''
|
||||
LeftParen, // '('
|
||||
RightParen, // ')'
|
||||
Asterisk, // '*'
|
||||
Plus, // '+'
|
||||
Comma, // ','
|
||||
Minus, // '-'
|
||||
Period, // '.'
|
||||
ForwardSlash, // '/'
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
Nine,
|
||||
Colon, // ':'
|
||||
SemiColon, // ';'
|
||||
LessThan, // '<'
|
||||
Equal, // '='
|
||||
GreaterThan, // '>'
|
||||
Question, // '?'
|
||||
At, // '@'
|
||||
UppercaseA,
|
||||
UppercaseB,
|
||||
UppercaseC,
|
||||
UppercaseD,
|
||||
UppercaseE,
|
||||
UppercaseF,
|
||||
UppercaseG,
|
||||
UppercaseH,
|
||||
UppercaseI,
|
||||
UppercaseJ,
|
||||
UppercaseK,
|
||||
UppercaseL,
|
||||
UppercaseM,
|
||||
UppercaseN,
|
||||
UppercaseO,
|
||||
UppercaseP,
|
||||
UppercaseQ,
|
||||
UppercaseR,
|
||||
UppercaseS,
|
||||
UppercaseT,
|
||||
UppercaseU,
|
||||
UppercaseV,
|
||||
UppercaseW,
|
||||
UppercaseX,
|
||||
UppercaseY,
|
||||
UppercaseZ,
|
||||
LeftBracket, // '['
|
||||
BackSlash, // '\'
|
||||
RightBracket, // ']'
|
||||
Caret, // '^'
|
||||
Underscore, // '_'
|
||||
Backtick, // '`'
|
||||
LowercaseA,
|
||||
LowercaseB,
|
||||
LowercaseC,
|
||||
LowercaseD,
|
||||
LowercaseE,
|
||||
LowercaseF,
|
||||
LowercaseG,
|
||||
LowercaseH,
|
||||
LowercaseI,
|
||||
LowercaseJ,
|
||||
LowercaseK,
|
||||
LowercaseL,
|
||||
LowercaseM,
|
||||
LowercaseN,
|
||||
LowercaseO,
|
||||
LowercaseP,
|
||||
LowercaseQ,
|
||||
LowercaseR,
|
||||
LowercaseS,
|
||||
LowercaseT,
|
||||
LowercaseU,
|
||||
LowercaseV,
|
||||
LowercaseW,
|
||||
LowercaseX,
|
||||
LowercaseY,
|
||||
LowercaseZ,
|
||||
LeftBrace, // '{'
|
||||
Pipe, // '|'
|
||||
RightBrace, // '}'
|
||||
Tilde, // '~'
|
||||
Del,
|
||||
UpArrow,
|
||||
DownArrow,
|
||||
RightArrow,
|
||||
LeftArrow,
|
||||
End,
|
||||
Home,
|
||||
Insert,
|
||||
Delete,
|
||||
PageUp,
|
||||
PageDown,
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
}
|
||||
|
||||
impl InputKey {
|
||||
/// Gets the xterm control sequence for this key.
|
||||
///
|
||||
/// Reference: <https://invisible-island.net/xterm/ctlseqs/ctlseqs.pdf>
|
||||
pub fn as_xterm_control_sequence(&self) -> &[u8] {
|
||||
match self {
|
||||
InputKey::Ign => &[],
|
||||
// ASCII control characters (character code 0-31)
|
||||
InputKey::Nul => &[0x00],
|
||||
InputKey::Soh => &[0x01],
|
||||
InputKey::Stx => &[0x02],
|
||||
InputKey::Etx => &[0x03],
|
||||
InputKey::Eot => &[0x04],
|
||||
InputKey::Enq => &[0x05],
|
||||
InputKey::Ack => &[0x06],
|
||||
InputKey::Bel => &[0x07],
|
||||
InputKey::Bs => &[0x08],
|
||||
InputKey::Tab => &[0x09],
|
||||
InputKey::Lf => &[0x0A],
|
||||
InputKey::Vt => &[0x0B],
|
||||
InputKey::Ff => &[0x0C],
|
||||
InputKey::Cr => &[0x0D],
|
||||
InputKey::So => &[0x0E],
|
||||
InputKey::Si => &[0x0F],
|
||||
InputKey::Dle => &[0x10],
|
||||
InputKey::Dc1 => &[0x11],
|
||||
InputKey::Dc2 => &[0x12],
|
||||
InputKey::Dc3 => &[0x13],
|
||||
InputKey::Dc4 => &[0x14],
|
||||
InputKey::Nak => &[0x15],
|
||||
InputKey::Syn => &[0x16],
|
||||
InputKey::Etb => &[0x17],
|
||||
InputKey::Can => &[0x18],
|
||||
InputKey::Em => &[0x19],
|
||||
InputKey::Sub => &[0x1A],
|
||||
InputKey::Esc => &[0x1B],
|
||||
InputKey::Fs => &[0x1C],
|
||||
InputKey::Gs => &[0x1D],
|
||||
InputKey::Rs => &[0x1E],
|
||||
InputKey::Us => &[0x1F],
|
||||
// ASCII printable characters (character code 32-127)
|
||||
InputKey::Space => b" ",
|
||||
InputKey::Exclamation => b"!",
|
||||
InputKey::DoubleQuote => b"\"",
|
||||
InputKey::Hash => b"#",
|
||||
InputKey::Dollar => b"$",
|
||||
InputKey::Percent => b"%",
|
||||
InputKey::Ampersand => b"&",
|
||||
InputKey::SingleQuote => b"'",
|
||||
InputKey::LeftParen => b"(",
|
||||
InputKey::RightParen => b")",
|
||||
InputKey::Asterisk => b"*",
|
||||
InputKey::Plus => b"+",
|
||||
InputKey::Comma => b",",
|
||||
InputKey::Minus => b"-",
|
||||
InputKey::Period => b".",
|
||||
InputKey::ForwardSlash => b"/",
|
||||
InputKey::Zero => b"0",
|
||||
InputKey::One => b"1",
|
||||
InputKey::Two => b"2",
|
||||
InputKey::Three => b"3",
|
||||
InputKey::Four => b"4",
|
||||
InputKey::Five => b"5",
|
||||
InputKey::Six => b"6",
|
||||
InputKey::Seven => b"7",
|
||||
InputKey::Eight => b"8",
|
||||
InputKey::Nine => b"9",
|
||||
InputKey::Colon => b":",
|
||||
InputKey::SemiColon => b";",
|
||||
InputKey::LessThan => b"<",
|
||||
InputKey::Equal => b"=",
|
||||
InputKey::GreaterThan => b">",
|
||||
InputKey::Question => b"?",
|
||||
InputKey::At => b"@",
|
||||
InputKey::UppercaseA => b"A",
|
||||
InputKey::UppercaseB => b"B",
|
||||
InputKey::UppercaseC => b"C",
|
||||
InputKey::UppercaseD => b"D",
|
||||
InputKey::UppercaseE => b"E",
|
||||
InputKey::UppercaseF => b"F",
|
||||
InputKey::UppercaseG => b"G",
|
||||
InputKey::UppercaseH => b"H",
|
||||
InputKey::UppercaseI => b"I",
|
||||
InputKey::UppercaseJ => b"J",
|
||||
InputKey::UppercaseK => b"K",
|
||||
InputKey::UppercaseL => b"L",
|
||||
InputKey::UppercaseM => b"M",
|
||||
InputKey::UppercaseN => b"N",
|
||||
InputKey::UppercaseO => b"O",
|
||||
InputKey::UppercaseP => b"P",
|
||||
InputKey::UppercaseQ => b"Q",
|
||||
InputKey::UppercaseR => b"R",
|
||||
InputKey::UppercaseS => b"S",
|
||||
InputKey::UppercaseT => b"T",
|
||||
InputKey::UppercaseU => b"U",
|
||||
InputKey::UppercaseV => b"V",
|
||||
InputKey::UppercaseW => b"W",
|
||||
InputKey::UppercaseX => b"X",
|
||||
InputKey::UppercaseY => b"Y",
|
||||
InputKey::UppercaseZ => b"Z",
|
||||
InputKey::LeftBracket => b"[",
|
||||
InputKey::BackSlash => b"\\",
|
||||
InputKey::RightBracket => b"]",
|
||||
InputKey::Caret => b"^",
|
||||
InputKey::Underscore => b"_",
|
||||
InputKey::Backtick => b"`",
|
||||
InputKey::LowercaseA => b"a",
|
||||
InputKey::LowercaseB => b"b",
|
||||
InputKey::LowercaseC => b"c",
|
||||
InputKey::LowercaseD => b"d",
|
||||
InputKey::LowercaseE => b"e",
|
||||
InputKey::LowercaseF => b"f",
|
||||
InputKey::LowercaseG => b"g",
|
||||
InputKey::LowercaseH => b"h",
|
||||
InputKey::LowercaseI => b"i",
|
||||
InputKey::LowercaseJ => b"j",
|
||||
InputKey::LowercaseK => b"k",
|
||||
InputKey::LowercaseL => b"l",
|
||||
InputKey::LowercaseM => b"m",
|
||||
InputKey::LowercaseN => b"n",
|
||||
InputKey::LowercaseO => b"o",
|
||||
InputKey::LowercaseP => b"p",
|
||||
InputKey::LowercaseQ => b"q",
|
||||
InputKey::LowercaseR => b"r",
|
||||
InputKey::LowercaseS => b"s",
|
||||
InputKey::LowercaseT => b"t",
|
||||
InputKey::LowercaseU => b"u",
|
||||
InputKey::LowercaseV => b"v",
|
||||
InputKey::LowercaseW => b"w",
|
||||
InputKey::LowercaseX => b"x",
|
||||
InputKey::LowercaseY => b"y",
|
||||
InputKey::LowercaseZ => b"z",
|
||||
InputKey::LeftBrace => b"{",
|
||||
InputKey::Pipe => b"|",
|
||||
InputKey::RightBrace => b"}",
|
||||
InputKey::Tilde => b"~",
|
||||
InputKey::Del => &[0x7F],
|
||||
// PC-Style Function Keys
|
||||
InputKey::UpArrow => b"\x1B[A",
|
||||
InputKey::DownArrow => b"\x1B[B",
|
||||
InputKey::RightArrow => b"\x1B[C",
|
||||
InputKey::LeftArrow => b"\x1B[D",
|
||||
InputKey::End => b"\x1B[F",
|
||||
InputKey::Home => b"\x1B[H",
|
||||
InputKey::Insert => b"\x1B[2~",
|
||||
InputKey::Delete => b"\x1B[3~",
|
||||
InputKey::PageUp => b"\x1B[5~",
|
||||
InputKey::PageDown => b"\x1B[6~",
|
||||
InputKey::F1 => b"\x1BOP",
|
||||
InputKey::F2 => b"\x1BOQ",
|
||||
InputKey::F3 => b"\x1BOR",
|
||||
InputKey::F4 => b"\x1BOS",
|
||||
InputKey::F5 => b"\x1B[15~",
|
||||
InputKey::F6 => b"\x1B[17~",
|
||||
InputKey::F7 => b"\x1B[18~",
|
||||
InputKey::F8 => b"\x1B[19~",
|
||||
InputKey::F9 => b"\x1B[20~",
|
||||
InputKey::F10 => b"\x1B[21~",
|
||||
InputKey::F11 => b"\x1B[23~",
|
||||
InputKey::F12 => b"\x1B[24~",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -109,7 +109,6 @@ QEMU_ARGS="\
|
|||
-machine q35,kernel-irqchip=split \
|
||||
-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,serial=vext2,disable-legacy=on,disable-modern=off,queue-size=64,num-queues=1,request-merging=off,backend_defaults=off,discard=off,write-zeroes=off,event_idx=off,indirect_desc=off,queue_reset=off$IOMMU_DEV_EXTRA \
|
||||
-device virtio-blk-pci,bus=pcie.0,addr=0x7,drive=x1,serial=vexfat,disable-legacy=on,disable-modern=off,queue-size=64,num-queues=1,request-merging=off,backend_defaults=off,discard=off,write-zeroes=off,event_idx=off,indirect_desc=off,queue_reset=off$IOMMU_DEV_EXTRA \
|
||||
-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off$IOMMU_DEV_EXTRA \
|
||||
-device virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off$VIRTIO_NET_FEATURES$IOMMU_DEV_EXTRA \
|
||||
-device virtio-serial-pci,disable-legacy=on,disable-modern=off$IOMMU_DEV_EXTRA \
|
||||
-device virtconsole,chardev=mux \
|
||||
|
|
|
|||
Loading…
Reference in New Issue