diff --git a/kernel/comps/i8042/src/controller.rs b/kernel/comps/i8042/src/controller.rs index 246fae528..3a7ef639d 100644 --- a/kernel/comps/i8042/src/controller.rs +++ b/kernel/comps/i8042/src/controller.rs @@ -13,14 +13,6 @@ use ostd::{ }; use spin::Once; -pub(super) const PS2_CMD_RESET: u8 = 0xFF; -pub(super) const PS2_BAT_OK: u8 = 0xAA; - -pub(super) const PS2_ACK: u8 = 0xFA; -pub(super) const PS2_NAK: u8 = 0xFE; -pub(super) const PS2_ERR: u8 = 0xFC; -pub(super) const PS2_RESULTS: &[u8] = &[PS2_ACK, PS2_NAK, PS2_ERR]; - /// The `I8042Controller` singleton. pub(super) static I8042_CONTROLLER: Once> = Once::new(); diff --git a/kernel/comps/i8042/src/keyboard.rs b/kernel/comps/i8042/src/keyboard.rs index 6aaa8a8b8..0061d2a48 100644 --- a/kernel/comps/i8042/src/keyboard.rs +++ b/kernel/comps/i8042/src/keyboard.rs @@ -2,7 +2,10 @@ //! The i8042 keyboard driver. -use alloc::{string::String, sync::Arc}; +use alloc::{ + string::{String, ToString}, + sync::Arc, +}; use core::sync::atomic::{AtomicBool, Ordering}; use aster_input::{ @@ -18,10 +21,10 @@ use ostd::{ }; use spin::Once; -use super::controller::{ - I8042Controller, I8042ControllerError, I8042_CONTROLLER, PS2_ACK, PS2_BAT_OK, PS2_CMD_RESET, +use crate::{ + controller::{I8042Controller, I8042ControllerError, I8042_CONTROLLER}, + ps2::{Command, CommandCtx}, }; -use crate::{alloc::string::ToString, controller::PS2_RESULTS}; /// IRQ line for i8042 keyboard. static IRQ_LINE: Once = Once::new(); @@ -33,25 +36,19 @@ static REGISTERED_DEVICE: Once = Once::new(); const ISA_INTR_NUM: u8 = 1; pub(super) fn init(controller: &mut I8042Controller) -> Result<(), I8042ControllerError> { - // Reset the keyboard device by sending `PS2_CMD_RESET` (reset command, supported by all PS/2 - // devices) to port 1 and waiting for a response. - controller.wait_and_send_data(PS2_CMD_RESET)?; + // Reference: + const DEVICE_ID_REGULAR_KEYBOARD: u8 = 0xAB; - // The response should be `PS2_ACK` and `PS2_BAT_OK`, followed by the device PS/2 ID. - if controller.wait_for_specific_data(PS2_RESULTS)? != PS2_ACK { - return Err(I8042ControllerError::DeviceResetFailed); - } - // The reset command may take some time to finish. - if controller.wait_long_and_recv_data()? != PS2_BAT_OK { - return Err(I8042ControllerError::DeviceResetFailed); - } - // See 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 init_ctx = InitCtx(controller); + + init_ctx.reset()?; + + // Determine the keyboard's type. + let (device_id, _) = init_ctx.get_device_id()?; + log::info!("PS/2 keyboard device ID: 0x{:02X}", device_id); + if device_id != DEVICE_ID_REGULAR_KEYBOARD { + // TODO: Support other kinds of keyboards. + return Err(I8042ControllerError::DeviceUnknown); } let mut irq_line = IrqLine::alloc() @@ -73,6 +70,34 @@ pub(super) fn init(controller: &mut I8042Controller) -> Result<(), I8042Controll Ok(()) } +struct InitCtx<'a>(&'a mut I8042Controller); + +impl InitCtx<'_> { + fn get_device_id(&mut self) -> Result<(u8, u8), I8042ControllerError> { + let mut buf = [0u8; cmd::GetDeviceId::RES_LEN]; + self.command::(&[], &mut buf)?; + Ok((buf[0], buf[1])) + } +} + +impl CommandCtx for InitCtx<'_> { + fn controller(&mut self) -> &mut I8042Controller { + self.0 + } + + fn write_to_port(&mut self, data: u8) -> Result<(), I8042ControllerError> { + self.0.wait_and_send_data(data) + } +} + +mod cmd { + use crate::ps2::{define_commands, Command}; + + define_commands! { + GetDeviceId, 0xF2, fn([u8; 0]) -> [u8; 2]; + } +} + #[derive(Debug)] struct I8042Keyboard { name: String, @@ -141,10 +166,6 @@ impl InputDevice for I8042Keyboard { } fn handle_keyboard_input(_trap_frame: &TrapFrame) { - if !I8042_CONTROLLER.is_completed() { - return; - } - let Some(scancode_event) = ScancodeInfo::read() else { return; }; @@ -212,14 +233,14 @@ impl ScancodeInfo { fn read() -> Option { static EXTENDED_KEY: AtomicBool = AtomicBool::new(false); - let Some(data) = I8042_CONTROLLER.get().unwrap().lock().receive_data() else { - log::warn!("i8042 keyboard has no input data"); + let Some(data) = I8042_CONTROLLER.get()?.lock().receive_data() else { + log::warn!("PS/2 keyboard has no input data"); return None; }; let code = ScanCode(data); if code.has_error() { - log::warn!("i8042 keyboard key detection error or internal buffer overrun"); + log::warn!("PS/2 keyboard key detection error or internal buffer overrun"); return None; } diff --git a/kernel/comps/i8042/src/lib.rs b/kernel/comps/i8042/src/lib.rs index 27792d34b..e2c7f33c3 100644 --- a/kernel/comps/i8042/src/lib.rs +++ b/kernel/comps/i8042/src/lib.rs @@ -12,6 +12,7 @@ use component::{init_component, ComponentInitError}; mod controller; mod keyboard; mod mouse; +mod ps2; #[init_component] fn init() -> Result<(), ComponentInitError> { diff --git a/kernel/comps/i8042/src/mouse.rs b/kernel/comps/i8042/src/mouse.rs index e03f4354f..3ae55f1f3 100644 --- a/kernel/comps/i8042/src/mouse.rs +++ b/kernel/comps/i8042/src/mouse.rs @@ -2,7 +2,11 @@ //! The i8042 mouse driver. -use alloc::{string::String, sync::Arc, vec::Vec}; +use alloc::{ + string::{String, ToString}, + sync::Arc, + vec::Vec, +}; use aster_input::{ event_type_codes::{KeyCode, KeyStatus, RelCode, SynEvent}, @@ -18,10 +22,10 @@ use ostd::{ }; use spin::Once; -use super::controller::{ - I8042Controller, I8042ControllerError, I8042_CONTROLLER, PS2_ACK, PS2_BAT_OK, PS2_CMD_RESET, +use crate::{ + controller::{I8042Controller, I8042ControllerError, I8042_CONTROLLER}, + ps2::{Command, CommandCtx}, }; -use crate::{alloc::string::ToString, controller::PS2_RESULTS}; /// IRQ line for i8042 mouse. static IRQ_LINE: Once = Once::new(); @@ -36,26 +40,14 @@ const ISA_INTR_NUM: u8 = 12; static PACKET_STATE: SpinLock = SpinLock::new(PacketState::new()); pub(super) fn init(controller: &mut I8042Controller) -> Result<(), I8042ControllerError> { - // Reset the mouse device by sending `PS2_CMD_RESET` (reset command, supported by all PS/2 - // devices) to port 2 and waiting for a response. - controller.write_to_second_port(PS2_CMD_RESET)?; - - // The response should be `PS2_ACK` and `PS2_BAT_OK`, followed by the device PS/2 ID. - if controller.wait_for_specific_data(PS2_RESULTS)? != PS2_ACK { - return Err(I8042ControllerError::DeviceResetFailed); - } - // The reset command may take some time to finish. - if controller.wait_long_and_recv_data()? != PS2_BAT_OK { - return Err(I8042ControllerError::DeviceResetFailed); - } - // See for a list of IDs. - let device_id = controller.wait_and_recv_data()?; - log::info!("PS/2 mouse device ID: 0x{:02X}", device_id); - let mut init_ctx = InitCtx(controller); + let device_id = init_ctx + .reset()? + .ok_or(I8042ControllerError::DeviceUnknown)?; + log::info!("PS/2 mouse device ID: 0x{:02X}", device_id); + // Determine the mouse's type. - // Reference: let mut mouse_type = MouseType::from_device_id(device_id).ok_or(I8042ControllerError::DeviceUnknown)?; if mouse_type == MouseType::Standard && init_ctx.enable_intellimouse().is_ok() { @@ -125,60 +117,20 @@ impl InitCtx<'_> { self.command::(&[], &mut buf)?; Ok(()) } - - fn command( - &mut self, - args: &[u8], - out: &mut [u8], - ) -> Result<(), I8042ControllerError> { - assert_eq!(args.len(), C::DATA_LEN); - assert_eq!(out.len(), C::RES_LEN); - - self.0.write_to_second_port(C::CMD_BYTE)?; - if self.0.wait_for_specific_data(PS2_RESULTS)? != PS2_ACK { - return Err(I8042ControllerError::DeviceResetFailed); - } - - for &arg in args { - self.0.write_to_second_port(arg)?; - if self.0.wait_for_specific_data(PS2_RESULTS)? != PS2_ACK { - return Err(I8042ControllerError::DeviceResetFailed); - } - } - - for slot in out.iter_mut() { - *slot = self.0.wait_and_recv_data()?; - } - - Ok(()) - } } -trait MouseCommand { - const CMD_BYTE: u8; - const DATA_LEN: usize; - const RES_LEN: usize; +impl CommandCtx for InitCtx<'_> { + fn controller(&mut self) -> &mut I8042Controller { + self.0 + } + + fn write_to_port(&mut self, data: u8) -> Result<(), I8042ControllerError> { + self.0.write_to_second_port(data) + } } mod cmd { - use super::MouseCommand; - - macro_rules! define_commands { - ( - $( - $name:ident, $cmd:literal, fn([u8; $dlen:literal]) -> [u8; $rlen:literal]; - )* - ) => { - $( - pub(super) struct $name; - impl MouseCommand for $name { - const CMD_BYTE: u8 = $cmd; - const DATA_LEN: usize = $dlen; - const RES_LEN: usize = $rlen; - } - )* - }; - } + use crate::ps2::{define_commands, Command}; define_commands! { GetDeviceId, 0xF2, fn([u8; 0]) -> [u8; 1]; @@ -292,6 +244,7 @@ impl MouseType { const PACKET_LEN_MAX: usize = 4; fn from_device_id(device_id: u8) -> Option { + // Reference: const DEVICE_ID_STANDARD_MOUSE: u8 = 0x00; const DEVICE_ID_INTELLIMOUSE: u8 = 0x03; const DEVICE_ID_INTELLIMOUSE_EXPLORER: u8 = 0x04; diff --git a/kernel/comps/i8042/src/ps2.rs b/kernel/comps/i8042/src/ps2.rs new file mode 100644 index 000000000..6e3d6dc98 --- /dev/null +++ b/kernel/comps/i8042/src/ps2.rs @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Common utilities for PS/2 devices. + +use crate::controller::{I8042Controller, I8042ControllerError}; + +const PS2_CMD_RESET: u8 = 0xFF; +const PS2_BAT_OK: u8 = 0xAA; + +const PS2_ACK: u8 = 0xFA; +const PS2_NAK: u8 = 0xFE; +const PS2_ERR: u8 = 0xFC; +const PS2_RESULTS: &[u8] = &[PS2_ACK, PS2_NAK, PS2_ERR]; + +/// PS/2 device commands. +pub(super) trait Command { + const CMD_BYTE: u8; + const DATA_LEN: usize; + const RES_LEN: usize; +} + +macro_rules! define_commands { + ( + $( + $name:ident, $cmd:literal, fn([u8; $dlen:literal]) -> [u8; $rlen:literal]; + )* + ) => { + $( + pub(super) struct $name; + impl Command for $name { + const CMD_BYTE: u8 = $cmd; + const DATA_LEN: usize = $dlen; + const RES_LEN: usize = $rlen; + } + )* + }; +} +pub(super) use define_commands; + +/// Context to perform PS/2 commands. +pub(super) trait CommandCtx { + fn controller(&mut self) -> &mut I8042Controller; + + fn write_to_port(&mut self, data: u8) -> Result<(), I8042ControllerError>; + + fn reset(&mut self) -> Result, I8042ControllerError> { + // Reset the device by sending `PS2_CMD_RESET` (reset command, supported by all PS/2 + // devices). + self.write_to_port(PS2_CMD_RESET)?; + + let controller = self.controller(); + + // The response should be `PS2_ACK` and `PS2_BAT_OK`, followed by the device PS/2 ID. + if controller.wait_for_specific_data(PS2_RESULTS)? != PS2_ACK { + return Err(I8042ControllerError::DeviceResetFailed); + } + // The reset command may take some time to finish. So we use `wait_long_and_recv_data`. + if controller.wait_long_and_recv_data()? != PS2_BAT_OK { + return Err(I8042ControllerError::DeviceResetFailed); + } + // Some keyboards won't reply its device ID. So we don't report any error here. + Ok(controller.wait_and_recv_data().ok()) + } + + fn command( + &mut self, + args: &[u8], + out: &mut [u8], + ) -> Result<(), I8042ControllerError> { + assert_eq!(args.len(), C::DATA_LEN); + assert_eq!(out.len(), C::RES_LEN); + + // Send the command. + self.write_to_port(C::CMD_BYTE)?; + if self.controller().wait_for_specific_data(PS2_RESULTS)? != PS2_ACK { + return Err(I8042ControllerError::DeviceResetFailed); + } + + // Send the arguments. + for &arg in args { + self.write_to_port(arg)?; + if self.controller().wait_for_specific_data(PS2_RESULTS)? != PS2_ACK { + return Err(I8042ControllerError::DeviceResetFailed); + } + } + + // Receive the response. + for slot in out.iter_mut() { + *slot = self.controller().wait_and_recv_data()?; + } + + Ok(()) + } +}