diff --git a/devices/src/virtio/input/constants.rs b/devices/src/virtio/input/constants.rs new file mode 100644 index 0000000000..6cc3574e71 --- /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 0000000000..f5343f754e --- /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 { + let mut absinfo: BTreeMap = 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 { + let mut supported_events: BTreeMap = 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 { + let mut supported_events: BTreeMap = 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 { + let mut supported_events: BTreeMap = 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 0000000000..34fedb9a05 --- /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 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(fd: &T) -> Result { + 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(fd: &T) -> Result> { + 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(fd: &T) -> Result> { + 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(fd: &T) -> Result { + 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(fd: &T) -> Result> { + let mut evts: BTreeMap = 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(fd: &T) -> BTreeMap { + let mut ret: BTreeMap = 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(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(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::()) + }; + 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 0000000000..0f28f4d532 --- /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::(); + + 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; + /// 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::(); + +// 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 { + source: T, + queue: VecDeque, + 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 EventSourceImpl { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + 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 EventSourceImpl { + fn write bool>( + &mut self, + buf: &[u8], + event_filter: F, + ) -> std::io::Result { + 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 EventSourceImpl { + fn as_raw_fd(&self) -> RawFd { + self.source.as_raw_fd() + } +} + +impl EventSourceImpl +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 bool>(&mut self, event_filter: F) -> Result { + 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 { + 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 { + evt_source_impl: EventSourceImpl, +} + +impl SocketEventSource +where + T: Read + Write + AsRawFd, +{ + pub fn new(source: T) -> SocketEventSource { + SocketEventSource { + evt_source_impl: EventSourceImpl::new(source), + } + } +} + +impl Read for SocketEventSource { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.evt_source_impl.read(buf) + } +} + +impl Write for SocketEventSource +where + T: Read + Write + AsRawFd, +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.evt_source_impl.write(buf, |_evt| true) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.evt_source_impl.flush() + } +} + +impl AsRawFd for SocketEventSource { + fn as_raw_fd(&self) -> RawFd { + self.evt_source_impl.as_raw_fd() + } +} + +impl EventSource for SocketEventSource +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 { + 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 { + evt_source_impl: EventSourceImpl, +} + +impl EvdevEventSource +where + T: Read + Write + AsRawFd, +{ + pub fn new(source: T) -> EvdevEventSource { + EvdevEventSource { + evt_source_impl: EventSourceImpl::new(source), + } + } +} + +impl Read for EvdevEventSource { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.evt_source_impl.read(buf) + } +} + +impl Write for EvdevEventSource +where + T: Read + Write + AsRawFd, +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + 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 AsRawFd for EvdevEventSource { + fn as_raw_fd(&self) -> RawFd { + self.evt_source_impl.as_raw_fd() + } +} + +impl EventSource for EvdevEventSource +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 { + 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 0000000000..82c15c0ec1 --- /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 = std::result::Result; + +#[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, + serial_name: Vec, + properties: virtio_input_bitmap, + supported_events: BTreeMap, + axis_info: BTreeMap, +} + +impl VirtioInputConfig { + const CONFIG_MEM_SIZE: usize = size_of::(); + + fn new( + device_ids: virtio_input_device_ids, + name: Vec, + serial_name: Vec, + properties: virtio_input_bitmap, + supported_events: BTreeMap, + axis_info: BTreeMap, + ) -> VirtioInputConfig { + VirtioInputConfig { + select: 0, + subsel: 0, + device_ids, + name, + serial_name, + properties, + supported_events, + axis_info, + } + } + + fn from_evdev(source: &T) -> Result { + 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::>(), + ); + 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::(); + 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 { + event_source: T, + event_queue: Queue, + status_queue: Queue, + guest_memory: GuestMemory, + interrupt_status: Arc, + interrupt_evt: EventFd, + interrupt_resample_evt: EventFd, +} + +impl Worker { + 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 { + 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 = 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 { + kill_evt: Option, + config: VirtioInputConfig, + source: Option, +} + +impl Drop for Input { + 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 VirtioDevice for Input +where + T: 'static + EventSource + Send, +{ + fn keep_fds(&self) -> Vec { + 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, + mut queues: Vec, + mut queue_evts: Vec, + ) { + 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(source: T) -> Result>> +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(source: T, width: u32, height: u32) -> Result>> +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(source: T) -> Result>> +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(source: T) -> Result>> +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 78cfeb28ea..a9fe525f03 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 0000000000..877f80edb8 --- /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 0000000000..877f80edb8 --- /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 fb327ec88d..d2e0610e1a 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::().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> { + 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::().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> { + 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 939236018d..8e836215ac 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, memory: Option, @@ -97,6 +116,10 @@ pub struct Config { gpu: bool, cras_audio: bool, null_audio: bool, + virtio_trackpad: Option, + virtio_mouse: Option, + virtio_keyboard: Option, + virtio_input_evdevs: Vec, } 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();