From a004a8ffc0b9c3538dc21852b4dc6e4a7d8602c3 Mon Sep 17 00:00:00 2001 From: Amerikranian Date: Sat, 19 Apr 2025 12:52:55 -0500 Subject: [PATCH 1/5] Fix keyboard and mouse to properly follow ps2 protocol --- kernel/Cargo.lock | 25 +- kernel/Cargo.toml | 1 + kernel/src/devices/mod.rs | 6 +- kernel/src/devices/mouse.rs | 632 ------------------- kernel/src/devices/ps2_dev/controller.rs | 123 ++++ kernel/src/devices/{ => ps2_dev}/keyboard.rs | 148 +++-- kernel/src/devices/ps2_dev/mod.rs | 35 + kernel/src/devices/ps2_dev/mouse.rs | 393 ++++++++++++ kernel/src/interrupts/idt.rs | 6 +- kernel/src/interrupts/io_apic.rs | 8 +- 10 files changed, 681 insertions(+), 696 deletions(-) delete mode 100644 kernel/src/devices/mouse.rs create mode 100644 kernel/src/devices/ps2_dev/controller.rs rename kernel/src/devices/{ => ps2_dev}/keyboard.rs (54%) create mode 100644 kernel/src/devices/ps2_dev/mod.rs create mode 100644 kernel/src/devices/ps2_dev/mouse.rs diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 2e4691cf..54355de8 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -372,6 +372,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ps2" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfef03bb1362e6a23a211efe8c72e2eeb6888f51fb329cdfa07bb470986ea7d0" +dependencies = [ + "bitflags 1.3.2", + "x86_64 0.14.13", +] + [[package]] name = "quote" version = "1.0.40" @@ -527,13 +537,14 @@ dependencies = [ "limine", "log", "pc-keyboard", + "ps2", "rand", "raw-cpuid 11.5.0", "smoltcp", "spin", "talc", "uart_16550", - "x86_64", + "x86_64 0.15.2", "zerocopy", ] @@ -597,6 +608,18 @@ dependencies = [ "raw-cpuid 10.7.0", ] +[[package]] +name = "x86_64" +version = "0.14.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c101112411baafbb4bf8d33e4c4a80ab5b02d74d2612331c61e8192fc9710491" +dependencies = [ + "bit_field", + "bitflags 2.9.0", + "rustversion", + "volatile", +] + [[package]] name = "x86_64" version = "0.15.2" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 65de2f92..52d9527d 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -56,3 +56,4 @@ futures-util = { version = "0.3.31", default-features = false, features = [ "async-await-macro", "futures-macro", ] } +ps2 = "0.2.0" diff --git a/kernel/src/devices/mod.rs b/kernel/src/devices/mod.rs index 4840b9c8..b6fd0488 100644 --- a/kernel/src/devices/mod.rs +++ b/kernel/src/devices/mod.rs @@ -11,10 +11,9 @@ use sd_card::{find_sd_card, initalize_sd_card}; use xhci::{find_xhci_inferface, initalize_xhci_hub}; pub mod graphics; use graphics::framebuffer::{self, colors}; -pub mod keyboard; pub mod mmio; -pub mod mouse; pub mod pci; +pub mod ps2_dev; pub mod sd_card; pub mod serial; pub mod xhci; @@ -74,7 +73,6 @@ pub fn init(cpu_id: u32) { find_xhci_inferface(&devices).expect("Build system currently sets up xhci device"); initalize_xhci_hub(&xhci_device).unwrap(); - keyboard::init().expect("Failed to initialize keyboard"); - mouse::init().expect("Failed to initialize mouse"); + ps2_dev::init(); } } diff --git a/kernel/src/devices/mouse.rs b/kernel/src/devices/mouse.rs deleted file mode 100644 index 156fb7f0..00000000 --- a/kernel/src/devices/mouse.rs +++ /dev/null @@ -1,632 +0,0 @@ -//! Mouse management for PS/2 compatible mice -//! -//! Handles mouse initialization, event processing, and provides both -//! synchronous and asynchronous interfaces for mouse events - -use crate::{ - events::schedule_kernel, - interrupts::{idt::without_interrupts, x2apic}, - serial_println, -}; -use core::{ - fmt, - pin::Pin, - sync::atomic::{AtomicBool, AtomicU64, Ordering}, - task::{Context, Poll, Waker}, -}; -use futures_util::stream::{Stream, StreamExt}; -use spin::Mutex; -use x86_64::{instructions::port::Port, structures::idt::InterruptStackFrame}; - -/// Maximum number of mouse events to store in the buffer -const MOUSE_BUFFER_SIZE: usize = 32; - -/// The global mouse state -pub static MOUSE: Mutex = Mutex::new(MouseState::new()); - -/// Has the mouse been initialized -static MOUSE_INITIALIZED: AtomicBool = AtomicBool::new(false); - -/// The number of mouse interrupts received -static MOUSE_INTERRUPT_COUNT: AtomicU64 = AtomicU64::new(0); - -/// Wake task waiting for mouse input -static MOUSE_WAKER: Mutex> = Mutex::new(None); - -/// PS/2 mouse error types -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum MouseError { - /// PS/2 controller initialization error - ControllerInitError, - /// Timeout waiting for PS/2 controller to be ready - ControllerTimeout, - /// Mouse failed to acknowledge a command - CommandAckFailed, - /// Invalid packet data - InvalidPacketData, - /// Buffer overflow - BufferOverflow, - /// Mouse already initialized - AlreadyInitialized, -} - -/// Result type for mouse operations -pub type MouseResult = core::result::Result; - -impl fmt::Display for MouseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::ControllerInitError => write!(f, "PS/2 controller initialization error"), - Self::ControllerTimeout => write!(f, "Timeout waiting for PS/2 controller"), - Self::CommandAckFailed => write!(f, "Mouse did not acknowledge command"), - Self::InvalidPacketData => write!(f, "Invalid mouse packet data"), - Self::BufferOverflow => write!(f, "Mouse event buffer overflow"), - Self::AlreadyInitialized => write!(f, "Mouse already initialized"), - } - } -} - -/// PS/2 mouse packet structure -struct MousePacket { - flags: u8, - x_movement: i8, - y_movement: i8, - z_movement: i8, // Scroll wheel -} - -/// Button state for mouse buttons -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ButtonState { - /// Button is pressed - Pressed, - /// Button is released - Released, -} - -/// Mouse button identifiers -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum MouseButton { - /// Left mouse button - Left, - /// Right mouse button - Right, - /// Middle mouse button (scroll wheel click) - Middle, -} - -/// Represents a mouse event with additional metadata -#[derive(Debug, Clone)] -pub struct MouseEvent { - /// X movement delta - pub dx: i8, - /// Y movement delta - pub dy: i8, - /// Z movement delta (scroll wheel) - pub dz: i8, - /// Left button state - pub left_button: ButtonState, - /// Right button state - pub right_button: ButtonState, - /// Middle button state - pub middle_button: ButtonState, - /// Absolute X position at time of event - pub x: i16, - /// Absolute Y position at time of event - pub y: i16, -} - -/// Structure to track mouse state -#[derive(Debug)] -pub struct MouseState { - /// Circular buffer for mouse events - buffer: [Option; MOUSE_BUFFER_SIZE], - /// Read position in the buffer - read_pos: usize, - /// Write position in the buffer - write_pos: usize, - /// Is the buffer full - full: bool, - /// Current packet being assembled - packet_bytes: [u8; 4], - /// Current position in the packet - packet_index: usize, - /// Previous button states for tracking changes - prev_left_button: ButtonState, - prev_right_button: ButtonState, - prev_middle_button: ButtonState, - /// Current mouse X position - mouse_x: i16, - /// Current mouse Y position - mouse_y: i16, - /// Maximum X position (screen width - 1) - max_x: i16, - /// Maximum Y position (screen height - 1) - max_y: i16, -} - -/// Stream that yields mouse events -pub struct MouseStream; - -impl Default for MouseState { - fn default() -> Self { - Self::new() - } -} - -impl MouseState { - /// Create a new mouse state - pub const fn new() -> Self { - const NONE_OPTION: Option = None; - Self { - buffer: [NONE_OPTION; MOUSE_BUFFER_SIZE], - read_pos: 0, - write_pos: 0, - full: false, - packet_bytes: [0; 4], - packet_index: 0, - prev_left_button: ButtonState::Released, - prev_right_button: ButtonState::Released, - prev_middle_button: ButtonState::Released, - mouse_x: 0, - mouse_y: 0, - // Default to VGA text mode dimensions, can be updated with set_bounds - max_x: 79, - max_y: 24, - } - } - - /// Set the screen boundaries for mouse movement - pub fn set_bounds(&mut self, width: i16, height: i16) { - self.max_x = width - 1; - self.max_y = height - 1; - - self.clamp_position(); - } - - /// Ensure mouse position stays within bounds - fn clamp_position(&mut self) { - if self.mouse_x < 0 { - self.mouse_x = 0; - } else if self.mouse_x > self.max_x { - self.mouse_x = self.max_x; - } - - if self.mouse_y < 0 { - self.mouse_y = 0; - } else if self.mouse_y > self.max_y { - self.mouse_y = self.max_y; - } - } - - /// Get current mouse position - pub fn get_position(&self) -> (i16, i16) { - (self.mouse_x, self.mouse_y) - } - - /// Set mouse position - pub fn set_position(&mut self, x: i16, y: i16) { - self.mouse_x = x; - self.mouse_y = y; - self.clamp_position(); - } - - /// Check if the buffer is empty - pub fn is_empty(&self) -> bool { - !self.full && self.read_pos == self.write_pos - } - - /// Process a mouse data byte - pub fn process_mouse_byte(&mut self, data: u8) -> MouseResult<()> { - self.packet_bytes[self.packet_index] = data; - self.packet_index += 1; - - // Standard PS/2 mouse packets are 3 bytes - // With scroll wheel, we need 4 bytes - if self.packet_index >= 3 { - let packet = MousePacket { - flags: self.packet_bytes[0], - x_movement: self.packet_bytes[1] as i8, - y_movement: self.packet_bytes[2] as i8, - z_movement: if self.packet_index >= 4 { - self.packet_bytes[3] as i8 - } else { - 0 - }, - }; - - self.process_packet(packet)?; - self.packet_index = 0; - } - - Ok(()) - } - - /// Process a complete mouse packet and create an event - fn process_packet(&mut self, packet: MousePacket) -> MouseResult<()> { - // Extract button states - let left_button = if packet.flags & 0x01 != 0 { - ButtonState::Pressed - } else { - ButtonState::Released - }; - - let right_button = if packet.flags & 0x02 != 0 { - ButtonState::Pressed - } else { - ButtonState::Released - }; - - let middle_button = if packet.flags & 0x04 != 0 { - ButtonState::Pressed - } else { - ButtonState::Released - }; - - // Handle movement - let mut dx = packet.x_movement; - let mut dy = packet.y_movement; - - // PS/2 mouse protocol: Overflow bits - if packet.flags & 0x40 != 0 { - // X overflow - dx = if dx > 0 { 127 } else { -128 }; - } - if packet.flags & 0x80 != 0 { - // Y overflow - dy = if dy > 0 { 127 } else { -128 }; - } - - // PS/2 Y-axis is inverted - dy = if dy == -128 { 127 } else { -dy }; - - // Update absolute position - let new_x = self.mouse_x + dx as i16; - let new_y = self.mouse_y + dy as i16; - - self.mouse_x = new_x; - self.mouse_y = new_y; - - // Ensure position stays within bounds - self.clamp_position(); - - // Create event only if there's actual movement or button state change - if dx != 0 - || dy != 0 - || packet.z_movement != 0 - || left_button != self.prev_left_button - || right_button != self.prev_right_button - || middle_button != self.prev_middle_button - { - // Create event - let event = MouseEvent { - dx, - dy, - dz: packet.z_movement, - left_button, - right_button, - middle_button, - x: self.mouse_x, - y: self.mouse_y, - }; - - if self.push_event(event).is_err() { - // Buffer overflow, but we can continue processing - serial_println!("Warning: Mouse event buffer overflow"); - } - - // Update previous button states - self.prev_left_button = left_button; - self.prev_right_button = right_button; - self.prev_middle_button = middle_button; - } - - Ok(()) - } - - /// Push an event to the buffer - fn push_event(&mut self, event: MouseEvent) -> MouseResult<()> { - if self.full { - self.read_pos = (self.read_pos + 1) % MOUSE_BUFFER_SIZE; - } - - self.buffer[self.write_pos] = Some(event); - - self.write_pos = (self.write_pos + 1) % MOUSE_BUFFER_SIZE; - - if self.write_pos == self.read_pos { - self.full = true; - } - - without_interrupts(|| { - let mut waker = MOUSE_WAKER.lock(); - if let Some(w) = waker.take() { - w.wake(); - } - }); - - Ok(()) - } - - /// Read a mouse event from the buffer - pub fn read_event(&mut self) -> Option { - if self.is_empty() { - return None; - } - - let event = self.buffer[self.read_pos].clone(); - - self.buffer[self.read_pos] = None; - self.read_pos = (self.read_pos + 1) % MOUSE_BUFFER_SIZE; - - self.full = false; - - event - } - - /// Clear mouse buffer - pub fn clear_buffer(&mut self) { - const NONE_OPTION: Option = None; - self.buffer = [NONE_OPTION; MOUSE_BUFFER_SIZE]; - self.read_pos = 0; - self.write_pos = 0; - self.full = false; - } -} - -/// PS/2 mouse commands -#[repr(u8)] -#[allow(dead_code)] -enum PS2Command { - /// Reset the mouse - Reset = 0xFF, - /// Set defaults - SetDefaults = 0xF6, - /// Disable data reporting - DisableReporting = 0xF5, - /// Enable data reporting - EnableReporting = 0xF4, - /// Set sample rate - SetSampleRate = 0xF3, - /// Get device ID - GetDeviceId = 0xF2, - /// Set resolution - SetResolution = 0xE8, - /// Status request - StatusRequest = 0xE9, -} - -/// PS/2 controller commands -#[repr(u8)] -#[allow(dead_code)] -enum PS2ControllerCommand { - /// Read controller configuration byte - ReadConfig = 0x20, - /// Write controller configuration byte - WriteConfig = 0x60, - /// Disable second PS/2 port - DisablePort2 = 0xA7, - /// Enable second PS/2 port - EnablePort2 = 0xA8, - /// Test second PS/2 port - TestPort2 = 0xA9, - /// Test PS/2 controller - TestController = 0xAA, - /// Test first PS/2 port - TestPort1 = 0xAB, - /// Disable first PS/2 port - DisablePort1 = 0xAD, - /// Enable first PS/2 port - EnablePort1 = 0xAE, - /// Write to second PS/2 port - WritePort2 = 0xD4, -} - -/// PS/2 port addresses -struct PS2Port; - -impl PS2Port { - /// Data port (read/write) - const DATA: u16 = 0x60; - /// Status register (read), command register (write) - const CMD_STATUS: u16 = 0x64; -} - -/// PS/2 controller response bytes -struct PS2Response; - -#[allow(dead_code)] -impl PS2Response { - /// Command acknowledgement - const ACK: u8 = 0xFA; - /// Command not recognized - const RESEND: u8 = 0xFE; - /// Self-test passed - const TEST_PASSED: u8 = 0xAA; -} - -/// Initialize the mouse -pub fn init() -> MouseResult<()> { - if MOUSE_INITIALIZED.load(Ordering::SeqCst) { - return Err(MouseError::AlreadyInitialized); - } - - wait_write_ready()?; - - let mut command_port = Port::new(PS2Port::CMD_STATUS); - unsafe { - command_port.write(PS2ControllerCommand::WritePort2 as u8); - } - - wait_write_ready()?; - - // Tell the mouse to enable data reporting - let mut data_port = Port::new(PS2Port::DATA); - unsafe { - data_port.write(PS2Command::EnableReporting as u8); - } - - wait_read_ready()?; - let response: u8 = unsafe { data_port.read() }; - - if response != PS2Response::ACK { - return Err(MouseError::CommandAckFailed); - } - - MOUSE_INITIALIZED.store(true, Ordering::SeqCst); - - Ok(()) -} - -/// Wait for the PS/2 controller to be ready for reading -fn wait_read_ready() -> MouseResult<()> { - let mut status_port = Port::new(PS2Port::CMD_STATUS); - - for _ in 0..1000 { - let status: u8 = unsafe { status_port.read() }; - if status & 0x01 != 0 { - return Ok(()); - } - } - - Err(MouseError::ControllerTimeout) -} - -/// Wait for the PS/2 controller to be ready for writing -fn wait_write_ready() -> MouseResult<()> { - let mut status_port = Port::::new(PS2Port::CMD_STATUS); - - for _ in 0..1000 { - let status: u8 = unsafe { status_port.read() }; - if status & 0x02 == 0 { - return Ok(()); - } - } - - Err(MouseError::ControllerTimeout) -} - -/// Send a command to the PS/2 mouse -fn mouse_send_command(command: PS2Command) -> MouseResult { - wait_write_ready()?; - - let mut command_port = Port::new(PS2Port::CMD_STATUS); - unsafe { - command_port.write(PS2ControllerCommand::WritePort2 as u8); - } - - wait_write_ready()?; - - let mut data_port = Port::new(PS2Port::DATA); - unsafe { - data_port.write(command as u8); - } - - wait_read_ready()?; - let response: u8 = unsafe { data_port.read() }; - - Ok(response) -} - -/// Get a stream of mouse events -pub fn get_stream() -> MouseStream { - MouseStream -} - -/// Wait for and return the next mouse event -pub async fn next_event() -> MouseEvent { - MouseStream.next().await.unwrap() -} - -/// Read a mouse event without waiting -pub fn try_read_event() -> Option { - without_interrupts(|| MOUSE.lock().read_event()) -} - -/// Get current mouse position -pub fn get_position() -> (i16, i16) { - without_interrupts(|| { - let mouse = MOUSE.lock(); - mouse.get_position() - }) -} - -/// Set mouse position -pub fn set_position(x: i16, y: i16) { - without_interrupts(|| { - let mut mouse = MOUSE.lock(); - mouse.set_position(x, y); - }) -} - -/// Set screen boundaries for mouse movement -pub fn set_bounds(width: i16, height: i16) { - without_interrupts(|| { - let mut mouse = MOUSE.lock(); - mouse.set_bounds(width, height); - }) -} - -/// Reset the mouse to default settings -pub fn reset() -> MouseResult<()> { - if !MOUSE_INITIALIZED.load(Ordering::SeqCst) { - return Err(MouseError::ControllerInitError); - } - - let response = mouse_send_command(PS2Command::Reset)?; - if response != PS2Response::ACK { - return Err(MouseError::CommandAckFailed); - } - - without_interrupts(|| { - let mut mouse = MOUSE.lock(); - mouse.clear_buffer(); - mouse.set_position(0, 0); - }); - - Ok(()) -} - -/// Get mouse interrupt count -pub fn get_interrupt_count() -> u64 { - MOUSE_INTERRUPT_COUNT.load(Ordering::SeqCst) -} - -/// Mouse interrupt handler -pub extern "x86-interrupt" fn mouse_handler(_frame: InterruptStackFrame) { - MOUSE_INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst); - - let mut data_port = Port::new(PS2Port::DATA); - let data: u8 = unsafe { data_port.read() }; - - schedule_kernel( - async move { - let mut mouse = MOUSE.lock(); - if let Err(e) = mouse.process_mouse_byte(data) { - serial_println!("Error processing mouse data: {:?}", e); - } - }, - 0, - ); - - x2apic::send_eoi(); -} - -impl Stream for MouseStream { - type Item = MouseEvent; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut mouse = MOUSE.lock(); - - if let Some(event) = mouse.read_event() { - return Poll::Ready(Some(event)); - } - - // No event available, register waker for notification - without_interrupts(|| { - let mut waker = MOUSE_WAKER.lock(); - *waker = Some(cx.waker().clone()); - }); - - Poll::Pending - } -} diff --git a/kernel/src/devices/ps2_dev/controller.rs b/kernel/src/devices/ps2_dev/controller.rs new file mode 100644 index 00000000..bd4e84b5 --- /dev/null +++ b/kernel/src/devices/ps2_dev/controller.rs @@ -0,0 +1,123 @@ +//! PS/2 Controller management +//! +//! This module handles the low-level interaction with the PS/2 controller +//! using the ps2 crate to interface with the hardware. + +use crate::serial_println; +use core::sync::atomic::{AtomicBool, Ordering}; +use ps2::{error::ControllerError, flags::ControllerConfigFlags, Controller}; +use spin::Mutex; + +/// The global PS/2 controller +static PS2_CONTROLLER: Mutex> = Mutex::new(None); + +/// Has the controller been initialized +static CONTROLLER_INITIALIZED: AtomicBool = AtomicBool::new(false); + +/// Initialize the PS/2 controller +pub fn init() -> Result<(), &'static str> { + if CONTROLLER_INITIALIZED.load(Ordering::SeqCst) { + return Ok(()); + } + + // Create a new controller instance and configure it + match initialize_controller() { + Ok(controller) => { + let mut controller_lock = PS2_CONTROLLER.lock(); + *controller_lock = Some(controller); + CONTROLLER_INITIALIZED.store(true, Ordering::SeqCst); + Ok(()) + } + Err(e) => { + serial_println!("PS/2 controller initialization failed: {:?}", e); + Err("PS/2 controller initialization failed") + } + } +} + +/// Initialize and configure the PS/2 controller +fn initialize_controller() -> Result { + // Create a new controller instance + let mut controller = unsafe { Controller::new() }; + + // Step 1: Disable devices during configuration + controller.disable_keyboard()?; + controller.disable_mouse()?; + + // Step 2: Flush any pending data + let _ = controller.read_data(); + + // Step 3: Set controller configuration + let mut config = controller.read_config()?; + + // Disable interrupts and scancode translation during setup + config.set( + ControllerConfigFlags::ENABLE_KEYBOARD_INTERRUPT + | ControllerConfigFlags::ENABLE_MOUSE_INTERRUPT + | ControllerConfigFlags::ENABLE_TRANSLATE, + false, + ); + + controller.write_config(config)?; + + // Step 4: Perform controller self-test + controller.test_controller()?; + + // Step 5: Test PS/2 ports + let keyboard_works = controller.test_keyboard().is_ok(); + let mouse_works = controller.test_mouse().is_ok(); + + // Step 6: Enable devices + config = controller.read_config()?; + + if keyboard_works { + controller.enable_keyboard()?; + config.set(ControllerConfigFlags::DISABLE_KEYBOARD, false); + config.set(ControllerConfigFlags::ENABLE_KEYBOARD_INTERRUPT, true); + } + + if mouse_works { + controller.enable_mouse()?; + config.set(ControllerConfigFlags::DISABLE_MOUSE, false); + config.set(ControllerConfigFlags::ENABLE_MOUSE_INTERRUPT, true); + } + + // Step 7: Write the final configuration + controller.write_config(config)?; + + Ok(controller) +} + +/// Perform an operation with the PS/2 controller +/// +/// This function takes a closure that is given a mutable reference to the controller +/// and returns the result of that closure. This ensures the controller is only +/// accessed while the mutex is held. +pub fn with_controller(f: F) -> Option +where + F: FnOnce(&mut Controller) -> R, +{ + let mut lock = PS2_CONTROLLER.lock(); + (*lock).as_mut().map(f) +} + +/// Check if the PS/2 controller is initialized +pub fn is_initialized() -> bool { + CONTROLLER_INITIALIZED.load(Ordering::SeqCst) +} + +/// Reset the PS/2 controller +pub fn reset() -> Result<(), &'static str> { + if !is_initialized() { + return Err("PS/2 controller not initialized"); + } + + match initialize_controller() { + Ok(controller) => { + let mut controller_lock = PS2_CONTROLLER.lock(); + *controller_lock = Some(controller); + Ok(()) + } + Err(_) => Err("Failed to reset PS/2 controller"), + } +} diff --git a/kernel/src/devices/keyboard.rs b/kernel/src/devices/ps2_dev/keyboard.rs similarity index 54% rename from kernel/src/devices/keyboard.rs rename to kernel/src/devices/ps2_dev/keyboard.rs index d065d5ad..4efad85b 100644 --- a/kernel/src/devices/keyboard.rs +++ b/kernel/src/devices/ps2_dev/keyboard.rs @@ -1,21 +1,19 @@ -//! Keyboard management +//! PS/2 Keyboard management //! -//! Currently does not support fancy stuff like key repeats -use crate::{ - interrupts::{idt::without_interrupts, x2apic}, - serial_println, -}; +//! This module handles keyboard initialization, event processing, +//! and provides both synchronous and asynchronous interfaces for keyboard events. + +use crate::{devices::ps2_dev::controller, interrupts::idt::without_interrupts, serial_println}; use core::{ pin::Pin, - sync::atomic::{AtomicBool, AtomicU64, Ordering}, + sync::atomic::{AtomicU64, Ordering}, task::{Context, Poll, Waker}, }; -use futures_util::stream::{Stream, StreamExt}; // StreamExt trait for .next() method +use futures_util::stream::{Stream, StreamExt}; use pc_keyboard::{ - layouts, DecodedKey, Error, HandleControl, KeyCode, KeyState, Keyboard, Modifiers, ScancodeSet1, + layouts, DecodedKey, Error, HandleControl, KeyCode, KeyState, Keyboard, Modifiers, ScancodeSet2, }; use spin::Mutex; -use x86_64::{instructions::port::Port, structures::idt::InterruptStackFrame}; /// Maximum number of keyboard events to store in the buffer const KEYBOARD_BUFFER_SIZE: usize = 32; @@ -23,32 +21,50 @@ const KEYBOARD_BUFFER_SIZE: usize = 32; /// The global keyboard state pub static KEYBOARD: Mutex = Mutex::new(KeyboardState::new()); -/// Has the keyboard been initialized -static KEYBOARD_INITIALIZED: AtomicBool = AtomicBool::new(false); - /// The number of keyboard interrupts received static KEYBOARD_INTERRUPT_COUNT: AtomicU64 = AtomicU64::new(0); /// Wake task waiting for keyboard input static KEYBOARD_WAKER: Mutex> = Mutex::new(None); +/// Keyboard error types +#[derive(Debug, Clone, Copy)] +pub enum KeyboardError { + /// PS/2 controller initialization error + ControllerError, + /// Keyboard command error + CommandError, + /// Invalid scancode + InvalidScancode, + /// PC Keyboard error + PCKeyboardError(Error), +} + +impl From for KeyboardError { + fn from(error: Error) -> Self { + KeyboardError::PCKeyboardError(error) + } +} + /// Represents a key event with additional metadata #[derive(Debug, Clone)] -pub struct BufferKeyEvent { +pub struct KeyboardEvent { /// The key code from the event pub key_code: KeyCode, /// The key state (up or down) pub state: KeyState, /// The decoded key if applicable pub decoded: Option, + /// The raw scancode + pub scancode: u8, } -/// Structure to track keyboard state +/// Keyboard state structure pub struct KeyboardState { /// The pc_keyboard handler - keyboard: Keyboard, - /// Circular buffer for key events - buffer: [Option; KEYBOARD_BUFFER_SIZE], + keyboard: Keyboard, + /// Circular buffer for keyboard events + buffer: [Option; KEYBOARD_BUFFER_SIZE], /// Read position in the buffer read_pos: usize, /// Write position in the buffer @@ -69,10 +85,10 @@ impl Default for KeyboardState { impl KeyboardState { /// Create a new keyboard state pub const fn new() -> Self { - const NONE_OPTION: Option = None; + const NONE_OPTION: Option = None; Self { keyboard: Keyboard::new( - ScancodeSet1::new(), + ScancodeSet2::new(), layouts::Us104Key, HandleControl::Ignore, ), @@ -88,31 +104,31 @@ impl KeyboardState { !self.full && self.read_pos == self.write_pos } - /// Process a scancode and add resulting key events to the buffer - pub fn process_scancode(&mut self, scancode: u8) -> Result<(), Error> { + /// Process a scancode + pub fn process_scancode(&mut self, scancode: u8) -> Result<(), KeyboardError> { if let Some(key_event) = self.keyboard.add_byte(scancode)? { let decoded = self.keyboard.process_keyevent(key_event.clone()); - let buff_event = BufferKeyEvent { + let event = KeyboardEvent { key_code: key_event.code, state: key_event.state, decoded, + scancode, }; - self.push_event(buff_event); + self.push_event(event)?; } Ok(()) } /// Push an event to the buffer - fn push_event(&mut self, event: BufferKeyEvent) { + fn push_event(&mut self, event: KeyboardEvent) -> Result<(), KeyboardError> { if self.full { self.read_pos = (self.read_pos + 1) % KEYBOARD_BUFFER_SIZE; } self.buffer[self.write_pos] = Some(event); - self.write_pos = (self.write_pos + 1) % KEYBOARD_BUFFER_SIZE; if self.write_pos == self.read_pos { @@ -125,19 +141,19 @@ impl KeyboardState { w.wake(); } }); + + Ok(()) } - /// Read a key event from the buffer - pub fn read_event(&mut self) -> Option { + /// Read a keyboard event from the buffer + pub fn read_event(&mut self) -> Option { if self.is_empty() { return None; } let event = self.buffer[self.read_pos].clone(); - self.buffer[self.read_pos] = None; self.read_pos = (self.read_pos + 1) % KEYBOARD_BUFFER_SIZE; - self.full = false; event @@ -150,7 +166,7 @@ impl KeyboardState { /// Clear keyboard buffer pub fn clear_buffer(&mut self) { - const NONE_OPTION: Option = None; + const NONE_OPTION: Option = None; self.buffer = [NONE_OPTION; KEYBOARD_BUFFER_SIZE]; self.read_pos = 0; self.write_pos = 0; @@ -158,15 +174,22 @@ impl KeyboardState { } } -/// Initialize the keyboard system -pub fn init() -> Result<(), &'static str> { - if KEYBOARD_INITIALIZED.load(Ordering::SeqCst) { - return Ok(()); - } +/// Initialize the keyboard +pub fn init() { + controller::with_controller(initialize_keyboard); +} - KEYBOARD_INITIALIZED.store(true, Ordering::SeqCst); +/// Initialize and reset the keyboard +fn initialize_keyboard(controller: &mut ps2::Controller) { + let mut keyboard = controller.keyboard(); - Ok(()) + keyboard + .reset_and_self_test() + .expect("Failed keyboard reset test"); + + keyboard + .enable_scanning() + .expect("Failed to enable scanning for keyboard"); } /// Get a stream of keyboard events @@ -174,32 +197,53 @@ pub fn get_stream() -> KeyboardStream { KeyboardStream } -/// Wait for and return the next key event -pub async fn next_key() -> BufferKeyEvent { +/// Wait for and return the next keyboard event +pub async fn next_event() -> KeyboardEvent { KeyboardStream.next().await.unwrap() } -/// Read a key without waiting -pub fn try_read_key() -> Option { +/// Try to read a keyboard event without waiting +pub fn try_read_event() -> Option { without_interrupts(|| KEYBOARD.lock().read_event()) } -pub extern "x86-interrupt" fn keyboard_handler(_frame: InterruptStackFrame) { - KEYBOARD_INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst); +/// Get keyboard interrupt count +pub fn get_interrupt_count() -> u64 { + KEYBOARD_INTERRUPT_COUNT.load(Ordering::SeqCst) +} - let mut port = Port::new(0x60); - let scancode: u8 = unsafe { port.read() }; +/// Keyboard interrupt handler +pub fn keyboard_handler() { + KEYBOARD_INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst); - let mut keyboard = KEYBOARD.lock(); - if let Err(e) = keyboard.process_scancode(scancode) { - serial_println!("Error processing keyboard scancode: {:?}", e); - } + controller::with_controller(|controller| { + // Read from the controller as long as the OUTPUT_FULL bit is set + loop { + let status = controller.read_status(); + if !status.contains(ps2::flags::ControllerStatusFlags::OUTPUT_FULL) { + // No more data available + break; + } - x2apic::send_eoi(); + match controller.read_data() { + Ok(scancode) => { + let mut keyboard = KEYBOARD.lock(); + if let Err(e) = keyboard.process_scancode(scancode) { + serial_println!("Error processing keyboard scancode: {:?}", e); + } + } + Err(_) => { + // If we can't read data despite OUTPUT_FULL being set + serial_println!("Keyboard: Full bit set but got error while reading"); + break; + } + } + } + }); } impl Stream for KeyboardStream { - type Item = BufferKeyEvent; + type Item = KeyboardEvent; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut keyboard = KEYBOARD.lock(); diff --git a/kernel/src/devices/ps2_dev/mod.rs b/kernel/src/devices/ps2_dev/mod.rs new file mode 100644 index 00000000..c10422a0 --- /dev/null +++ b/kernel/src/devices/ps2_dev/mod.rs @@ -0,0 +1,35 @@ +//! PS/2 device management module +//! +//! This module provides interfaces for PS/2 devices including +//! keyboard and mouse through the PS/2 controller. + +pub mod controller; +pub mod keyboard; +pub mod mouse; + +use crate::interrupts::x2apic; + +/// Initialize the PS/2 subsystem +pub fn init() { + // Initialize controller first + controller::init().expect("Failed to initialize controller"); + + keyboard::init(); + mouse::init(); +} + +/// PS/2 keyboard interrupt handler +pub extern "x86-interrupt" fn keyboard_interrupt_handler( + _frame: x86_64::structures::idt::InterruptStackFrame, +) { + keyboard::keyboard_handler(); + x2apic::send_eoi(); +} + +/// PS/2 mouse interrupt handler +pub extern "x86-interrupt" fn mouse_interrupt_handler( + _frame: x86_64::structures::idt::InterruptStackFrame, +) { + mouse::mouse_handler(); + x2apic::send_eoi(); +} diff --git a/kernel/src/devices/ps2_dev/mouse.rs b/kernel/src/devices/ps2_dev/mouse.rs new file mode 100644 index 00000000..7f212939 --- /dev/null +++ b/kernel/src/devices/ps2_dev/mouse.rs @@ -0,0 +1,393 @@ +//! PS/2 Mouse management +//! +//! This module handles mouse initialization, event processing, +//! and provides both synchronous and asynchronous interfaces for mouse events. + +use crate::{devices::ps2_dev::controller, interrupts::idt::without_interrupts, serial_println}; +use core::{ + fmt, + pin::Pin, + sync::atomic::{AtomicU64, Ordering}, + task::{Context, Poll, Waker}, +}; +use futures_util::stream::{Stream, StreamExt}; +use ps2::flags::MouseMovementFlags; +use spin::Mutex; + +/// Maximum number of mouse events to store in the buffer +const MOUSE_BUFFER_SIZE: usize = 32; + +/// The global mouse state +pub static MOUSE: Mutex = Mutex::new(MouseState::new()); + +/// The number of mouse interrupts received +static MOUSE_INTERRUPT_COUNT: AtomicU64 = AtomicU64::new(0); + +/// Wake task waiting for mouse input +static MOUSE_WAKER: Mutex> = Mutex::new(None); + +/// PS/2 mouse error types +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MouseError { + /// PS/2 controller initialization error + ControllerError, + /// Mouse command error + CommandError, + /// Invalid packet data + InvalidPacketData, +} + +impl fmt::Display for MouseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ControllerError => write!(f, "PS/2 controller error"), + Self::CommandError => write!(f, "Mouse command error"), + Self::InvalidPacketData => write!(f, "Invalid mouse packet data"), + } + } +} + +/// Button state for mouse buttons +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ButtonState { + /// Button is pressed + Pressed, + /// Button is released + Released, +} + +/// Represents a mouse event with additional metadata +#[derive(Debug, Clone)] +pub struct MouseEvent { + /// X movement delta + pub dx: i8, + /// Y movement delta + pub dy: i8, + /// Z movement delta (scroll wheel) + pub dz: i8, + /// Left button state + pub left_button: ButtonState, + /// Right button state + pub right_button: ButtonState, + /// Middle button state + pub middle_button: ButtonState, + /// Absolute X position at time of event + pub x: i16, + /// Absolute Y position at time of event + pub y: i16, +} + +/// Structure to track mouse state +pub struct MouseState { + /// Circular buffer for mouse events + buffer: [Option; MOUSE_BUFFER_SIZE], + /// Read position in the buffer + read_pos: usize, + /// Write position in the buffer + write_pos: usize, + /// Is the buffer full + full: bool, + /// Previous button states for tracking changes + prev_left_button: ButtonState, + prev_right_button: ButtonState, + prev_middle_button: ButtonState, + /// Current mouse X position + mouse_x: i16, + /// Current mouse Y position + mouse_y: i16, + /// Maximum X position (screen width - 1) + max_x: i16, + /// Maximum Y position (screen height - 1) + max_y: i16, +} + +/// Stream that yields mouse events +pub struct MouseStream; + +impl Default for MouseState { + fn default() -> Self { + Self::new() + } +} + +impl MouseState { + /// Create a new mouse state + pub const fn new() -> Self { + const NONE_OPTION: Option = None; + Self { + buffer: [NONE_OPTION; MOUSE_BUFFER_SIZE], + read_pos: 0, + write_pos: 0, + full: false, + prev_left_button: ButtonState::Released, + prev_right_button: ButtonState::Released, + prev_middle_button: ButtonState::Released, + mouse_x: 0, + mouse_y: 0, + // Default to VGA text mode dimensions, can be updated with set_bounds + max_x: 79, + max_y: 24, + } + } + + /// Set the screen boundaries for mouse movement + pub fn set_bounds(&mut self, width: i16, height: i16) { + self.max_x = width - 1; + self.max_y = height - 1; + self.clamp_position(); + } + + /// Ensure mouse position stays within bounds + fn clamp_position(&mut self) { + if self.mouse_x < 0 { + self.mouse_x = 0; + } else if self.mouse_x > self.max_x { + self.mouse_x = self.max_x; + } + + if self.mouse_y < 0 { + self.mouse_y = 0; + } else if self.mouse_y > self.max_y { + self.mouse_y = self.max_y; + } + } + + /// Get current mouse position + pub fn get_position(&self) -> (i16, i16) { + (self.mouse_x, self.mouse_y) + } + + /// Set mouse position + pub fn set_position(&mut self, x: i16, y: i16) { + self.mouse_x = x; + self.mouse_y = y; + self.clamp_position(); + } + + /// Check if the buffer is empty + pub fn is_empty(&self) -> bool { + !self.full && self.read_pos == self.write_pos + } + + /// Process a complete mouse packet + pub fn process_packet( + &mut self, + flags: MouseMovementFlags, + dx: i16, + dy: i16, + ) -> Result<(), MouseError> { + // Extract button states + let left_button = if flags.contains(MouseMovementFlags::LEFT_BUTTON_PRESSED) { + ButtonState::Pressed + } else { + ButtonState::Released + }; + + let right_button = if flags.contains(MouseMovementFlags::RIGHT_BUTTON_PRESSED) { + ButtonState::Pressed + } else { + ButtonState::Released + }; + + let middle_button = if flags.contains(MouseMovementFlags::MIDDLE_BUTTON_PRESSED) { + ButtonState::Pressed + } else { + ButtonState::Released + }; + + // Update absolute position + self.mouse_x += dx; + self.mouse_y += dy; + + // Ensure position stays within bounds + self.clamp_position(); + + // Create event only if there's actual movement or button state change + if dx != 0 + || dy != 0 + || left_button != self.prev_left_button + || right_button != self.prev_right_button + || middle_button != self.prev_middle_button + { + let event = MouseEvent { + dx: dx as i8, + dy: dy as i8, + dz: 0, // No scroll wheel support in basic PS/2 mouse + left_button, + right_button, + middle_button, + x: self.mouse_x, + y: self.mouse_y, + }; + + self.push_event(event)?; + + // Update previous button states + self.prev_left_button = left_button; + self.prev_right_button = right_button; + self.prev_middle_button = middle_button; + } + + Ok(()) + } + + /// Push an event to the buffer + fn push_event(&mut self, event: MouseEvent) -> Result<(), MouseError> { + if self.full { + self.read_pos = (self.read_pos + 1) % MOUSE_BUFFER_SIZE; + } + + self.buffer[self.write_pos] = Some(event); + self.write_pos = (self.write_pos + 1) % MOUSE_BUFFER_SIZE; + + if self.write_pos == self.read_pos { + self.full = true; + } + + without_interrupts(|| { + let mut waker = MOUSE_WAKER.lock(); + if let Some(w) = waker.take() { + w.wake(); + } + }); + + Ok(()) + } + + /// Read a mouse event from the buffer + pub fn read_event(&mut self) -> Option { + if self.is_empty() { + return None; + } + + let event = self.buffer[self.read_pos].clone(); + self.buffer[self.read_pos] = None; + self.read_pos = (self.read_pos + 1) % MOUSE_BUFFER_SIZE; + self.full = false; + + event + } + + /// Clear mouse buffer + pub fn clear_buffer(&mut self) { + const NONE_OPTION: Option = None; + self.buffer = [NONE_OPTION; MOUSE_BUFFER_SIZE]; + self.read_pos = 0; + self.write_pos = 0; + self.full = false; + } +} + +/// Initialize the mouse +pub fn init() { + controller::with_controller(initialize_mouse); +} + +/// Initialize and reset the mouse +fn initialize_mouse(controller: &mut ps2::Controller) { + let mut mouse = controller.mouse(); + + mouse + .reset_and_self_test() + .expect("Failed to self-test mouse"); + + mouse.set_defaults().expect("Failed to set mouse defaults"); + + mouse + .enable_data_reporting() + .expect("Failed to enable data reporting"); +} + +/// Get a stream of mouse events +pub fn get_stream() -> MouseStream { + MouseStream +} + +/// Wait for and return the next mouse event +pub async fn next_event() -> MouseEvent { + MouseStream.next().await.unwrap() +} + +/// Try to read a mouse event without waiting +pub fn try_read_event() -> Option { + without_interrupts(|| MOUSE.lock().read_event()) +} + +/// Get current mouse position +pub fn get_position() -> (i16, i16) { + without_interrupts(|| { + let mouse = MOUSE.lock(); + mouse.get_position() + }) +} + +/// Set mouse position +pub fn set_position(x: i16, y: i16) { + without_interrupts(|| { + let mut mouse = MOUSE.lock(); + mouse.set_position(x, y); + }) +} + +/// Set screen boundaries for mouse movement +pub fn set_bounds(width: i16, height: i16) { + without_interrupts(|| { + let mut mouse = MOUSE.lock(); + mouse.set_bounds(width, height); + }) +} + +/// Get mouse interrupt count +pub fn get_interrupt_count() -> u64 { + MOUSE_INTERRUPT_COUNT.load(Ordering::SeqCst) +} + +/// Mouse interrupt handler +pub fn mouse_handler() { + MOUSE_INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst); + + controller::with_controller(|controller| { + // Continue reading as long as data is available + loop { + let status = controller.read_status(); + if !status.contains(ps2::flags::ControllerStatusFlags::OUTPUT_FULL) { + // No more data available + break; + } + let mut mouse_dev = controller.mouse(); + + match mouse_dev.read_data_packet() { + Ok((flags, dx, dy)) => { + let mut mouse = MOUSE.lock(); + if let Err(e) = mouse.process_packet(flags, dx, dy) { + serial_println!("Error processing mouse packet: {:?}", e); + } + } + Err(_) => { + // If we can't read a complete packet, break the loop + break; + } + } + } + }); +} + +impl Stream for MouseStream { + type Item = MouseEvent; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut mouse = MOUSE.lock(); + + if let Some(event) = mouse.read_event() { + return Poll::Ready(Some(event)); + } + + // No event available, register waker for notification + without_interrupts(|| { + let mut waker = MOUSE_WAKER.lock(); + *waker = Some(cx.waker().clone()); + }); + + Poll::Pending + } +} diff --git a/kernel/src/interrupts/idt.rs b/kernel/src/interrupts/idt.rs index 9862f209..c4db91d2 100644 --- a/kernel/src/interrupts/idt.rs +++ b/kernel/src/interrupts/idt.rs @@ -20,7 +20,7 @@ use crate::{ idt::{KEYBOARD_VECTOR, MOUSE_VECTOR, SYSCALL_HANDLER, TIMER_VECTOR, TLB_SHOOTDOWN_VECTOR}, syscalls::{SYSCALL_EXIT, SYSCALL_MMAP, SYSCALL_NANOSLEEP, SYSCALL_PRINT}, }, - devices::{keyboard::keyboard_handler, mouse::mouse_handler}, + devices::ps2_dev::{keyboard_interrupt_handler, mouse_interrupt_handler}, events::inc_runner_clock, interrupts::x2apic::{self, current_core_id, TLB_SHOOTDOWN_ADDR}, memory::{ @@ -61,8 +61,8 @@ lazy_static! { .set_handler_fn(naked_syscall_handler) .set_privilege_level(x86_64::PrivilegeLevel::Ring3); idt[TLB_SHOOTDOWN_VECTOR].set_handler_fn(tlb_shootdown_handler); - idt[KEYBOARD_VECTOR].set_handler_fn(keyboard_handler); - idt[MOUSE_VECTOR].set_handler_fn(mouse_handler); + idt[KEYBOARD_VECTOR].set_handler_fn(keyboard_interrupt_handler); + idt[MOUSE_VECTOR].set_handler_fn(mouse_interrupt_handler); idt }; } diff --git a/kernel/src/interrupts/io_apic.rs b/kernel/src/interrupts/io_apic.rs index 6d6595d3..dfed6036 100644 --- a/kernel/src/interrupts/io_apic.rs +++ b/kernel/src/interrupts/io_apic.rs @@ -237,8 +237,8 @@ impl IoApicManager { vector: KEYBOARD_VECTOR, destination: 0, // BSP masked: false, // Enable immediately - trigger_mode: TriggerMode::Edge, - polarity: Polarity::HighActive, + trigger_mode: TriggerMode::Level, + polarity: Polarity::LowActive, dest_mode: DestinationMode::Physical, delivery_mode: DeliveryMode::Fixed, }, @@ -251,8 +251,8 @@ impl IoApicManager { vector: MOUSE_VECTOR, destination: 0, // BSP masked: false, // Enable immediately - trigger_mode: TriggerMode::Edge, - polarity: Polarity::HighActive, + trigger_mode: TriggerMode::Level, + polarity: Polarity::LowActive, dest_mode: DestinationMode::Physical, delivery_mode: DeliveryMode::Fixed, }, From 562267baedcba31b327ae80589f485cb66533a9f Mon Sep 17 00:00:00 2001 From: Kiran Chandrasekhar Date: Tue, 22 Apr 2025 16:27:27 -0500 Subject: [PATCH 2/5] Async keyboard again --- kernel/src/devices/ps2_dev/keyboard.rs | 18 +++++++++++----- kernel/src/devices/ps2_dev/mouse.rs | 18 +++++++++++----- kernel/src/events/futures/sync.rs | 30 +++++++++++++++++++------- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/kernel/src/devices/ps2_dev/keyboard.rs b/kernel/src/devices/ps2_dev/keyboard.rs index 4efad85b..60467ec7 100644 --- a/kernel/src/devices/ps2_dev/keyboard.rs +++ b/kernel/src/devices/ps2_dev/keyboard.rs @@ -3,7 +3,10 @@ //! This module handles keyboard initialization, event processing, //! and provides both synchronous and asynchronous interfaces for keyboard events. -use crate::{devices::ps2_dev::controller, interrupts::idt::without_interrupts, serial_println}; +use crate::{ + devices::ps2_dev::controller, events::schedule_kernel, interrupts::idt::without_interrupts, + serial_println, +}; use core::{ pin::Pin, sync::atomic::{AtomicU64, Ordering}, @@ -227,10 +230,15 @@ pub fn keyboard_handler() { match controller.read_data() { Ok(scancode) => { - let mut keyboard = KEYBOARD.lock(); - if let Err(e) = keyboard.process_scancode(scancode) { - serial_println!("Error processing keyboard scancode: {:?}", e); - } + schedule_kernel( + async move { + let mut keyboard = KEYBOARD.lock(); + if let Err(e) = keyboard.process_scancode(scancode) { + serial_println!("Error processing keyboard scancode: {:?}", e); + } + }, + 0, + ); } Err(_) => { // If we can't read data despite OUTPUT_FULL being set diff --git a/kernel/src/devices/ps2_dev/mouse.rs b/kernel/src/devices/ps2_dev/mouse.rs index 7f212939..d94fbdf8 100644 --- a/kernel/src/devices/ps2_dev/mouse.rs +++ b/kernel/src/devices/ps2_dev/mouse.rs @@ -3,7 +3,10 @@ //! This module handles mouse initialization, event processing, //! and provides both synchronous and asynchronous interfaces for mouse events. -use crate::{devices::ps2_dev::controller, interrupts::idt::without_interrupts, serial_println}; +use crate::{ + devices::ps2_dev::controller, events::schedule_kernel, interrupts::idt::without_interrupts, + serial_println, +}; use core::{ fmt, pin::Pin, @@ -358,10 +361,15 @@ pub fn mouse_handler() { match mouse_dev.read_data_packet() { Ok((flags, dx, dy)) => { - let mut mouse = MOUSE.lock(); - if let Err(e) = mouse.process_packet(flags, dx, dy) { - serial_println!("Error processing mouse packet: {:?}", e); - } + schedule_kernel( + async move { + let mut mouse = MOUSE.lock(); + if let Err(e) = mouse.process_packet(flags, dx, dy) { + serial_println!("Error processing mouse packet: {:?}", e); + } + }, + 0, + ); } Err(_) => { // If we can't read a complete packet, break the loop diff --git a/kernel/src/events/futures/sync.rs b/kernel/src/events/futures/sync.rs index c33b1f45..7e8ed1da 100644 --- a/kernel/src/events/futures/sync.rs +++ b/kernel/src/events/futures/sync.rs @@ -1,5 +1,6 @@ use alloc::{sync::Arc, vec::Vec}; use core::{ + cell::UnsafeCell, future::Future, ops::{Deref, DerefMut}, pin::Pin, @@ -153,7 +154,7 @@ impl BoundedBuffer { pub struct BlockMutex { unlocked: Arc, - data: T, + data: UnsafeCell, } unsafe impl Send for BlockMutex {} @@ -163,39 +164,52 @@ impl BlockMutex { pub fn new(data: T) -> BlockMutex { BlockMutex { unlocked: Arc::new(AtomicBool::new(true)), - data, + data: UnsafeCell::new(data), } } - pub async fn lock(&mut self) -> BlockMutexGuard { + pub async fn lock(&self) -> BlockMutexGuard { let event = current_running_event().expect("Using BlockMutex outside event"); Condition::new(self.unlocked.clone(), event).await; self.unlocked.store(false, Ordering::Relaxed); - BlockMutexGuard { mutex: self } + BlockMutexGuard { + mutex: self, + data: self.data.get(), + } } - fn unlock(&mut self) { + fn unlock(&self) { self.unlocked.store(true, Ordering::Relaxed); } } +impl From for BlockMutex { + fn from(data: T) -> Self { + BlockMutex::new(data) + } +} + pub struct BlockMutexGuard<'a, T> { - mutex: &'a mut BlockMutex, + mutex: &'a BlockMutex, + data: *mut T, } +unsafe impl Send for BlockMutexGuard<'_, T> {} +unsafe impl Sync for BlockMutexGuard<'_, T> {} + impl Deref for BlockMutexGuard<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { - &self.mutex.data + unsafe { &*self.data } } } impl DerefMut for BlockMutexGuard<'_, T> { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.mutex.data + unsafe { &mut *self.data } } } From 7eecb7d305742e1e61cd092f33ec470735bb61f4 Mon Sep 17 00:00:00 2001 From: Kiran Chandrasekhar Date: Tue, 22 Apr 2025 16:38:08 -0500 Subject: [PATCH 3/5] Rust version naked function change --- kernel/Cargo.lock | 4 +- kernel/src/interrupts/idt.rs | 168 ++++++++++++------------ kernel/src/lib.rs | 1 - kernel/src/main.rs | 1 - kernel/src/processes/process.rs | 4 +- kernel/src/syscalls/syscall_handlers.rs | 2 +- 6 files changed, 87 insertions(+), 93 deletions(-) diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 54355de8..7497933c 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -365,9 +365,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] diff --git a/kernel/src/interrupts/idt.rs b/kernel/src/interrupts/idt.rs index c4db91d2..d740662c 100644 --- a/kernel/src/interrupts/idt.rs +++ b/kernel/src/interrupts/idt.rs @@ -254,52 +254,50 @@ extern "x86-interrupt" fn page_fault_handler( // TODO: Refactor this to follow the way 64 bit works #[no_mangle] -#[naked] +#[unsafe(naked)] pub extern "x86-interrupt" fn naked_syscall_handler(_: InterruptStackFrame) { - unsafe { - naked_asm!( - // Push registers for potential yielding - " - push rbp - push r15 - push r14 - push r13 - push r12 - push r11 - push r10 - push r9 - push r8 - push rdi - push rsi - push rdx - push rcx - push rbx - push rax - ", - "mov rdi, rsp", - // Call the syscall_handler - "call syscall_handler", - // Restore registers - " - pop rax - pop rbx - pop rcx - pop rdx - pop rsi - pop rdi - pop r8 - pop r9 - pop r10 - pop r11 - pop r12 - pop r13 - pop r14 - pop r15 - pop rbp - iretq - " - ); - } + naked_asm!( + // Push registers for potential yielding + " + push rbp + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rdi + push rsi + push rdx + push rcx + push rbx + push rax + ", + "mov rdi, rsp", + // Call the syscall_handler + "call syscall_handler", + // Restore registers + " + pop rax + pop rbx + pop rcx + pop rdx + pop rsi + pop rdi + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + pop rbp + iretq + " + ); } #[no_mangle] @@ -369,50 +367,48 @@ fn syscall_handler(rsp: u64) { // } } -#[naked] +#[unsafe(naked)] extern "x86-interrupt" fn naked_timer_handler(_: InterruptStackFrame) { - unsafe { - core::arch::naked_asm!( - " - push rbp - push r15 - push r14 - push r13 - push r12 - push r11 - push r10 - push r9 - push r8 - push rdi - push rsi - push rdx - push rcx - push rbx - push rax + core::arch::naked_asm!( + " + push rbp + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rdi + push rsi + push rdx + push rcx + push rbx + push rax - cld - mov rdi, rsp - call timer_handler + cld + mov rdi, rsp + call timer_handler - pop rax - pop rbx - pop rcx - pop rdx - pop rsi - pop rdi - pop r8 - pop r9 - pop r10 - pop r11 - pop r12 - pop r13 - pop r14 - pop r15 - pop rbp - iretq - " - ); - } + pop rax + pop rbx + pop rcx + pop rdx + pop rsi + pop rdi + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + pop rbp + iretq + " + ); } #[no_mangle] diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index db04971b..ed3ebd75 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,6 +1,5 @@ #![feature(naked_functions_rustic_abi)] #![feature(abi_x86_interrupt)] -#![feature(naked_functions)] #![cfg_attr(feature = "strict", deny(warnings))] #![no_std] #![cfg_attr(test, no_main)] diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 00c2311b..93f4d58d 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -3,7 +3,6 @@ #![no_std] #![no_main] #![feature(custom_test_frameworks)] -#![feature(naked_functions)] #![test_runner(taos::test_runner)] #![reexport_test_harness_main = "test_main"] diff --git a/kernel/src/processes/process.rs b/kernel/src/processes/process.rs index 61dd2a7b..8875ba15 100644 --- a/kernel/src/processes/process.rs +++ b/kernel/src/processes/process.rs @@ -378,7 +378,7 @@ pub async unsafe fn run_process_ring3(pid: u32) { } } -#[naked] +#[unsafe(naked)] #[no_mangle] unsafe fn call_process( registers: *const Registers, @@ -436,7 +436,7 @@ unsafe fn call_process( ); } -#[naked] +#[unsafe(naked)] #[no_mangle] unsafe fn return_process() { naked_asm!( diff --git a/kernel/src/syscalls/syscall_handlers.rs b/kernel/src/syscalls/syscall_handlers.rs index e1ccf246..221121ba 100644 --- a/kernel/src/syscalls/syscall_handlers.rs +++ b/kernel/src/syscalls/syscall_handlers.rs @@ -60,7 +60,7 @@ pub struct SyscallRegisters { /// /// # Safety /// This function is unsafe as it manually saves state and switches stacks -#[naked] +#[unsafe(naked)] #[no_mangle] pub unsafe extern "C" fn syscall_handler_64_naked() -> ! { naked_asm!( From 48109e0e3e2416d4523c187d00c558688be8a4b4 Mon Sep 17 00:00:00 2001 From: Kiran Chandrasekhar Date: Tue, 22 Apr 2025 16:40:51 -0500 Subject: [PATCH 4/5] Reintroduce unecessary unsafe --- kernel/src/interrupts/idt.rs | 168 ++++++++++++++++++----------------- 1 file changed, 87 insertions(+), 81 deletions(-) diff --git a/kernel/src/interrupts/idt.rs b/kernel/src/interrupts/idt.rs index d740662c..13a3b34c 100644 --- a/kernel/src/interrupts/idt.rs +++ b/kernel/src/interrupts/idt.rs @@ -255,49 +255,52 @@ extern "x86-interrupt" fn page_fault_handler( // TODO: Refactor this to follow the way 64 bit works #[no_mangle] #[unsafe(naked)] +#[allow(unused_unsafe)] pub extern "x86-interrupt" fn naked_syscall_handler(_: InterruptStackFrame) { - naked_asm!( - // Push registers for potential yielding - " - push rbp - push r15 - push r14 - push r13 - push r12 - push r11 - push r10 - push r9 - push r8 - push rdi - push rsi - push rdx - push rcx - push rbx - push rax - ", - "mov rdi, rsp", - // Call the syscall_handler - "call syscall_handler", - // Restore registers - " - pop rax - pop rbx - pop rcx - pop rdx - pop rsi - pop rdi - pop r8 - pop r9 - pop r10 - pop r11 - pop r12 - pop r13 - pop r14 - pop r15 - pop rbp - iretq - " - ); + unsafe { + naked_asm!( + // Push registers for potential yielding + " + push rbp + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rdi + push rsi + push rdx + push rcx + push rbx + push rax + ", + "mov rdi, rsp", + // Call the syscall_handler + "call syscall_handler", + // Restore registers + " + pop rax + pop rbx + pop rcx + pop rdx + pop rsi + pop rdi + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + pop rbp + iretq + " + ); + } } #[no_mangle] @@ -368,47 +371,50 @@ fn syscall_handler(rsp: u64) { } #[unsafe(naked)] +#[allow(unused_unsafe)] extern "x86-interrupt" fn naked_timer_handler(_: InterruptStackFrame) { - core::arch::naked_asm!( + unsafe { + core::arch::naked_asm!( + " + push rbp + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rdi + push rsi + push rdx + push rcx + push rbx + push rax + + cld + mov rdi, rsp + call timer_handler + + pop rax + pop rbx + pop rcx + pop rdx + pop rsi + pop rdi + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + pop rbp + iretq " - push rbp - push r15 - push r14 - push r13 - push r12 - push r11 - push r10 - push r9 - push r8 - push rdi - push rsi - push rdx - push rcx - push rbx - push rax - - cld - mov rdi, rsp - call timer_handler - - pop rax - pop rbx - pop rcx - pop rdx - pop rsi - pop rdi - pop r8 - pop r9 - pop r10 - pop r11 - pop r12 - pop r13 - pop r14 - pop r15 - pop rbp - iretq - " - ); + ); + } } #[no_mangle] From d0afe28a74c729933ba1b82b4e9c1ee83563c60c Mon Sep 17 00:00:00 2001 From: Kiran Chandrasekhar Date: Tue, 22 Apr 2025 17:53:40 -0500 Subject: [PATCH 5/5] try lock --- kernel/src/devices/ps2_dev/keyboard.rs | 27 ++++++++++++----- kernel/src/devices/ps2_dev/mouse.rs | 37 ++++++++++++++++-------- kernel/src/events/futures/sync.rs | 40 ++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 22 deletions(-) diff --git a/kernel/src/devices/ps2_dev/keyboard.rs b/kernel/src/devices/ps2_dev/keyboard.rs index 60467ec7..5371fe84 100644 --- a/kernel/src/devices/ps2_dev/keyboard.rs +++ b/kernel/src/devices/ps2_dev/keyboard.rs @@ -4,7 +4,9 @@ //! and provides both synchronous and asynchronous interfaces for keyboard events. use crate::{ - devices::ps2_dev::controller, events::schedule_kernel, interrupts::idt::without_interrupts, + devices::ps2_dev::controller, + events::{futures::sync::BlockMutex, schedule_kernel}, + interrupts::idt::without_interrupts, serial_println, }; use core::{ @@ -13,6 +15,7 @@ use core::{ task::{Context, Poll, Waker}, }; use futures_util::stream::{Stream, StreamExt}; +use lazy_static::lazy_static; use pc_keyboard::{ layouts, DecodedKey, Error, HandleControl, KeyCode, KeyState, Keyboard, Modifiers, ScancodeSet2, }; @@ -21,8 +24,10 @@ use spin::Mutex; /// Maximum number of keyboard events to store in the buffer const KEYBOARD_BUFFER_SIZE: usize = 32; -/// The global keyboard state -pub static KEYBOARD: Mutex = Mutex::new(KeyboardState::new()); +lazy_static! { + /// The global keyboard state + pub static ref KEYBOARD: BlockMutex = BlockMutex::new(KeyboardState::new()); +} /// The number of keyboard interrupts received static KEYBOARD_INTERRUPT_COUNT: AtomicU64 = AtomicU64::new(0); @@ -206,8 +211,11 @@ pub async fn next_event() -> KeyboardEvent { } /// Try to read a keyboard event without waiting -pub fn try_read_event() -> Option { - without_interrupts(|| KEYBOARD.lock().read_event()) +pub async fn try_read_event() -> Option { + without_interrupts(|| match KEYBOARD.try_lock() { + Ok(mut keyboard) => keyboard.read_event(), + Err(_) => None, + }) } /// Get keyboard interrupt count @@ -232,7 +240,7 @@ pub fn keyboard_handler() { Ok(scancode) => { schedule_kernel( async move { - let mut keyboard = KEYBOARD.lock(); + let mut keyboard = KEYBOARD.lock().await; if let Err(e) = keyboard.process_scancode(scancode) { serial_println!("Error processing keyboard scancode: {:?}", e); } @@ -254,7 +262,12 @@ impl Stream for KeyboardStream { type Item = KeyboardEvent; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut keyboard = KEYBOARD.lock(); + let mut keyboard = match KEYBOARD.try_lock() { + Ok(keyboard) => keyboard, + Err(_) => { + return Poll::Pending; + } + }; if let Some(event) = keyboard.read_event() { return Poll::Ready(Some(event)); diff --git a/kernel/src/devices/ps2_dev/mouse.rs b/kernel/src/devices/ps2_dev/mouse.rs index d94fbdf8..c81ce6af 100644 --- a/kernel/src/devices/ps2_dev/mouse.rs +++ b/kernel/src/devices/ps2_dev/mouse.rs @@ -4,7 +4,9 @@ //! and provides both synchronous and asynchronous interfaces for mouse events. use crate::{ - devices::ps2_dev::controller, events::schedule_kernel, interrupts::idt::without_interrupts, + devices::ps2_dev::controller, + events::{futures::sync::BlockMutex, schedule_kernel}, + interrupts::idt::without_interrupts, serial_println, }; use core::{ @@ -14,14 +16,17 @@ use core::{ task::{Context, Poll, Waker}, }; use futures_util::stream::{Stream, StreamExt}; +use lazy_static::lazy_static; use ps2::flags::MouseMovementFlags; use spin::Mutex; /// Maximum number of mouse events to store in the buffer const MOUSE_BUFFER_SIZE: usize = 32; -/// The global mouse state -pub static MOUSE: Mutex = Mutex::new(MouseState::new()); +lazy_static! { + /// The global mouse state + pub static ref MOUSE: BlockMutex = BlockMutex::new(MouseState::new()); +} /// The number of mouse interrupts received static MOUSE_INTERRUPT_COUNT: AtomicU64 = AtomicU64::new(0); @@ -313,29 +318,32 @@ pub async fn next_event() -> MouseEvent { /// Try to read a mouse event without waiting pub fn try_read_event() -> Option { - without_interrupts(|| MOUSE.lock().read_event()) + without_interrupts(|| match MOUSE.try_lock() { + Ok(mut mouse) => mouse.read_event(), + Err(_) => None, + }) } /// Get current mouse position -pub fn get_position() -> (i16, i16) { +pub async fn get_position() -> (i16, i16) { without_interrupts(|| { - let mouse = MOUSE.lock(); + let mouse = MOUSE.spin(); mouse.get_position() }) } /// Set mouse position -pub fn set_position(x: i16, y: i16) { +pub async fn set_position(x: i16, y: i16) { without_interrupts(|| { - let mut mouse = MOUSE.lock(); + let mut mouse = MOUSE.spin(); mouse.set_position(x, y); }) } /// Set screen boundaries for mouse movement -pub fn set_bounds(width: i16, height: i16) { +pub async fn set_bounds(width: i16, height: i16) { without_interrupts(|| { - let mut mouse = MOUSE.lock(); + let mut mouse = MOUSE.spin(); mouse.set_bounds(width, height); }) } @@ -363,7 +371,7 @@ pub fn mouse_handler() { Ok((flags, dx, dy)) => { schedule_kernel( async move { - let mut mouse = MOUSE.lock(); + let mut mouse = MOUSE.lock().await; if let Err(e) = mouse.process_packet(flags, dx, dy) { serial_println!("Error processing mouse packet: {:?}", e); } @@ -384,7 +392,12 @@ impl Stream for MouseStream { type Item = MouseEvent; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut mouse = MOUSE.lock(); + let mut mouse = match MOUSE.try_lock() { + Ok(mouse) => mouse, + Err(_) => { + return Poll::Pending; + } + }; if let Some(event) = mouse.read_event() { return Poll::Ready(Some(event)); diff --git a/kernel/src/events/futures/sync.rs b/kernel/src/events/futures/sync.rs index 7e8ed1da..6c2cad74 100644 --- a/kernel/src/events/futures/sync.rs +++ b/kernel/src/events/futures/sync.rs @@ -160,6 +160,10 @@ pub struct BlockMutex { unsafe impl Send for BlockMutex {} unsafe impl Sync for BlockMutex {} +pub enum BlockMutexError { + WouldBlock, +} + impl BlockMutex { pub fn new(data: T) -> BlockMutex { BlockMutex { @@ -169,10 +173,40 @@ impl BlockMutex { } pub async fn lock(&self) -> BlockMutexGuard { - let event = current_running_event().expect("Using BlockMutex outside event"); - Condition::new(self.unlocked.clone(), event).await; + while self + .unlocked + .compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed) + .is_err() + { + let event = current_running_event().expect("Using BlockMutex outside event"); + Condition::new(self.unlocked.clone(), event).await; + } + + BlockMutexGuard { + mutex: self, + data: self.data.get(), + } + } + + pub fn try_lock(&self) -> Result, BlockMutexError> { + match self + .unlocked + .compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed) + { + Ok(_) => Ok(BlockMutexGuard { + mutex: self, + data: self.data.get(), + }), + Err(_) => Err(BlockMutexError::WouldBlock), + } + } - self.unlocked.store(false, Ordering::Relaxed); + pub fn spin(&self) -> BlockMutexGuard { + while self + .unlocked + .compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed) + .is_err() + {} BlockMutexGuard { mutex: self,