#keyboard #mouse #driver #no-std

nightly no-std ps2

A general PS/2 device driver for nightly Rust

5 unstable releases

0.2.0 Jul 13, 2021
0.1.2 Jun 4, 2021
0.1.1 Dec 19, 2020
0.1.0 Nov 26, 2020
0.0.0 Oct 6, 2020

#1897 in Hardware support

MIT license

47KB
706 lines

ps2

CI MIT licensed crates.io API docs

A general PS/2 device driver for nightly Rust.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/lucis-fluxum/ps2-rs.

License

This crate is available as open source under the terms of the MIT License.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in ps2-rs by you shall be licensed as MIT, without any additional terms or conditions.


lib.rs:

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. We skip steps 1 and 2 by assuming the PS/2 controller exists and is supported on the current hardware.

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.

Dependencies

~565KB
~11K SLoC