Add virtio-input Linux test binary.

The binary reads from the Linux character devices `/dev/input/eventX`
and ensures that a series of keystrokes is received.

Bug: MAC-213, MAC-214

Change-Id: Ic50b70d63a55c8f603917db3546a3310e9e6b164
diff --git a/Cargo.toml b/Cargo.toml
index 55f6868..422772a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,8 @@
 [dependencies]
 structopt = { path = "rust_crates/vendor/structopt" }
 libc = { path = "rust_crates/vendor/libc" }
+clap = { path = "rust_crates/vendor/clap" }
+matches = { path = "rust_crates/vendor/matches" }
 
 [patch.crates-io]
 winapi = { path = "rust_crates/tiny_mirrors/winapi" }
diff --git a/build.sh b/build.sh
index e10c3e6..d4313f8 100755
--- a/build.sh
+++ b/build.sh
@@ -48,6 +48,7 @@
 
 mkdir -p ${OUT_DIR}
 cp target/${TARGET}/debug/virtio_block_test_util ${OUT_DIR}
+cp target/${TARGET}/debug/virtio_input_test_util ${OUT_DIR}
 cp target/${TARGET}/debug/virtio_net_test_util ${OUT_DIR}
 cp target/${TARGET}/debug/virtio_rng_test_util ${OUT_DIR}
 cp target/${TARGET}/debug/virtio_vsock_test_util ${OUT_DIR}
diff --git a/src/bin/virtio_input_test_util/events.rs b/src/bin/virtio_input_test_util/events.rs
new file mode 100644
index 0000000..ee86c63
--- /dev/null
+++ b/src/bin/virtio_input_test_util/events.rs
@@ -0,0 +1,153 @@
+/// Functionality for parsing events on Linux's `/dev/input/eventX` character
+/// devices.
+use std::fs::OpenOptions;
+use std::io::{Error, Read};
+
+/// Input event types, used in the Linux `input_event` struct.
+///
+/// See Linux `include/uapi/linux/input.h` for details.
+#[repr(u16)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[allow(dead_code)]
+pub enum EventType {
+    Syn = 0x00,
+    Key = 0x01,
+    Rel = 0x02,
+    Abs = 0x03,
+    Msc = 0x04,
+    Sw = 0x05,
+    Led = 0x11,
+    Snd = 0x12,
+    Rep = 0x14,
+    Ff = 0x15,
+    Pwr = 0x16,
+    FfStatus = 0x17,
+}
+
+impl Default for EventType {
+    fn default() -> EventType {
+        // By default, return the value with a zero representation.
+        EventType::Syn
+    }
+}
+
+/// Linux's `struct time_val`.
+///
+/// See Linux `include/uapi/linux/time.h` for details.
+#[repr(C, packed)]
+#[derive(Debug, Clone, Copy, Default)]
+pub struct TimeVal {
+    pub tv_sec: libc::c_long,
+    pub tv_usec: libc::c_long,
+}
+
+/// Linux's `struct input_event`.
+///
+/// This is the format returned by input devices such as `/dev/input/eventX', which provide raw
+/// input events from input devices.
+///
+/// See Linux `include/uapi/linux/input.h` for details.
+#[repr(C, packed)]
+#[derive(Debug, Clone, Copy, Default)]
+pub struct InputEvent {
+    pub time: TimeVal,
+    pub type_: EventType,
+    pub code: u16,
+    pub value: i32,
+}
+
+// Keyboard `value` values.
+#[repr(i32)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[allow(dead_code)]
+pub enum KeyboardValue {
+    KeyDown = 0,
+    KeyUp = 1,
+    KeyRepeat = 2,
+}
+
+// Keyboard `code` values.
+//
+// This is not the complete list, but just a subset needed for tests.
+//
+// See Linux's `include/uapi/linux/input.h` for details.
+#[repr(u16)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum KeyboardCode {
+    A = 30,
+    B = 48,
+    C = 46,
+    LeftShift = 42,
+}
+
+/// An EventReader parses events from the `/dev/input/eventX` interface.
+pub struct EventReader {
+    input: Box<Read + Send>,
+}
+
+impl EventReader {
+    /// Create a new EventReader, parsing bytes from the given reader.
+    pub fn new_from_reader(reader: Box<Read + Send>) -> EventReader {
+        EventReader { input: reader }
+    }
+
+    /// Create a new EventReader from the given file. The input file may be a character special
+    /// file, such as `/dev/input/eventX`.
+    pub fn new_from_path(path: &std::path::Path) -> Result<EventReader, Error> {
+        let input = OpenOptions::new()
+            .read(true)
+            .write(false)
+            .create(false)
+            .open(&path)?;
+        Ok(EventReader::new_from_reader(Box::new(input)))
+    }
+
+    pub fn read(self: &mut EventReader) -> Result<InputEvent, Error> {
+        let mut result: InputEvent = Default::default();
+
+        // Read an InputEvent from the input stream by reading the raw bytes into "result".
+        //
+        // The Linux kernel requires us to perform the read of a full event in a single read()
+        // syscall.
+        unsafe {
+            let raw_bytes = std::slice::from_raw_parts_mut(
+                &mut result as *mut _ as *mut u8,
+                std::mem::size_of::<InputEvent>(),
+            );
+            self.input.read_exact(raw_bytes)?;
+        }
+
+        Ok(result)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use matches::matches;
+
+    #[test]
+    fn test_parse_event() {
+        // Raw bytes of event pulled from /dev/input/event0 from USB Keyboard pressing the "a"
+        // button.
+        #[rustfmt::skip]
+        let raw_event = vec![
+            /* 0x00 */ 0x5b, 0x98, 0xe4, 0x5c, 0x00, 0x00, 0x00, 0x00,
+            /* 0x08 */ 0x33, 0xfd, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+            /* 0x10 */ 0x01, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00,
+        ];
+
+        let mut reader = EventReader::new_from_reader(Box::new(std::io::Cursor::new(raw_event)));
+        let event = reader.read().expect("Could not parse event");
+        assert_eq!({ event.type_ }, EventType::Key);
+        assert_eq!({ event.code }, KeyboardCode::A as u16);
+        assert_eq!({ event.value }, KeyboardValue::KeyDown as i32);
+    }
+
+    #[test]
+    fn test_eof() {
+        let empty_stream = vec![];
+        let mut reader = EventReader::new_from_reader(Box::new(std::io::Cursor::new(empty_stream)));
+        assert!(matches!(reader.read(), Err(_)));
+    }
+}
diff --git a/src/bin/virtio_input_test_util/main.rs b/src/bin/virtio_input_test_util/main.rs
new file mode 100644
index 0000000..739908c
--- /dev/null
+++ b/src/bin/virtio_input_test_util/main.rs
@@ -0,0 +1,90 @@
+mod events;
+
+use events::{EventReader, EventType, InputEvent, KeyboardCode, KeyboardValue};
+use std::path::Path;
+use std::sync::mpsc;
+
+/// Wait for the given key to be pressed on the given `events` interator.
+///
+/// Returns `true` if the keystroke was detected before the iterator finished.
+fn wait_for_key_press<I>(code: KeyboardCode, events: &mut I) -> Result<(), String>
+where
+    I: Iterator<Item = InputEvent>,
+{
+    for e in events {
+        if { e.type_ } == EventType::Key
+            && e.code == (code as u16)
+            && e.value == (KeyboardValue::KeyDown as i32)
+        {
+            return Ok(());
+        }
+    }
+    Err("Device closed while waiting for key press.".to_string())
+}
+
+fn run_keyboard_test(input_devices: &[&Path]) -> Result<(), String> {
+    // Open the set of character devices.
+    let files = input_devices
+        .iter()
+        .map(|x| {
+            EventReader::new_from_path(Path::new(x))
+                .map_err(|e| format!("Failed to open file '{}': {}", x.display(), e))
+        })
+        .collect::<Result<Vec<EventReader>, String>>()?;
+
+    // Create a EventReader object for each input file, all sending to the channel `tx`.
+    let (tx, rx) = mpsc::sync_channel(0);
+    let _threads = files
+        .into_iter()
+        .map(|mut r| {
+            let tx = tx.clone();
+            std::thread::spawn(move || {
+                while let Ok(event) = r.read() {
+                    tx.send(event).ok();
+                }
+            })
+        })
+        .collect::<Vec<_>>();
+
+    // Wait for key strokes.
+    println!("Type 'abc<shift>' to pass test.");
+    let mut iter = rx.iter();
+    let codes = [
+        KeyboardCode::A,
+        KeyboardCode::B,
+        KeyboardCode::C,
+        KeyboardCode::LeftShift,
+    ];
+    for code in &codes {
+        println!("  waiting for {:?} ...", code);
+        if let error @ Err(_) = wait_for_key_press(*code, &mut iter) {
+            return error;
+        }
+    }
+    println!("PASS");
+    Ok(())
+}
+
+fn main() -> Result<(), String> {
+    // Parse command line arguments.
+    let matches = clap::App::new("VirtIO Input Tester")
+        .subcommand(
+            clap::SubCommand::with_name("keyboard")
+                .about("runs a keyboard test")
+                .arg(clap::Arg::with_name("files").required(true).min_values(1)),
+        )
+        .get_matches();
+
+    // Run the user-specified command
+    match matches.subcommand() {
+        ("keyboard", Some(keyboard_matches)) => {
+            let files = keyboard_matches
+                .values_of("files")
+                .unwrap()
+                .map(Path::new)
+                .collect::<Vec<&Path>>();
+            run_keyboard_test(&files)
+        }
+        _ => Err("Must provide a subcommand indicating which test to run.".to_string()),
+    }
+}