180 lines
4.3 KiB
Rust
180 lines
4.3 KiB
Rust
//! Intel 8042 PS/2 controller driver implemenation
|
|
use abi::{
|
|
error::Error,
|
|
io::{KeyboardKey, KeyboardKeyEvent},
|
|
};
|
|
use alloc::sync::Arc;
|
|
use device_api::{
|
|
device::Device,
|
|
interrupt::{InterruptHandler, Irq},
|
|
};
|
|
use kernel_arch_x86::{
|
|
intrinsics::{IoPort, IoPortAccess},
|
|
ISA_IRQ_OFFSET,
|
|
};
|
|
use libk::device::external_interrupt_controller;
|
|
use libk_util::sync::IrqSafeSpinlock;
|
|
|
|
use codeset::{CODE_SET_1_00, CODE_SET_1_E0};
|
|
|
|
mod codeset;
|
|
|
|
struct Inner {
|
|
command: IoPort<u8>,
|
|
data: IoPort<u8>,
|
|
}
|
|
|
|
/// PS/2 controller driver
|
|
pub struct PS2Controller {
|
|
primary_irq: Irq,
|
|
#[allow(unused)]
|
|
auxiliary_irq: Irq,
|
|
|
|
inner: IrqSafeSpinlock<Inner>,
|
|
}
|
|
|
|
fn translate(e0: bool, key: u8) -> KeyboardKey {
|
|
debug_assert!(key < 0x80);
|
|
|
|
if e0 {
|
|
CODE_SET_1_E0[key as usize]
|
|
} else {
|
|
CODE_SET_1_00[key as usize]
|
|
}
|
|
}
|
|
|
|
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 InterruptHandler for PS2Controller {
|
|
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
|
|
let mut count = 0;
|
|
let mut inner = self.inner.lock();
|
|
|
|
loop {
|
|
let Some(mut scancode) = inner.try_recv() else {
|
|
break;
|
|
};
|
|
|
|
count += 1;
|
|
|
|
let e0 = scancode == 0xE0;
|
|
let release = scancode >= 0x80;
|
|
|
|
if release {
|
|
scancode -= 0x80;
|
|
}
|
|
|
|
let key = translate(e0, scancode);
|
|
let event = if release {
|
|
KeyboardKeyEvent::Released(key)
|
|
} else {
|
|
KeyboardKeyEvent::Pressed(key)
|
|
};
|
|
|
|
ygg_driver_input::send_event(event);
|
|
}
|
|
|
|
count != 0
|
|
}
|
|
}
|
|
|
|
impl Device for PS2Controller {
|
|
fn display_name(&self) -> &'static str {
|
|
"PS/2 Controller"
|
|
}
|
|
|
|
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
|
|
Ok(())
|
|
}
|
|
|
|
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
|
let intc = external_interrupt_controller()?;
|
|
|
|
let mut inner = self.inner.lock();
|
|
// let intc = PLATFORM.interrupt_controller();
|
|
|
|
intc.register_irq(self.primary_irq, Default::default(), self.clone())?;
|
|
|
|
// Disable PS/2 devices from sending any further data
|
|
inner.send_command(0xAD);
|
|
inner.send_command(0xA7);
|
|
|
|
// Flush the buffer
|
|
while inner.command.read() & Inner::STATUS_OUTPUT_FULL != 0 {
|
|
inner.data.read();
|
|
}
|
|
|
|
// Enable primary port
|
|
inner.send_command(0xAE);
|
|
|
|
intc.enable_irq(self.primary_irq)?;
|
|
intc.enable_irq(self.auxiliary_irq)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl PS2Controller {
|
|
/// Constructs a new instance of the device
|
|
const fn new(primary_irq: Irq, auxiliary_irq: Irq, cmd_port: u16, data_port: u16) -> Self {
|
|
let inner = Inner {
|
|
command: IoPort::new(cmd_port),
|
|
data: IoPort::new(data_port),
|
|
};
|
|
|
|
Self {
|
|
primary_irq,
|
|
auxiliary_irq,
|
|
inner: IrqSafeSpinlock::new(inner),
|
|
}
|
|
}
|
|
|
|
pub fn setup() -> Result<Arc<Self>, Error> {
|
|
let this = Arc::new(PS2Controller::new(
|
|
Irq::External(ISA_IRQ_OFFSET + 1),
|
|
Irq::External(ISA_IRQ_OFFSET + 12),
|
|
0x64,
|
|
0x60,
|
|
));
|
|
unsafe { this.clone().init_irq() }?;
|
|
Ok(this)
|
|
}
|
|
}
|
|
|
|
// pub static PS2: PS2Controller = PS2Controller::new(
|
|
// Irq::External(ISA_IRQ_OFFSET + 1),
|
|
// Irq::External(ISA_IRQ_OFFSET + 12),
|
|
// 0x64,
|
|
// 0x60,
|
|
// );
|