akern-gkgoat-fork/ableos/src/ps2_mouse.rs

301 lines
8.6 KiB
Rust

use bitflags::bitflags;
use x86_64::instructions::port::Port;
const ADDRESS_PORT_ADDRESS: u16 = 0x64;
const DATA_PORT_ADDRESS: u16 = 0x60;
const GET_STATUS_BYTE: u8 = 0x20;
const SET_STATUS_BYTE: u8 = 0x60;
const DISABLE_FIRST: u8 = 0xAD;
const DISABLE_SECOND: u8 = 0xA7;
const ENABLE_FIRST: u8 = 0xAE;
const ENABLE_SECOND: u8 = 0xA8;
bitflags! {
/// Represents the flags currently set for the mouse.
#[derive(Default)]
pub struct MouseFlags: u8 {
/// Whether or not the left mouse button is pressed.
const LEFT_BUTTON = 0b0000_0001;
/// Whether or not the right mouse button is pressed.
const RIGHT_BUTTON = 0b0000_0010;
/// Whether or not the middle mouse button is pressed.
const MIDDLE_BUTTON = 0b0000_0100;
/// Whether or not the packet is valid or not.
const ALWAYS_ONE = 0b0000_1000;
/// Whether or not the x delta is negative.
const X_SIGN = 0b0001_0000;
/// Whether or not the y delta is negative.
const Y_SIGN = 0b0010_0000;
/// Whether or not the x delta overflowed.
const X_OVERFLOW = 0b0100_0000;
/// Whether or not the y delta overflowed.
const Y_OVERFLOW = 0b1000_0000;
}
}
#[repr(u8)]
enum Command {
EnablePacketStreaming = 0xF4,
SetDefaults = 0xF6,
}
/// A basic interface to interact with a PS2 mouse.
#[derive(Debug)]
pub struct Mouse {
command_port: Port<u8>,
data_port: Port<u8>,
current_packet: u8,
current_state: MouseState,
completed_state: MouseState,
on_complete: Option<fn(MouseState)>,
}
impl Default for Mouse {
fn default() -> Mouse {
Mouse::new()
}
}
/// A snapshot of the mouse flags, x delta and y delta.
#[derive(Debug, Copy, Clone, Default)]
pub struct MouseState {
flags: MouseFlags,
x: i16,
y: i16,
}
impl MouseState {
/// Returns a new `MouseState`.
pub const fn new() -> MouseState {
MouseState {
flags: MouseFlags::empty(),
x: 0,
y: 0,
}
}
/// Returns true if the left mouse button is currently down.
pub fn _left_button_down(&self) -> bool {
self.flags.contains(MouseFlags::LEFT_BUTTON)
}
/// Returns true if the left mouse button is currently up.
pub fn _left_button_up(&self) -> bool {
!self.flags.contains(MouseFlags::LEFT_BUTTON)
}
/// Returns true if the right mouse button is currently down.
pub fn _right_button_down(&self) -> bool {
self.flags.contains(MouseFlags::RIGHT_BUTTON)
}
/// Returns true if the right mouse button is currently up.
pub fn _right_button_up(&self) -> bool {
!self.flags.contains(MouseFlags::RIGHT_BUTTON)
}
/// Returns true if the x axis has moved.
pub fn _x_moved(&self) -> bool {
self.x != 0
}
/// Returns true if the y axis has moved.
pub fn _y_moved(&self) -> bool {
self.y != 0
}
/// Returns true if the x or y axis has moved.
pub fn _moved(&self) -> bool {
self._x_moved() || self._y_moved()
}
/// Returns the x delta of the mouse state.
pub fn get_x(&self) -> i16 {
self.x
}
/// Returns the y delta of the mouse state.
pub fn get_y(&self) -> i16 {
self.y
}
}
impl Mouse {
/// Creates a new `Mouse`.
pub const fn new() -> Mouse {
Mouse {
command_port: Port::new(ADDRESS_PORT_ADDRESS),
data_port: Port::new(DATA_PORT_ADDRESS),
current_packet: 0,
current_state: MouseState::new(),
completed_state: MouseState::new(),
on_complete: None,
}
}
/// Returns the last completed state of the mouse.
pub fn _get_state(&self) -> MouseState {
self.completed_state
}
// super helpful resource, albeit in C
// https://github.com/29jm/SnowflakeOS/blob/master/kernel/src/devices/ps2.c#L18
/// Attempts to initialize a `Mouse`. If successful, interrupts will be generated
/// as `PIC offset + 12`.
pub fn init(&mut self) -> Result<(), &'static str> {
// Disable both PS/2 device ports
// Even if only one is present, disabling the second is harmless
self.write_command_port(DISABLE_FIRST)?;
self.write_command_port(DISABLE_SECOND)?;
// Flush output buffer: if the controller had anything to say, ignore it
unsafe {
self.data_port.read();
}
debug!("mouse driver: writing GET_STATUS to port...");
self.write_command_port(GET_STATUS_BYTE)?;
debug!("mouse driver: reading status from port...");
let status = self.read_data_port()? | 0x02;
debug!("Got status {}", status);
// self.write_command_port(0xa8)?;
self.write_command_port(SET_STATUS_BYTE)?;
self.write_data_port(status & 0xDF)?;
self.send_command(Command::SetDefaults)?;
self.send_command(Command::EnablePacketStreaming)?;
self.write_command_port(ENABLE_FIRST)?;
self.write_command_port(ENABLE_SECOND)?;
// Some keyboards actually send a reply, flush it
unsafe {
self.data_port.read();
}
Ok(())
}
/// Attempts to process a packet.
pub fn process_packet(&mut self, packet: u8) {
match self.current_packet {
0 => {
let flags = MouseFlags::from_bits_truncate(packet);
if !flags.contains(MouseFlags::ALWAYS_ONE) {
return;
}
self.current_state.flags = flags;
}
1 => self.process_x_movement(packet),
2 => {
self.process_y_movement(packet);
self.completed_state = self.current_state;
if let Some(on_complete) = self.on_complete {
on_complete(self.completed_state);
}
}
_ => unreachable!(),
}
self.current_packet = (self.current_packet + 1) % 3;
}
/// Sets the `on_complete` function to be called when a packet is completed.
pub fn set_on_complete(&mut self, handler: fn(MouseState)) {
self.on_complete = Some(handler);
}
fn process_x_movement(&mut self, packet: u8) {
if !self.current_state.flags.contains(MouseFlags::X_OVERFLOW) {
self.current_state.x = if self.current_state.flags.contains(MouseFlags::X_SIGN) {
self.sign_extend(packet)
} else {
packet as i16
};
}
}
fn process_y_movement(&mut self, packet: u8) {
if !self.current_state.flags.contains(MouseFlags::Y_OVERFLOW) {
self.current_state.y = if self.current_state.flags.contains(MouseFlags::Y_SIGN) {
self.sign_extend(packet)
} else {
packet as i16
};
}
}
fn read_data_port(&mut self) -> Result<u8, &'static str> {
// INFO: What the fuck
debug!("owo");
self.wait_for_read()?;
// HERESY: Stop
debug!("what's this");
Ok(unsafe { self.data_port.read() })
}
fn send_command(&mut self, command: Command) -> Result<(), &'static str> {
self.write_command_port(0xD4)?;
self.write_data_port(command as u8)?;
if self.read_data_port()? != 0xFA {
return Err("mouse did not respond to the command");
}
Ok(())
}
fn sign_extend(&self, packet: u8) -> i16 {
((packet as u16) | 0xFF00) as i16
}
fn write_command_port(&mut self, value: u8) -> Result<(), &'static str> {
debug!("mouse driver: waiting for write");
self.wait_for_write()?;
unsafe {
self.command_port.write(value);
}
debug!("mouse driver: command written");
Ok(())
}
fn write_data_port(&mut self, value: u8) -> Result<(), &'static str> {
self.wait_for_write()?;
unsafe {
self.data_port.write(value);
}
Ok(())
}
fn wait_for_read(&mut self) -> Result<(), &'static str> {
let timeout = 100_000;
for _x in 0..timeout {
let value = unsafe { self.command_port.read() };
if (value & 0x1) == 0x1 {
return Ok(());
}
}
Err("wait for mouse read timeout")
}
fn wait_for_write(&mut self) -> Result<(), &'static str> {
let timeout = 100_000;
for _ in 0..timeout {
let value = unsafe { self.command_port.read() };
if (value & 0x2) == 0x0 {
return Ok(());
}
}
Err("wait for mouse write timeout")
}
}