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>
This commit is contained in:
Jorge E. Moreira 2019-01-14 18:44:49 -08:00 committed by chrome-bot
parent 5329be3634
commit dffec507fb
10 changed files with 2516 additions and 12 deletions

View file

@ -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;

View file

@ -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
}

View file

@ -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()))
}
}

View file

@ -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()
}
}

View file

@ -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)),
})
}

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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() {}

View file

@ -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();