Add i8042 mouse

This commit is contained in:
Cautreoxit 2025-11-01 11:51:22 +08:00 committed by Tate, Hongliang Tian
parent aa6f248d03
commit 44768021a1
3 changed files with 494 additions and 2 deletions

View File

@ -83,7 +83,7 @@ pub(super) fn init() -> Result<(), I8042ControllerError> {
}
}
// Enable the first PS/2 port.
// Enable the first PS/2 port (keyboard).
controller.wait_and_send_command(Command::EnableFirstPort)?;
if let Err(err) = super::keyboard::init(&mut controller) {
log::warn!("i8042 keyboard initialization failed: {:?}", err);
@ -96,7 +96,17 @@ pub(super) fn init() -> Result<(), I8042ControllerError> {
);
}
// TODO: Add a mouse driver and enable the second PS/2 port (if it exists).
// Enable the second PS/2 port (mouse) if it exists.
if has_second_port {
controller.wait_and_send_command(Command::EnableSecondPort)?;
if let Err(err) = super::mouse::init(&mut controller) {
log::warn!("i8042 mouse initialization failed: {:?}", err);
controller.wait_and_send_command(Command::DisableSecondPort)?;
} else {
config.remove(Configuration::SECOND_PORT_CLOCK_DISABLED);
config.insert(Configuration::SECOND_PORT_INTERRUPT_ENABLED);
}
}
I8042_CONTROLLER.call_once(|| SpinLock::new(controller));
let mut controller = I8042_CONTROLLER.get().unwrap().lock();
@ -188,6 +198,11 @@ impl I8042Controller {
Err(I8042ControllerError::OutputBusy)
}
pub(super) fn write_to_second_port(&mut self, data: u8) -> Result<(), I8042ControllerError> {
self.wait_and_send_command(Command::WriteToSecondPort)?;
self.wait_and_send_data(data)
}
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);
@ -257,6 +272,7 @@ enum Command {
TestFirstPort = 0xAB,
DisableFirstPort = 0xAD,
EnableFirstPort = 0xAE,
WriteToSecondPort = 0xD4,
}
bitflags! {

View File

@ -2,6 +2,7 @@
mod controller;
mod keyboard;
mod mouse;
pub(crate) fn init() {
if let Err(err) = controller::init() {

View File

@ -0,0 +1,475 @@
// SPDX-License-Identifier: MPL-2.0
//! The i8042 mouse driver.
use alloc::{string::String, sync::Arc, vec::Vec};
use aster_input::{
event_type_codes::{KeyCode, KeyStatus, RelCode, SynEvent},
input_dev::{InputCapability, InputDevice, InputEvent, InputId, RegisteredInputDevice},
};
use ostd::{
arch::{
irq::{MappedIrqLine, IRQ_CHIP},
trap::TrapFrame,
},
irq::IrqLine,
sync::{LocalIrqDisabled, SpinLock},
};
use spin::Once;
use super::controller::{
I8042Controller, I8042ControllerError, I8042_CONTROLLER, PS2_ACK, PS2_BAT_OK, PS2_CMD_RESET,
};
use crate::alloc::string::ToString;
/// IRQ line for i8042 mouse.
static IRQ_LINE: Once<MappedIrqLine> = Once::new();
/// Registered device instance for event submission.
static REGISTERED_DEVICE: Once<RegisteredInputDevice> = Once::new();
/// ISA interrupt number for i8042 mouse.
const ISA_INTR_NUM: u8 = 12;
/// Mouse packet state machine.
static PACKET_STATE: SpinLock<PacketState, LocalIrqDisabled> = SpinLock::new(PacketState::new());
pub(super) fn init(controller: &mut I8042Controller) -> Result<(), I8042ControllerError> {
// Reset mouse device by sending `PS2_CMD_RESET` to the second PS/2 port.
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_and_recv_data()? != PS2_ACK {
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(PS2_BAT_OK) {
return Err(I8042ControllerError::DeviceResetFailed);
}
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);
// Determine the mouse's type.
// Reference: <https://wiki.osdev.org/Mouse_Input>
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() {
// Query the device ID again.
let new_device_id = init_ctx.get_device_id()?;
log::info!("PS/2 mouse upgraded device ID: 0x{:02X}", new_device_id);
mouse_type =
MouseType::from_device_id(new_device_id).ok_or(I8042ControllerError::DeviceUnknown)?;
}
// Enable data reporting.
init_ctx.enable_data_reporting()?;
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_mouse_input);
IRQ_LINE.call_once(|| irq_line);
// Create and register the i8042 mouse device.
let mouse_device = Arc::new(I8042Mouse::new());
let registered_device = aster_input::register_device(mouse_device);
REGISTERED_DEVICE.call_once(|| registered_device);
// Configure the mouse's type based on its device ID.
let mut state = PACKET_STATE.lock();
state.set_mouse_type(mouse_type);
Ok(())
}
struct InitCtx<'a>(&'a mut I8042Controller);
impl InitCtx<'_> {
fn get_device_id(&mut self) -> Result<u8, I8042ControllerError> {
let mut buf = [0u8; cmd::GetDeviceId::RES_LEN];
self.command::<cmd::GetDeviceId>(&[], &mut buf)?;
Ok(buf[0])
}
fn enable_intellimouse(&mut self) -> Result<(), I8042ControllerError> {
const SAMPLE_RATE_200: u8 = 200;
const SAMPLE_RATE_100: u8 = 100;
const SAMPLE_RATE_80: u8 = 80;
// Set the sample rate to 200, then to 100, and finally to 80.
// Reference: <https://wiki.osdev.org/Mouse_Input#Init/Detection_Command_Sequences>
self.set_sample_rate(SAMPLE_RATE_200)?;
self.set_sample_rate(SAMPLE_RATE_100)?;
self.set_sample_rate(SAMPLE_RATE_80)?;
Ok(())
}
fn set_sample_rate(&mut self, rate: u8) -> Result<(), I8042ControllerError> {
let mut buf = [0u8; cmd::SetSampleRate::RES_LEN];
self.command::<cmd::SetSampleRate>(&[rate], &mut buf)?;
Ok(())
}
fn enable_data_reporting(&mut self) -> Result<(), I8042ControllerError> {
let mut buf = [0u8; cmd::EnableDataReporting::RES_LEN];
self.command::<cmd::EnableDataReporting>(&[], &mut buf)?;
Ok(())
}
fn command<C: MouseCommand>(
&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_and_recv_data()? != PS2_ACK {
return Err(I8042ControllerError::DeviceResetFailed);
}
for &arg in args {
self.0.write_to_second_port(arg)?;
if self.0.wait_and_recv_data()? != 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;
}
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;
}
)*
};
}
define_commands! {
GetDeviceId, 0xF2, fn([u8; 0]) -> [u8; 1];
SetSampleRate, 0xF3, fn([u8; 1]) -> [u8; 0];
EnableDataReporting, 0xF4, fn([u8; 0]) -> [u8; 0];
}
}
#[derive(Debug)]
struct I8042Mouse {
name: String,
phys: String,
uniq: String,
id: InputId,
capability: InputCapability,
}
impl I8042Mouse {
fn new() -> Self {
let mut capability = InputCapability::new();
// Mouse supports key events and relative movement events.
capability.set_supported_event_type(aster_input::event_type_codes::EventTypes::KEY);
capability.set_supported_event_type(aster_input::event_type_codes::EventTypes::REL);
capability.set_supported_event_type(aster_input::event_type_codes::EventTypes::SYN);
// Add mouse buttons.
capability.set_supported_key(KeyCode::BtnLeft);
capability.set_supported_key(KeyCode::BtnRight);
capability.set_supported_key(KeyCode::BtnMiddle);
// Add relative axes for movement.
capability.set_supported_relative_axis(RelCode::X);
capability.set_supported_relative_axis(RelCode::Y);
capability.set_supported_relative_axis(RelCode::Wheel);
Self {
// Standard name for i8042 PS/2 mouse devices.
name: "i8042_mouse".to_string(),
// Physical path describing the device's connection topology
// isa0060: ISA bus port 0x60 (i8042 data port)
// serio1: Serial I/O device 1 (second PS/2 port)
// input0: Input device 0 (first mouse device on this controller)
phys: "isa0060/serio1/input0".to_string(),
// Unique identifier - empty string because traditional i8042 mice
// don't have unique hardware identifiers
uniq: "".to_string(),
// Device ID with standard values for i8042 mice
// BUS_I8042 (0x11): PS/2 interface bus type
// vendor (0x0002): Generic vendor ID for standard mice
// product (0x0001): Generic product ID for standard mice
// version (0x0001): Version 1.0 - standard PS/2 mouse protocol
id: InputId::new(InputId::BUS_I8042, 0x0002, 0x0001, 0x0001),
capability,
}
}
}
impl InputDevice for I8042Mouse {
fn name(&self) -> &str {
&self.name
}
fn phys(&self) -> &str {
&self.phys
}
fn uniq(&self) -> &str {
&self.uniq
}
fn id(&self) -> InputId {
self.id
}
fn capability(&self) -> &InputCapability {
&self.capability
}
}
fn handle_mouse_input(_trap_frame: &TrapFrame) {
let Some(controller) = I8042_CONTROLLER.get() else {
return;
};
let Some(data) = controller.lock().receive_data() else {
log::warn!("PS/2 mouse has no input data");
return;
};
let mut packet_state = PACKET_STATE.lock();
if let Some(events) = packet_state.process_byte(data) {
if let Some(registered_device) = REGISTERED_DEVICE.get() {
registered_device.submit_events(&events);
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum MouseType {
Standard,
IntelliMouse,
}
impl MouseType {
const PACKET_LEN_STANDARD: usize = 3;
const PACKET_LEN_INTELLIMOUSE: usize = 4;
const PACKET_LEN_MAX: usize = 4;
fn from_device_id(device_id: u8) -> Option<Self> {
const DEVICE_ID_STANDARD_MOUSE: u8 = 0x00;
const DEVICE_ID_INTELLIMOUSE: u8 = 0x03;
const DEVICE_ID_INTELLIMOUSE_EXPLORER: u8 = 0x04;
match device_id {
DEVICE_ID_STANDARD_MOUSE => Some(MouseType::Standard),
DEVICE_ID_INTELLIMOUSE | DEVICE_ID_INTELLIMOUSE_EXPLORER => {
Some(MouseType::IntelliMouse)
}
_ => None,
}
}
fn packet_len(&self) -> usize {
match self {
MouseType::Standard => Self::PACKET_LEN_STANDARD,
MouseType::IntelliMouse => Self::PACKET_LEN_INTELLIMOUSE,
}
}
}
/// Mouse packet state machine for handling 3-byte or 4-byte PS/2 mouse packets.
struct PacketState {
packet: [u8; MouseType::PACKET_LEN_MAX],
byte_index: usize,
mouse_type: MouseType,
prev_left: bool,
prev_right: bool,
prev_middle: bool,
}
impl PacketState {
const fn new() -> Self {
Self {
packet: [0; MouseType::PACKET_LEN_MAX],
byte_index: 0,
mouse_type: MouseType::Standard,
prev_left: false,
prev_right: false,
prev_middle: false,
}
}
fn set_mouse_type(&mut self, mouse_type: MouseType) {
self.mouse_type = mouse_type;
self.reset();
}
/// Processes a mouse data byte and returns input events if a complete packet is ready.
fn process_byte(&mut self, data: u8) -> Option<Vec<InputEvent>> {
const PACKET_SYNC_MASK: u8 = 0x08;
// The first byte must have bit 3 set (always 1 in PS/2 mouse packets).
if self.byte_index == 0 && (data & PACKET_SYNC_MASK) == 0 {
return None;
}
// Collect the byte into the packet.
self.packet[self.byte_index] = data;
self.byte_index += 1;
if self.byte_index < self.mouse_type.packet_len() {
return None;
}
// A complete packet has been received.
let events = self.parse_packet();
self.reset();
Some(events)
}
/// Parses a complete PS/2 mouse packet and returns input events.
///
/// Reference: <https://wiki.osdev.org/Mouse_Input#Format_of_First_3_Packet_Bytes>.
fn parse_packet(&mut self) -> Vec<InputEvent> {
const BUTTON_LEFT_MASK: u8 = 0x01;
const BUTTON_RIGHT_MASK: u8 = 0x02;
const BUTTON_MIDDLE_MASK: u8 = 0x04;
const X_SIGN_MASK: u8 = 0x10;
const Y_SIGN_MASK: u8 = 0x20;
const OVERFLOWED_MASK: u8 = 0xC0;
const INDEX_STATUS: usize = 0;
const INDEX_X: usize = 1;
const INDEX_Y: usize = 2;
const INDEX_WHEEL: usize = 3;
// Currently, this method can generate at most 7 events. Don't forget to update this when
// modifying the logic below!
let mut events = Vec::with_capacity(7);
let status = self.packet[INDEX_STATUS];
if (status & OVERFLOWED_MASK) != 0 {
log::warn!("PS/2 mouse packet overflowed");
return events;
}
let x_delta = add_sign_bit(self.packet[INDEX_X], (status & X_SIGN_MASK) != 0);
let y_delta = add_sign_bit(self.packet[INDEX_Y], (status & Y_SIGN_MASK) != 0);
let left_button = (status & BUTTON_LEFT_MASK) != 0;
let right_button = (status & BUTTON_RIGHT_MASK) != 0;
let middle_button = (status & BUTTON_MIDDLE_MASK) != 0;
let prev_left = self.prev_left;
let prev_right = self.prev_right;
let prev_middle = self.prev_middle;
self.prev_left = left_button;
self.prev_right = right_button;
self.prev_middle = middle_button;
if left_button != prev_left {
events.push(InputEvent::from_key_and_status(
KeyCode::BtnLeft,
if left_button {
KeyStatus::Pressed
} else {
KeyStatus::Released
},
));
}
if right_button != prev_right {
events.push(InputEvent::from_key_and_status(
KeyCode::BtnRight,
if right_button {
KeyStatus::Pressed
} else {
KeyStatus::Released
},
));
}
if middle_button != prev_middle {
events.push(InputEvent::from_key_and_status(
KeyCode::BtnMiddle,
if middle_button {
KeyStatus::Pressed
} else {
KeyStatus::Released
},
));
}
if x_delta != 0 {
events.push(InputEvent::from_relative_move(RelCode::X, x_delta));
}
if y_delta != 0 {
events.push(InputEvent::from_relative_move(RelCode::Y, -y_delta));
}
if self.mouse_type == MouseType::IntelliMouse {
let z_delta = self.packet[INDEX_WHEEL] as i8;
if z_delta != 0 {
events.push(InputEvent::from_relative_move(
RelCode::Wheel,
-(z_delta as i32),
));
}
}
// Add a sync event to indicate end of this input report.
if !events.is_empty() {
events.push(InputEvent::from_sync_event(SynEvent::Report));
}
events
}
/// Resets the packet state machine to wait for the next packet.
fn reset(&mut self) {
self.byte_index = 0;
}
}
/// Extends a byte to a signed 32-bit integer using the sign bit from the status byte.
fn add_sign_bit(byte: u8, sign_bit: bool) -> i32 {
let mut byte = byte as u32;
if sign_bit {
byte |= 0xFFFFFF00;
}
byte as i32
}