ps2_mouse/
lib.rs

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