diff --git a/Cargo.lock b/Cargo.lock index d261da0..2b5e048 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,11 @@ dependencies = [ "toml 0.5.9 (git+https://git.ablecorp.us/theoddgarlic/toml-rs)", ] +[[package]] +name = "cpuio" +version = "0.3.2" +source = "git+https://git.ablecorp.us/ondra05/cpuio.git#093cc103101b4ba4abd02d77c884113a376cdc64" + [[package]] name = "cryptography" version = "0.1.1" @@ -239,6 +244,15 @@ dependencies = [ name = "ps2_keyboard" version = "0.1.0" +[[package]] +name = "ps2_mouse" +version = "0.1.0" +dependencies = [ + "bitflags", + "cpuio", + "log", +] + [[package]] name = "quote" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index ad2744b..33b291e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ "drivers/graphics/vgable", "drivers/keyboards/ps2_keyboard", - + "drivers/mice/ps2_mouse", "libraries/able_graphics_library", "libraries/clparse", "libraries/cryptography", diff --git a/drivers/mice/ps2_mouse/.cargo/config.toml b/drivers/mice/ps2_mouse/.cargo/config.toml new file mode 100644 index 0000000..f4e8c00 --- /dev/null +++ b/drivers/mice/ps2_mouse/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/drivers/mice/ps2_mouse/Cargo.toml b/drivers/mice/ps2_mouse/Cargo.toml new file mode 100644 index 0000000..fafc27b --- /dev/null +++ b/drivers/mice/ps2_mouse/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ps2_mouse" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitflags = "1.3" + +cpuio = { git = "https://git.ablecorp.us/ondra05/cpuio.git" } + +[dependencies.log] +version = "0.4.17" +default-features = false diff --git a/drivers/mice/ps2_mouse/src/main.rs b/drivers/mice/ps2_mouse/src/main.rs new file mode 100644 index 0000000..c174b7c --- /dev/null +++ b/drivers/mice/ps2_mouse/src/main.rs @@ -0,0 +1,308 @@ +#![no_std] +#![no_main] +use bitflags::bitflags; +use cpuio::Port; +use log::debug; + +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, + data_port: Port, + current_packet: u8, + current_state: MouseState, + completed_state: MouseState, + on_complete: Option, +} + +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: unsafe { Port::new(ADDRESS_PORT_ADDRESS) }, + data_port: unsafe { 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 { + // 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") + } +} + +#[no_mangle] +fn start() { + // Simple driver +} diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..07ade69 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file