1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#![no_std]
#![feature(const_mut_refs)]
//! This crate provides comprehensive low-level access to the PS/2 controller and PS/2 devices. It
//! uses a poll-based approach with a timeout to read and write data to the IO ports.
//!
//! # Examples
//!
//! The below example implements the initialization process outlined on the [OSDev wiki](https://web.archive.org/web/20201112021519/https://wiki.osdev.org/%228042%22_PS/2_Controller#Initialising_the_PS.2F2_Controller).
//! We skip steps 1 and 2 by assuming the PS/2 controller exists and is supported on the current
//! hardware.
//! ```no_run
//! use ps2::{Controller, error::ControllerError, flags::ControllerConfigFlags};
//!
//! fn initialize() -> Result<(), ControllerError> {
//!     let mut controller = unsafe { Controller::new() };
//!
//!     // Step 3: Disable devices
//!     controller.disable_keyboard()?;
//!     controller.disable_mouse()?;
//!
//!     // Step 4: Flush data buffer
//!     let _ = controller.read_data();
//!
//!     // Step 5: Set config
//!     let mut config = controller.read_config()?;
//!     // Disable interrupts and scancode translation
//!     config.set(
//!         ControllerConfigFlags::ENABLE_KEYBOARD_INTERRUPT
//!             | ControllerConfigFlags::ENABLE_MOUSE_INTERRUPT
//!             | ControllerConfigFlags::ENABLE_TRANSLATE,
//!         false,
//!     );
//!     controller.write_config(config)?;
//!
//!     // Step 6: Controller self-test
//!     controller.test_controller()?;
//!     // Write config again in case of controller reset
//!     controller.write_config(config)?;
//!
//!     // Step 7: Determine if there are 2 devices
//!     let has_mouse = if config.contains(ControllerConfigFlags::DISABLE_MOUSE) {
//!         controller.enable_mouse()?;
//!         config = controller.read_config()?;
//!         // If mouse is working, this should now be unset
//!         !config.contains(ControllerConfigFlags::DISABLE_MOUSE)
//!     } else {
//!         false
//!     };
//!     // Disable mouse. If there's no mouse, this is ignored
//!     controller.disable_mouse()?;
//!
//!     // Step 8: Interface tests
//!     let keyboard_works = controller.test_keyboard().is_ok();
//!     let mouse_works = has_mouse && controller.test_mouse().is_ok();
//!
//!     // Step 9 - 10: Enable and reset devices
//!     config = controller.read_config()?;
//!     if keyboard_works {
//!         controller.enable_keyboard()?;
//!         config.set(ControllerConfigFlags::DISABLE_KEYBOARD, false);
//!         config.set(ControllerConfigFlags::ENABLE_KEYBOARD_INTERRUPT, true);
//!         controller.keyboard().reset_and_self_test().unwrap();
//!     }
//!     if mouse_works {
//!         controller.enable_mouse()?;
//!         config.set(ControllerConfigFlags::DISABLE_MOUSE, false);
//!         config.set(ControllerConfigFlags::ENABLE_MOUSE_INTERRUPT, true);
//!         controller.mouse().reset_and_self_test().unwrap();
//!         // This will start streaming events from the mouse
//!         controller.mouse().enable_data_reporting().unwrap();
//!     }
//!
//!     // Write last configuration to enable devices and interrupts
//!     controller.write_config(config)?;
//!
//!     Ok(())
//! }
//! ```
//!
//! Once the controller is initialized and the devices are working properly, they will place input
//! in the data buffer at IO port `0x60`. You can read from this buffer at any time using
//! [`Controller::read_data`]. If you plan on using a poll-based approach to handle device input,
//! be aware that either device may write data to this buffer at any time, and as far as I know
//! there is no way to tell which bytes come from which device.
//!
//! A much better way of handling input is to use interrupts: define handlers for IRQ1 (keyboard)
//! and IRQ12 (mouse) and read the data then. You can use [`Controller::read_data`] for both
//! keyboard and mouse data, or you can use [`Mouse::read_data_packet`] which is a convenient
//! wrapper around [`Controller::read_data`] for mouse packets.
//!
//! # Further Reading
//!
//! Below are some resources I used to develop this library. Note that some resources describing
//! the PS/2 protocol conflict with each other, so this library is my best effort at testing and
//! verifying the accuracy of these resources. If you find that something is missing or doesn't
//! seem quite right, feel free to open an [issue](https://github.com/lucis-fluxum/ps2-rs/issues/new).
//!
//! - [Adam Chapweske's old site][chepweske], which has several detailed write-ups.
//! - [Andries Brouwer's "Keyboard scancodes"][brouwer], which is about much more than just
//!   scancodes.
//! - The OSDev wiki's pages on the [PS/2 controller][osdev_ps2], [keyboard][osdev_keyboard],
//!   [mouse][osdev_mouse], and [mouse input][osdev_mouse_input].
//! - [This PDF][mike_2009] from what appears to be an operating systems development course. I found
//!   the language very approachable and helpful for learning a lot of the relevant terminology.
//! - [This summary][netcore2k_8042] of the PS/2 registers and commands. See [here][netcore2k_keyboard] for keyboard commands.
//!
//! [chepweske]: https://web.archive.org/web/20091124055529/http://www.computer-engineering.org/index.php?title=Main_Page
//! [brouwer]: https://web.archive.org/web/20200616182211/https://www.win.tue.nl/~aeb/linux/kbd/scancodes.html
//! [osdev_ps2]: https://web.archive.org/web/20201112021519/https://wiki.osdev.org/%228042%22_PS/2_Controller
//! [osdev_keyboard]: https://web.archive.org/web/20201112022746/https://wiki.osdev.org/PS/2_Keyboard
//! [osdev_mouse]: https://web.archive.org/web/20200930160800/https://wiki.osdev.org/PS/2_Mouse
//! [osdev_mouse_input]: https://web.archive.org/web/20201112014603/https://wiki.osdev.org/Mouse_Input
//! [mike_2009]: http://www.s100computers.com/My%20System%20Pages/MSDOS%20Board/PC%20Keyboard.pdf
//! [netcore2k_8042]: https://web.archive.org/web/20201023081327/http://helppc.netcore2k.net/hardware/8042
//! [netcore2k_keyboard]: https://web.archive.org/web/20201023082815/http://helppc.netcore2k.net/hardware/keyboard-commands

pub use self::{
    controller::Controller,
    keyboard::{Keyboard, KeyboardType},
    mouse::{Mouse, MouseType},
};

mod controller;
mod keyboard;
mod mouse;

pub mod error;
pub mod flags;

const COMMAND_ACKNOWLEDGED: u8 = 0xfa;
const SELF_TEST_PASSED: u8 = 0xaa;
const SELF_TEST_FAILED: u8 = 0xfc;
const RESEND: u8 = 0xfe;