From 1bbd71d7d8fa1847ba2ab43979bb942639905f61 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Mon, 19 Sep 2016 17:19:49 -0600 Subject: [PATCH] Move PS/2 driver to userspace --- io/Cargo.toml | 3 + io/src/io.rs | 67 ++++++++++++ io/src/lib.rs | 14 +++ io/src/mmio.rs | 31 ++++++ io/src/pio.rs | 89 ++++++++++++++++ ps2d/Cargo.toml | 7 +- ps2d/src/controller.rs | 237 +++++++++++++++++++++++++++++++++++++++++ ps2d/src/keyboard.rs | 34 ++++++ ps2d/src/main.rs | 71 ++++-------- ps2d/src/mouse.rs | 73 +++++++++++++ 10 files changed, 571 insertions(+), 55 deletions(-) create mode 100644 io/Cargo.toml create mode 100644 io/src/io.rs create mode 100644 io/src/lib.rs create mode 100644 io/src/mmio.rs create mode 100644 io/src/pio.rs create mode 100644 ps2d/src/controller.rs create mode 100644 ps2d/src/keyboard.rs create mode 100644 ps2d/src/mouse.rs diff --git a/io/Cargo.toml b/io/Cargo.toml new file mode 100644 index 0000000000..b0ee40b297 --- /dev/null +++ b/io/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "io" +version = "0.1.0" diff --git a/io/src/io.rs b/io/src/io.rs new file mode 100644 index 0000000000..fb866b581b --- /dev/null +++ b/io/src/io.rs @@ -0,0 +1,67 @@ +use core::cmp::PartialEq; +use core::ops::{BitAnd, BitOr, Not}; + +pub trait Io { + type Value: Copy + PartialEq + BitAnd + BitOr + Not; + + fn read(&self) -> Self::Value; + fn write(&mut self, value: Self::Value); + + #[inline(always)] + fn readf(&self, flags: Self::Value) -> bool { + (self.read() & flags) as Self::Value == flags + } + + #[inline(always)] + fn writef(&mut self, flags: Self::Value, value: bool) { + let tmp: Self::Value = match value { + true => self.read() | flags, + false => self.read() & !flags, + }; + self.write(tmp); + } +} + +pub struct ReadOnly { + inner: I +} + +impl ReadOnly { + pub const fn new(inner: I) -> ReadOnly { + ReadOnly { + inner: inner + } + } + + #[inline(always)] + pub fn read(&self) -> I::Value { + self.inner.read() + } + + #[inline(always)] + pub fn readf(&self, flags: I::Value) -> bool { + self.inner.readf(flags) + } +} + +pub struct WriteOnly { + inner: I +} + +impl WriteOnly { + pub const fn new(inner: I) -> WriteOnly { + WriteOnly { + inner: inner + } + } + + #[inline(always)] + pub fn write(&mut self, value: I::Value) { + self.inner.write(value) + } + + #[inline(always)] + pub fn writef(&mut self, flags: I::Value, value: bool) { + self.inner.writef(flags, value) + } +} diff --git a/io/src/lib.rs b/io/src/lib.rs new file mode 100644 index 0000000000..22f8eb72c0 --- /dev/null +++ b/io/src/lib.rs @@ -0,0 +1,14 @@ +//! I/O functions + +#![feature(asm)] +#![feature(const_fn)] +#![feature(core_intrinsics)] +#![no_std] + +pub use self::io::*; +pub use self::mmio::*; +pub use self::pio::*; + +mod io; +mod mmio; +mod pio; diff --git a/io/src/mmio.rs b/io/src/mmio.rs new file mode 100644 index 0000000000..1a1d1996d1 --- /dev/null +++ b/io/src/mmio.rs @@ -0,0 +1,31 @@ +use core::intrinsics::{volatile_load, volatile_store}; +use core::mem::uninitialized; +use core::ops::{BitAnd, BitOr, Not}; + +use super::io::Io; + +#[repr(packed)] +pub struct Mmio { + value: T, +} + +impl Mmio { + /// Create a new Mmio without initializing + pub fn new() -> Self { + Mmio { + value: unsafe { uninitialized() } + } + } +} + +impl Io for Mmio where T: Copy + PartialEq + BitAnd + BitOr + Not { + type Value = T; + + fn read(&self) -> T { + unsafe { volatile_load(&self.value) } + } + + fn write(&mut self, value: T) { + unsafe { volatile_store(&mut self.value, value) }; + } +} diff --git a/io/src/pio.rs b/io/src/pio.rs new file mode 100644 index 0000000000..91ae310b62 --- /dev/null +++ b/io/src/pio.rs @@ -0,0 +1,89 @@ +use core::marker::PhantomData; + +use super::io::Io; + +/// Generic PIO +#[derive(Copy, Clone)] +pub struct Pio { + port: u16, + value: PhantomData, +} + +impl Pio { + /// Create a PIO from a given port + pub const fn new(port: u16) -> Self { + Pio:: { + port: port, + value: PhantomData, + } + } +} + +/// Read/Write for byte PIO +impl Io for Pio { + type Value = u8; + + /// Read + #[inline(always)] + fn read(&self) -> u8 { + let value: u8; + unsafe { + asm!("in $0, $1" : "={al}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + #[inline(always)] + fn write(&mut self, value: u8) { + unsafe { + asm!("out $1, $0" : : "{al}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} + +/// Read/Write for word PIO +impl Io for Pio { + type Value = u16; + + /// Read + #[inline(always)] + fn read(&self) -> u16 { + let value: u16; + unsafe { + asm!("in $0, $1" : "={ax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + #[inline(always)] + fn write(&mut self, value: u16) { + unsafe { + asm!("out $1, $0" : : "{ax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} + +/// Read/Write for doubleword PIO +impl Io for Pio { + type Value = u32; + + /// Read + #[inline(always)] + fn read(&self) -> u32 { + let value: u32; + unsafe { + asm!("in $0, $1" : "={eax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + value + } + + /// Write + #[inline(always)] + fn write(&mut self, value: u32) { + unsafe { + asm!("out $1, $0" : : "{eax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile"); + } + } +} diff --git a/ps2d/Cargo.toml b/ps2d/Cargo.toml index f49dee1313..665e3c19c8 100644 --- a/ps2d/Cargo.toml +++ b/ps2d/Cargo.toml @@ -2,5 +2,8 @@ name = "ps2d" version = "0.1.0" -[dependencies.syscall] -path = "../../syscall/" +[dependencies] +bitflags = "*" +io = { path = "../io/" } +spin = "*" +syscall = { path = "../../syscall/" } diff --git a/ps2d/src/controller.rs b/ps2d/src/controller.rs new file mode 100644 index 0000000000..8355dba7bc --- /dev/null +++ b/ps2d/src/controller.rs @@ -0,0 +1,237 @@ +use io::{Io, Pio, ReadOnly, WriteOnly}; + +pub unsafe fn init() { + Ps2::new().init(); +} + +bitflags! { + flags StatusFlags: u8 { + const OUTPUT_FULL = 1, + const INPUT_FULL = 1 << 1, + const SYSTEM = 1 << 2, + const COMMAND = 1 << 3, + // Chipset specific + const KEYBOARD_LOCK = 1 << 4, + // Chipset specific + const SECOND_OUTPUT_FULL = 1 << 5, + const TIME_OUT = 1 << 6, + const PARITY = 1 << 7 + } +} + +bitflags! { + flags ConfigFlags: u8 { + const FIRST_INTERRUPT = 1, + const SECOND_INTERRUPT = 1 << 1, + const POST_PASSED = 1 << 2, + // 1 << 3 should be zero + const CONFIG_RESERVED_3 = 1 << 3, + const FIRST_DISABLED = 1 << 4, + const SECOND_DISABLED = 1 << 5, + const FIRST_TRANSLATE = 1 << 6, + // 1 << 7 should be zero + const CONFIG_RESERVED_7 = 1 << 7, + } +} + +#[repr(u8)] +#[allow(dead_code)] +enum Command { + ReadConfig = 0x20, + WriteConfig = 0x60, + DisableSecond = 0xA7, + EnableSecond = 0xA8, + TestSecond = 0xA9, + TestController = 0xAA, + TestFirst = 0xAB, + Diagnostic = 0xAC, + DisableFirst = 0xAD, + EnableFirst = 0xAE, + WriteSecond = 0xD4 +} + +#[repr(u8)] +#[allow(dead_code)] +enum KeyboardCommand { + EnableReporting = 0xF4, + SetDefaults = 0xF6, + Reset = 0xFF +} + +#[repr(u8)] +enum KeyboardCommandData { + ScancodeSet = 0xF0 +} + +#[repr(u8)] +#[allow(dead_code)] +enum MouseCommand { + GetDeviceId = 0xF2, + EnableReporting = 0xF4, + SetDefaults = 0xF6, + Reset = 0xFF +} + +#[repr(u8)] +enum MouseCommandData { + SetSampleRate = 0xF3, +} + +pub struct Ps2 { + data: Pio, + status: ReadOnly>, + command: WriteOnly> +} + +impl Ps2 { + pub fn new() -> Self { + Ps2 { + data: Pio::new(0x60), + status: ReadOnly::new(Pio::new(0x64)), + command: WriteOnly::new(Pio::new(0x64)), + } + } + + fn status(&mut self) -> StatusFlags { + StatusFlags::from_bits_truncate(self.status.read()) + } + + fn wait_write(&mut self) { + while self.status().contains(INPUT_FULL) {} + } + + fn wait_read(&mut self) { + while ! self.status().contains(OUTPUT_FULL) {} + } + + fn flush_read(&mut self) { + while self.status().contains(OUTPUT_FULL) { + print!("FLUSH: {:X}\n", self.data.read()); + } + } + + fn command(&mut self, command: Command) { + self.wait_write(); + self.command.write(command as u8); + } + + fn read(&mut self) -> u8 { + self.wait_read(); + self.data.read() + } + + fn write(&mut self, data: u8) { + self.wait_write(); + self.data.write(data); + } + + fn config(&mut self) -> ConfigFlags { + self.command(Command::ReadConfig); + ConfigFlags::from_bits_truncate(self.read()) + } + + fn set_config(&mut self, config: ConfigFlags) { + self.command(Command::WriteConfig); + self.write(config.bits()); + } + + fn keyboard_command(&mut self, command: KeyboardCommand) -> u8 { + self.write(command as u8); + self.read() + } + + fn keyboard_command_data(&mut self, command: KeyboardCommandData, data: u8) -> u8 { + self.write(command as u8); + assert_eq!(self.read(), 0xFA); + self.write(data as u8); + self.read() + } + + fn mouse_command(&mut self, command: MouseCommand) -> u8 { + self.command(Command::WriteSecond); + self.write(command as u8); + self.read() + } + + fn mouse_command_data(&mut self, command: MouseCommandData, data: u8) -> u8 { + self.command(Command::WriteSecond); + self.write(command as u8); + assert_eq!(self.read(), 0xFA); + self.command(Command::WriteSecond); + self.write(data as u8); + self.read() + } + + pub fn init(&mut self) -> bool { + // Disable devices + self.command(Command::DisableFirst); + self.command(Command::DisableSecond); + + // Clear remaining data + self.flush_read(); + + // Disable clocks, disable interrupts, and disable translate + { + let mut config = self.config(); + config.insert(FIRST_DISABLED); + config.insert(SECOND_DISABLED); + config.remove(FIRST_TRANSLATE); + config.remove(FIRST_INTERRUPT); + config.remove(SECOND_INTERRUPT); + self.set_config(config); + } + + // Perform the self test + self.command(Command::TestController); + assert_eq!(self.read(), 0x55); + + // Enable devices + self.command(Command::EnableFirst); + self.command(Command::EnableSecond); + + // Reset keyboard + assert_eq!(self.keyboard_command(KeyboardCommand::Reset), 0xFA); + assert_eq!(self.read(), 0xAA); + self.flush_read(); + + // Set scancode set to 2 + assert_eq!(self.keyboard_command_data(KeyboardCommandData::ScancodeSet, 2), 0xFA); + + // Reset mouse and set up scroll + // TODO: Check for ack + assert_eq!(self.mouse_command(MouseCommand::Reset), 0xFA); + assert_eq!(self.read(), 0xAA); + assert_eq!(self.read(), 0x00); + self.flush_read(); + + // Enable extra packet on mouse + assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 200), 0xFA); + assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 100), 0xFA); + assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 80), 0xFA); + assert_eq!(self.mouse_command(MouseCommand::GetDeviceId), 0xFA); + let mouse_id = self.read(); + let mouse_extra = mouse_id == 3; + + // Set sample rate to maximum + assert_eq!(self.mouse_command_data(MouseCommandData::SetSampleRate, 200), 0xFA); + + // Enable data reporting + assert_eq!(self.keyboard_command(KeyboardCommand::EnableReporting), 0xFA); + assert_eq!(self.mouse_command(MouseCommand::EnableReporting), 0xFA); + + // Enable clocks and interrupts + { + let mut config = self.config(); + config.remove(FIRST_DISABLED); + config.remove(SECOND_DISABLED); + config.insert(FIRST_TRANSLATE); + config.insert(FIRST_INTERRUPT); + config.insert(SECOND_INTERRUPT); + self.set_config(config); + } + + self.flush_read(); + + mouse_extra + } +} diff --git a/ps2d/src/keyboard.rs b/ps2d/src/keyboard.rs new file mode 100644 index 0000000000..109819a725 --- /dev/null +++ b/ps2d/src/keyboard.rs @@ -0,0 +1,34 @@ +use std::fs::File; +use std::io::{Read, Write}; +use std::mem; +use std::thread; + +use keymap; + +pub fn keyboard() { + let mut file = File::open("irq:1").expect("ps2d: failed to open irq:1"); + + loop { + let mut irqs = [0; 8]; + if file.read(&mut irqs).expect("ps2d: failed to read irq:1") >= mem::size_of::() { + let data: u8; + unsafe { + asm!("in al, dx" : "={al}"(data) : "{dx}"(0x60) : : "intel", "volatile"); + } + + let (scancode, pressed) = if data >= 0x80 { + (data - 0x80, false) + } else { + (data, true) + }; + + if pressed { + print!("{}", keymap::get_char(scancode)); + } + + file.write(&irqs).expect("ps2d: failed to write irq:1"); + } else { + thread::yield_now(); + } + } +} diff --git a/ps2d/src/main.rs b/ps2d/src/main.rs index f5818a6b7a..4a3888eab8 100644 --- a/ps2d/src/main.rs +++ b/ps2d/src/main.rs @@ -1,77 +1,42 @@ #![feature(asm)] +#[macro_use] +extern crate bitflags; +extern crate io; extern crate syscall; -use std::fs::File; -use std::io::{Read, Write}; -use std::mem; use std::thread; use syscall::iopl; +mod controller; +mod keyboard; mod keymap; - -fn keyboard() { - let mut file = File::open("irq:1").expect("pskbd: failed to open irq:1"); - - loop { - let mut irqs = [0; 8]; - if file.read(&mut irqs).expect("pskbd: failed to read irq:1") >= mem::size_of::() { - let data: u8; - unsafe { - asm!("in al, dx" : "={al}"(data) : "{dx}"(0x60) : : "intel", "volatile"); - } - - let (scancode, pressed) = if data >= 0x80 { - (data - 0x80, false) - } else { - (data, true) - }; - println!("pskbd: IRQ {}: {:X}: {:X}: {}: {}", unsafe { *(irqs.as_ptr() as *const usize) }, data, scancode, keymap::get_char(scancode), pressed); - - file.write(&irqs).expect("pskbd: failed to write irq:1"); - } else { - thread::yield_now(); - } - } -} - -fn mouse() { - let mut file = File::open("irq:12").expect("psmsd: failed to open irq:12"); - - loop { - let mut irqs = [0; 8]; - if file.read(&mut irqs).expect("psmsd: failed to read irq:12") >= mem::size_of::() { - let data: u8; - unsafe { - asm!("in al, dx" : "={al}"(data) : "{dx}"(0x60) : : "intel", "volatile"); - } - - println!("psmsd: IRQ {}: {:X}", unsafe { *(irqs.as_ptr() as *const usize) }, data); - - file.write(&irqs).expect("psmsd: failed to write irq:12"); - } else { - thread::yield_now(); - } - } -} +mod mouse; fn main() { + unsafe { + iopl(3).expect("ps2d: failed to get I/O permission"); + asm!("cli" :::: "intel", "volatile"); + } + + let extra_packet = controller::Ps2::new().init(); + thread::spawn(|| { unsafe { - iopl(3).expect("pskbd: failed to get I/O permission"); + iopl(3).expect("ps2d: failed to get I/O permission"); asm!("cli" :::: "intel", "volatile"); } - keyboard(); + keyboard::keyboard(); }); - thread::spawn(|| { + thread::spawn(move || { unsafe { - iopl(3).expect("psmsd: failed to get I/O permission"); + iopl(3).expect("ps2d: failed to get I/O permission"); asm!("cli" :::: "intel", "volatile"); } - mouse(); + mouse::mouse(extra_packet); }); } diff --git a/ps2d/src/mouse.rs b/ps2d/src/mouse.rs new file mode 100644 index 0000000000..f273de9670 --- /dev/null +++ b/ps2d/src/mouse.rs @@ -0,0 +1,73 @@ +use std::fs::File; +use std::io::{Read, Write}; +use std::mem; +use std::thread; + +bitflags! { + flags MousePacketFlags: u8 { + const LEFT_BUTTON = 1, + const RIGHT_BUTTON = 1 << 1, + const MIDDLE_BUTTON = 1 << 2, + const ALWAYS_ON = 1 << 3, + const X_SIGN = 1 << 4, + const Y_SIGN = 1 << 5, + const X_OVERFLOW = 1 << 6, + const Y_OVERFLOW = 1 << 7 + } +} + +pub fn mouse(extra_packet: bool) { + let mut file = File::open("irq:12").expect("ps2d: failed to open irq:12"); + + let mut packets = [0; 4]; + let mut packet_i = 0; + loop { + let mut irqs = [0; 8]; + if file.read(&mut irqs).expect("ps2d: failed to read irq:12") >= mem::size_of::() { + let data: u8; + unsafe { + asm!("in al, dx" : "={al}"(data) : "{dx}"(0x60) : : "intel", "volatile"); + } + + packets[packet_i] = data; + packet_i += 1; + + let flags = MousePacketFlags::from_bits_truncate(packets[0]); + if ! flags.contains(ALWAYS_ON) { + println!("MOUSE MISALIGN {:X}", packets[0]); + + packets = [0; 4]; + packet_i = 0; + } else if packet_i >= packets.len() || (!extra_packet && packet_i >= 3) { + if ! flags.contains(X_OVERFLOW) && ! flags.contains(Y_OVERFLOW) { + let mut dx = packets[1] as isize; + if flags.contains(X_SIGN) { + dx -= 0x100; + } + + let mut dy = packets[2] as isize; + if flags.contains(Y_SIGN) { + dy -= 0x100; + } + + let extra = if extra_packet { + packets[3] + } else { + 0 + }; + + print!("ps2d: IRQ {:?}, {}, {}, {}\n", flags, dx, dy, extra); + } else { + println!("ps2d: overflow {:X} {:X} {:X} {:X}", packets[0], packets[1], packets[2], packets[3]); + } + + packets = [0; 4]; + packet_i = 0; + } + + file.write(&irqs).expect("ps2d: failed to write irq:12"); + } else { + thread::yield_now(); + } + } +}