summary refs log tree commit diff
diff options
context:
space:
mode:
authorJorge E. Moreira <jemoreira@google.com>2019-01-14 18:44:49 -0800
committerchrome-bot <chrome-bot@chromium.org>2019-02-01 06:21:08 -0800
commitdffec507fba14da82b914c9c195df324360e9e28 (patch)
tree36906570c97e639a495beb38bb8ac1f8cd6043f2
parent5329be3634547fe383aff7981854484088e6a622 (diff)
downloadcrosvm-dffec507fba14da82b914c9c195df324360e9e28.tar
crosvm-dffec507fba14da82b914c9c195df324360e9e28.tar.gz
crosvm-dffec507fba14da82b914c9c195df324360e9e28.tar.bz2
crosvm-dffec507fba14da82b914c9c195df324360e9e28.tar.lz
crosvm-dffec507fba14da82b914c9c195df324360e9e28.tar.xz
crosvm-dffec507fba14da82b914c9c195df324360e9e28.tar.zst
crosvm-dffec507fba14da82b914c9c195df324360e9e28.zip
Adds Virtio-Input device simulation
This allows decoupling input from the wayland socket while using a
standard virtio device for it. The proposed virtio input spec can be
found at
https://www.kraxel.org/virtio/virtio-v1.0-cs03-virtio-input.pdf, it
has already been implemented in qemu and (guest) kernel support exists
since version 4.1.

This change adds the following options to crosvm:
--evdev: Grabs a host device and passes it through to the guest
--<device>: Creates a default configuration for <device>,
receives the input events from a unix socket. <device> can be
'keyboard', 'mouse' or 'trackpad'.

Bug=chromium:921271
Test=booted on x86 linux and manually tried virtio-input devices
Change-Id: I8455b72c53ea2f431009ee8140799b0797775e76
Reviewed-on: https://chromium-review.googlesource.com/1412355
Commit-Ready: Jorge Moreira Broche <jemoreira@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
-rw-r--r--devices/src/virtio/input/constants.rs738
-rw-r--r--devices/src/virtio/input/defaults.rs201
-rw-r--r--devices/src/virtio/input/evdev.rs257
-rw-r--r--devices/src/virtio/input/event_source.rs337
-rw-r--r--devices/src/virtio/input/mod.rs702
-rw-r--r--devices/src/virtio/mod.rs3
-rw-r--r--seccomp/arm/input_device.policy37
-rw-r--r--seccomp/x86_64/input_device.policy37
-rw-r--r--src/linux.rs140
-rw-r--r--src/main.rs76
10 files changed, 2516 insertions, 12 deletions
diff --git a/devices/src/virtio/input/constants.rs b/devices/src/virtio/input/constants.rs
new file mode 100644
index 0000000..6cc3574
--- /dev/null
+++ b/devices/src/virtio/input/constants.rs
@@ -0,0 +1,738 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Should match linux/input-event-codes.h
+pub const INPUT_PROP_POINTER: u16 = 0x00;
+pub const INPUT_PROP_DIRECT: u16 = 0x01;
+pub const INPUT_PROP_BUTTONPAD: u16 = 0x02;
+pub const INPUT_PROP_SEMI_MT: u16 = 0x03;
+pub const INPUT_PROP_TOPBUTTONPAD: u16 = 0x04;
+pub const INPUT_PROP_POINTING_STICK: u16 = 0x05;
+pub const INPUT_PROP_ACCELEROMETER: u16 = 0x06;
+
+pub const INPUT_PROP_MAX: u16 = 0x1f;
+pub const INPUT_PROP_CNT: u16 = (INPUT_PROP_MAX + 1);
+
+pub const EV_SYN: u16 = 0x00;
+pub const EV_KEY: u16 = 0x01;
+pub const EV_REL: u16 = 0x02;
+pub const EV_ABS: u16 = 0x03;
+pub const EV_MSC: u16 = 0x04;
+pub const EV_SW: u16 = 0x05;
+pub const EV_LED: u16 = 0x11;
+pub const EV_SND: u16 = 0x12;
+pub const EV_REP: u16 = 0x14;
+pub const EV_FF: u16 = 0x15;
+pub const EV_PWR: u16 = 0x16;
+pub const EV_FF_STATUS: u16 = 0x17;
+pub const EV_MAX: u16 = 0x1f;
+pub const EV_CNT: u16 = EV_MAX + 1;
+
+pub const SYN_REPORT: u16 = 0;
+pub const SYN_CONFIG: u16 = 1;
+pub const SYN_MT_REPORT: u16 = 2;
+pub const SYN_DROPPED: u16 = 3;
+
+pub const KEY_RESERVED: u16 = 0;
+pub const KEY_ESC: u16 = 1;
+pub const KEY_1: u16 = 2;
+pub const KEY_2: u16 = 3;
+pub const KEY_3: u16 = 4;
+pub const KEY_4: u16 = 5;
+pub const KEY_5: u16 = 6;
+pub const KEY_6: u16 = 7;
+pub const KEY_7: u16 = 8;
+pub const KEY_8: u16 = 9;
+pub const KEY_9: u16 = 10;
+pub const KEY_0: u16 = 11;
+pub const KEY_MINUS: u16 = 12;
+pub const KEY_EQUAL: u16 = 13;
+pub const KEY_BACKSPACE: u16 = 14;
+pub const KEY_TAB: u16 = 15;
+pub const KEY_Q: u16 = 16;
+pub const KEY_W: u16 = 17;
+pub const KEY_E: u16 = 18;
+pub const KEY_R: u16 = 19;
+pub const KEY_T: u16 = 20;
+pub const KEY_Y: u16 = 21;
+pub const KEY_U: u16 = 22;
+pub const KEY_I: u16 = 23;
+pub const KEY_O: u16 = 24;
+pub const KEY_P: u16 = 25;
+pub const KEY_LEFTBRACE: u16 = 26;
+pub const KEY_RIGHTBRACE: u16 = 27;
+pub const KEY_ENTER: u16 = 28;
+pub const KEY_LEFTCTRL: u16 = 29;
+pub const KEY_A: u16 = 30;
+pub const KEY_S: u16 = 31;
+pub const KEY_D: u16 = 32;
+pub const KEY_F: u16 = 33;
+pub const KEY_G: u16 = 34;
+pub const KEY_H: u16 = 35;
+pub const KEY_J: u16 = 36;
+pub const KEY_K: u16 = 37;
+pub const KEY_L: u16 = 38;
+pub const KEY_SEMICOLON: u16 = 39;
+pub const KEY_APOSTROPHE: u16 = 40;
+pub const KEY_GRAVE: u16 = 41;
+pub const KEY_LEFTSHIFT: u16 = 42;
+pub const KEY_BACKSLASH: u16 = 43;
+pub const KEY_Z: u16 = 44;
+pub const KEY_X: u16 = 45;
+pub const KEY_C: u16 = 46;
+pub const KEY_V: u16 = 47;
+pub const KEY_B: u16 = 48;
+pub const KEY_N: u16 = 49;
+pub const KEY_M: u16 = 50;
+pub const KEY_COMMA: u16 = 51;
+pub const KEY_DOT: u16 = 52;
+pub const KEY_SLASH: u16 = 53;
+pub const KEY_RIGHTSHIFT: u16 = 54;
+pub const KEY_KPASTERISK: u16 = 55;
+pub const KEY_LEFTALT: u16 = 56;
+pub const KEY_SPACE: u16 = 57;
+pub const KEY_CAPSLOCK: u16 = 58;
+pub const KEY_F1: u16 = 59;
+pub const KEY_F2: u16 = 60;
+pub const KEY_F3: u16 = 61;
+pub const KEY_F4: u16 = 62;
+pub const KEY_F5: u16 = 63;
+pub const KEY_F6: u16 = 64;
+pub const KEY_F7: u16 = 65;
+pub const KEY_F8: u16 = 66;
+pub const KEY_F9: u16 = 67;
+pub const KEY_F10: u16 = 68;
+pub const KEY_NUMLOCK: u16 = 69;
+pub const KEY_SCROLLLOCK: u16 = 70;
+pub const KEY_KP7: u16 = 71;
+pub const KEY_KP8: u16 = 72;
+pub const KEY_KP9: u16 = 73;
+pub const KEY_KPMINUS: u16 = 74;
+pub const KEY_KP4: u16 = 75;
+pub const KEY_KP5: u16 = 76;
+pub const KEY_KP6: u16 = 77;
+pub const KEY_KPPLUS: u16 = 78;
+pub const KEY_KP1: u16 = 79;
+pub const KEY_KP2: u16 = 80;
+pub const KEY_KP3: u16 = 81;
+pub const KEY_KP0: u16 = 82;
+pub const KEY_KPDOT: u16 = 83;
+
+pub const KEY_ZENKAKUHANKAKU: u16 = 85;
+pub const KEY_102ND: u16 = 86;
+pub const KEY_F11: u16 = 87;
+pub const KEY_F12: u16 = 88;
+pub const KEY_RO: u16 = 89;
+pub const KEY_KATAKANA: u16 = 90;
+pub const KEY_HIRAGANA: u16 = 91;
+pub const KEY_HENKAN: u16 = 92;
+pub const KEY_KATAKANAHIRAGANA: u16 = 93;
+pub const KEY_MUHENKAN: u16 = 94;
+pub const KEY_KPJPCOMMA: u16 = 95;
+pub const KEY_KPENTER: u16 = 96;
+pub const KEY_RIGHTCTRL: u16 = 97;
+pub const KEY_KPSLASH: u16 = 98;
+pub const KEY_SYSRQ: u16 = 99;
+pub const KEY_RIGHTALT: u16 = 100;
+pub const KEY_LINEFEED: u16 = 101;
+pub const KEY_HOME: u16 = 102;
+pub const KEY_UP: u16 = 103;
+pub const KEY_PAGEUP: u16 = 104;
+pub const KEY_LEFT: u16 = 105;
+pub const KEY_RIGHT: u16 = 106;
+pub const KEY_END: u16 = 107;
+pub const KEY_DOWN: u16 = 108;
+pub const KEY_PAGEDOWN: u16 = 109;
+pub const KEY_INSERT: u16 = 110;
+pub const KEY_DELETE: u16 = 111;
+pub const KEY_MACRO: u16 = 112;
+pub const KEY_MUTE: u16 = 113;
+pub const KEY_VOLUMEDOWN: u16 = 114;
+pub const KEY_VOLUMEUP: u16 = 115;
+pub const KEY_POWER: u16 = 116;
+pub const KEY_KPEQUAL: u16 = 117;
+pub const KEY_KPPLUSMINUS: u16 = 118;
+pub const KEY_PAUSE: u16 = 119;
+pub const KEY_SCALE: u16 = 120;
+
+pub const KEY_KPCOMMA: u16 = 121;
+pub const KEY_HANGEUL: u16 = 122;
+pub const KEY_HANGUEL: u16 = KEY_HANGEUL;
+pub const KEY_HANJA: u16 = 123;
+pub const KEY_YEN: u16 = 124;
+pub const KEY_LEFTMETA: u16 = 125;
+pub const KEY_RIGHTMETA: u16 = 126;
+pub const KEY_COMPOSE: u16 = 127;
+
+pub const KEY_STOP: u16 = 128;
+pub const KEY_AGAIN: u16 = 129;
+pub const KEY_PROPS: u16 = 130;
+pub const KEY_UNDO: u16 = 131;
+pub const KEY_FRONT: u16 = 132;
+pub const KEY_COPY: u16 = 133;
+pub const KEY_OPEN: u16 = 134;
+pub const KEY_PASTE: u16 = 135;
+pub const KEY_FIND: u16 = 136;
+pub const KEY_CUT: u16 = 137;
+pub const KEY_HELP: u16 = 138;
+pub const KEY_MENU: u16 = 139;
+pub const KEY_CALC: u16 = 140;
+pub const KEY_SETUP: u16 = 141;
+pub const KEY_SLEEP: u16 = 142;
+pub const KEY_WAKEUP: u16 = 143;
+pub const KEY_FILE: u16 = 144;
+pub const KEY_SENDFILE: u16 = 145;
+pub const KEY_DELETEFILE: u16 = 146;
+pub const KEY_XFER: u16 = 147;
+pub const KEY_PROG1: u16 = 148;
+pub const KEY_PROG2: u16 = 149;
+pub const KEY_WWW: u16 = 150;
+pub const KEY_MSDOS: u16 = 151;
+pub const KEY_COFFEE: u16 = 152;
+pub const KEY_SCREENLOCK: u16 = KEY_COFFEE;
+pub const KEY_ROTATE_DISPLAY: u16 = 153;
+pub const KEY_DIRECTION: u16 = KEY_ROTATE_DISPLAY;
+pub const KEY_CYCLEWINDOWS: u16 = 154;
+pub const KEY_MAIL: u16 = 155;
+pub const KEY_BOOKMARKS: u16 = 156;
+pub const KEY_COMPUTER: u16 = 157;
+pub const KEY_BACK: u16 = 158;
+pub const KEY_FORWARD: u16 = 159;
+pub const KEY_CLOSECD: u16 = 160;
+pub const KEY_EJECTCD: u16 = 161;
+pub const KEY_EJECTCLOSECD: u16 = 162;
+pub const KEY_NEXTSONG: u16 = 163;
+pub const KEY_PLAYPAUSE: u16 = 164;
+pub const KEY_PREVIOUSSONG: u16 = 165;
+pub const KEY_STOPCD: u16 = 166;
+pub const KEY_RECORD: u16 = 167;
+pub const KEY_REWIND: u16 = 168;
+pub const KEY_PHONE: u16 = 169;
+pub const KEY_ISO: u16 = 170;
+pub const KEY_CONFIG: u16 = 171;
+pub const KEY_HOMEPAGE: u16 = 172;
+pub const KEY_REFRESH: u16 = 173;
+pub const KEY_EXIT: u16 = 174;
+pub const KEY_MOVE: u16 = 175;
+pub const KEY_EDIT: u16 = 176;
+pub const KEY_SCROLLUP: u16 = 177;
+pub const KEY_SCROLLDOWN: u16 = 178;
+pub const KEY_KPLEFTPAREN: u16 = 179;
+pub const KEY_KPRIGHTPAREN: u16 = 180;
+pub const KEY_NEW: u16 = 181;
+pub const KEY_REDO: u16 = 182;
+
+pub const KEY_F13: u16 = 183;
+pub const KEY_F14: u16 = 184;
+pub const KEY_F15: u16 = 185;
+pub const KEY_F16: u16 = 186;
+pub const KEY_F17: u16 = 187;
+pub const KEY_F18: u16 = 188;
+pub const KEY_F19: u16 = 189;
+pub const KEY_F20: u16 = 190;
+pub const KEY_F21: u16 = 191;
+pub const KEY_F22: u16 = 192;
+pub const KEY_F23: u16 = 193;
+pub const KEY_F24: u16 = 194;
+
+pub const KEY_PLAYCD: u16 = 200;
+pub const KEY_PAUSECD: u16 = 201;
+pub const KEY_PROG3: u16 = 202;
+pub const KEY_PROG4: u16 = 203;
+pub const KEY_DASHBOARD: u16 = 204;
+pub const KEY_SUSPEND: u16 = 205;
+pub const KEY_CLOSE: u16 = 206;
+pub const KEY_PLAY: u16 = 207;
+pub const KEY_FASTFORWARD: u16 = 208;
+pub const KEY_BASSBOOST: u16 = 209;
+pub const KEY_PRINT: u16 = 210;
+pub const KEY_HP: u16 = 211;
+pub const KEY_CAMERA: u16 = 212;
+pub const KEY_SOUND: u16 = 213;
+pub const KEY_QUESTION: u16 = 214;
+pub const KEY_EMAIL: u16 = 215;
+pub const KEY_CHAT: u16 = 216;
+pub const KEY_SEARCH: u16 = 217;
+pub const KEY_CONNECT: u16 = 218;
+pub const KEY_FINANCE: u16 = 219;
+pub const KEY_SPORT: u16 = 220;
+pub const KEY_SHOP: u16 = 221;
+pub const KEY_ALTERASE: u16 = 222;
+pub const KEY_CANCEL: u16 = 223;
+pub const KEY_BRIGHTNESSDOWN: u16 = 224;
+pub const KEY_BRIGHTNESSUP: u16 = 225;
+pub const KEY_MEDIA: u16 = 226;
+
+pub const KEY_SWITCHVIDEOMODE: u16 = 227;
+pub const KEY_KBDILLUMTOGGLE: u16 = 228;
+pub const KEY_KBDILLUMDOWN: u16 = 229;
+pub const KEY_KBDILLUMUP: u16 = 230;
+
+pub const KEY_SEND: u16 = 231;
+pub const KEY_REPLY: u16 = 232;
+pub const KEY_FORWARDMAIL: u16 = 233;
+pub const KEY_SAVE: u16 = 234;
+pub const KEY_DOCUMENTS: u16 = 235;
+
+pub const KEY_BATTERY: u16 = 236;
+
+pub const KEY_BLUETOOTH: u16 = 237;
+pub const KEY_WLAN: u16 = 238;
+pub const KEY_UWB: u16 = 239;
+
+pub const KEY_UNKNOWN: u16 = 240;
+
+pub const KEY_VIDEO_NEXT: u16 = 241;
+pub const KEY_VIDEO_PREV: u16 = 242;
+pub const KEY_BRIGHTNESS_CYCLE: u16 = 243;
+pub const KEY_BRIGHTNESS_AUTO: u16 = 244;
+pub const KEY_BRIGHTNESS_ZERO: u16 = KEY_BRIGHTNESS_AUTO;
+pub const KEY_DISPLAY_OFF: u16 = 245;
+
+pub const KEY_WWAN: u16 = 246;
+pub const KEY_WIMAX: u16 = KEY_WWAN;
+pub const KEY_RFKILL: u16 = 247;
+
+pub const KEY_MICMUTE: u16 = 248;
+
+pub const BTN_MISC: u16 = 0x100;
+pub const BTN_0: u16 = 0x100;
+pub const BTN_1: u16 = 0x101;
+pub const BTN_2: u16 = 0x102;
+pub const BTN_3: u16 = 0x103;
+pub const BTN_4: u16 = 0x104;
+pub const BTN_5: u16 = 0x105;
+pub const BTN_6: u16 = 0x106;
+pub const BTN_7: u16 = 0x107;
+pub const BTN_8: u16 = 0x108;
+pub const BTN_9: u16 = 0x109;
+
+pub const BTN_MOUSE: u16 = 0x110;
+pub const BTN_LEFT: u16 = 0x110;
+pub const BTN_RIGHT: u16 = 0x111;
+pub const BTN_MIDDLE: u16 = 0x112;
+pub const BTN_SIDE: u16 = 0x113;
+pub const BTN_EXTRA: u16 = 0x114;
+pub const BTN_FORWARD: u16 = 0x115;
+pub const BTN_BACK: u16 = 0x116;
+pub const BTN_TASK: u16 = 0x117;
+
+pub const BTN_JOYSTICK: u16 = 0x120;
+pub const BTN_TRIGGER: u16 = 0x120;
+pub const BTN_THUMB: u16 = 0x121;
+pub const BTN_THUMB2: u16 = 0x122;
+pub const BTN_TOP: u16 = 0x123;
+pub const BTN_TOP2: u16 = 0x124;
+pub const BTN_PINKIE: u16 = 0x125;
+pub const BTN_BASE: u16 = 0x126;
+pub const BTN_BASE2: u16 = 0x127;
+pub const BTN_BASE3: u16 = 0x128;
+pub const BTN_BASE4: u16 = 0x129;
+pub const BTN_BASE5: u16 = 0x12a;
+pub const BTN_BASE6: u16 = 0x12b;
+pub const BTN_DEAD: u16 = 0x12f;
+
+pub const BTN_GAMEPAD: u16 = 0x130;
+pub const BTN_SOUTH: u16 = 0x130;
+pub const BTN_A: u16 = BTN_SOUTH;
+pub const BTN_EAST: u16 = 0x131;
+pub const BTN_B: u16 = BTN_EAST;
+pub const BTN_C: u16 = 0x132;
+pub const BTN_NORTH: u16 = 0x133;
+pub const BTN_X: u16 = BTN_NORTH;
+pub const BTN_WEST: u16 = 0x134;
+pub const BTN_Y: u16 = BTN_WEST;
+pub const BTN_Z: u16 = 0x135;
+pub const BTN_TL: u16 = 0x136;
+pub const BTN_TR: u16 = 0x137;
+pub const BTN_TL2: u16 = 0x138;
+pub const BTN_TR2: u16 = 0x139;
+pub const BTN_SELECT: u16 = 0x13a;
+pub const BTN_START: u16 = 0x13b;
+pub const BTN_MODE: u16 = 0x13c;
+pub const BTN_THUMBL: u16 = 0x13d;
+pub const BTN_THUMBR: u16 = 0x13e;
+
+pub const BTN_DIGI: u16 = 0x140;
+pub const BTN_TOOL_PEN: u16 = 0x140;
+pub const BTN_TOOL_RUBBER: u16 = 0x141;
+pub const BTN_TOOL_BRUSH: u16 = 0x142;
+pub const BTN_TOOL_PENCIL: u16 = 0x143;
+pub const BTN_TOOL_AIRBRUSH: u16 = 0x144;
+pub const BTN_TOOL_FINGER: u16 = 0x145;
+pub const BTN_TOOL_MOUSE: u16 = 0x146;
+pub const BTN_TOOL_LENS: u16 = 0x147;
+pub const BTN_TOOL_QUINTTAP: u16 = 0x148;
+pub const BTN_STYLUS3: u16 = 0x149;
+pub const BTN_TOUCH: u16 = 0x14a;
+pub const BTN_STYLUS: u16 = 0x14b;
+pub const BTN_STYLUS2: u16 = 0x14c;
+pub const BTN_TOOL_DOUBLETAP: u16 = 0x14d;
+pub const BTN_TOOL_TRIPLETAP: u16 = 0x14e;
+pub const BTN_TOOL_QUADTAP: u16 = 0x14f;
+
+pub const BTN_WHEEL: u16 = 0x150;
+pub const BTN_GEAR_DOWN: u16 = 0x150;
+pub const BTN_GEAR_UP: u16 = 0x151;
+
+pub const KEY_OK: u16 = 0x160;
+pub const KEY_SELECT: u16 = 0x161;
+pub const KEY_GOTO: u16 = 0x162;
+pub const KEY_CLEAR: u16 = 0x163;
+pub const KEY_POWER2: u16 = 0x164;
+pub const KEY_OPTION: u16 = 0x165;
+pub const KEY_INFO: u16 = 0x166;
+pub const KEY_TIME: u16 = 0x167;
+pub const KEY_VENDOR: u16 = 0x168;
+pub const KEY_ARCHIVE: u16 = 0x169;
+pub const KEY_PROGRAM: u16 = 0x16a;
+pub const KEY_CHANNEL: u16 = 0x16b;
+pub const KEY_FAVORITES: u16 = 0x16c;
+pub const KEY_EPG: u16 = 0x16d;
+pub const KEY_PVR: u16 = 0x16e;
+pub const KEY_MHP: u16 = 0x16f;
+pub const KEY_LANGUAGE: u16 = 0x170;
+pub const KEY_TITLE: u16 = 0x171;
+pub const KEY_SUBTITLE: u16 = 0x172;
+pub const KEY_ANGLE: u16 = 0x173;
+pub const KEY_ZOOM: u16 = 0x174;
+pub const KEY_MODE: u16 = 0x175;
+pub const KEY_KEYBOARD: u16 = 0x176;
+pub const KEY_SCREEN: u16 = 0x177;
+pub const KEY_PC: u16 = 0x178;
+pub const KEY_TV: u16 = 0x179;
+pub const KEY_TV2: u16 = 0x17a;
+pub const KEY_VCR: u16 = 0x17b;
+pub const KEY_VCR2: u16 = 0x17c;
+pub const KEY_SAT: u16 = 0x17d;
+pub const KEY_SAT2: u16 = 0x17e;
+pub const KEY_CD: u16 = 0x17f;
+pub const KEY_TAPE: u16 = 0x180;
+pub const KEY_RADIO: u16 = 0x181;
+pub const KEY_TUNER: u16 = 0x182;
+pub const KEY_PLAYER: u16 = 0x183;
+pub const KEY_TEXT: u16 = 0x184;
+pub const KEY_DVD: u16 = 0x185;
+pub const KEY_AUX: u16 = 0x186;
+pub const KEY_MP3: u16 = 0x187;
+pub const KEY_AUDIO: u16 = 0x188;
+pub const KEY_VIDEO: u16 = 0x189;
+pub const KEY_DIRECTORY: u16 = 0x18a;
+pub const KEY_LIST: u16 = 0x18b;
+pub const KEY_MEMO: u16 = 0x18c;
+pub const KEY_CALENDAR: u16 = 0x18d;
+pub const KEY_RED: u16 = 0x18e;
+pub const KEY_GREEN: u16 = 0x18f;
+pub const KEY_YELLOW: u16 = 0x190;
+pub const KEY_BLUE: u16 = 0x191;
+pub const KEY_CHANNELUP: u16 = 0x192;
+pub const KEY_CHANNELDOWN: u16 = 0x193;
+pub const KEY_FIRST: u16 = 0x194;
+pub const KEY_LAST: u16 = 0x195;
+pub const KEY_AB: u16 = 0x196;
+pub const KEY_NEXT: u16 = 0x197;
+pub const KEY_RESTART: u16 = 0x198;
+pub const KEY_SLOW: u16 = 0x199;
+pub const KEY_SHUFFLE: u16 = 0x19a;
+pub const KEY_BREAK: u16 = 0x19b;
+pub const KEY_PREVIOUS: u16 = 0x19c;
+pub const KEY_DIGITS: u16 = 0x19d;
+pub const KEY_TEEN: u16 = 0x19e;
+pub const KEY_TWEN: u16 = 0x19f;
+pub const KEY_VIDEOPHONE: u16 = 0x1a0;
+pub const KEY_GAMES: u16 = 0x1a1;
+pub const KEY_ZOOMIN: u16 = 0x1a2;
+pub const KEY_ZOOMOUT: u16 = 0x1a3;
+pub const KEY_ZOOMRESET: u16 = 0x1a4;
+pub const KEY_WORDPROCESSOR: u16 = 0x1a5;
+pub const KEY_EDITOR: u16 = 0x1a6;
+pub const KEY_SPREADSHEET: u16 = 0x1a7;
+pub const KEY_GRAPHICSEDITOR: u16 = 0x1a8;
+pub const KEY_PRESENTATION: u16 = 0x1a9;
+pub const KEY_DATABASE: u16 = 0x1aa;
+pub const KEY_NEWS: u16 = 0x1ab;
+pub const KEY_VOICEMAIL: u16 = 0x1ac;
+pub const KEY_ADDRESSBOOK: u16 = 0x1ad;
+pub const KEY_MESSENGER: u16 = 0x1ae;
+pub const KEY_DISPLAYTOGGLE: u16 = 0x1af;
+pub const KEY_BRIGHTNESS_TOGGLE: u16 = KEY_DISPLAYTOGGLE;
+pub const KEY_SPELLCHECK: u16 = 0x1b0;
+pub const KEY_LOGOFF: u16 = 0x1b1;
+
+pub const KEY_DOLLAR: u16 = 0x1b2;
+pub const KEY_EURO: u16 = 0x1b3;
+
+pub const KEY_FRAMEBACK: u16 = 0x1b4;
+pub const KEY_FRAMEFORWARD: u16 = 0x1b5;
+pub const KEY_CONTEXT_MENU: u16 = 0x1b6;
+pub const KEY_MEDIA_REPEAT: u16 = 0x1b7;
+pub const KEY_10CHANNELSUP: u16 = 0x1b8;
+pub const KEY_10CHANNELSDOWN: u16 = 0x1b9;
+pub const KEY_IMAGES: u16 = 0x1ba;
+
+pub const KEY_DEL_EOL: u16 = 0x1c0;
+pub const KEY_DEL_EOS: u16 = 0x1c1;
+pub const KEY_INS_LINE: u16 = 0x1c2;
+pub const KEY_DEL_LINE: u16 = 0x1c3;
+
+pub const KEY_FN: u16 = 0x1d0;
+pub const KEY_FN_ESC: u16 = 0x1d1;
+pub const KEY_FN_F1: u16 = 0x1d2;
+pub const KEY_FN_F2: u16 = 0x1d3;
+pub const KEY_FN_F3: u16 = 0x1d4;
+pub const KEY_FN_F4: u16 = 0x1d5;
+pub const KEY_FN_F5: u16 = 0x1d6;
+pub const KEY_FN_F6: u16 = 0x1d7;
+pub const KEY_FN_F7: u16 = 0x1d8;
+pub const KEY_FN_F8: u16 = 0x1d9;
+pub const KEY_FN_F9: u16 = 0x1da;
+pub const KEY_FN_F10: u16 = 0x1db;
+pub const KEY_FN_F11: u16 = 0x1dc;
+pub const KEY_FN_F12: u16 = 0x1dd;
+pub const KEY_FN_1: u16 = 0x1de;
+pub const KEY_FN_2: u16 = 0x1df;
+pub const KEY_FN_D: u16 = 0x1e0;
+pub const KEY_FN_E: u16 = 0x1e1;
+pub const KEY_FN_F: u16 = 0x1e2;
+pub const KEY_FN_S: u16 = 0x1e3;
+pub const KEY_FN_B: u16 = 0x1e4;
+
+pub const KEY_BRL_DOT1: u16 = 0x1f1;
+pub const KEY_BRL_DOT2: u16 = 0x1f2;
+pub const KEY_BRL_DOT3: u16 = 0x1f3;
+pub const KEY_BRL_DOT4: u16 = 0x1f4;
+pub const KEY_BRL_DOT5: u16 = 0x1f5;
+pub const KEY_BRL_DOT6: u16 = 0x1f6;
+pub const KEY_BRL_DOT7: u16 = 0x1f7;
+pub const KEY_BRL_DOT8: u16 = 0x1f8;
+pub const KEY_BRL_DOT9: u16 = 0x1f9;
+pub const KEY_BRL_DOT10: u16 = 0x1fa;
+
+pub const KEY_NUMERIC_0: u16 = 0x200;
+pub const KEY_NUMERIC_1: u16 = 0x201;
+pub const KEY_NUMERIC_2: u16 = 0x202;
+pub const KEY_NUMERIC_3: u16 = 0x203;
+pub const KEY_NUMERIC_4: u16 = 0x204;
+pub const KEY_NUMERIC_5: u16 = 0x205;
+pub const KEY_NUMERIC_6: u16 = 0x206;
+pub const KEY_NUMERIC_7: u16 = 0x207;
+pub const KEY_NUMERIC_8: u16 = 0x208;
+pub const KEY_NUMERIC_9: u16 = 0x209;
+pub const KEY_NUMERIC_STAR: u16 = 0x20a;
+pub const KEY_NUMERIC_POUND: u16 = 0x20b;
+pub const KEY_NUMERIC_A: u16 = 0x20c;
+pub const KEY_NUMERIC_B: u16 = 0x20d;
+pub const KEY_NUMERIC_C: u16 = 0x20e;
+pub const KEY_NUMERIC_D: u16 = 0x20f;
+
+pub const KEY_CAMERA_FOCUS: u16 = 0x210;
+pub const KEY_WPS_BUTTON: u16 = 0x211;
+
+pub const KEY_TOUCHPAD_TOGGLE: u16 = 0x212;
+pub const KEY_TOUCHPAD_ON: u16 = 0x213;
+pub const KEY_TOUCHPAD_OFF: u16 = 0x214;
+
+pub const KEY_CAMERA_ZOOMIN: u16 = 0x215;
+pub const KEY_CAMERA_ZOOMOUT: u16 = 0x216;
+pub const KEY_CAMERA_UP: u16 = 0x217;
+pub const KEY_CAMERA_DOWN: u16 = 0x218;
+pub const KEY_CAMERA_LEFT: u16 = 0x219;
+pub const KEY_CAMERA_RIGHT: u16 = 0x21a;
+
+pub const KEY_ATTENDANT_ON: u16 = 0x21b;
+pub const KEY_ATTENDANT_OFF: u16 = 0x21c;
+pub const KEY_ATTENDANT_TOGGLE: u16 = 0x21d;
+pub const KEY_LIGHTS_TOGGLE: u16 = 0x21e;
+
+pub const BTN_DPAD_UP: u16 = 0x220;
+pub const BTN_DPAD_DOWN: u16 = 0x221;
+pub const BTN_DPAD_LEFT: u16 = 0x222;
+pub const BTN_DPAD_RIGHT: u16 = 0x223;
+
+pub const KEY_ALS_TOGGLE: u16 = 0x230;
+pub const KEY_ROTATE_LOCK_TOGGLE: u16 = 0x231;
+
+pub const KEY_BUTTONCONFIG: u16 = 0x240;
+pub const KEY_TASKMANAGER: u16 = 0x241;
+pub const KEY_JOURNAL: u16 = 0x242;
+pub const KEY_CONTROLPANEL: u16 = 0x243;
+pub const KEY_APPSELECT: u16 = 0x244;
+pub const KEY_SCREENSAVER: u16 = 0x245;
+pub const KEY_VOICECOMMAND: u16 = 0x246;
+pub const KEY_ASSISTANT: u16 = 0x247;
+
+pub const KEY_BRIGHTNESS_MIN: u16 = 0x250;
+pub const KEY_BRIGHTNESS_MAX: u16 = 0x251;
+
+pub const KEY_KBDINPUTASSIST_PREV: u16 = 0x260;
+pub const KEY_KBDINPUTASSIST_NEXT: u16 = 0x261;
+pub const KEY_KBDINPUTASSIST_PREVGROUP: u16 = 0x262;
+pub const KEY_KBDINPUTASSIST_NEXTGROUP: u16 = 0x263;
+pub const KEY_KBDINPUTASSIST_ACCEPT: u16 = 0x264;
+pub const KEY_KBDINPUTASSIST_CANCEL: u16 = 0x265;
+
+pub const KEY_RIGHT_UP: u16 = 0x266;
+pub const KEY_RIGHT_DOWN: u16 = 0x267;
+pub const KEY_LEFT_UP: u16 = 0x268;
+pub const KEY_LEFT_DOWN: u16 = 0x269;
+
+pub const KEY_ROOT_MENU: u16 = 0x26a;
+
+pub const KEY_MEDIA_TOP_MENU: u16 = 0x26b;
+pub const KEY_NUMERIC_11: u16 = 0x26c;
+pub const KEY_NUMERIC_12: u16 = 0x26d;
+pub const KEY_AUDIO_DESC: u16 = 0x26e;
+pub const KEY_3D_MODE: u16 = 0x26f;
+pub const KEY_NEXT_FAVORITE: u16 = 0x270;
+pub const KEY_STOP_RECORD: u16 = 0x271;
+pub const KEY_PAUSE_RECORD: u16 = 0x272;
+pub const KEY_VOD: u16 = 0x273;
+pub const KEY_UNMUTE: u16 = 0x274;
+pub const KEY_FASTREVERSE: u16 = 0x275;
+pub const KEY_SLOWREVERSE: u16 = 0x276;
+pub const KEY_DATA: u16 = 0x277;
+pub const KEY_ONSCREEN_KEYBOARD: u16 = 0x278;
+
+pub const BTN_TRIGGER_HAPPY: u16 = 0x2c0;
+pub const BTN_TRIGGER_HAPPY1: u16 = 0x2c0;
+pub const BTN_TRIGGER_HAPPY2: u16 = 0x2c1;
+pub const BTN_TRIGGER_HAPPY3: u16 = 0x2c2;
+pub const BTN_TRIGGER_HAPPY4: u16 = 0x2c3;
+pub const BTN_TRIGGER_HAPPY5: u16 = 0x2c4;
+pub const BTN_TRIGGER_HAPPY6: u16 = 0x2c5;
+pub const BTN_TRIGGER_HAPPY7: u16 = 0x2c6;
+pub const BTN_TRIGGER_HAPPY8: u16 = 0x2c7;
+pub const BTN_TRIGGER_HAPPY9: u16 = 0x2c8;
+pub const BTN_TRIGGER_HAPPY10: u16 = 0x2c9;
+pub const BTN_TRIGGER_HAPPY11: u16 = 0x2ca;
+pub const BTN_TRIGGER_HAPPY12: u16 = 0x2cb;
+pub const BTN_TRIGGER_HAPPY13: u16 = 0x2cc;
+pub const BTN_TRIGGER_HAPPY14: u16 = 0x2cd;
+pub const BTN_TRIGGER_HAPPY15: u16 = 0x2ce;
+pub const BTN_TRIGGER_HAPPY16: u16 = 0x2cf;
+pub const BTN_TRIGGER_HAPPY17: u16 = 0x2d0;
+pub const BTN_TRIGGER_HAPPY18: u16 = 0x2d1;
+pub const BTN_TRIGGER_HAPPY19: u16 = 0x2d2;
+pub const BTN_TRIGGER_HAPPY20: u16 = 0x2d3;
+pub const BTN_TRIGGER_HAPPY21: u16 = 0x2d4;
+pub const BTN_TRIGGER_HAPPY22: u16 = 0x2d5;
+pub const BTN_TRIGGER_HAPPY23: u16 = 0x2d6;
+pub const BTN_TRIGGER_HAPPY24: u16 = 0x2d7;
+pub const BTN_TRIGGER_HAPPY25: u16 = 0x2d8;
+pub const BTN_TRIGGER_HAPPY26: u16 = 0x2d9;
+pub const BTN_TRIGGER_HAPPY27: u16 = 0x2da;
+pub const BTN_TRIGGER_HAPPY28: u16 = 0x2db;
+pub const BTN_TRIGGER_HAPPY29: u16 = 0x2dc;
+pub const BTN_TRIGGER_HAPPY30: u16 = 0x2dd;
+pub const BTN_TRIGGER_HAPPY31: u16 = 0x2de;
+pub const BTN_TRIGGER_HAPPY32: u16 = 0x2df;
+pub const BTN_TRIGGER_HAPPY33: u16 = 0x2e0;
+pub const BTN_TRIGGER_HAPPY34: u16 = 0x2e1;
+pub const BTN_TRIGGER_HAPPY35: u16 = 0x2e2;
+pub const BTN_TRIGGER_HAPPY36: u16 = 0x2e3;
+pub const BTN_TRIGGER_HAPPY37: u16 = 0x2e4;
+pub const BTN_TRIGGER_HAPPY38: u16 = 0x2e5;
+pub const BTN_TRIGGER_HAPPY39: u16 = 0x2e6;
+pub const BTN_TRIGGER_HAPPY40: u16 = 0x2e7;
+
+pub const KEY_MIN_INTERESTING: u16 = KEY_MUTE;
+pub const KEY_MAX: u16 = 0x2ff;
+pub const KEY_CNT: u16 = (KEY_MAX + 1);
+
+pub const REL_X: u16 = 0x00;
+pub const REL_Y: u16 = 0x01;
+pub const REL_Z: u16 = 0x02;
+pub const REL_RX: u16 = 0x03;
+pub const REL_RY: u16 = 0x04;
+pub const REL_RZ: u16 = 0x05;
+pub const REL_HWHEEL: u16 = 0x06;
+pub const REL_DIAL: u16 = 0x07;
+pub const REL_WHEEL: u16 = 0x08;
+pub const REL_MISC: u16 = 0x09;
+pub const REL_MAX: u16 = 0x0f;
+pub const REL_CNT: u16 = (REL_MAX + 1);
+
+pub const ABS_X: u16 = 0x00;
+pub const ABS_Y: u16 = 0x01;
+pub const ABS_Z: u16 = 0x02;
+pub const ABS_RX: u16 = 0x03;
+pub const ABS_RY: u16 = 0x04;
+pub const ABS_RZ: u16 = 0x05;
+pub const ABS_THROTTLE: u16 = 0x06;
+pub const ABS_RUDDER: u16 = 0x07;
+pub const ABS_WHEEL: u16 = 0x08;
+pub const ABS_GAS: u16 = 0x09;
+pub const ABS_BRAKE: u16 = 0x0a;
+pub const ABS_HAT0X: u16 = 0x10;
+pub const ABS_HAT0Y: u16 = 0x11;
+pub const ABS_HAT1X: u16 = 0x12;
+pub const ABS_HAT1Y: u16 = 0x13;
+pub const ABS_HAT2X: u16 = 0x14;
+pub const ABS_HAT2Y: u16 = 0x15;
+pub const ABS_HAT3X: u16 = 0x16;
+pub const ABS_HAT3Y: u16 = 0x17;
+pub const ABS_PRESSURE: u16 = 0x18;
+pub const ABS_DISTANCE: u16 = 0x19;
+pub const ABS_TILT_X: u16 = 0x1a;
+pub const ABS_TILT_Y: u16 = 0x1b;
+pub const ABS_TOOL_WIDTH: u16 = 0x1c;
+
+pub const ABS_VOLUME: u16 = 0x20;
+
+pub const ABS_MISC: u16 = 0x28;
+
+pub const ABS_MT_SLOT: u16 = 0x2f;
+pub const ABS_MT_TOUCH_MAJOR: u16 = 0x30;
+pub const ABS_MT_TOUCH_MINOR: u16 = 0x31;
+pub const ABS_MT_WIDTH_MAJOR: u16 = 0x32;
+pub const ABS_MT_WIDTH_MINOR: u16 = 0x33;
+pub const ABS_MT_ORIENTATION: u16 = 0x34;
+pub const ABS_MT_POSITION_X: u16 = 0x35;
+pub const ABS_MT_POSITION_Y: u16 = 0x36;
+pub const ABS_MT_TOOL_TYPE: u16 = 0x37;
+pub const ABS_MT_BLOB_ID: u16 = 0x38;
+pub const ABS_MT_TRACKING_ID: u16 = 0x39;
+pub const ABS_MT_PRESSURE: u16 = 0x3a;
+pub const ABS_MT_DISTANCE: u16 = 0x3b;
+pub const ABS_MT_TOOL_X: u16 = 0x3c;
+pub const ABS_MT_TOOL_Y: u16 = 0x3d;
+
+pub const ABS_MAX: u16 = 0x3f;
+pub const ABS_CNT: u16 = (ABS_MAX + 1);
+
+pub const MSC_SERIAL: u16 = 0x00;
+pub const MSC_PULSELED: u16 = 0x01;
+pub const MSC_GESTURE: u16 = 0x02;
+pub const MSC_RAW: u16 = 0x03;
+pub const MSC_SCAN: u16 = 0x04;
+pub const MSC_TIMESTAMP: u16 = 0x05;
+pub const MSC_MAX: u16 = 0x07;
+pub const MSC_CNT: u16 = (MSC_MAX + 1);
+
+pub const LED_NUML: u16 = 0x00;
+pub const LED_CAPSL: u16 = 0x01;
+pub const LED_SCROLLL: u16 = 0x02;
+pub const LED_COMPOSE: u16 = 0x03;
+pub const LED_KANA: u16 = 0x04;
+pub const LED_SLEEP: u16 = 0x05;
+pub const LED_SUSPEND: u16 = 0x06;
+pub const LED_MUTE: u16 = 0x07;
+pub const LED_MISC: u16 = 0x08;
+pub const LED_MAIL: u16 = 0x09;
+pub const LED_CHARGING: u16 = 0x0a;
+pub const LED_MAX: u16 = 0x0f;
+pub const LED_CNT: u16 = (LED_MAX + 1);
+
+pub const REP_DELAY: u16 = 0x00;
+pub const REP_PERIOD: u16 = 0x01;
+pub const REP_MAX: u16 = 0x01;
+pub const REP_CNT: u16 = (REP_MAX + 1);
+
+// Should match linux/virtio_input.h
+pub const VIRTIO_INPUT_CFG_ID_NAME: u8 = 0x01;
+pub const VIRTIO_INPUT_CFG_ID_SERIAL: u8 = 0x02;
+pub const VIRTIO_INPUT_CFG_PROP_BITS: u8 = 0x10;
+pub const VIRTIO_INPUT_CFG_EV_BITS: u8 = 0x11;
+pub const VIRTIO_INPUT_CFG_ABS_INFO: u8 = 0x12;
+pub const VIRTIO_INPUT_CFG_ID_DEVIDS: u8 = 0x03;
diff --git a/devices/src/virtio/input/defaults.rs b/devices/src/virtio/input/defaults.rs
new file mode 100644
index 0000000..f5343f7
--- /dev/null
+++ b/devices/src/virtio/input/defaults.rs
@@ -0,0 +1,201 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::constants::*;
+use super::virtio_input_bitmap;
+use super::virtio_input_device_ids;
+use super::VirtioInputConfig;
+use std::collections::BTreeMap;
+use virtio::input::virtio_input_absinfo;
+
+/// Instantiates a VirtioInputConfig object with the default configuration for a trackpad. It
+/// supports touch, left button and right button events, as well as X and Y axis.
+pub fn new_trackpad_config(width: u32, height: u32) -> VirtioInputConfig {
+    VirtioInputConfig::new(
+        virtio_input_device_ids::new(0, 0, 0, 0),
+        "Crosvm Virtio Trackpad".as_bytes().to_vec(),
+        "virtio-trackpad".as_bytes().to_vec(),
+        virtio_input_bitmap::new([0u8; 128]),
+        default_trackpad_events(),
+        default_trackpad_absinfo(width, height),
+    )
+}
+
+/// Instantiates a VirtioInputConfig object with the default configuration for a mouse.
+/// It supports left, right and middle buttons, as wel as X, Y and wheel relative axes.
+pub fn new_mouse_config() -> VirtioInputConfig {
+    VirtioInputConfig::new(
+        virtio_input_device_ids::new(0, 0, 0, 0),
+        "Crosvm Virtio Mouse".as_bytes().to_vec(),
+        "virtio-mouse".as_bytes().to_vec(),
+        virtio_input_bitmap::new([0u8; 128]),
+        default_mouse_events(),
+        BTreeMap::new(),
+    )
+}
+
+/// Instantiates a VirtioInputConfig object with the default configuration for a keyboard.
+/// It supports the same keys as a en-us keyboard and the CAPSLOCK, NUMLOCK and SCROLLLOCK leds.
+pub fn new_keyboard_config() -> VirtioInputConfig {
+    VirtioInputConfig::new(
+        virtio_input_device_ids::new(0, 0, 0, 0),
+        "Crosvm Virtio Keyboard".as_bytes().to_vec(),
+        "virtio-keyboard".as_bytes().to_vec(),
+        virtio_input_bitmap::new([0u8; 128]),
+        default_keyboard_events(),
+        BTreeMap::new(),
+    )
+}
+
+fn default_trackpad_absinfo(width: u32, height: u32) -> BTreeMap<u16, virtio_input_absinfo> {
+    let mut absinfo: BTreeMap<u16, virtio_input_absinfo> = BTreeMap::new();
+    absinfo.insert(ABS_X, virtio_input_absinfo::new(0, width, 0, 0));
+    absinfo.insert(ABS_Y, virtio_input_absinfo::new(0, height, 0, 0));
+    absinfo
+}
+
+fn default_trackpad_events() -> BTreeMap<u16, virtio_input_bitmap> {
+    let mut supported_events: BTreeMap<u16, virtio_input_bitmap> = BTreeMap::new();
+    supported_events.insert(
+        EV_KEY,
+        virtio_input_bitmap::from_bits(&[BTN_TOOL_FINGER, BTN_TOUCH, BTN_LEFT, BTN_RIGHT]),
+    );
+    supported_events.insert(EV_ABS, virtio_input_bitmap::from_bits(&[ABS_X, ABS_Y]));
+    supported_events
+}
+
+fn default_mouse_events() -> BTreeMap<u16, virtio_input_bitmap> {
+    let mut supported_events: BTreeMap<u16, virtio_input_bitmap> = BTreeMap::new();
+    supported_events.insert(
+        EV_KEY,
+        virtio_input_bitmap::from_bits(&[BTN_LEFT, BTN_RIGHT, BTN_MIDDLE]),
+    );
+    supported_events.insert(
+        EV_REL,
+        virtio_input_bitmap::from_bits(&[REL_X, REL_Y, REL_WHEEL]),
+    );
+    supported_events
+}
+
+fn default_keyboard_events() -> BTreeMap<u16, virtio_input_bitmap> {
+    let mut supported_events: BTreeMap<u16, virtio_input_bitmap> = BTreeMap::new();
+    supported_events.insert(
+        EV_KEY,
+        virtio_input_bitmap::from_bits(&[
+            KEY_ESC,
+            KEY_1,
+            KEY_2,
+            KEY_3,
+            KEY_4,
+            KEY_5,
+            KEY_6,
+            KEY_7,
+            KEY_8,
+            KEY_9,
+            KEY_0,
+            KEY_MINUS,
+            KEY_EQUAL,
+            KEY_BACKSPACE,
+            KEY_TAB,
+            KEY_Q,
+            KEY_W,
+            KEY_E,
+            KEY_R,
+            KEY_T,
+            KEY_Y,
+            KEY_U,
+            KEY_I,
+            KEY_O,
+            KEY_P,
+            KEY_LEFTBRACE,
+            KEY_RIGHTBRACE,
+            KEY_ENTER,
+            KEY_LEFTCTRL,
+            KEY_A,
+            KEY_S,
+            KEY_D,
+            KEY_F,
+            KEY_G,
+            KEY_H,
+            KEY_J,
+            KEY_K,
+            KEY_L,
+            KEY_SEMICOLON,
+            KEY_APOSTROPHE,
+            KEY_GRAVE,
+            KEY_LEFTSHIFT,
+            KEY_BACKSLASH,
+            KEY_Z,
+            KEY_X,
+            KEY_C,
+            KEY_V,
+            KEY_B,
+            KEY_N,
+            KEY_M,
+            KEY_COMMA,
+            KEY_DOT,
+            KEY_SLASH,
+            KEY_RIGHTSHIFT,
+            KEY_KPASTERISK,
+            KEY_LEFTALT,
+            KEY_SPACE,
+            KEY_CAPSLOCK,
+            KEY_F1,
+            KEY_F2,
+            KEY_F3,
+            KEY_F4,
+            KEY_F5,
+            KEY_F6,
+            KEY_F7,
+            KEY_F8,
+            KEY_F9,
+            KEY_F10,
+            KEY_NUMLOCK,
+            KEY_SCROLLLOCK,
+            KEY_KP7,
+            KEY_KP8,
+            KEY_KP9,
+            KEY_KPMINUS,
+            KEY_KP4,
+            KEY_KP5,
+            KEY_KP6,
+            KEY_KPPLUS,
+            KEY_KP1,
+            KEY_KP2,
+            KEY_KP3,
+            KEY_KP0,
+            KEY_KPDOT,
+            KEY_F11,
+            KEY_F12,
+            KEY_KPENTER,
+            KEY_RIGHTCTRL,
+            KEY_KPSLASH,
+            KEY_SYSRQ,
+            KEY_RIGHTALT,
+            KEY_HOME,
+            KEY_UP,
+            KEY_PAGEUP,
+            KEY_LEFT,
+            KEY_RIGHT,
+            KEY_END,
+            KEY_DOWN,
+            KEY_PAGEDOWN,
+            KEY_INSERT,
+            KEY_DELETE,
+            KEY_PAUSE,
+            KEY_MENU,
+            KEY_PRINT,
+            KEY_POWER,
+        ]),
+    );
+    supported_events.insert(
+        EV_REP,
+        virtio_input_bitmap::from_bits(&[REP_DELAY, REP_PERIOD]),
+    );
+    supported_events.insert(
+        EV_LED,
+        virtio_input_bitmap::from_bits(&[LED_CAPSL, LED_NUML, LED_SCROLLL]),
+    );
+    supported_events
+}
diff --git a/devices/src/virtio/input/evdev.rs b/devices/src/virtio/input/evdev.rs
new file mode 100644
index 0000000..34fedb9
--- /dev/null
+++ b/devices/src/virtio/input/evdev.rs
@@ -0,0 +1,257 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use std::os::unix::io::AsRawFd;
+
+use data_model::Le32;
+use sys_util::{ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};
+
+use super::constants::*;
+use super::virtio_input_absinfo;
+use super::virtio_input_bitmap;
+use super::virtio_input_device_ids;
+use super::InputError;
+use super::Result;
+
+use std::collections::BTreeMap;
+use std::os::raw::c_uint;
+use std::ptr::null;
+
+const EVDEV: c_uint = 69;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct evdev_buffer {
+    buffer: [std::os::raw::c_uchar; 128],
+}
+
+impl evdev_buffer {
+    fn new() -> evdev_buffer {
+        evdev_buffer {
+            buffer: [0 as std::os::raw::c_uchar; 128],
+        }
+    }
+
+    fn get(&self, bit: usize) -> bool {
+        let idx = bit / 8;
+        let inner_bit = bit % 8;
+        self.buffer
+            .get(idx)
+            .map_or(false, |val| val & (1u8 << inner_bit) != 0)
+    }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct evdev_id {
+    bustype: u16,
+    vendor: u16,
+    product: u16,
+    version: u16,
+}
+
+impl evdev_id {
+    fn new() -> evdev_id {
+        evdev_id {
+            bustype: 0,
+            vendor: 0,
+            product: 0,
+            version: 0,
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct evdev_abs_info {
+    // These should technically by signed ints, but Le32 is only compatible with u32 and we only
+    // forward the bytes but don't care about its actual values.
+    value: u32,
+    minimum: u32,
+    maximum: u32,
+    fuzz: u32,
+    flat: u32,
+    resolution: u32,
+}
+
+impl evdev_abs_info {
+    fn new() -> evdev_abs_info {
+        evdev_abs_info {
+            value: 0,
+            minimum: 0,
+            maximum: 0,
+            fuzz: 0,
+            flat: 0,
+            resolution: 0,
+        }
+    }
+}
+
+impl From<evdev_abs_info> for virtio_input_absinfo {
+    fn from(other: evdev_abs_info) -> Self {
+        virtio_input_absinfo {
+            min: Le32::from(other.minimum),
+            max: Le32::from(other.maximum),
+            fuzz: Le32::from(other.fuzz),
+            flat: Le32::from(other.flat),
+        }
+    }
+}
+
+ioctl_ior_nr!(EVIOCGID, EVDEV, 0x02, evdev_id);
+ioctl_ior_nr!(EVIOCGNAME, EVDEV, 0x06, evdev_buffer);
+ioctl_ior_nr!(EVIOCGUNIQ, EVDEV, 0x08, evdev_buffer);
+ioctl_ior_nr!(EVIOCGPROP, EVDEV, 0x09, evdev_buffer);
+ioctl_ior_nr!(EVIOCGBIT, EVDEV, 0x20 + evt, evdev_buffer, evt);
+ioctl_ior_nr!(EVIOCGABS, EVDEV, 0x40 + abs, evdev_abs_info, abs);
+ioctl_iow_nr!(EVIOCGRAB, EVDEV, 0x90, u32);
+
+fn errno() -> sys_util::Error {
+    sys_util::Error::last()
+}
+
+/// Gets id information from an event device (see EVIOCGID ioctl for details).
+pub fn device_ids<T: AsRawFd>(fd: &T) -> Result<virtio_input_device_ids> {
+    let mut dev_id = evdev_id::new();
+    let len = unsafe {
+        // Safe because the kernel won't write more than size of evdev_id and we check the return
+        // value
+        ioctl_with_mut_ref(fd, EVIOCGID(), &mut dev_id)
+    };
+    if len < 0 {
+        return Err(InputError::EvdevIdError(errno()));
+    }
+    Ok(virtio_input_device_ids::new(
+        dev_id.bustype,
+        dev_id.vendor,
+        dev_id.product,
+        dev_id.version,
+    ))
+}
+
+/// Gets the name of an event device (see EVIOCGNAME ioctl for details).
+pub fn name<T: AsRawFd>(fd: &T) -> Result<Vec<u8>> {
+    let mut name = evdev_buffer::new();
+    let len = unsafe {
+        // Safe because the kernel won't write more than size of evdev_buffer and we check the
+        // return value
+        ioctl_with_mut_ref(fd, EVIOCGNAME(), &mut name)
+    };
+    if len < 0 {
+        return Err(InputError::EvdevNameError(errno()));
+    }
+    Ok(name.buffer[0..len as usize].to_vec())
+}
+
+/// Gets the unique (serial) name of an event device (see EVIOCGUNIQ ioctl for details).
+pub fn serial_name<T: AsRawFd>(fd: &T) -> Result<Vec<u8>> {
+    let mut uniq = evdev_buffer::new();
+    let len = unsafe {
+        // Safe because the kernel won't write more than size of evdev_buffer and we check the
+        // return value
+        ioctl_with_mut_ref(fd, EVIOCGUNIQ(), &mut uniq)
+    };
+    if len < 0 {
+        return Err(InputError::EvdevSerialError(errno()));
+    }
+    Ok(uniq.buffer[0..len as usize].to_vec())
+}
+
+/// Gets the properties of an event device (see EVIOCGPROP ioctl for details).
+pub fn properties<T: AsRawFd>(fd: &T) -> Result<virtio_input_bitmap> {
+    let mut props = evdev_buffer::new();
+    let len = unsafe {
+        // Safe because the kernel won't write more than size of evdev_buffer and we check the
+        // return value
+        ioctl_with_mut_ref(fd, EVIOCGPROP(), &mut props)
+    };
+    if len < 0 {
+        return Err(InputError::EvdevPropertiesError(errno()));
+    }
+    Ok(virtio_input_bitmap::new(props.buffer))
+}
+
+/// Gets the event types supported by an event device as well as the event codes supported for each
+/// type (see EVIOCGBIT ioctl for details).
+pub fn supported_events<T: AsRawFd>(fd: &T) -> Result<BTreeMap<u16, virtio_input_bitmap>> {
+    let mut evts: BTreeMap<u16, virtio_input_bitmap> = BTreeMap::new();
+
+    let mut evt_types = evdev_buffer::new();
+    let len = unsafe {
+        // Safe because the kernel won't write more than size of evdev_buffer and we check the
+        // return value
+        ioctl_with_mut_ref(fd, EVIOCGBIT(0), &mut evt_types)
+    };
+    if len < 0 {
+        return Err(InputError::EvdevEventTypesError(errno()));
+    }
+
+    // no need to ask for zero (EV_SYN) since it's always supported and treated as a special case
+    for ev in 1..EV_MAX {
+        if ev == EV_REP || !evt_types.get(ev as usize) {
+            // Event type not supported, skip it.
+            continue;
+        }
+        // Create a new zero-filled buffer every time to avoid carry-overs.
+        let mut evt_codes = evdev_buffer::new();
+        let len = unsafe {
+            // Safe because the kernel won't write more than size of evdev_buffer and we check the
+            // return value
+            ioctl_with_mut_ref(fd, EVIOCGBIT(ev as c_uint), &mut evt_codes)
+        };
+        if len < 0 {
+            return Err(InputError::EvdevEventTypesError(errno()));
+        }
+        evts.insert(ev, virtio_input_bitmap::new(evt_codes.buffer));
+    }
+    Ok(evts)
+}
+
+/// Gets the absolute axes of an event device (see EVIOCGABS ioctl for details).
+pub fn abs_info<T: AsRawFd>(fd: &T) -> BTreeMap<u16, virtio_input_absinfo> {
+    let mut ret: BTreeMap<u16, virtio_input_absinfo> = BTreeMap::new();
+
+    for abs in 0..ABS_MAX {
+        // Create a new one, zero-ed out every time to avoid carry-overs.
+        let mut abs_info = evdev_abs_info::new();
+        let len = unsafe {
+            // Safe because the kernel won't write more than size of evdev_buffer and we check the
+            // return value
+            ioctl_with_mut_ref(fd, EVIOCGABS(abs as c_uint), &mut abs_info)
+        };
+        if len > 0 {
+            ret.insert(abs, virtio_input_absinfo::from(abs_info));
+        }
+    }
+    ret
+}
+
+/// Grabs an event device (see EVIOCGGRAB ioctl for details). After this function succeeds the given
+/// fd has exclusive access to the device, effectively making it unusable for any other process in
+/// the host.
+pub fn grab_evdev<T: AsRawFd>(fd: &mut T) -> Result<()> {
+    let val: u32 = 1;
+    let ret = unsafe {
+        // Safe because the kernel only read the value of the ptr and we check the return value
+        ioctl_with_ref(fd, EVIOCGRAB(), &val)
+    };
+    if ret == 0 {
+        Ok(())
+    } else {
+        Err(InputError::EvdevGrabError(errno()))
+    }
+}
+
+pub fn ungrab_evdev<T: AsRawFd>(fd: &mut T) -> Result<()> {
+    let ret = unsafe {
+        // Safe because the kernel only reads the value of the ptr (doesn't dereference) and
+        // we check the return value
+        ioctl_with_ptr(fd, EVIOCGRAB(), null::<u32>())
+    };
+    if ret == 0 {
+        Ok(())
+    } else {
+        Err(InputError::EvdevGrabError(errno()))
+    }
+}
diff --git a/devices/src/virtio/input/event_source.rs b/devices/src/virtio/input/event_source.rs
new file mode 100644
index 0000000..0f28f4d
--- /dev/null
+++ b/devices/src/virtio/input/event_source.rs
@@ -0,0 +1,337 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use super::constants::*;
+use super::evdev::{grab_evdev, ungrab_evdev};
+use super::virtio_input_event;
+use super::InputError;
+use super::Result;
+use data_model::DataInit;
+use std::collections::VecDeque;
+use std::io::Read;
+use std::io::Write;
+use std::mem::size_of;
+use std::os::unix::io::{AsRawFd, RawFd};
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct input_event {
+    timestamp_fields: [u64; 2],
+    pub type_: u16,
+    pub code: u16,
+    pub value: u32,
+}
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for input_event {}
+
+impl input_event {
+    const EVENT_SIZE: usize = size_of::<input_event>();
+
+    fn from_virtio_input_event(other: &virtio_input_event) -> input_event {
+        input_event {
+            timestamp_fields: [0, 0],
+            type_: other.type_.into(),
+            code: other.code.into(),
+            value: other.value.into(),
+        }
+    }
+}
+
+/// Encapsulates a socket or device node into an abstract event source, providing a common
+/// interface.
+/// It supports read and write operations to provide and accept events just like an event device
+/// node would, except that it handles virtio_input_event instead of input_event structures.
+/// It's necessary to call receive_events() before events are available for read.
+pub trait EventSource: Read + Write + AsRawFd {
+    /// Perform any necessary initialization before receiving and sending events from/to the source.
+    fn init(&mut self) -> Result<()> {
+        Ok(())
+    }
+    /// Perform any necessary cleanup when the device will no longer be used.
+    fn finalize(&mut self) -> Result<()> {
+        Ok(())
+    }
+
+    /// Receive events from the source, filters them and stores them in a queue for future
+    /// consumption by reading from this object. Returns the number of new non filtered events
+    /// received. This function may block waiting for events to be available.
+    fn receive_events(&mut self) -> Result<usize>;
+    /// Returns the number of received events that have not been filtered or consumed yet.
+    fn available_events_count(&self) -> usize;
+}
+
+// Try to read 16 events at a time to match what the linux guest driver does.
+const READ_BUFFER_SIZE: usize = 16 * size_of::<input_event>();
+
+// The read buffer needs to be aligned to the alignment of input_event, which is aligned as u64
+#[repr(align(8))]
+pub struct ReadBuffer {
+    buffer: [u8; READ_BUFFER_SIZE],
+}
+
+/// Encapsulates implementation details common to all kinds of event sources.
+pub struct EventSourceImpl<T> {
+    source: T,
+    queue: VecDeque<virtio_input_event>,
+    read_buffer: ReadBuffer,
+    // The read index accounts for incomplete events read previously.
+    read_idx: usize,
+}
+
+// Reads input events from the source.
+// Events are originally read as input_event structs and converted to virtio_input_event internally.
+impl<T: Read> EventSourceImpl<T> {
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        let mut bytes = 0usize;
+        for evt_slice in buf.chunks_exact_mut(virtio_input_event::EVENT_SIZE) {
+            match self.queue.pop_front() {
+                None => {
+                    break;
+                }
+                Some(evt) => {
+                    evt_slice.copy_from_slice(evt.as_slice());
+                    bytes += evt_slice.len();
+                }
+            }
+        }
+        Ok(bytes)
+    }
+}
+
+// Writes input events to the source.
+// Events come as virtio_input_event structs and are converted to input_event internally.
+impl<T: Write> EventSourceImpl<T> {
+    fn write<F: Fn(&virtio_input_event) -> bool>(
+        &mut self,
+        buf: &[u8],
+        event_filter: F,
+    ) -> std::io::Result<usize> {
+        for evt_slice in buf.chunks_exact(virtio_input_event::EVENT_SIZE) {
+            // Don't use from_slice() here, the buffer is not guaranteed to be properly aligned.
+            let mut vio_evt = virtio_input_event::new(0, 0, 0);
+            vio_evt.as_mut_slice().copy_from_slice(evt_slice);
+            if !event_filter(&vio_evt) {
+                continue;
+            }
+            let evt = input_event::from_virtio_input_event(&vio_evt);
+            self.source.write_all(evt.as_slice())?;
+        }
+
+        let len = buf.len() - buf.len() % virtio_input_event::EVENT_SIZE;
+        Ok(len)
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        self.source.flush()
+    }
+}
+
+impl<T: AsRawFd> EventSourceImpl<T> {
+    fn as_raw_fd(&self) -> RawFd {
+        self.source.as_raw_fd()
+    }
+}
+
+impl<T> EventSourceImpl<T>
+where
+    T: Read + Write + AsRawFd,
+{
+    // Receive events from the source and store them in a queue, unless they should be filtered out.
+    fn receive_events<F: Fn(&input_event) -> bool>(&mut self, event_filter: F) -> Result<usize> {
+        let read = self
+            .source
+            .read(&mut self.read_buffer.buffer[self.read_idx..])
+            .map_err(|e| InputError::EventsReadError(e))?;
+        let buff_size = read + self.read_idx;
+
+        for evt_slice in self
+            .read_buffer
+            .buffer
+            .chunks_exact(input_event::EVENT_SIZE)
+        {
+            let input_evt = match input_event::from_slice(evt_slice) {
+                Some(x) => x,
+                None => {
+                    // This shouldn't happen because all slices (even the last one) are guaranteed
+                    // to have the correct size and be properly aligned.
+                    error!(
+                        "Failed converting a slice of sice {} to input_event",
+                        evt_slice.len()
+                    );
+                    // Skipping the event here effectively means no events will be received, because
+                    // if from_slice fails once it will fail always.
+                    continue;
+                }
+            };
+            if !event_filter(&input_evt) {
+                continue;
+            }
+            let vio_evt = virtio_input_event::from_input_event(input_evt);
+            self.queue.push_back(vio_evt);
+        }
+
+        let remainder = buff_size % input_event::EVENT_SIZE;
+        // If there is an incomplete event at the end of the buffer, it needs to be moved to the
+        // beginning and the next read operation must write right after it.
+        if remainder != 0 {
+            warn!("read incomplete event from source");
+            // The copy should only happen if there is at least one complete event in the buffer,
+            // otherwise source and destination would be the same.
+            if buff_size != remainder {
+                let (des, src) = self.read_buffer.buffer.split_at_mut(buff_size - remainder);
+                des[..remainder].copy_from_slice(src);
+            }
+        }
+        self.read_idx = remainder;
+
+        let received_events = buff_size / input_event::EVENT_SIZE;
+
+        Ok(received_events)
+    }
+
+    fn available_events(&self) -> usize {
+        self.queue.len()
+    }
+
+    fn new(source: T) -> EventSourceImpl<T> {
+        EventSourceImpl {
+            source,
+            queue: VecDeque::new(),
+            read_buffer: ReadBuffer {
+                buffer: [0u8; READ_BUFFER_SIZE],
+            },
+            read_idx: 0,
+        }
+    }
+}
+
+/// Encapsulates a (unix) socket as an event source.
+pub struct SocketEventSource<T> {
+    evt_source_impl: EventSourceImpl<T>,
+}
+
+impl<T> SocketEventSource<T>
+where
+    T: Read + Write + AsRawFd,
+{
+    pub fn new(source: T) -> SocketEventSource<T> {
+        SocketEventSource {
+            evt_source_impl: EventSourceImpl::new(source),
+        }
+    }
+}
+
+impl<T: Read> Read for SocketEventSource<T> {
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.evt_source_impl.read(buf)
+    }
+}
+
+impl<T> Write for SocketEventSource<T>
+where
+    T: Read + Write + AsRawFd,
+{
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.evt_source_impl.write(buf, |_evt| true)
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        self.evt_source_impl.flush()
+    }
+}
+
+impl<T: AsRawFd> AsRawFd for SocketEventSource<T> {
+    fn as_raw_fd(&self) -> RawFd {
+        self.evt_source_impl.as_raw_fd()
+    }
+}
+
+impl<T> EventSource for SocketEventSource<T>
+where
+    T: Read + Write + AsRawFd,
+{
+    fn init(&mut self) -> Result<()> {
+        grab_evdev(self)
+    }
+
+    fn finalize(&mut self) -> Result<()> {
+        ungrab_evdev(self)
+    }
+
+    fn receive_events(&mut self) -> Result<usize> {
+        self.evt_source_impl.receive_events(|_evt| true)
+    }
+
+    fn available_events_count(&self) -> usize {
+        self.evt_source_impl.available_events()
+    }
+}
+
+/// Encapsulates an event device node as an event source
+pub struct EvdevEventSource<T> {
+    evt_source_impl: EventSourceImpl<T>,
+}
+
+impl<T> EvdevEventSource<T>
+where
+    T: Read + Write + AsRawFd,
+{
+    pub fn new(source: T) -> EvdevEventSource<T> {
+        EvdevEventSource {
+            evt_source_impl: EventSourceImpl::new(source),
+        }
+    }
+}
+
+impl<T: Read> Read for EvdevEventSource<T> {
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.evt_source_impl.read(buf)
+    }
+}
+
+impl<T> Write for EvdevEventSource<T>
+where
+    T: Read + Write + AsRawFd,
+{
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.evt_source_impl.write(buf, |evt| {
+            // Miscellaneous events produced by the device are sent back to it by the kernel input
+            // subsystem, but because these events are handled by the host kernel as well as the
+            // guest the device would get them twice. Which would prompt the device to send the
+            // event to the guest again entering an infinite loop.
+            evt.type_ != EV_MSC
+        })
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        self.evt_source_impl.flush()
+    }
+}
+
+impl<T: AsRawFd> AsRawFd for EvdevEventSource<T> {
+    fn as_raw_fd(&self) -> RawFd {
+        self.evt_source_impl.as_raw_fd()
+    }
+}
+
+impl<T> EventSource for EvdevEventSource<T>
+where
+    T: Read + Write + AsRawFd,
+{
+    fn init(&mut self) -> Result<()> {
+        grab_evdev(self)
+    }
+
+    fn finalize(&mut self) -> Result<()> {
+        ungrab_evdev(self)
+    }
+
+    fn receive_events(&mut self) -> Result<usize> {
+        self.evt_source_impl.receive_events(|_evt| true)
+    }
+
+    fn available_events_count(&self) -> usize {
+        self.evt_source_impl.available_events()
+    }
+}
diff --git a/devices/src/virtio/input/mod.rs b/devices/src/virtio/input/mod.rs
new file mode 100644
index 0000000..82c15c0
--- /dev/null
+++ b/devices/src/virtio/input/mod.rs
@@ -0,0 +1,702 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[allow(dead_code)]
+mod constants;
+mod defaults;
+mod evdev;
+mod event_source;
+
+use self::constants::*;
+
+use std::os::unix::io::{AsRawFd, RawFd};
+
+use data_model::{DataInit, Le16, Le32};
+use sys_util::{EventFd, GuestMemory, PollContext, PollToken};
+
+use self::event_source::{input_event, EvdevEventSource, EventSource, SocketEventSource};
+use super::{Queue, VirtioDevice, INTERRUPT_STATUS_USED_RING, TYPE_INPUT};
+use std::cmp::min;
+use std::collections::BTreeMap;
+use std::io::Read;
+use std::io::Write;
+use std::mem::size_of;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::thread;
+
+const EVENT_QUEUE_SIZE: u16 = 64;
+const STATUS_QUEUE_SIZE: u16 = 64;
+const QUEUE_SIZES: &'static [u16] = &[EVENT_QUEUE_SIZE, STATUS_QUEUE_SIZE];
+
+#[derive(Debug)]
+pub enum InputError {
+    /// Failed to write events to the source
+    EventsWriteError(sys_util::GuestMemoryError),
+    /// Failed to read events from the source
+    EventsReadError(std::io::Error),
+    // Failed to get name of event device
+    EvdevIdError(sys_util::Error),
+    // Failed to get name of event device
+    EvdevNameError(sys_util::Error),
+    // Failed to get serial name of event device
+    EvdevSerialError(sys_util::Error),
+    // Failed to get properties of event device
+    EvdevPropertiesError(sys_util::Error),
+    // Failed to get event types supported by device
+    EvdevEventTypesError(sys_util::Error),
+    // Failed to get axis information of event device
+    EvdevAbsInfoError(sys_util::Error),
+    // Failed to grab event device
+    EvdevGrabError(sys_util::Error),
+}
+pub type Result<T> = std::result::Result<T, InputError>;
+
+#[derive(Copy, Clone, Default, Debug)]
+#[repr(C)]
+pub struct virtio_input_device_ids {
+    bustype: Le16,
+    vendor: Le16,
+    product: Le16,
+    version: Le16,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_input_device_ids {}
+
+impl virtio_input_device_ids {
+    fn new(bustype: u16, product: u16, vendor: u16, version: u16) -> virtio_input_device_ids {
+        virtio_input_device_ids {
+            bustype: Le16::from(bustype),
+            vendor: Le16::from(vendor),
+            product: Le16::from(product),
+            version: Le16::from(version),
+        }
+    }
+}
+
+#[derive(Copy, Clone, Default, Debug)]
+#[repr(C)]
+pub struct virtio_input_absinfo {
+    min: Le32,
+    max: Le32,
+    fuzz: Le32,
+    flat: Le32,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_input_absinfo {}
+
+impl virtio_input_absinfo {
+    fn new(min: u32, max: u32, fuzz: u32, flat: u32) -> virtio_input_absinfo {
+        virtio_input_absinfo {
+            min: Le32::from(min),
+            max: Le32::from(max),
+            fuzz: Le32::from(fuzz),
+            flat: Le32::from(flat),
+        }
+    }
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct virtio_input_config {
+    select: u8,
+    subsel: u8,
+    size: u8,
+    reserved: [u8; 5],
+    payload: [u8; 128],
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_input_config {}
+
+impl virtio_input_config {
+    fn new() -> virtio_input_config {
+        virtio_input_config {
+            select: 0,
+            subsel: 0,
+            size: 0,
+            reserved: [0u8; 5],
+            payload: [0u8; 128],
+        }
+    }
+
+    fn set_payload_slice(&mut self, slice: &[u8]) {
+        let bytes_written = match (&mut self.payload[..]).write(slice) {
+            Ok(x) => x,
+            Err(_) => {
+                // This won't happen because write is guaranteed to succeed with slices
+                unreachable!();
+            }
+        };
+        self.size = bytes_written as u8;
+        if bytes_written < slice.len() {
+            // This shouldn't happen since everywhere this function is called the size is guaranteed
+            // to be at most 128 bytes (the size of the payload)
+            warn!("Slice is too long to fit in payload");
+        }
+    }
+
+    fn set_payload_bitmap(&mut self, bitmap: &virtio_input_bitmap) {
+        self.size = bitmap.min_size();
+        self.payload.copy_from_slice(&bitmap.bitmap);
+    }
+
+    fn set_absinfo(&mut self, absinfo: &virtio_input_absinfo) {
+        self.set_payload_slice(absinfo.as_slice());
+    }
+
+    fn set_device_ids(&mut self, device_ids: &virtio_input_device_ids) {
+        self.set_payload_slice(device_ids.as_slice());
+    }
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct virtio_input_bitmap {
+    bitmap: [u8; 128],
+}
+
+impl virtio_input_bitmap {
+    fn new(bitmap: [u8; 128]) -> virtio_input_bitmap {
+        virtio_input_bitmap { bitmap }
+    }
+
+    fn len(&self) -> usize {
+        self.bitmap.len()
+    }
+
+    // Creates a bitmap from an array of bit indices
+    fn from_bits(set_indices: &[u16]) -> virtio_input_bitmap {
+        let mut ret = virtio_input_bitmap { bitmap: [0u8; 128] };
+        for idx in set_indices {
+            let byte_pos = (idx / 8) as usize;
+            let bit_byte = 1u8 << (idx % 8);
+            if byte_pos < ret.len() {
+                ret.bitmap[byte_pos] |= bit_byte;
+            } else {
+                // This would only happen if new event codes (or types, or ABS_*, etc) are defined to be
+                // larger than or equal to 1024, in which case a new version of the virtio input
+                // protocol needs to be defined.
+                // There is nothing we can do about this error except log it.
+                error!("Attempted to set an out of bounds bit: {}", idx);
+            }
+        }
+        ret
+    }
+
+    // Returns the length of the minimum array that can hold all set bits in the map
+    fn min_size(&self) -> u8 {
+        self.bitmap
+            .iter()
+            .rposition(|v| *v != 0)
+            .map_or(0, |i| i + 1) as u8
+    }
+}
+
+pub struct VirtioInputConfig {
+    select: u8,
+    subsel: u8,
+    device_ids: virtio_input_device_ids,
+    name: Vec<u8>,
+    serial_name: Vec<u8>,
+    properties: virtio_input_bitmap,
+    supported_events: BTreeMap<u16, virtio_input_bitmap>,
+    axis_info: BTreeMap<u16, virtio_input_absinfo>,
+}
+
+impl VirtioInputConfig {
+    const CONFIG_MEM_SIZE: usize = size_of::<virtio_input_config>();
+
+    fn new(
+        device_ids: virtio_input_device_ids,
+        name: Vec<u8>,
+        serial_name: Vec<u8>,
+        properties: virtio_input_bitmap,
+        supported_events: BTreeMap<u16, virtio_input_bitmap>,
+        axis_info: BTreeMap<u16, virtio_input_absinfo>,
+    ) -> VirtioInputConfig {
+        VirtioInputConfig {
+            select: 0,
+            subsel: 0,
+            device_ids,
+            name,
+            serial_name,
+            properties,
+            supported_events,
+            axis_info,
+        }
+    }
+
+    fn from_evdev<T: AsRawFd>(source: &T) -> Result<VirtioInputConfig> {
+        Ok(VirtioInputConfig::new(
+            evdev::device_ids(source)?,
+            evdev::name(source)?,
+            evdev::serial_name(source)?,
+            evdev::properties(source)?,
+            evdev::supported_events(source)?,
+            evdev::abs_info(source),
+        ))
+    }
+
+    fn validate_read_offsets(&self, offset: usize, len: usize) -> bool {
+        if offset + len > VirtioInputConfig::CONFIG_MEM_SIZE {
+            error!(
+                "Attempt to read from invalid config range: [{}..{}], valid ranges in [0..{}]",
+                offset,
+                offset + len,
+                VirtioInputConfig::CONFIG_MEM_SIZE
+            );
+            return false;
+        }
+        true
+    }
+
+    fn build_config_memory(&self) -> virtio_input_config {
+        let mut cfg = virtio_input_config::new();
+        cfg.select = self.select;
+        cfg.subsel = self.subsel;
+        match self.select {
+            VIRTIO_INPUT_CFG_ID_NAME => {
+                cfg.set_payload_slice(&self.name);
+            }
+            VIRTIO_INPUT_CFG_ID_SERIAL => {
+                cfg.set_payload_slice(&self.serial_name);
+            }
+            VIRTIO_INPUT_CFG_PROP_BITS => {
+                cfg.set_payload_bitmap(&self.properties);
+            }
+            VIRTIO_INPUT_CFG_EV_BITS => {
+                let ev_type = self.subsel as u16;
+                // zero is a special case: return all supported event types (just like EVIOCGBIT)
+                if ev_type == 0 {
+                    let events_bm = virtio_input_bitmap::from_bits(
+                        &self.supported_events.keys().cloned().collect::<Vec<u16>>(),
+                    );
+                    cfg.set_payload_bitmap(&events_bm);
+                } else if let Some(supported_codes) = self.supported_events.get(&ev_type) {
+                    cfg.set_payload_bitmap(&supported_codes);
+                }
+            }
+            VIRTIO_INPUT_CFG_ABS_INFO => {
+                let abs_axis = self.subsel as u16;
+                if let Some(absinfo) = self.axis_info.get(&abs_axis) {
+                    cfg.set_absinfo(absinfo);
+                } // else all zeroes in the payload
+            }
+            VIRTIO_INPUT_CFG_ID_DEVIDS => {
+                cfg.set_device_ids(&self.device_ids);
+            }
+            _ => {
+                warn!("Unsuported virtio input config selection: {}", self.select);
+            }
+        }
+        cfg
+    }
+
+    fn read(&self, offset: usize, data: &mut [u8]) {
+        let data_len = data.len();
+        if self.validate_read_offsets(offset, data_len) {
+            let config = self.build_config_memory();
+            data.clone_from_slice(&config.as_slice()[offset..offset + data_len]);
+        }
+    }
+
+    fn validate_write_offsets(&self, offset: usize, len: usize) -> bool {
+        const MAX_WRITABLE_BYTES: usize = 2;
+        if offset + len > MAX_WRITABLE_BYTES {
+            error!(
+                "Attempt to write to invalid config range: [{}..{}], valid ranges in [0..{}]",
+                offset,
+                offset + len,
+                MAX_WRITABLE_BYTES
+            );
+            return false;
+        }
+        true
+    }
+
+    fn write(&mut self, offset: usize, data: &[u8]) {
+        let len = data.len();
+        if self.validate_write_offsets(offset, len) {
+            let mut selectors: [u8; 2] = [self.select, self.subsel];
+            selectors[offset..offset + len].clone_from_slice(&data);
+            self.select = selectors[0];
+            self.subsel = selectors[1];
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+struct virtio_input_event {
+    type_: Le16,
+    code: Le16,
+    value: Le32,
+}
+
+// Safe because it only has data and has no implicit padding.
+unsafe impl DataInit for virtio_input_event {}
+
+impl virtio_input_event {
+    const EVENT_SIZE: usize = size_of::<virtio_input_event>();
+    fn new(type_: u16, code: u16, value: u32) -> virtio_input_event {
+        virtio_input_event {
+            type_: Le16::from(type_),
+            code: Le16::from(code),
+            value: Le32::from(value),
+        }
+    }
+
+    fn from_input_event(other: &input_event) -> virtio_input_event {
+        virtio_input_event {
+            type_: Le16::from(other.type_),
+            code: Le16::from(other.code),
+            value: Le32::from(other.value),
+        }
+    }
+}
+
+struct Worker<T: EventSource> {
+    event_source: T,
+    event_queue: Queue,
+    status_queue: Queue,
+    guest_memory: GuestMemory,
+    interrupt_status: Arc<AtomicUsize>,
+    interrupt_evt: EventFd,
+    interrupt_resample_evt: EventFd,
+}
+
+impl<T: EventSource> Worker<T> {
+    fn signal_used_queue(&self) {
+        self.interrupt_status
+            .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
+        self.interrupt_evt.write(1).unwrap();
+    }
+
+    // Send events from the source to the guest
+    fn send_events(&mut self) -> bool {
+        let queue = &mut self.event_queue;
+
+        let mut used_desc_heads = [(0, 0); EVENT_QUEUE_SIZE as usize];
+        let mut used_count = 0;
+
+        // Only consume from the queue iterator if we know we have events to send
+        while self.event_source.available_events_count() > 0 {
+            let mut queue_iter = queue.iter(&self.guest_memory);
+            match queue_iter.next() {
+                None => {
+                    break;
+                }
+                Some(avail_desc) => {
+                    if !avail_desc.is_write_only() {
+                        panic!("Received a read only descriptor on event queue");
+                    }
+                    let avail_events_size =
+                        self.event_source.available_events_count() * virtio_input_event::EVENT_SIZE;
+                    let len = min(avail_desc.len as usize, avail_events_size);
+                    if let Err(e) = self.guest_memory.read_to_memory(
+                        avail_desc.addr,
+                        &mut self.event_source,
+                        len,
+                    ) {
+                        // Read is guaranteed to succeed here, so the only possible failure would be
+                        // writing outside the guest memory region, which would mean the address and
+                        // length given in the queue descriptor are wrong.
+                        panic!("failed reading events into guest memory: {:?}", e);
+                    }
+                    used_desc_heads[used_count] = (avail_desc.index, len as u32);
+                    used_count += 1;
+                }
+            }
+        }
+        for &(desc_index, len) in &used_desc_heads[..used_count] {
+            queue.add_used(&self.guest_memory, desc_index, len);
+        }
+
+        used_count > 0
+    }
+
+    fn process_status_queue(&mut self) -> Result<bool> {
+        let queue = &mut self.status_queue;
+
+        let mut used_desc_heads = [(0, 0); EVENT_QUEUE_SIZE as usize];
+        let mut used_count = 0;
+        for avail_desc in queue.iter(&self.guest_memory) {
+            if !avail_desc.is_read_only() {
+                panic!("Received a writable descriptor on status queue");
+            }
+            let len = avail_desc.len as usize;
+            if len % virtio_input_event::EVENT_SIZE != 0 {
+                warn!(
+                    "Ignoring buffer of unexpected size on status queue: {:0}",
+                    len
+                );
+            } else {
+                self.guest_memory
+                    .write_from_memory(avail_desc.addr, &mut self.event_source, len)
+                    .map_err(|e| InputError::EventsWriteError(e))?;
+            }
+
+            used_desc_heads[used_count] = (avail_desc.index, len);
+            used_count += 1;
+        }
+
+        for &(desc_index, len) in &used_desc_heads[..used_count] {
+            queue.add_used(&self.guest_memory, desc_index, len as u32);
+        }
+        Ok(used_count > 0)
+    }
+
+    fn run(
+        &mut self,
+        event_queue_evt_fd: EventFd,
+        status_queue_evt_fd: EventFd,
+        kill_evt: EventFd,
+    ) {
+        if let Err(e) = self.event_source.init() {
+            error!("failed initializing event source: {:?}", e);
+            return;
+        }
+
+        #[derive(PollToken)]
+        enum Token {
+            EventQAvailable,
+            StatusQAvailable,
+            InputEventsAvailable,
+            InterruptResample,
+            Kill,
+        }
+        let poll_ctx: PollContext<Token> = match PollContext::new()
+            .and_then(|pc| {
+                pc.add(&event_queue_evt_fd, Token::EventQAvailable)
+                    .and(Ok(pc))
+            })
+            .and_then(|pc| {
+                pc.add(&status_queue_evt_fd, Token::StatusQAvailable)
+                    .and(Ok(pc))
+            })
+            .and_then(|pc| {
+                pc.add(&self.event_source, Token::InputEventsAvailable)
+                    .and(Ok(pc))
+            })
+            .and_then(|pc| {
+                pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+                    .and(Ok(pc))
+            })
+            .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+        {
+            Ok(poll_ctx) => poll_ctx,
+            Err(e) => {
+                error!("failed creating PollContext: {:?}", e);
+                return;
+            }
+        };
+
+        'poll: loop {
+            let poll_events = match poll_ctx.wait() {
+                Ok(poll_events) => poll_events,
+                Err(e) => {
+                    error!("failed polling for events: {:?}", e);
+                    break;
+                }
+            };
+
+            let mut needs_interrupt = false;
+            for poll_event in poll_events.iter_readable() {
+                match poll_event.token() {
+                    Token::EventQAvailable => {
+                        if let Err(e) = event_queue_evt_fd.read() {
+                            error!("failed reading event queue EventFd: {:?}", e);
+                            break 'poll;
+                        }
+                        needs_interrupt |= self.send_events();
+                    }
+                    Token::StatusQAvailable => {
+                        if let Err(e) = status_queue_evt_fd.read() {
+                            error!("failed reading status queue EventFd: {:?}", e);
+                            break 'poll;
+                        }
+                        match self.process_status_queue() {
+                            Ok(b) => needs_interrupt |= b,
+                            Err(e) => error!("failed processing status events: {:?}", e),
+                        }
+                    }
+                    Token::InputEventsAvailable => match self.event_source.receive_events() {
+                        Err(e) => error!("error receiving events: {:?}", e),
+                        Ok(_cnt) => needs_interrupt |= self.send_events(),
+                    },
+                    Token::InterruptResample => {
+                        let _ = self.interrupt_resample_evt.read();
+                        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+                            self.interrupt_evt.write(1).unwrap();
+                        }
+                    }
+                    Token::Kill => {
+                        let _ = kill_evt.read();
+                        break 'poll;
+                    }
+                }
+            }
+            if needs_interrupt {
+                self.signal_used_queue();
+            }
+        }
+
+        if let Err(e) = self.event_source.finalize() {
+            error!("failed finalizing event source: {:?}", e);
+            return;
+        }
+    }
+}
+
+/// Virtio input device
+
+pub struct Input<T: EventSource> {
+    kill_evt: Option<EventFd>,
+    config: VirtioInputConfig,
+    source: Option<T>,
+}
+
+impl<T: EventSource> Drop for Input<T> {
+    fn drop(&mut self) {
+        if let Some(kill_evt) = self.kill_evt.take() {
+            // Ignore the result because there is nothing we can do about it.
+            let _ = kill_evt.write(1);
+        }
+    }
+}
+
+impl<T> VirtioDevice for Input<T>
+where
+    T: 'static + EventSource + Send,
+{
+    fn keep_fds(&self) -> Vec<RawFd> {
+        if let Some(ref source) = self.source {
+            return vec![source.as_raw_fd()];
+        }
+        Vec::new()
+    }
+
+    fn device_type(&self) -> u32 {
+        TYPE_INPUT
+    }
+
+    fn queue_max_sizes(&self) -> &[u16] {
+        QUEUE_SIZES
+    }
+
+    fn read_config(&self, offset: u64, data: &mut [u8]) {
+        self.config.read(offset as usize, data);
+    }
+
+    fn write_config(&mut self, offset: u64, data: &[u8]) {
+        self.config.write(offset as usize, data);
+    }
+
+    fn activate(
+        &mut self,
+        mem: GuestMemory,
+        interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
+        status: Arc<AtomicUsize>,
+        mut queues: Vec<Queue>,
+        mut queue_evts: Vec<EventFd>,
+    ) {
+        if queues.len() != 2 || queue_evts.len() != 2 {
+            return;
+        }
+
+        let (self_kill_evt, kill_evt) = match EventFd::new().and_then(|e| Ok((e.try_clone()?, e))) {
+            Ok(v) => v,
+            Err(e) => {
+                error!("failed to create kill EventFd pair: {:?}", e);
+                return;
+            }
+        };
+        self.kill_evt = Some(self_kill_evt);
+
+        // Status is queue 1, event is queue 0
+        let status_queue = queues.remove(1);
+        let status_queue_evt_fd = queue_evts.remove(1);
+
+        let event_queue = queues.remove(0);
+        let event_queue_evt_fd = queue_evts.remove(0);
+
+        if let Some(source) = self.source.take() {
+            let worker_result = thread::Builder::new()
+                .name(String::from("virtio_input"))
+                .spawn(move || {
+                    let mut worker = Worker {
+                        event_source: source,
+                        event_queue,
+                        status_queue,
+                        guest_memory: mem,
+                        interrupt_status: status,
+                        interrupt_evt,
+                        interrupt_resample_evt,
+                    };
+                    worker.run(event_queue_evt_fd, status_queue_evt_fd, kill_evt);
+                });
+
+            if let Err(e) = worker_result {
+                error!("failed to spawn virtio_input worker: {}", e);
+                return;
+            }
+        } else {
+            error!("tried to activate device without a source for events");
+            return;
+        }
+    }
+}
+
+/// Creates a new virtio input device from an event device node
+pub fn new_evdev<T>(source: T) -> Result<Input<EvdevEventSource<T>>>
+where
+    T: Read + Write + AsRawFd,
+{
+    Ok(Input {
+        kill_evt: None,
+        config: VirtioInputConfig::from_evdev(&source)?,
+        source: Some(EvdevEventSource::new(source)),
+    })
+}
+
+/// Creates a new virtio trackpad device which supports (single) touch, primary and secondary
+/// buttons as well as X and Y axis.
+pub fn new_trackpad<T>(source: T, width: u32, height: u32) -> Result<Input<SocketEventSource<T>>>
+where
+    T: Read + Write + AsRawFd,
+{
+    Ok(Input {
+        kill_evt: None,
+        config: defaults::new_trackpad_config(width, height),
+        source: Some(SocketEventSource::new(source)),
+    })
+}
+
+/// Creates a new virtio mouse which supports primary, secondary, wheel and REL events.
+pub fn new_mouse<T>(source: T) -> Result<Input<SocketEventSource<T>>>
+where
+    T: Read + Write + AsRawFd,
+{
+    Ok(Input {
+        kill_evt: None,
+        config: defaults::new_mouse_config(),
+        source: Some(SocketEventSource::new(source)),
+    })
+}
+
+/// Creates a new virtio keyboard, which supports the same events as an en-us physical keyboard.
+pub fn new_keyboard<T>(source: T) -> Result<Input<SocketEventSource<T>>>
+where
+    T: Read + Write + AsRawFd,
+{
+    Ok(Input {
+        kill_evt: None,
+        config: defaults::new_keyboard_config(),
+        source: Some(SocketEventSource::new(source)),
+    })
+}
diff --git a/devices/src/virtio/mod.rs b/devices/src/virtio/mod.rs
index 78cfeb2..a9fe525 100644
--- a/devices/src/virtio/mod.rs
+++ b/devices/src/virtio/mod.rs
@@ -8,6 +8,7 @@ mod balloon;
 mod block;
 #[cfg(feature = "gpu")]
 mod gpu;
+mod input;
 mod net;
 mod p9;
 mod queue;
@@ -26,6 +27,7 @@ pub use self::balloon::*;
 pub use self::block::*;
 #[cfg(feature = "gpu")]
 pub use self::gpu::*;
+pub use self::input::*;
 pub use self::net::*;
 pub use self::p9::*;
 pub use self::queue::*;
@@ -50,6 +52,7 @@ const TYPE_BALLOON: u32 = 5;
 #[allow(dead_code)]
 const TYPE_GPU: u32 = 16;
 const TYPE_9P: u32 = 9;
+const TYPE_INPUT: u32 = 18;
 const TYPE_VSOCK: u32 = 19;
 // Additional types invented by crosvm
 const TYPE_WL: u32 = 30;
diff --git a/seccomp/arm/input_device.policy b/seccomp/arm/input_device.policy
new file mode 100644
index 0000000..877f80e
--- /dev/null
+++ b/seccomp/arm/input_device.policy
@@ -0,0 +1,37 @@
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+close: 1
+dup: 1
+dup2: 1
+exit_group: 1
+futex: 1
+# Disallow mmap with PROT_EXEC set.  The syntax here doesn't allow bit
+# negation, thus the manually negated mask constant.
+mmap: arg2 in 0xfffffffb
+mprotect: arg2 in 0xfffffffb
+# Allow MADV_DONTDUMP only.
+madvise: arg2 == 0x00000010
+munmap: 1
+read: 1
+recvfrom: 1
+sched_getaffinity: 1
+set_robust_list: 1
+sigaltstack: 1
+# Disallow clone's other than new threads.
+clone: arg0 & 0x00010000
+write: 1
+eventfd2: 1
+poll: 1
+ppoll: 1
+getpid: 1
+# Allow PR_SET_NAME only.
+prctl: arg0 == 15
+restart_syscall: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_wait: 1
+ioctl: 1
+fcntl: 1
+getsockname: 1
diff --git a/seccomp/x86_64/input_device.policy b/seccomp/x86_64/input_device.policy
new file mode 100644
index 0000000..877f80e
--- /dev/null
+++ b/seccomp/x86_64/input_device.policy
@@ -0,0 +1,37 @@
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+close: 1
+dup: 1
+dup2: 1
+exit_group: 1
+futex: 1
+# Disallow mmap with PROT_EXEC set.  The syntax here doesn't allow bit
+# negation, thus the manually negated mask constant.
+mmap: arg2 in 0xfffffffb
+mprotect: arg2 in 0xfffffffb
+# Allow MADV_DONTDUMP only.
+madvise: arg2 == 0x00000010
+munmap: 1
+read: 1
+recvfrom: 1
+sched_getaffinity: 1
+set_robust_list: 1
+sigaltstack: 1
+# Disallow clone's other than new threads.
+clone: arg0 & 0x00010000
+write: 1
+eventfd2: 1
+poll: 1
+ppoll: 1
+getpid: 1
+# Allow PR_SET_NAME only.
+prctl: arg0 == 15
+restart_syscall: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_wait: 1
+ioctl: 1
+fcntl: 1
+getsockname: 1
diff --git a/src/linux.rs b/src/linux.rs
index fb327ec..d2e0610 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -10,8 +10,8 @@ use std::fmt;
 use std::fs::{File, OpenOptions};
 use std::io::{self, stdin, Read};
 use std::mem;
-use std::os::unix::io::FromRawFd;
-use std::os::unix::net::UnixDatagram;
+use std::os::unix::io::{FromRawFd, RawFd};
+use std::os::unix::net::{UnixDatagram, UnixStream};
 use std::path::{Path, PathBuf};
 use std::str;
 use std::sync::{Arc, Barrier};
@@ -86,6 +86,8 @@ pub enum Error {
     RegisterWayland(arch::DeviceRegistrationError),
     ResetTimerFd(sys_util::Error),
     RngDeviceNew(devices::virtio::RngError),
+    InputDeviceNew(devices::virtio::InputError),
+    InputEventsOpen(std::io::Error),
     SettingGidMap(io_jail::Error),
     SettingUidMap(io_jail::Error),
     SignalFd(sys_util::SignalFdError),
@@ -156,6 +158,8 @@ impl fmt::Display for Error {
             Error::RegisterWayland(e) => write!(f, "error registering wayland device: {}", e),
             Error::ResetTimerFd(e) => write!(f, "failed to reset timerfd: {}", e),
             Error::RngDeviceNew(e) => write!(f, "failed to set up rng: {:?}", e),
+            Error::InputDeviceNew(ref e) => write!(f, "failed to set up input device: {:?}", e),
+            Error::InputEventsOpen(ref e) => write!(f, "failed to open event device: {:?}", e),
             Error::SettingGidMap(e) => write!(f, "error setting GID map: {}", e),
             Error::SettingUidMap(e) => write!(f, "error setting UID map: {}", e),
             Error::SignalFd(e) => write!(f, "failed to read signal fd: {:?}", e),
@@ -233,17 +237,8 @@ fn create_virtio_devs(
 
         // Special case '/proc/self/fd/*' paths. The FD is already open, just use it.
         let mut raw_image: File = if disk.path.parent() == Some(Path::new("/proc/self/fd")) {
-            if !disk.path.is_file() {
-                return Err(Box::new(Error::InvalidFdPath));
-            }
-            let raw_fd = disk
-                .path
-                .file_name()
-                .and_then(|fd_osstr| fd_osstr.to_str())
-                .and_then(|fd_str| fd_str.parse::<c_int>().ok())
-                .ok_or(Error::InvalidFdPath)?;
             // Safe because we will validate |raw_fd|.
-            unsafe { File::from_raw_fd(validate_raw_fd(raw_fd).map_err(Error::ValidateRawFd)?) }
+            unsafe { File::from_raw_fd(raw_fd_from_path(&disk.path)?) }
         } else {
             OpenOptions::new()
                 .read(true)
@@ -325,6 +320,101 @@ fn create_virtio_devs(
         });
     }
 
+    if let Some(trackpad_spec) = cfg.virtio_trackpad {
+        match create_input_socket(&trackpad_spec.path) {
+            Ok(socket) => {
+                let trackpad_box = Box::new(
+                    devices::virtio::new_trackpad(
+                        socket,
+                        trackpad_spec.width,
+                        trackpad_spec.height,
+                    )
+                    .map_err(Error::InputDeviceNew)?,
+                );
+                let trackpad_jail = if cfg.multiprocess {
+                    let policy_path: PathBuf = cfg.seccomp_policy_dir.join("input_device.policy");
+                    Some(create_base_minijail(empty_root_path, &policy_path)?)
+                } else {
+                    None
+                };
+                devs.push(VirtioDeviceStub {
+                    dev: trackpad_box,
+                    jail: trackpad_jail,
+                });
+            }
+            Err(e) => {
+                error!("failed configuring virtio trackpad: {:?}", e);
+                return Err(e);
+            }
+        }
+    }
+
+    if let Some(mouse_socket) = cfg.virtio_mouse {
+        match create_input_socket(&mouse_socket) {
+            Ok(socket) => {
+                let mouse_box =
+                    Box::new(devices::virtio::new_mouse(socket).map_err(Error::InputDeviceNew)?);
+                let mouse_jail = if cfg.multiprocess {
+                    let policy_path: PathBuf = cfg.seccomp_policy_dir.join("input_device.policy");
+                    Some(create_base_minijail(empty_root_path, &policy_path)?)
+                } else {
+                    None
+                };
+                devs.push(VirtioDeviceStub {
+                    dev: mouse_box,
+                    jail: mouse_jail,
+                });
+            }
+            Err(e) => {
+                error!("failed configuring virtio mouse: {:?}", e);
+                return Err(e);
+            }
+        }
+    }
+
+    if let Some(keyboard_socket) = cfg.virtio_keyboard {
+        match create_input_socket(&keyboard_socket) {
+            Ok(socket) => {
+                let keyboard_box =
+                    Box::new(devices::virtio::new_keyboard(socket).map_err(Error::InputDeviceNew)?);
+                let keyboard_jail = if cfg.multiprocess {
+                    let policy_path: PathBuf = cfg.seccomp_policy_dir.join("input_device.policy");
+                    Some(create_base_minijail(empty_root_path, &policy_path)?)
+                } else {
+                    None
+                };
+                devs.push(VirtioDeviceStub {
+                    dev: keyboard_box,
+                    jail: keyboard_jail,
+                });
+            }
+            Err(e) => {
+                error!("failed configuring virtio keyboard: {:?}", e);
+                return Err(e);
+            }
+        }
+    }
+
+    for dev_path in cfg.virtio_input_evdevs {
+        let dev_file = OpenOptions::new()
+            .read(true)
+            .write(true)
+            .open(dev_path)
+            .map_err(|e| Box::new(e))?;
+        let vinput_box =
+            Box::new(devices::virtio::new_evdev(dev_file).map_err(Error::InputDeviceNew)?);
+        let vinput_jail = if cfg.multiprocess {
+            let policy_path: PathBuf = cfg.seccomp_policy_dir.join("input_device.policy");
+            Some(create_base_minijail(empty_root_path, &policy_path)?)
+        } else {
+            None
+        };
+        devs.push(VirtioDeviceStub {
+            dev: vinput_box,
+            jail: vinput_jail,
+        });
+    }
+
     let balloon_box = Box::new(
         devices::virtio::Balloon::new(balloon_device_socket).map_err(Error::BalloonDeviceNew)?,
     );
@@ -669,6 +759,32 @@ fn create_virtio_devs(
     Ok(pci_devices)
 }
 
+fn raw_fd_from_path(path: &PathBuf) -> std::result::Result<RawFd, Box<Error>> {
+    if !path.is_file() {
+        return Err(Box::new(Error::InvalidFdPath));
+    }
+    let raw_fd = path
+        .file_name()
+        .and_then(|fd_osstr| fd_osstr.to_str())
+        .and_then(|fd_str| fd_str.parse::<c_int>().ok())
+        .ok_or(Error::InvalidFdPath)?;
+    validate_raw_fd(raw_fd).map_err(|e| Box::new(Error::ValidateRawFd(e)))
+}
+
+fn create_input_socket(path: &PathBuf) -> std::result::Result<UnixStream, Box<Error>> {
+    if path.parent() == Some(Path::new("/proc/self/fd")) {
+        // Safe because we will validate |raw_fd|.
+        unsafe { Ok(UnixStream::from_raw_fd(raw_fd_from_path(path)?)) }
+    } else {
+        match UnixStream::connect(path) {
+            Ok(us) => return Ok(us),
+            Err(e) => {
+                return Err(Box::new(Error::InputEventsOpen(e)));
+            }
+        }
+    }
+}
+
 fn setup_vcpu_signal_handler() -> Result<()> {
     unsafe {
         extern "C" fn handle_signal() {}
diff --git a/src/main.rs b/src/main.rs
index 9392360..8e83621 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -72,6 +72,25 @@ struct BindMount {
     writable: bool,
 }
 
+const DEFAULT_TRACKPAD_WIDTH: u32 = 800;
+const DEFAULT_TRACKPAD_HEIGHT: u32 = 1280;
+
+struct TrackpadOption {
+    path: PathBuf,
+    width: u32,
+    height: u32,
+}
+
+impl TrackpadOption {
+    fn new(path: PathBuf) -> TrackpadOption {
+        TrackpadOption {
+            path,
+            width: DEFAULT_TRACKPAD_WIDTH,
+            height: DEFAULT_TRACKPAD_HEIGHT,
+        }
+    }
+}
+
 pub struct Config {
     vcpu_count: Option<u32>,
     memory: Option<usize>,
@@ -97,6 +116,10 @@ pub struct Config {
     gpu: bool,
     cras_audio: bool,
     null_audio: bool,
+    virtio_trackpad: Option<TrackpadOption>,
+    virtio_mouse: Option<PathBuf>,
+    virtio_keyboard: Option<PathBuf>,
+    virtio_input_evdevs: Vec<PathBuf>,
 }
 
 impl Default for Config {
@@ -126,6 +149,10 @@ impl Default for Config {
             seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
             cras_audio: false,
             null_audio: false,
+            virtio_trackpad: None,
+            virtio_mouse: None,
+            virtio_keyboard: None,
+            virtio_input_evdevs: Vec::new(),
         }
     }
 }
@@ -492,6 +519,51 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
         "gpu" => {
             cfg.gpu = true;
         }
+        "trackpad" => {
+            if cfg.virtio_trackpad.is_some() {
+                return Err(argument::Error::TooManyArguments(
+                    "`trackpad` already given".to_owned(),
+                ));
+            }
+            let mut it = value.unwrap().split(":");
+
+            let mut trackpad_spec =
+                TrackpadOption::new(PathBuf::from(it.next().unwrap().to_owned()));
+            if let Some(width) = it.next() {
+                trackpad_spec.width = width.trim().parse().unwrap();
+            }
+            if let Some(height) = it.next() {
+                trackpad_spec.height = height.trim().parse().unwrap();
+            }
+
+            cfg.virtio_trackpad = Some(trackpad_spec);
+        }
+        "mouse" => {
+            if cfg.virtio_mouse.is_some() {
+                return Err(argument::Error::TooManyArguments(
+                    "`mouse` already given".to_owned(),
+                ));
+            }
+            cfg.virtio_mouse = Some(PathBuf::from(value.unwrap().to_owned()));
+        }
+        "keyboard" => {
+            if cfg.virtio_keyboard.is_some() {
+                return Err(argument::Error::TooManyArguments(
+                    "`keyboard` already given".to_owned(),
+                ));
+            }
+            cfg.virtio_keyboard = Some(PathBuf::from(value.unwrap().to_owned()));
+        }
+        "evdev" => {
+            let dev_path = PathBuf::from(value.unwrap());
+            if !dev_path.exists() {
+                return Err(argument::Error::InvalidValue {
+                    value: value.unwrap().to_owned(),
+                    expected: "this input device path does not exist",
+                });
+            }
+            cfg.virtio_input_evdevs.push(dev_path);
+        }
         "help" => return Err(argument::Error::PrintHelp),
         _ => unreachable!(),
     }
@@ -551,6 +623,10 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
                           "File descriptor for configured tap device.  Mutually exclusive with `host_ip`, `netmask`, and `mac`."),
           #[cfg(feature = "gpu")]
           Argument::flag("gpu", "(EXPERIMENTAL) enable virtio-gpu device"),
+          Argument::value("evdev", "PATH", "Path to an event device node. The device will be grabbed (unusable from the host) and made available to the guest with the same configuration it shows on the host"),
+          Argument::value("trackpad", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read trackpad input events and write status updates to, optionally followed by screen width and height (defaults to 800x1280)."),
+          Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."),
+          Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),
           Argument::short_flag('h', "help", "Print help message.")];
 
     let mut cfg = Config::default();