Skip to content

Commit 0322e55

Browse files
committed
Initial commit
0 parents  commit 0322e55

File tree

3 files changed

+353
-0
lines changed

3 files changed

+353
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
Cargo.lock

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "ps2-mouse"
3+
version = "0.1.0"
4+
authors = ["Ryan Kennedy <rkennedy9064@gmail.com>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
bitflags = "1.2.1"
11+
x86_64 = "0.9.6"

src/lib.rs

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
//! This crate provides basic access to a ps2 mouse in x86 environments.
2+
3+
#![no_std]
4+
#![warn(missing_docs)]
5+
6+
use bitflags::bitflags;
7+
use x86_64::instructions::port::Port;
8+
9+
const ADDRESS_PORT_ADDRESS: u16 = 0x64;
10+
const DATA_PORT_ADDRESS: u16 = 0x60;
11+
const GET_STATUS_BYTE: u8 = 0x20;
12+
const SET_STATUS_BYTE: u8 = 0x60;
13+
14+
bitflags! {
15+
#[derive(Default)]
16+
pub struct MouseFlags: u8 {
17+
const LEFT_BUTTON = 0b0000_0001;
18+
const RIGHT_BUTTON = 0b0000_0010;
19+
const MIDDLE_BUTTON = 0b0000_0100;
20+
const ALWAYS_ONE = 0b0000_1000;
21+
const X_SIGN = 0b0001_0000;
22+
const Y_SIGN = 0b0010_0000;
23+
const X_OVERFLOW = 0b0100_0000;
24+
const Y_OVERFLOW = 0b1000_0000;
25+
}
26+
}
27+
28+
#[repr(u8)]
29+
enum Command {
30+
EnablePacketStreaming = 0xF4,
31+
SetDefaults = 0xF6,
32+
}
33+
34+
/// A basic interface to interact with a PS2 mouse.
35+
#[derive(Debug)]
36+
pub struct Mouse {
37+
command_port: Port<u8>,
38+
data_port: Port<u8>,
39+
current_packet: u8,
40+
current_state: MouseState,
41+
completed_state: MouseState,
42+
on_complete: Option<fn(MouseState)>,
43+
}
44+
45+
/// A snapshot of the mouse flags, x delta and y delta.
46+
#[derive(Debug, Copy, Clone, Default)]
47+
pub struct MouseState {
48+
flags: MouseFlags,
49+
x: i16,
50+
y: i16,
51+
}
52+
53+
impl MouseState {
54+
/// Returns true if the left mouse button is currently down.
55+
pub fn left_button_down(&self) -> bool {
56+
self.flags.contains(MouseFlags::LEFT_BUTTON)
57+
}
58+
59+
/// Returns true if the left mouse button is currently up.
60+
pub fn left_button_up(&self) -> bool {
61+
!self.flags.contains(MouseFlags::LEFT_BUTTON)
62+
}
63+
64+
/// Returns true if the right mouse button is currently down.
65+
pub fn right_button_down(&self) -> bool {
66+
self.flags.contains(MouseFlags::RIGHT_BUTTON)
67+
}
68+
69+
/// Returns true if the right mouse button is currently up.
70+
pub fn right_button_up(&self) -> bool {
71+
!self.flags.contains(MouseFlags::RIGHT_BUTTON)
72+
}
73+
74+
/// Returns true if the x axis has moved.
75+
pub fn x_moved(&self) -> bool {
76+
self.x != 0
77+
}
78+
79+
/// Returns true if the y axis has moved.
80+
pub fn y_moved(&self) -> bool {
81+
self.y != 0
82+
}
83+
84+
/// Returns true if the x or y axis has moved.
85+
pub fn moved(&self) -> bool {
86+
self.x_moved() || self.y_moved()
87+
}
88+
}
89+
90+
impl Mouse {
91+
/// Creates a new `Mouse`.
92+
pub fn new() -> Mouse {
93+
Mouse {
94+
command_port: Port::new(ADDRESS_PORT_ADDRESS),
95+
data_port: Port::new(DATA_PORT_ADDRESS),
96+
current_packet: 0,
97+
current_state: MouseState::default(),
98+
completed_state: MouseState::default(),
99+
on_complete: None,
100+
}
101+
}
102+
103+
/// Returns the last completed state of the mouse.
104+
pub fn get_state(&self) -> MouseState {
105+
self.completed_state
106+
}
107+
108+
/// Attempts to initialize a `Mouse`. If successful, interrupts will be generated
109+
/// as `PIC offset + 12`.
110+
pub fn init(&mut self) -> Result<(), &'static str> {
111+
self.write_command_port(GET_STATUS_BYTE)?;
112+
let status = self.read_data_port()? | 0x02;
113+
self.write_command_port(SET_STATUS_BYTE)?;
114+
self.write_data_port(status & 0xDF)?;
115+
self.send_command(Command::SetDefaults)?;
116+
self.send_command(Command::EnablePacketStreaming)?;
117+
Ok(())
118+
}
119+
120+
/// Attempts to process a packet.
121+
pub fn process_packet(&mut self, packet: u8) {
122+
match self.current_packet {
123+
0 => {
124+
let flags = MouseFlags::from_bits_truncate(packet);
125+
if !flags.contains(MouseFlags::ALWAYS_ONE) {
126+
return;
127+
}
128+
self.current_state.flags = flags;
129+
}
130+
1 => self.process_x_movement(packet),
131+
2 => {
132+
self.process_y_movement(packet);
133+
self.completed_state = self.current_state;
134+
if let Some(on_complete) = self.on_complete {
135+
on_complete(self.completed_state);
136+
}
137+
}
138+
_ => unreachable!(),
139+
}
140+
self.current_packet = (self.current_packet + 1) % 3;
141+
}
142+
143+
/// Sets the `on_complete` function to be called when a packet is completed.
144+
pub fn set_on_complete(&mut self, handler: fn(MouseState)) {
145+
self.on_complete = Some(handler);
146+
}
147+
148+
fn process_x_movement(&mut self, packet: u8) {
149+
if !self.current_state.flags.contains(MouseFlags::X_OVERFLOW) {
150+
self.current_state.x = if self.current_state.flags.contains(MouseFlags::X_SIGN) {
151+
self.sign_extend(packet)
152+
} else {
153+
packet as i16
154+
};
155+
}
156+
}
157+
158+
fn process_y_movement(&mut self, packet: u8) {
159+
if !self.current_state.flags.contains(MouseFlags::Y_OVERFLOW) {
160+
self.current_state.y = if self.current_state.flags.contains(MouseFlags::Y_SIGN) {
161+
self.sign_extend(packet)
162+
} else {
163+
packet as i16
164+
};
165+
}
166+
}
167+
168+
fn read_data_port(&mut self) -> Result<u8, &'static str> {
169+
self.wait_for_read()?;
170+
Ok(unsafe { self.data_port.read() })
171+
}
172+
173+
fn send_command(&mut self, command: Command) -> Result<(), &'static str> {
174+
self.write_command_port(0xD4)?;
175+
self.write_data_port(command as u8)?;
176+
if self.read_data_port()? != 0xFA {
177+
return Err("mouse did not respond to the command");
178+
}
179+
Ok(())
180+
}
181+
182+
fn sign_extend(&self, packet: u8) -> i16 {
183+
((packet as u16) | 0xFF00) as i16
184+
}
185+
186+
fn write_command_port(&mut self, value: u8) -> Result<(), &'static str> {
187+
self.wait_for_write()?;
188+
unsafe {
189+
self.command_port.write(value);
190+
}
191+
Ok(())
192+
}
193+
194+
fn write_data_port(&mut self, value: u8) -> Result<(), &'static str> {
195+
self.wait_for_write()?;
196+
unsafe {
197+
self.data_port.write(value);
198+
}
199+
Ok(())
200+
}
201+
202+
fn wait_for_read(&mut self) -> Result<(), &'static str> {
203+
let timeout = 100_000;
204+
for _ in 0..timeout {
205+
let value = unsafe { self.command_port.read() };
206+
if (value & 0x1) == 0x1 {
207+
return Ok(());
208+
}
209+
}
210+
Err("wait for mouse read timeout")
211+
}
212+
213+
fn wait_for_write(&mut self) -> Result<(), &'static str> {
214+
let timeout = 100_000;
215+
for _ in 0..timeout {
216+
let value = unsafe { self.command_port.read() };
217+
if (value & 0x2) == 0x0 {
218+
return Ok(());
219+
}
220+
}
221+
Err("wait for mouse write timeout")
222+
}
223+
}
224+
225+
#[cfg(test)]
226+
mod test {
227+
use super::*;
228+
229+
const EMPTY_PACKET: u8 = 0;
230+
const VALID_PACKET: u8 = MouseFlags::ALWAYS_ONE.bits();
231+
const NEGATIVE_PACKET: u8 =
232+
MouseFlags::ALWAYS_ONE.bits() | MouseFlags::X_SIGN.bits() | MouseFlags::Y_SIGN.bits();
233+
const NEGATIVE_PACKET_WITH_OVERFLOW: u8 = MouseFlags::ALWAYS_ONE.bits()
234+
| MouseFlags::X_SIGN.bits()
235+
| MouseFlags::Y_SIGN.bits()
236+
| MouseFlags::X_OVERFLOW.bits()
237+
| MouseFlags::Y_OVERFLOW.bits();
238+
const LEFT_MOUSE_BUTTON_DOWN_PACKET: u8 =
239+
MouseFlags::ALWAYS_ONE.bits() | MouseFlags::LEFT_BUTTON.bits();
240+
const RIGHT_MOUSE_BUTTON_DOWN_PACKET: u8 =
241+
MouseFlags::ALWAYS_ONE.bits() | MouseFlags::RIGHT_BUTTON.bits();
242+
const POSITIVE_X_PACKET: u8 = 0x5;
243+
const POSITIVE_Y_PACKET: u8 = 0x8;
244+
const NEGATIVE_X_PACKET: u8 = 0xD8;
245+
const NEGATIVE_Y_PACKET: u8 = 0xD9;
246+
247+
#[test]
248+
fn process_packets() {
249+
let mut mouse = Mouse::new();
250+
251+
mouse.process_packet(VALID_PACKET);
252+
assert_eq!(mouse.current_packet, 1);
253+
254+
mouse.process_packet(EMPTY_PACKET);
255+
assert_eq!(mouse.current_packet, 2);
256+
257+
mouse.process_packet(EMPTY_PACKET);
258+
assert_eq!(mouse.current_packet, 0);
259+
260+
let mouse_state = mouse.completed_state;
261+
assert_eq!(mouse_state.flags, MouseFlags::ALWAYS_ONE);
262+
assert_eq!(mouse_state.x, 0);
263+
assert_eq!(mouse_state.y, 0);
264+
}
265+
266+
#[test]
267+
fn always_one_bit_not_set() {
268+
let mut mouse = Mouse::new();
269+
mouse.process_packet(EMPTY_PACKET);
270+
assert_eq!(mouse.current_packet, 0);
271+
}
272+
273+
#[test]
274+
fn positive_movement() {
275+
let mut mouse = Mouse::new();
276+
mouse.process_packet(VALID_PACKET);
277+
mouse.process_packet(POSITIVE_X_PACKET);
278+
mouse.process_packet(POSITIVE_Y_PACKET);
279+
280+
let mouse_state = mouse.completed_state;
281+
assert_eq!(mouse_state.x, POSITIVE_X_PACKET as i16);
282+
assert_eq!(mouse_state.y, POSITIVE_Y_PACKET as i16);
283+
}
284+
285+
#[test]
286+
fn negative_movement() {
287+
let mut mouse = Mouse::new();
288+
mouse.process_packet(NEGATIVE_PACKET);
289+
mouse.process_packet(NEGATIVE_X_PACKET);
290+
mouse.process_packet(NEGATIVE_Y_PACKET);
291+
292+
let mouse_state = mouse.get_state();
293+
assert_eq!(mouse_state.x, -40);
294+
assert_eq!(mouse_state.y, -39);
295+
}
296+
297+
#[test]
298+
fn discard_overflow() {
299+
let mut mouse = Mouse::new();
300+
mouse.process_packet(VALID_PACKET);
301+
mouse.process_packet(POSITIVE_X_PACKET);
302+
mouse.process_packet(POSITIVE_Y_PACKET);
303+
304+
mouse.process_packet(NEGATIVE_PACKET_WITH_OVERFLOW);
305+
mouse.process_packet(NEGATIVE_X_PACKET);
306+
mouse.process_packet(NEGATIVE_Y_PACKET);
307+
308+
let mouse_state = mouse.completed_state;
309+
assert_eq!(mouse_state.x, POSITIVE_X_PACKET as i16);
310+
assert_eq!(mouse_state.y, POSITIVE_Y_PACKET as i16);
311+
}
312+
313+
#[test]
314+
fn left_mouse_button_down() {
315+
let mut mouse = Mouse::new();
316+
mouse.process_packet(LEFT_MOUSE_BUTTON_DOWN_PACKET);
317+
assert_eq!(mouse.current_state.left_button_down(), true);
318+
}
319+
320+
#[test]
321+
fn left_mouse_button_up() {
322+
let mut mouse = Mouse::new();
323+
mouse.process_packet(VALID_PACKET);
324+
assert_eq!(mouse.current_state.left_button_up(), true);
325+
}
326+
327+
#[test]
328+
fn right_mouse_button_down() {
329+
let mut mouse = Mouse::new();
330+
mouse.process_packet(RIGHT_MOUSE_BUTTON_DOWN_PACKET);
331+
assert_eq!(mouse.current_state.right_button_down(), true);
332+
}
333+
334+
#[test]
335+
fn right_mouse_button_up() {
336+
let mut mouse = Mouse::new();
337+
mouse.process_packet(VALID_PACKET);
338+
assert_eq!(mouse.current_state.right_button_up(), true);
339+
}
340+
}

0 commit comments

Comments
 (0)