Index: trunk/src/VBox/Devices/Input/DevPS2.cpp
===================================================================
--- trunk/src/VBox/Devices/Input/DevPS2.cpp	(revision 81765)
+++ trunk/src/VBox/Devices/Input/DevPS2.cpp	(revision 82170)
@@ -52,5 +52,5 @@
 
 #include "VBoxDD.h"
-#include "PS2Dev.h"
+#include "DevPS2.h"
 
 /* Do not remove this (unless eliminating the corresponding ifdefs), it will
Index: trunk/src/VBox/Devices/Input/DevPS2.h
===================================================================
--- trunk/src/VBox/Devices/Input/DevPS2.h	(revision 82170)
+++ trunk/src/VBox/Devices/Input/DevPS2.h	(revision 82170)
@@ -0,0 +1,94 @@
+/* $Id$ */
+/** @file
+ * PS/2 devices - Internal header file.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Input_PS2Dev_h
+#define VBOX_INCLUDED_SRC_Input_PS2Dev_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/** The size of the PS2K/PS2M structure fillers.
+ * @note Must be at least as big as the real struct. Compile time assert
+ *       makes sure this is so. */
+#define PS2K_STRUCT_FILLER  512
+#define PS2M_STRUCT_FILLER  512
+
+/* Hide the internal structure. */
+#if !(defined(IN_PS2K) || defined(VBOX_DEVICE_STRUCT_TESTCASE))
+typedef struct PS2K
+{
+    uint8_t     abFiller[PS2K_STRUCT_FILLER];
+} PS2K;
+#endif
+
+#if !(defined(IN_PS2M) || defined(VBOX_DEVICE_STRUCT_TESTCASE))
+typedef struct PS2M
+{
+    uint8_t     abFiller[PS2M_STRUCT_FILLER];
+} PS2M;
+#endif
+
+/* Internal PS/2 Keyboard interface. */
+typedef struct PS2K *PPS2K;
+
+int  PS2KByteToKbd(PPS2K pThis, uint8_t cmd);
+int  PS2KByteFromKbd(PPS2K pThis, uint8_t *pVal);
+
+int  PS2KConstruct(PPS2K pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance, PCFGMNODE pCfg);
+int  PS2KAttach(PPS2K pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags);
+void PS2KReset(PPS2K pThis);
+void PS2KRelocate(PPS2K pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns);
+void PS2KSaveState(PPS2K pThis, PSSMHANDLE pSSM);
+int  PS2KLoadState(PPS2K pThis, PSSMHANDLE pSSM, uint32_t uVersion);
+int  PS2KLoadDone(PPS2K pThis, PSSMHANDLE pSSM);
+
+PS2K *KBDGetPS2KFromDevIns(PPDMDEVINS pDevIns);
+
+
+/* Internal PS/2 Auxiliary device interface. */
+typedef struct PS2M *PPS2M;
+
+int  PS2MByteToAux(PPS2M pThis, uint8_t cmd);
+int  PS2MByteFromAux(PPS2M pThis, uint8_t *pVal);
+
+int  PS2MConstruct(PPS2M pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance);
+int  PS2MAttach(PPS2M pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags);
+void PS2MReset(PPS2M pThis);
+void PS2MRelocate(PPS2M pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns);
+void PS2MSaveState(PPS2M pThis, PSSMHANDLE pSSM);
+int  PS2MLoadState(PPS2M pThis, PSSMHANDLE pSSM, uint32_t uVersion);
+void PS2MFixupState(PPS2M pThis, uint8_t u8State, uint8_t u8Rate, uint8_t u8Proto);
+
+PS2M *KBDGetPS2MFromDevIns(PPDMDEVINS pDevIns);
+
+
+/* Shared keyboard/aux internal interface. */
+void KBCUpdateInterrupts(void *pKbc);
+
+
+///@todo: This should live with the KBC implementation.
+/** AT to PC scancode translator state.  */
+typedef enum
+{
+    XS_IDLE,    /**< Starting state. */
+    XS_BREAK,   /**< F0 break byte was received. */
+    XS_HIBIT    /**< Break code still active. */
+} xlat_state_t;
+
+int32_t XlateAT2PC(int32_t state, uint8_t scanIn, uint8_t *pScanOut);
+
+#endif /* !VBOX_INCLUDED_SRC_Input_PS2Dev_h */
Index: trunk/src/VBox/Devices/Input/DevPS2K.cpp
===================================================================
--- trunk/src/VBox/Devices/Input/DevPS2K.cpp	(revision 82170)
+++ trunk/src/VBox/Devices/Input/DevPS2K.cpp	(revision 82170)
@@ -0,0 +1,1744 @@
+/* $Id$ */
+/** @file
+ * PS2K - PS/2 keyboard emulation.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*
+ * References:
+ *
+ * IBM PS/2 Technical Reference, Keyboards (101- and 102-Key), 1990
+ * Keyboard Scan Code Specification, Microsoft, 2000
+ *
+ * Notes:
+ *  - The keyboard never sends partial scan-code sequences; if there isn't enough
+ *    room left in the buffer for the entire sequence, the keystroke is discarded
+ *    and an overrun code is sent instead.
+ *  - Command responses do not disturb stored keystrokes and always have priority.
+ *  - Caps Lock and Scroll Lock are normal keys from the keyboard's point of view.
+ *    However, Num Lock is not and the keyboard internally tracks its state.
+ *  - The way Print Screen works in scan set 1/2 is totally insane.
+ *  - A PS/2 keyboard can send at most 1,000 to 1,500 bytes per second. There is
+ *    software which relies on that fact and assumes that a scan code can be
+ *    read twice before the next scan code comes in.
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP   LOG_GROUP_DEV_KBD
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include "VBoxDD.h"
+#define IN_PS2K
+#include "DevPS2.h"
+
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+/** @name Keyboard commands sent by the system.
+ * @{ */
+#define KCMD_LEDS           0xED
+#define KCMD_ECHO           0xEE
+#define KCMD_INVALID_1      0xEF
+#define KCMD_SCANSET        0xF0
+#define KCMD_INVALID_2      0xF1
+#define KCMD_READ_ID        0xF2
+#define KCMD_RATE_DELAY     0xF3
+#define KCMD_ENABLE         0xF4
+#define KCMD_DFLT_DISABLE   0xF5
+#define KCMD_SET_DEFAULT    0xF6
+#define KCMD_ALL_TYPEMATIC  0xF7
+#define KCMD_ALL_MK_BRK     0xF8
+#define KCMD_ALL_MAKE       0xF9
+#define KCMD_ALL_TMB        0xFA
+#define KCMD_TYPE_MATIC     0xFB
+#define KCMD_TYPE_MK_BRK    0xFC
+#define KCMD_TYPE_MAKE      0xFD
+#define KCMD_RESEND         0xFE
+#define KCMD_RESET          0xFF
+/** @} */
+
+/** @name Keyboard responses sent to the system.
+ * @{ */
+#define KRSP_ID1            0xAB
+#define KRSP_ID2            0x83
+#define KRSP_BAT_OK         0xAA
+#define KRSP_BAT_FAIL       0xFC    /* Also a 'release keys' signal. */
+#define KRSP_ECHO           0xEE
+#define KRSP_ACK            0xFA
+#define KRSP_RESEND         0xFE
+/** @} */
+
+/** @name HID modifier range.
+ * @{ */
+#define HID_MODIFIER_FIRST  0xE0
+#define HID_MODIFIER_LAST   0xE8
+/** @} */
+
+/** @name USB HID additional constants
+ * @{ */
+/** The highest USB usage code reported by VirtualBox. */
+#define VBOX_USB_MAX_USAGE_CODE     0xE7
+/** The size of an array needed to store all USB usage codes */
+#define VBOX_USB_USAGE_ARRAY_SIZE   (VBOX_USB_MAX_USAGE_CODE + 1)
+/** USB HID Keyboard Usage Page. */
+#define USB_HID_KB_PAGE             7
+/** USB HID Consumer Control Usage Page. */
+#define USB_HID_CC_PAGE             12
+/** @} */
+
+/** @name Modifier key states. Sorted in USB HID code order.
+ * @{ */
+#define MOD_LCTRL           0x01
+#define MOD_LSHIFT          0x02
+#define MOD_LALT            0x04
+#define MOD_LGUI            0x08
+#define MOD_RCTRL           0x10
+#define MOD_RSHIFT          0x20
+#define MOD_RALT            0x40
+#define MOD_RGUI            0x80
+/** @} */
+
+/* Default typematic value. */
+#define KBD_DFL_RATE_DELAY  0x2B
+
+/* Input throttling delay in milliseconds. */
+#define KBD_THROTTLE_DELAY  1
+
+/** Define a simple PS/2 input device queue. */
+#define DEF_PS2Q_TYPE(name, size)   \
+     typedef struct {               \
+        uint32_t    rpos;           \
+        uint32_t    wpos;           \
+        uint32_t    cUsed;          \
+        uint32_t    cSize;          \
+        uint8_t     abQueue[size];  \
+     } name
+
+/* Internal keyboard queue sizes. The input queue doesn't need to be
+ * extra huge and the command queue only needs to handle a few bytes.
+ */
+#define KBD_KEY_QUEUE_SIZE         64
+#define KBD_CMD_QUEUE_SIZE          4
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+
+/** Typematic state. */
+typedef enum {
+    KBD_TMS_IDLE    = 0,    /* No typematic key active. */
+    KBD_TMS_DELAY   = 1,    /* In the initial delay period. */
+    KBD_TMS_REPEAT  = 2,    /* Key repeating at set rate. */
+    KBD_TMS_32BIT_HACK = 0x7fffffff
+} tmatic_state_t;
+
+
+DEF_PS2Q_TYPE(KbdKeyQ, KBD_KEY_QUEUE_SIZE);
+DEF_PS2Q_TYPE(KbdCmdQ, KBD_CMD_QUEUE_SIZE);
+DEF_PS2Q_TYPE(GeneriQ, 1);
+
+/**
+ * The PS/2 keyboard instance data.
+ */
+typedef struct PS2K
+{
+    /** Pointer to parent device (keyboard controller). */
+    R3PTRTYPE(void *)   pParent;
+    /** Set if keyboard is enabled ('scans' for input). */
+    bool                fScanning;
+    /** Set NumLock is on. */
+    bool                fNumLockOn;
+    /** Selected scan set. */
+    uint8_t             u8ScanSet;
+    /** Modifier key state. */
+    uint8_t             u8Modifiers;
+    /** Currently processed command (if any). */
+    uint8_t             u8CurrCmd;
+    /** Status indicator (LED) state. */
+    uint8_t             u8LEDs;
+    /** Selected typematic delay/rate. */
+    uint8_t             u8TypematicCfg;
+    /** Usage code of current typematic key, if any. */
+    uint32_t            u32TypematicKey;
+    /** Current typematic repeat state. */
+    tmatic_state_t      enmTypematicState;
+    /** Buffer holding scan codes to be sent to the host. */
+    KbdKeyQ             keyQ;
+    /** Command response queue (priority). */
+    KbdCmdQ             cmdQ;
+    /** Currently depressed keys. */
+    uint8_t             abDepressedKeys[VBOX_USB_USAGE_ARRAY_SIZE];
+    /** Typematic delay in milliseconds. */
+    unsigned            uTypematicDelay;
+    /** Typematic repeat period in milliseconds. */
+    unsigned            uTypematicRepeat;
+    /** Set if the throttle delay is currently active. */
+    bool                fThrottleActive;
+    /** Set if the input rate should be throttled. */
+    bool                fThrottleEnabled;
+
+    uint8_t             Alignment0[2];
+
+    /** Command delay timer - RC Ptr. */
+    PTMTIMERRC          pKbdDelayTimerRC;
+    /** Typematic timer - RC Ptr. */
+    PTMTIMERRC          pKbdTypematicTimerRC;
+    /** Input throttle timer - RC Ptr. */
+    PTMTIMERRC          pThrottleTimerRC;
+
+    /** The device critical section protecting everything - R3 Ptr */
+    R3PTRTYPE(PPDMCRITSECT) pCritSectR3;
+
+    /** Command delay timer - R3 Ptr. */
+    PTMTIMERR3          pKbdDelayTimerR3;
+    /** Typematic timer - R3 Ptr. */
+    PTMTIMERR3          pKbdTypematicTimerR3;
+    /** Input throttle timer - R3 Ptr. */
+    PTMTIMERR3          pThrottleTimerR3;
+
+    /** Command delay timer - R0 Ptr. */
+    PTMTIMERR0          pKbdDelayTimerR0;
+    /** Typematic timer - R0 Ptr. */
+    PTMTIMERR0          pKbdTypematicTimerR0;
+    /** Input throttle timer - R0 Ptr. */
+    PTMTIMERR0          pThrottleTimerR0;
+
+    /**
+     * Keyboard port - LUN#0.
+     *
+     * @implements  PDMIBASE
+     * @implements  PDMIKEYBOARDPORT
+     */
+    struct
+    {
+        /** The base interface for the keyboard port. */
+        PDMIBASE                            IBase;
+        /** The keyboard port base interface. */
+        PDMIKEYBOARDPORT                    IPort;
+
+        /** The base interface of the attached keyboard driver. */
+        R3PTRTYPE(PPDMIBASE)                pDrvBase;
+        /** The keyboard interface of the attached keyboard driver. */
+        R3PTRTYPE(PPDMIKEYBOARDCONNECTOR)   pDrv;
+    } Keyboard;
+} PS2K, *PPS2K;
+
+AssertCompile(PS2K_STRUCT_FILLER >= sizeof(PS2K));
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+/* Key type flags. */
+#define KF_E0        0x01    /* E0 prefix. */
+#define KF_NB        0x02    /* No break code. */
+#define KF_GK        0x04    /* Gray navigation key. */
+#define KF_PS        0x08    /* Print Screen key. */
+#define KF_PB        0x10    /* Pause/Break key. */
+#define KF_NL        0x20    /* Num Lock key. */
+#define KF_NS        0x40    /* NumPad '/' key. */
+
+/* Scan Set 3 typematic defaults. */
+#define T_U          0x00    /* Unknown value. */
+#define T_T          0x01    /* Key is typematic. */
+#define T_M          0x02    /* Key is make only. */
+#define T_B          0x04    /* Key is make/break. */
+
+/* Special key values. */
+#define NONE         0x93    /* No PS/2 scan code returned. */
+#define UNAS         0x94    /* No PS/2 scan assigned to key. */
+#define RSVD         0x95    /* Reserved, do not use. */
+#define UNKN         0x96    /* Translation unknown. */
+
+/* Key definition structure. */
+typedef struct {
+    uint8_t makeS1;      /* Set 1 make code. */
+    uint8_t makeS2;      /* Set 2 make code. */
+    uint8_t makeS3;      /* Set 3 make code. */
+    uint8_t keyFlags;    /* Key flags. */
+    uint8_t keyMatic;    /* Set 3 typematic default. */
+} key_def;
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+#ifdef IN_RING3
+/* USB to PS/2 conversion table for regular keys (HID Usage Page 7). */
+static const   key_def   aPS2Keys[] = {
+    /* 00 */ {NONE, NONE, NONE, KF_NB, T_U }, /* Key N/A: No Event */
+    /* 01 */ {0xFF, 0x00, 0x00, KF_NB, T_U }, /* Key N/A: Overrun Error */
+    /* 02 */ {0xFC, 0xFC, 0xFC, KF_NB, T_U }, /* Key N/A: POST Fail */
+    /* 03 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key N/A: ErrorUndefined */
+    /* 04 */ {0x1E, 0x1C, 0x1C,     0, T_T }, /* Key  31: a A */
+    /* 05 */ {0x30, 0x32, 0x32,     0, T_T }, /* Key  50: b B */
+    /* 06 */ {0x2E, 0x21, 0x21,     0, T_T }, /* Key  48: c C */
+    /* 07 */ {0x20, 0x23, 0x23,     0, T_T }, /* Key  33: d D */
+    /* 08 */ {0x12, 0x24, 0x24,     0, T_T }, /* Key  19: e E */
+    /* 09 */ {0x21, 0x2B, 0x2B,     0, T_T }, /* Key  34: f F */
+    /* 0A */ {0x22, 0x34, 0x34,     0, T_T }, /* Key  35: g G */
+    /* 0B */ {0x23, 0x33, 0x33,     0, T_T }, /* Key  36: h H */
+    /* 0C */ {0x17, 0x43, 0x43,     0, T_T }, /* Key  24: i I */
+    /* 0D */ {0x24, 0x3B, 0x3B,     0, T_T }, /* Key  37: j J */
+    /* 0E */ {0x25, 0x42, 0x42,     0, T_T }, /* Key  38: k K */
+    /* 0F */ {0x26, 0x4B, 0x4B,     0, T_T }, /* Key  39: l L */
+    /* 10 */ {0x32, 0x3A, 0x3A,     0, T_T }, /* Key  52: m M */
+    /* 11 */ {0x31, 0x31, 0x31,     0, T_T }, /* Key  51: n N */
+    /* 12 */ {0x18, 0x44, 0x44,     0, T_T }, /* Key  25: o O */
+    /* 13 */ {0x19, 0x4D, 0x4D,     0, T_T }, /* Key  26: p P */
+    /* 14 */ {0x10, 0x15, 0x15,     0, T_T }, /* Key  17: q Q */
+    /* 15 */ {0x13, 0x2D, 0x2D,     0, T_T }, /* Key  20: r R */
+    /* 16 */ {0x1F, 0x1B, 0x1B,     0, T_T }, /* Key  32: s S */
+    /* 17 */ {0x14, 0x2C, 0x2C,     0, T_T }, /* Key  21: t T */
+    /* 18 */ {0x16, 0x3C, 0x3C,     0, T_T }, /* Key  23: u U */
+    /* 19 */ {0x2F, 0x2A, 0x2A,     0, T_T }, /* Key  49: v V */
+    /* 1A */ {0x11, 0x1D, 0x1D,     0, T_T }, /* Key  18: w W */
+    /* 1B */ {0x2D, 0x22, 0x22,     0, T_T }, /* Key  47: x X */
+    /* 1C */ {0x15, 0x35, 0x35,     0, T_T }, /* Key  22: y Y */
+    /* 1D */ {0x2C, 0x1A, 0x1A,     0, T_T }, /* Key  46: z Z */
+    /* 1E */ {0x02, 0x16, 0x16,     0, T_T }, /* Key   2: 1 ! */
+    /* 1F */ {0x03, 0x1E, 0x1E,     0, T_T }, /* Key   3: 2 @ */
+    /* 20 */ {0x04, 0x26, 0x26,     0, T_T }, /* Key   4: 3 # */
+    /* 21 */ {0x05, 0x25, 0x25,     0, T_T }, /* Key   5: 4 $ */
+    /* 22 */ {0x06, 0x2E, 0x2E,     0, T_T }, /* Key   6: 5 % */
+    /* 23 */ {0x07, 0x36, 0x36,     0, T_T }, /* Key   7: 6 ^ */
+    /* 24 */ {0x08, 0x3D, 0x3D,     0, T_T }, /* Key   8: 7 & */
+    /* 25 */ {0x09, 0x3E, 0x3E,     0, T_T }, /* Key   9: 8 * */
+    /* 26 */ {0x0A, 0x46, 0x46,     0, T_T }, /* Key  10: 9 ( */
+    /* 27 */ {0x0B, 0x45, 0x45,     0, T_T }, /* Key  11: 0 ) */
+    /* 28 */ {0x1C, 0x5A, 0x5A,     0, T_T }, /* Key  43: Return */
+    /* 29 */ {0x01, 0x76, 0x08,     0, T_M }, /* Key 110: Escape */
+    /* 2A */ {0x0E, 0x66, 0x66,     0, T_T }, /* Key  15: Backspace */
+    /* 2B */ {0x0F, 0x0D, 0x0D,     0, T_T }, /* Key  16: Tab */
+    /* 2C */ {0x39, 0x29, 0x29,     0, T_T }, /* Key  61: Space */
+    /* 2D */ {0x0C, 0x4E, 0x4E,     0, T_T }, /* Key  12: - _ */
+    /* 2E */ {0x0D, 0x55, 0x55,     0, T_T }, /* Key  13: = + */
+    /* 2F */ {0x1A, 0x54, 0x54,     0, T_T }, /* Key  27: [ { */
+    /* 30 */ {0x1B, 0x5B, 0x5B,     0, T_T }, /* Key  28: ] } */
+    /* 31 */ {0x2B, 0x5D, 0x5C,     0, T_T }, /* Key  29: \ | */
+    /* 32 */ {0x2B, 0x5D, 0x5D,     0, T_T }, /* Key  42: Europe 1 (Note 2) */
+    /* 33 */ {0x27, 0x4C, 0x4C,     0, T_T }, /* Key  40: ; : */
+    /* 34 */ {0x28, 0x52, 0x52,     0, T_T }, /* Key  41: ' " */
+    /* 35 */ {0x29, 0x0E, 0x0E,     0, T_T }, /* Key   1: ` ~ */
+    /* 36 */ {0x33, 0x41, 0x41,     0, T_T }, /* Key  53: , < */
+    /* 37 */ {0x34, 0x49, 0x49,     0, T_T }, /* Key  54: . > */
+    /* 38 */ {0x35, 0x4A, 0x4A,     0, T_T }, /* Key  55: / ? */
+    /* 39 */ {0x3A, 0x58, 0x14,     0, T_B }, /* Key  30: Caps Lock */
+    /* 3A */ {0x3B, 0x05, 0x07,     0, T_M }, /* Key 112: F1 */
+    /* 3B */ {0x3C, 0x06, 0x0F,     0, T_M }, /* Key 113: F2 */
+    /* 3C */ {0x3D, 0x04, 0x17,     0, T_M }, /* Key 114: F3 */
+    /* 3D */ {0x3E, 0x0C, 0x1F,     0, T_M }, /* Key 115: F4 */
+    /* 3E */ {0x3F, 0x03, 0x27,     0, T_M }, /* Key 116: F5 */
+    /* 3F */ {0x40, 0x0B, 0x2F,     0, T_M }, /* Key 117: F6 */
+    /* 40 */ {0x41, 0x83, 0x37,     0, T_M }, /* Key 118: F7 */
+    /* 41 */ {0x42, 0x0A, 0x3F,     0, T_M }, /* Key 119: F8 */
+    /* 42 */ {0x43, 0x01, 0x47,     0, T_M }, /* Key 120: F9 */
+    /* 43 */ {0x44, 0x09, 0x4F,     0, T_M }, /* Key 121: F10 */
+    /* 44 */ {0x57, 0x78, 0x56,     0, T_M }, /* Key 122: F11 */
+    /* 45 */ {0x58, 0x07, 0x5E,     0, T_M }, /* Key 123: F12 */
+    /* 46 */ {0x37, 0x7C, 0x57, KF_PS, T_M }, /* Key 124: Print Screen (Note 1) */
+    /* 47 */ {0x46, 0x7E, 0x5F,     0, T_M }, /* Key 125: Scroll Lock */
+    /* 48 */ {RSVD, RSVD, RSVD, KF_PB, T_M }, /* Key 126: Break (Ctrl-Pause) */
+    /* 49 */ {0x52, 0x70, 0x67, KF_GK, T_M }, /* Key  75: Insert (Note 1) */
+    /* 4A */ {0x47, 0x6C, 0x6E, KF_GK, T_M }, /* Key  80: Home (Note 1) */
+    /* 4B */ {0x49, 0x7D, 0x6F, KF_GK, T_M }, /* Key  85: Page Up (Note 1) */
+    /* 4C */ {0x53, 0x71, 0x64, KF_GK, T_T }, /* Key  76: Delete (Note 1) */
+    /* 4D */ {0x4F, 0x69, 0x65, KF_GK, T_M }, /* Key  81: End (Note 1) */
+    /* 4E */ {0x51, 0x7A, 0x6D, KF_GK, T_M }, /* Key  86: Page Down (Note 1) */
+    /* 4F */ {0x4D, 0x74, 0x6A, KF_GK, T_T }, /* Key  89: Right Arrow (Note 1) */
+    /* 50 */ {0x4B, 0x6B, 0x61, KF_GK, T_T }, /* Key  79: Left Arrow (Note 1) */
+    /* 51 */ {0x50, 0x72, 0x60, KF_GK, T_T }, /* Key  84: Down Arrow (Note 1) */
+    /* 52 */ {0x48, 0x75, 0x63, KF_GK, T_T }, /* Key  83: Up Arrow (Note 1) */
+    /* 53 */ {0x45, 0x77, 0x76, KF_NL, T_M }, /* Key  90: Num Lock */
+    /* 54 */ {0x35, 0x4A, 0x77, KF_NS, T_M }, /* Key  95: Keypad / (Note 1) */
+    /* 55 */ {0x37, 0x7C, 0x7E,     0, T_M }, /* Key 100: Keypad * */
+    /* 56 */ {0x4A, 0x7B, 0x84,     0, T_M }, /* Key 105: Keypad - */
+    /* 57 */ {0x4E, 0x79, 0x7C,     0, T_T }, /* Key 106: Keypad + */
+    /* 58 */ {0x1C, 0x5A, 0x79, KF_E0, T_M }, /* Key 108: Keypad Enter */
+    /* 59 */ {0x4F, 0x69, 0x69,     0, T_M }, /* Key  93: Keypad 1 End */
+    /* 5A */ {0x50, 0x72, 0x72,     0, T_M }, /* Key  98: Keypad 2 Down */
+    /* 5B */ {0x51, 0x7A, 0x7A,     0, T_M }, /* Key 103: Keypad 3 PageDn */
+    /* 5C */ {0x4B, 0x6B, 0x6B,     0, T_M }, /* Key  92: Keypad 4 Left */
+    /* 5D */ {0x4C, 0x73, 0x73,     0, T_M }, /* Key  97: Keypad 5 */
+    /* 5E */ {0x4D, 0x74, 0x74,     0, T_M }, /* Key 102: Keypad 6 Right */
+    /* 5F */ {0x47, 0x6C, 0x6C,     0, T_M }, /* Key  91: Keypad 7 Home */
+    /* 60 */ {0x48, 0x75, 0x75,     0, T_M }, /* Key  96: Keypad 8 Up */
+    /* 61 */ {0x49, 0x7D, 0x7D,     0, T_M }, /* Key 101: Keypad 9 PageUp */
+    /* 62 */ {0x52, 0x70, 0x70,     0, T_M }, /* Key  99: Keypad 0 Insert */
+    /* 63 */ {0x53, 0x71, 0x71,     0, T_M }, /* Key 104: Keypad . Delete */
+    /* 64 */ {0x56, 0x61, 0x13,     0, T_T }, /* Key  45: Europe 2 (Note 2) */
+    /* 65 */ {0x5D, 0x2F, UNKN, KF_E0, T_U }, /* Key 129: App */
+    /* 66 */ {0x5E, 0x37, UNKN, KF_E0, T_U }, /* Key Unk: Keyboard Power */
+    /* 67 */ {0x59, 0x0F, UNKN,     0, T_U }, /* Key Unk: Keypad = */
+    /* 68 */ {0x64, 0x08, UNKN,     0, T_U }, /* Key Unk: F13 */
+    /* 69 */ {0x65, 0x10, UNKN,     0, T_U }, /* Key Unk: F14 */
+    /* 6A */ {0x66, 0x18, UNKN,     0, T_U }, /* Key Unk: F15 */
+    /* 6B */ {0x67, 0x20, UNKN,     0, T_U }, /* Key Unk: F16 */
+    /* 6C */ {0x68, 0x28, UNKN,     0, T_U }, /* Key Unk: F17 */
+    /* 6D */ {0x69, 0x30, UNKN,     0, T_U }, /* Key Unk: F18 */
+    /* 6E */ {0x6A, 0x38, UNKN,     0, T_U }, /* Key Unk: F19 */
+    /* 6F */ {0x6B, 0x40, UNKN,     0, T_U }, /* Key Unk: F20 */
+    /* 70 */ {0x6C, 0x48, UNKN,     0, T_U }, /* Key Unk: F21 */
+    /* 71 */ {0x6D, 0x50, UNKN,     0, T_U }, /* Key Unk: F22 */
+    /* 72 */ {0x6E, 0x57, UNKN,     0, T_U }, /* Key Unk: F23 */
+    /* 73 */ {0x76, 0x5F, UNKN,     0, T_U }, /* Key Unk: F24 */
+    /* 74 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Execute */
+    /* 75 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Help */
+    /* 76 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Menu */
+    /* 77 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Select */
+    /* 78 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Stop */
+    /* 79 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Again */
+    /* 7A */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Undo */
+    /* 7B */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Cut */
+    /* 7C */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Copy */
+    /* 7D */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Paste */
+    /* 7E */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Find */
+    /* 7F */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Mute */
+    /* 80 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Volume Up */
+    /* 81 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Volume Dn */
+    /* 82 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Locking Caps Lock */
+    /* 83 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Locking Num Lock */
+    /* 84 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Locking Scroll Lock */
+    /* 85 */ {0x7E, 0x6D, UNKN,     0, T_U }, /* Key Unk: Keypad , (Brazilian Keypad .) */
+    /* 86 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Equal Sign */
+    /* 87 */ {0x73, 0x51, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl 1 (Ro) */
+    /* 88 */ {0x70, 0x13, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl2 (K'kana/H'gana) */
+    /* 89 */ {0x7D, 0x6A, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl 2 (Yen) */
+    /* 8A */ {0x79, 0x64, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl 4 (Henkan) */
+    /* 8B */ {0x7B, 0x67, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl 5 (Muhenkan) */
+    /* 8C */ {0x5C, 0x27, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl 6 (PC9800 Pad ,) */
+    /* 8D */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Intl 7 */
+    /* 8E */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Intl 8 */
+    /* 8F */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Intl 9 */
+    /* 90 */ {0xF2, 0xF2, UNKN, KF_NB, T_U }, /* Key Unk: Keyboard Lang 1 (Hang'l/Engl) */
+    /* 91 */ {0xF1, 0xF1, UNKN, KF_NB, T_U }, /* Key Unk: Keyboard Lang 2 (Hanja) */
+    /* 92 */ {0x78, 0x63, UNKN,     0, T_U }, /* Key Unk: Keyboard Lang 3 (Katakana) */
+    /* 93 */ {0x77, 0x62, UNKN,     0, T_U }, /* Key Unk: Keyboard Lang 4 (Hiragana) */
+    /* 94 */ {0x76, 0x5F, UNKN,     0, T_U }, /* Key Unk: Keyboard Lang 5 (Zen/Han) */
+    /* 95 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Lang 6 */
+    /* 96 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Lang 7 */
+    /* 97 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Lang 8 */
+    /* 98 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Lang 9 */
+    /* 99 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Alternate Erase */
+    /* 9A */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard SysReq/Attention (Note 3) */
+    /* 9B */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Cancel */
+    /* 9C */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Clear */
+    /* 9D */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Prior */
+    /* 9E */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Return */
+    /* 9F */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Separator */
+    /* A0 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Out */
+    /* A1 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Oper */
+    /* A2 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Clear/Again */
+    /* A3 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard CrSel/Props */
+    /* A4 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard ExSel */
+};
+
+/*
+ * Note 1: The behavior of these keys depends on the state of modifier keys
+ * at the time the key was pressed.
+ *
+ * Note 2: The key label depends on the national version of the keyboard.
+ *
+ * Note 3: Certain keys which have their own PS/2 scancodes do not exist on
+ * USB keyboards; the SysReq key is an example. The SysReq key scancode needs
+ * to be translated to the Print Screen HID usage code. The HID usage to PS/2
+ * scancode conversion then generates the correct sequence depending on the
+ * keyboard state.
+ */
+
+/* USB to PS/2 conversion table for modifier keys (HID Usage Page 7). */
+static const   key_def   aPS2ModKeys[] = {
+    /* E0 */ {0x1D, 0x14, 0x11,     0, T_B }, /* Key  58: Left Control */
+    /* E1 */ {0x2A, 0x12, 0x12,     0, T_B }, /* Key  44: Left Shift */
+    /* E2 */ {0x38, 0x11, 0x19,     0, T_B }, /* Key  60: Left Alt */
+    /* E3 */ {0x5B, 0x1F, UNKN, KF_E0, T_U }, /* Key 127: Left GUI */
+    /* E4 */ {0x1D, 0x14, 0x58, KF_E0, T_M }, /* Key  64: Right Control */
+    /* E5 */ {0x36, 0x59, 0x59,     0, T_B }, /* Key  57: Right Shift */
+    /* E6 */ {0x38, 0x11, 0x39, KF_E0, T_M }, /* Key  62: Right Alt */
+    /* E7 */ {0x5C, 0x27, UNKN, KF_E0, T_U }, /* Key 128: Right GUI */
+};
+
+/* Extended key definition for sparse mapping. */
+typedef struct {
+    uint16_t    usageId;
+    key_def     kdef;
+} ext_key_def;
+
+
+/* USB to PS/2 conversion table for consumer control keys (HID Usage Page 12). */
+/* This usage page is very sparse so we'll just search through it. */
+static const   ext_key_def  aPS2CCKeys[] = {
+    {0x00B5, {0x19, 0x4D, UNKN, KF_E0, T_U}},   /* Scan Next Track */
+    {0x00B6, {0x10, 0x15, UNKN, KF_E0, T_U}},   /* Scan Previous Track */
+    {0x00B7, {0x24, 0x3B, UNKN, KF_E0, T_U}},   /* Stop */
+    {0x00CD, {0x22, 0x34, UNKN, KF_E0, T_U}},   /* Play/Pause */
+    {0x00E2, {0x20, 0x23, UNKN, KF_E0, T_U}},   /* Mute */
+    {0x00E5, {UNAS, UNAS, UNAS,     0, T_U}},   /* Bass Boost */
+    {0x00E7, {UNAS, UNAS, UNAS,     0, T_U}},   /* Loudness */
+    {0x00E9, {0x30, 0x32, UNKN, KF_E0, T_U}},   /* Volume Up */
+    {0x00EA, {0x2E, 0x21, UNKN, KF_E0, T_U}},   /* Volume Down */
+    {0x0152, {UNAS, UNAS, UNAS,     0, T_U}},   /* Bass Up */
+    {0x0153, {UNAS, UNAS, UNAS,     0, T_U}},   /* Bass Down */
+    {0x0154, {UNAS, UNAS, UNAS,     0, T_U}},   /* Treble Up */
+    {0x0155, {UNAS, UNAS, UNAS,     0, T_U}},   /* Treble Down */
+    {0x0183, {0x6D, 0x50, UNKN, KF_E0, T_U}},   /* Media Select  */
+    {0x018A, {0x6C, 0x48, UNKN, KF_E0, T_U}},   /* Mail */
+    {0x0192, {0x21, 0x2B, UNKN, KF_E0, T_U}},   /* Calculator */
+    {0x0194, {0x6B, 0x40, UNKN, KF_E0, T_U}},   /* My Computer */
+    {0x0221, {0x65, 0x10, UNKN, KF_E0, T_U}},   /* WWW Search */
+    {0x0223, {0x32, 0x3A, UNKN, KF_E0, T_U}},   /* WWW Home */
+    {0x0224, {0x6A, 0x38, UNKN, KF_E0, T_U}},   /* WWW Back */
+    {0x0225, {0x69, 0x30, UNKN, KF_E0, T_U}},   /* WWW Forward */
+    {0x0226, {0x68, 0x28, UNKN, KF_E0, T_U}},   /* WWW Stop */
+    {0x0227, {0x67, 0x20, UNKN, KF_E0, T_U}},   /* WWW Refresh */
+    {0x022A, {0x66, 0x18, UNKN, KF_E0, T_U}},   /* WWW Favorites */
+};
+
+#endif /* IN_RING3 */
+
+
+
+/**
+ * Clear a queue.
+ *
+ * @param   pQ                  Pointer to the queue.
+ */
+static void ps2kClearQueue(GeneriQ *pQ)
+{
+    LogFlowFunc(("Clearing queue %p\n", pQ));
+    pQ->wpos  = pQ->rpos;
+    pQ->cUsed = 0;
+}
+
+
+/**
+ * Add a byte to a queue.
+ *
+ * @param   pQ                  Pointer to the queue.
+ * @param   val                 The byte to store.
+ */
+static void ps2kInsertQueue(GeneriQ *pQ, uint8_t val)
+{
+    /* Check if queue is full. */
+    if (pQ->cUsed >= pQ->cSize)
+    {
+        LogRelFlowFunc(("queue %p full (%d entries)\n", pQ, pQ->cUsed));
+        return;
+    }
+    /* Insert data and update circular buffer write position. */
+    pQ->abQueue[pQ->wpos] = val;
+    if (++pQ->wpos == pQ->cSize)
+        pQ->wpos = 0;   /* Roll over. */
+    ++pQ->cUsed;
+    LogRelFlowFunc(("inserted 0x%02X into queue %p\n", val, pQ));
+}
+
+#ifdef IN_RING3
+
+/**
+ * Add a null-terminated byte sequence to a queue if there is enough room.
+ *
+ * @param   pQ                  Pointer to the queue.
+ * @param   pStr                Pointer to the bytes to store.
+ * @param   uReserve            Number of bytes that must still remain
+ *                              available in queue.
+ * @return  int                 VBox status/error code.
+ */
+static int ps2kInsertStrQueue(GeneriQ *pQ, const uint8_t *pStr, uint32_t uReserve)
+{
+    uint32_t    cbStr;
+    unsigned    i;
+
+    cbStr = (uint32_t)strlen((const char *)pStr);
+
+    /* Check if queue has enough room. */
+    if (pQ->cUsed + uReserve + cbStr >= pQ->cSize)
+    {
+        LogRelFlowFunc(("queue %p full (%u entries, want room for %u), cannot insert %u entries\n",
+                        pQ, pQ->cUsed, uReserve, cbStr));
+        return VERR_BUFFER_OVERFLOW;
+    }
+
+    /* Insert byte sequence and update circular buffer write position. */
+    for (i = 0; i < cbStr; ++i) {
+        pQ->abQueue[pQ->wpos] = pStr[i];
+        if (++pQ->wpos == pQ->cSize)
+            pQ->wpos = 0;   /* Roll over. */
+    }
+    pQ->cUsed += cbStr;
+    LogRelFlowFunc(("inserted %u bytes into queue %p\n", cbStr, pQ));
+    return VINF_SUCCESS;
+}
+
+/**
+ * Save a queue state.
+ *
+ * @param   pSSM                SSM handle to write the state to.
+ * @param   pQ                  Pointer to the queue.
+ */
+static void ps2kSaveQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
+{
+    uint32_t    cItems = pQ->cUsed;
+    int         i;
+
+    /* Only save the number of items. Note that the read/write
+     * positions aren't saved as they will be rebuilt on load.
+     */
+    SSMR3PutU32(pSSM, cItems);
+
+    LogFlow(("Storing %d items from queue %p\n", cItems, pQ));
+
+    /* Save queue data - only the bytes actually used (typically zero). */
+    for (i = pQ->rpos; cItems-- > 0; i = (i + 1) % pQ->cSize)
+        SSMR3PutU8(pSSM, pQ->abQueue[i]);
+}
+
+/**
+ * Load a queue state.
+ *
+ * @param   pSSM                SSM handle to read the state from.
+ * @param   pQ                  Pointer to the queue.
+ *
+ * @return  int                 VBox status/error code.
+ */
+static int ps2kLoadQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
+{
+    /* On load, always put the read pointer at zero. */
+    int rc = SSMR3GetU32(pSSM, &pQ->cUsed);
+    AssertRCReturn(rc, rc);
+    LogFlow(("Loading %u items to queue %p\n", pQ->cUsed, pQ));
+    AssertMsgReturn(pQ->cUsed <= pQ->cSize, ("Saved size=%u, actual=%u\n", pQ->cUsed, pQ->cSize),
+                    VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+
+    /* Recalculate queue positions and load data in one go. */
+    pQ->rpos = 0;
+    pQ->wpos = pQ->cUsed;
+    rc = SSMR3GetMem(pSSM, pQ->abQueue, pQ->cUsed);
+
+    return rc;
+}
+
+/**
+ * Notify listener about LEDs state change.
+ *
+ * @param   pThis           The PS/2 keyboard instance data.
+ * @param   u8State         Bitfield which reflects LEDs state.
+ */
+static void ps2kNotifyLedsState(PPS2K pThis, uint8_t u8State)
+{
+
+    PDMKEYBLEDS enmLeds = PDMKEYBLEDS_NONE;
+
+    if (u8State & 0x01)
+        enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_SCROLLLOCK);
+    if (u8State & 0x02)
+        enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_NUMLOCK);
+    if (u8State & 0x04)
+        enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_CAPSLOCK);
+
+    pThis->Keyboard.pDrv->pfnLedStatusChange(pThis->Keyboard.pDrv, enmLeds);
+
+}
+
+/**
+ * Query the number of items currently in a queue.
+ *
+ * @param   pQ                  Pointer to the queue.
+ *
+ * @return  uint32_t            Number of items in queue.
+ */
+static uint32_t ps2kInQueue(GeneriQ *pQ)
+{
+    return pQ->cUsed;
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Retrieve a byte from a queue.
+ *
+ * @param   pQ                  Pointer to the queue.
+ * @param   pVal                Pointer to storage for the byte.
+ *
+ * @return  int                 VINF_TRY_AGAIN if queue is empty,
+ *                              VINF_SUCCESS if a byte was read.
+ */
+static int ps2kRemoveQueue(GeneriQ *pQ, uint8_t *pVal)
+{
+    int     rc = VINF_TRY_AGAIN;
+
+    Assert(pVal);
+    if (pQ->cUsed)
+    {
+        *pVal = pQ->abQueue[pQ->rpos];
+        if (++pQ->rpos == pQ->cSize)
+            pQ->rpos = 0;   /* Roll over. */
+        --pQ->cUsed;
+        rc = VINF_SUCCESS;
+        LogFlowFunc(("removed 0x%02X from queue %p\n", *pVal, pQ));
+    } else
+        LogFlowFunc(("queue %p empty\n", pQ));
+    return rc;
+}
+
+/* Clears the currently active typematic key, if any. */
+static void ps2kStopTypematicRepeat(PPS2K pThis)
+{
+    if (pThis->u32TypematicKey)
+    {
+        LogFunc(("Typematic key %08X\n", pThis->u32TypematicKey));
+        pThis->enmTypematicState = KBD_TMS_IDLE;
+        pThis->u32TypematicKey = 0;
+        TMTimerStop(pThis->CTX_SUFF(pKbdTypematicTimer));
+    }
+}
+
+/* Convert encoded typematic value to milliseconds. Note that the values are rated
+ * with +/- 20% accuracy, so there's no need for high precision.
+ */
+static void ps2kSetupTypematic(PPS2K pThis, uint8_t val)
+{
+    int         A, B;
+    unsigned    period;
+
+    pThis->u8TypematicCfg = val;
+    /* The delay is easy: (1 + value) * 250 ms */
+    pThis->uTypematicDelay = (1 + ((val >> 5) & 3)) * 250;
+    /* The rate is more complicated: (8 + A) * 2^B * 4.17 ms */
+    A = val & 7;
+    B = (val >> 3) & 3;
+    period = (8 + A) * (1 << B) * 417 / 100;
+    pThis->uTypematicRepeat = period;
+    Log(("Typematic delay %u ms, repeat period %u ms\n",
+         pThis->uTypematicDelay, pThis->uTypematicRepeat));
+}
+
+static void ps2kSetDefaults(PPS2K pThis)
+{
+    LogFlowFunc(("Set keyboard defaults\n"));
+    ps2kClearQueue((GeneriQ *)&pThis->keyQ);
+    /* Set default Scan Set 3 typematic values. */
+    /* Set default typematic rate/delay. */
+    ps2kSetupTypematic(pThis, KBD_DFL_RATE_DELAY);
+    /* Clear last typematic key?? */
+    ps2kStopTypematicRepeat(pThis);
+}
+
+/**
+ * Receive and process a byte sent by the keyboard controller.
+ *
+ * @param   pThis               The PS/2 keyboard instance data.
+ * @param   cmd                 The command (or data) byte.
+ */
+int PS2KByteToKbd(PPS2K pThis, uint8_t cmd)
+{
+    bool    fHandled = true;
+
+    LogFlowFunc(("new cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
+
+    if (pThis->u8CurrCmd == KCMD_RESET)
+        /* In reset mode, do not respond at all. */
+        return VINF_SUCCESS;
+
+    switch (cmd)
+    {
+        case KCMD_ECHO:
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ECHO);
+            pThis->u8CurrCmd = 0;
+            break;
+        case KCMD_READ_ID:
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ID1);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ID2);
+            pThis->u8CurrCmd = 0;
+            break;
+        case KCMD_ENABLE:
+            pThis->fScanning = true;
+            ps2kClearQueue((GeneriQ *)&pThis->keyQ);
+            ps2kStopTypematicRepeat(pThis);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case KCMD_DFLT_DISABLE:
+            pThis->fScanning = false;
+            ps2kSetDefaults(pThis); /* Also clears buffer/typematic state. */
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case KCMD_SET_DEFAULT:
+            ps2kSetDefaults(pThis);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case KCMD_ALL_TYPEMATIC:
+        case KCMD_ALL_MK_BRK:
+        case KCMD_ALL_MAKE:
+        case KCMD_ALL_TMB:
+            /// @todo Set the key types here.
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case KCMD_RESEND:
+            pThis->u8CurrCmd = 0;
+            break;
+        case KCMD_RESET:
+            pThis->u8ScanSet = 2;
+            ps2kSetDefaults(pThis);
+            /// @todo reset more?
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+            pThis->u8CurrCmd = cmd;
+            /* Delay BAT completion; the test may take hundreds of ms. */
+            TMTimerSetMillies(pThis->CTX_SUFF(pKbdDelayTimer), 2);
+            break;
+        /* The following commands need a parameter. */
+        case KCMD_LEDS:
+        case KCMD_SCANSET:
+        case KCMD_RATE_DELAY:
+        case KCMD_TYPE_MATIC:
+        case KCMD_TYPE_MK_BRK:
+        case KCMD_TYPE_MAKE:
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+            pThis->u8CurrCmd = cmd;
+            break;
+        default:
+            /* Sending a command instead of a parameter starts the new command. */
+            switch (pThis->u8CurrCmd)
+            {
+                case KCMD_LEDS:
+#ifndef IN_RING3
+                    return VINF_IOM_R3_IOPORT_WRITE;
+#else
+                    {
+                        ps2kNotifyLedsState(pThis, cmd);
+                        pThis->fNumLockOn = !!(cmd & 0x02); /* Sync internal Num Lock state. */
+                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+                        pThis->u8LEDs = cmd;
+                        pThis->u8CurrCmd = 0;
+                    }
+#endif
+                    break;
+                case KCMD_SCANSET:
+                    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+                    if (cmd == 0)
+                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8ScanSet);
+                    else if (cmd < 4)
+                    {
+                        pThis->u8ScanSet = cmd;
+                        LogRel(("PS2K: Selected scan set %d\n", cmd));
+                    }
+                    /* Other values are simply ignored. */
+                    pThis->u8CurrCmd = 0;
+                    break;
+                case KCMD_RATE_DELAY:
+                    ps2kSetupTypematic(pThis, cmd);
+                    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+                    pThis->u8CurrCmd = 0;
+                    break;
+                default:
+                    fHandled = false;
+            }
+            /* Fall through only to handle unrecognized commands. */
+            if (fHandled)
+                break;
+            RT_FALL_THRU();
+
+        case KCMD_INVALID_1:
+        case KCMD_INVALID_2:
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_RESEND);
+            pThis->u8CurrCmd = 0;
+            break;
+    }
+    LogFlowFunc(("Active cmd now 0x%02X; updating interrupts\n", pThis->u8CurrCmd));
+//    KBCUpdateInterrupts(pThis->pParent);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Send a byte (keystroke or command response) to the keyboard controller.
+ *
+ * @returns VINF_SUCCESS or VINF_TRY_AGAIN.
+ * @param   pThis               The PS/2 keyboard instance data.
+ * @param   pb                  Where to return the byte we've read.
+ * @remarks Caller must have entered the device critical section.
+ */
+int PS2KByteFromKbd(PPS2K pThis, uint8_t *pb)
+{
+    int         rc;
+
+    AssertPtr(pb);
+
+    /* Anything in the command queue has priority over data
+     * in the keystroke queue. Additionally, keystrokes are
+     * blocked if a command is currently in progress, even if
+     * the command queue is empty.
+     */
+    rc = ps2kRemoveQueue((GeneriQ *)&pThis->cmdQ, pb);
+    if (rc != VINF_SUCCESS && !pThis->u8CurrCmd && pThis->fScanning)
+        if (!pThis->fThrottleActive)
+        {
+            rc = ps2kRemoveQueue((GeneriQ *)&pThis->keyQ, pb);
+            if (pThis->fThrottleEnabled) {
+                pThis->fThrottleActive = true;
+                TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), KBD_THROTTLE_DELAY);
+            }
+        }
+
+    LogFlowFunc(("keyboard sends 0x%02x (%svalid data)\n", *pb, rc == VINF_SUCCESS ? "" : "not "));
+    return rc;
+}
+
+#ifdef IN_RING3
+
+static int ps2kProcessKeyEvent(PPS2K pThis, uint32_t u32HidCode, bool fKeyDown)
+{
+    key_def const   *pKeyDef;
+    uint8_t         abCodes[16];
+    char            *pCodes;
+    size_t          cbLeft;
+    uint8_t         abScan[2];
+    uint8_t         u8HidPage;
+    uint8_t         u8HidCode;
+    uint16_t        u16HidUsage;
+
+    u8HidPage   = RT_LOBYTE(RT_HIWORD(u32HidCode));
+    u16HidUsage = RT_LOWORD(u32HidCode);
+    /* For the keyboard usage page (7) we use a 8-bit code. For other pages we use the full 16-bit ID. */
+    u8HidCode   = (u8HidPage == USB_HID_KB_PAGE) ? RT_LOBYTE(u32HidCode) : 0;
+
+    LogFlowFunc(("key %s: page 0x%02x ID 0x%04x (set %d)\n", fKeyDown ? "down" : "up", u8HidPage, u16HidUsage, pThis->u8ScanSet));
+
+    /* Find the key definition in somewhat sparse storage. */
+    if (u8HidPage == USB_HID_KB_PAGE)
+        /* For the standard keyboard usage page, thre are just two arrays. */
+        pKeyDef = u8HidCode >= HID_MODIFIER_FIRST ? &aPS2ModKeys[u8HidCode - HID_MODIFIER_FIRST] : &aPS2Keys[u8HidCode];
+    else if (u8HidPage == USB_HID_CC_PAGE)
+    {
+        /* For the consumer control usage page, we need to search. */
+        unsigned    i;
+
+        pKeyDef = &aPS2Keys[0]; /* Dummy no-event key. */
+        for (i = 0; i < RT_ELEMENTS(aPS2CCKeys); ++i)
+        {
+            if (aPS2CCKeys[i].usageId == u16HidUsage)
+            {
+                pKeyDef = &aPS2CCKeys[i].kdef;
+                break;
+            }
+        }
+    }
+    else
+    {
+        LogFlow(("Unsupported HID usage page, ignoring key.\n"));
+        return VINF_SUCCESS;
+    }
+
+    /* Some keys are not processed at all; early return. */
+    if (pKeyDef->makeS1 == NONE)
+    {
+        LogFlow(("Skipping key processing.\n"));
+        return VINF_SUCCESS;
+    }
+
+    /* Handle modifier keys (Ctrl/Alt/Shift/GUI). We need to keep track
+     * of their state in addition to sending the scan code.
+     */
+    if (u8HidCode >= HID_MODIFIER_FIRST)
+    {
+        unsigned    mod_bit = 1 << (u8HidCode - HID_MODIFIER_FIRST);
+
+        Assert((u8HidPage == USB_HID_KB_PAGE));
+        Assert((u8HidCode <= HID_MODIFIER_LAST));
+        if (fKeyDown)
+            pThis->u8Modifiers |= mod_bit;
+        else
+            pThis->u8Modifiers &= ~mod_bit;
+    }
+
+    /* Toggle NumLock state. */
+    if ((pKeyDef->keyFlags & KF_NL) && fKeyDown)
+        pThis->fNumLockOn ^= true;
+
+    abCodes[0] = 0;
+    pCodes = (char *)abCodes;
+    cbLeft = sizeof(abCodes);
+
+    if (pThis->u8ScanSet == 1 || pThis->u8ScanSet == 2)
+    {
+        /* The basic scan set 1 and 2 logic is the same, only the scan codes differ.
+         * Since scan set 2 is used almost all the time, that case is handled first.
+         */
+        if (fKeyDown)
+        {
+            /* Process key down event. */
+            if (pKeyDef->keyFlags & KF_PB)
+            {
+                /* Pause/Break sends different data if either Ctrl is held. */
+                if (pThis->u8Modifiers & (MOD_LCTRL | MOD_RCTRL))
+                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                              "\xE0\x7E\xE0\xF0\x7E" : "\xE0\x46\xE0\xC6");
+                else
+                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                              "\xE1\x14\x77\xE1\xF0\x14\xF0\x77" : "\xE1\x1D\x45\xE1\x9D\xC5");
+            }
+            else if (pKeyDef->keyFlags & KF_PS)
+            {
+                /* Print Screen depends on all of Ctrl, Shift, *and* Alt! */
+                if (pThis->u8Modifiers & (MOD_LALT | MOD_RALT))
+                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                              "\x84" : "\x54");
+                else if (pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT))
+                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                              "\xE0\x7C" : "\xE0\x37");
+                else
+                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                              "\xE0\x12\xE0\x7C" : "\xE0\x2A\xE0\x37");
+            }
+            else if (pKeyDef->keyFlags & (KF_GK | KF_NS))
+            {
+                /* The numeric pad keys fake Shift presses or releases
+                 * depending on Num Lock and Shift key state. The '/'
+                 * key behaves in a similar manner but does not depend on
+                 * the Num Lock state.
+                 */
+                if (!pThis->fNumLockOn || (pKeyDef->keyFlags & KF_NS))
+                {
+                    if (pThis->u8Modifiers & MOD_LSHIFT)
+                        RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                                  "\xE0\xF0\x12" : "\xE0\xAA");
+                    if (pThis->u8Modifiers & MOD_RSHIFT)
+                        RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                                  "\xE0\xF0\x59" : "\xE0\xB6");
+                }
+                else
+                {
+                    Assert(pThis->fNumLockOn);  /* Not for KF_NS! */
+                    if ((pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT)) == 0)
+                        RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                                  "\xE0\x12" : "\xE0\x2A");
+                    /* Else Shift cancels NumLock, so no prefix! */
+                }
+            }
+
+            /* Standard processing for regular keys only. */
+            abScan[0] = pThis->u8ScanSet == 2 ? pKeyDef->makeS2 : pKeyDef->makeS1;
+            abScan[1] = '\0';
+            if (!(pKeyDef->keyFlags & (KF_PB | KF_PS)))
+            {
+                if (pKeyDef->keyFlags & (KF_E0 | KF_GK | KF_NS))
+                    RTStrCatP(&pCodes, &cbLeft, "\xE0");
+                RTStrCatP(&pCodes, &cbLeft, (const char *)abScan);
+            }
+
+            /* Feed the bytes to the queue if there is room. */
+            /// @todo Send overrun code if sequence won't fit?
+            ps2kInsertStrQueue((GeneriQ *)&pThis->keyQ, abCodes, 0);
+        }
+        else if (!(pKeyDef->keyFlags & (KF_NB | KF_PB)))
+        {
+            /* Process key up event except for keys which produce none. */
+
+            /* Handle Print Screen release. */
+            if (pKeyDef->keyFlags & KF_PS)
+            {
+                /* Undo faked Print Screen state as needed. */
+                if (pThis->u8Modifiers & (MOD_LALT | MOD_RALT))
+                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                              "\xF0\x84" : "\xD4");
+                else if (pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT))
+                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                              "\xE0\xF0\x7C" : "\xE0\xB7");
+                else
+                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                              "\xE0\xF0\x7C\xE0\xF0\x12" : "\xE0\xB7\xE0\xAA");
+            }
+            else
+            {
+                /* Process base scan code for less unusual keys. */
+                abScan[0] = pThis->u8ScanSet == 2 ? pKeyDef->makeS2 : pKeyDef->makeS1 | 0x80;
+                abScan[1] = '\0';
+                if (pKeyDef->keyFlags & (KF_E0 | KF_GK | KF_NS))
+                    RTStrCatP(&pCodes, &cbLeft, "\xE0");
+                if (pThis->u8ScanSet == 2)
+                    RTStrCatP(&pCodes, &cbLeft, "\xF0");
+                RTStrCatP(&pCodes, &cbLeft, (const char *)abScan);
+
+                /* Restore shift state for gray keys. */
+                if (pKeyDef->keyFlags & (KF_GK | KF_NS))
+                {
+                    if (!pThis->fNumLockOn || (pKeyDef->keyFlags & KF_NS))
+                    {
+                        if (pThis->u8Modifiers & MOD_LSHIFT)
+                            RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                                      "\xE0\x12" : "\xE0\x2A");
+                        if (pThis->u8Modifiers & MOD_RSHIFT)
+                            RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                                      "\xE0\x59" : "\xE0\x36");
+                    }
+                    else
+                    {
+                        Assert(pThis->fNumLockOn);  /* Not for KF_NS! */
+                        if ((pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT)) == 0)
+                            RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
+                                      "\xE0\xF0\x12" : "\xE0\xAA");
+                    }
+                }
+            }
+
+            /* Feed the bytes to the queue if there is room. */
+            /// @todo Send overrun code if sequence won't fit?
+            ps2kInsertStrQueue((GeneriQ *)&pThis->keyQ, abCodes, 0);
+        }
+    }
+    else
+    {
+        /* Handle Scan Set 3 - very straightforward. */
+        Assert(pThis->u8ScanSet == 3);
+        abScan[0] = pKeyDef->makeS3;
+        abScan[1] = '\0';
+        if (fKeyDown)
+        {
+            RTStrCatP(&pCodes, &cbLeft, (const char *)abScan);
+        }
+        else
+        {
+            /* Send a key release code unless it's a make only key. */
+            /// @todo Look up the current typematic setting, not the default!
+            if (pKeyDef->keyMatic != T_M)
+            {
+                RTStrCatP(&pCodes, &cbLeft, "\xF0");
+                RTStrCatP(&pCodes, &cbLeft, (const char *)abScan);
+            }
+        }
+        /* Feed the bytes to the queue if there is room. */
+        /// @todo Send overrun code if sequence won't fit?
+        ps2kInsertStrQueue((GeneriQ *)&pThis->keyQ, abCodes, 0);
+    }
+
+    /* Set up or cancel typematic key repeat. For keyboard usage page only. */
+    if (u8HidPage == USB_HID_KB_PAGE)
+    {
+        if (fKeyDown)
+        {
+            if (pThis->u32TypematicKey != u32HidCode)
+            {
+                pThis->enmTypematicState = KBD_TMS_DELAY;
+                pThis->u32TypematicKey   = u32HidCode;
+                TMTimerSetMillies(pThis->CTX_SUFF(pKbdTypematicTimer), pThis->uTypematicDelay);
+                Log(("Typematic delay %u ms, key %08X\n", pThis->uTypematicDelay, u32HidCode));
+            }
+        }
+        else
+        {
+            /* "Typematic operation stops when the last key pressed is released, even
+             * if other keys are still held down." (IBM PS/2 Tech Ref). The last key pressed
+             * is the one that's being repeated.
+             */
+            if (pThis->u32TypematicKey == u32HidCode)
+            {
+                /* This disables the typematic repeat. */
+                pThis->u32TypematicKey   = 0;
+                pThis->enmTypematicState = KBD_TMS_IDLE;
+                /* For good measure, we cancel the timer, too. */
+                TMTimerStop(pThis->CTX_SUFF(pKbdTypematicTimer));
+                Log(("Typematic action cleared for key %08X\n", u32HidCode));
+            }
+        }
+    }
+
+    /* Poke the KBC to update its state. */
+    KBCUpdateInterrupts(pThis->pParent);
+
+    return VINF_SUCCESS;
+}
+
+/* Throttling timer to emulate the finite keyboard communication speed. A PS/2 keyboard is
+ * limited by the serial link speed and cannot send much more than 1,000 bytes per second.
+ * Some software (notably Borland Pascal and programs built with its run-time) relies on
+ * being able to read an incoming scan-code twice. Throttling the data rate enables such
+ * software to function, while human typists cannot tell any difference.
+ *
+ * Note: The throttling is currently only done for keyboard data, not command responses.
+ * The throttling could and perhaps should be done for any data (including command
+ * response) scoming from PS/2 devices, both keyboard and auxiliary. That is not currently
+ * done because it would needlessly slow things down.
+ */
+static DECLCALLBACK(void) ps2kThrottleTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+    RT_NOREF2(pDevIns, pTimer);
+    PPS2K       pThis = (PS2K *)pvUser;
+    unsigned    uHaveData;
+
+    /* Grab the lock to avoid races with event delivery or EMTs. */
+    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
+    AssertReleaseRC(rc);
+
+    /* If data is available, poke the KBC. Once the data
+     * is actually read, the timer may be re-triggered.
+     */
+    pThis->fThrottleActive = false;
+    uHaveData = ps2kInQueue((GeneriQ *)&pThis->keyQ);
+    LogFlowFunc(("Have%s bytes\n", uHaveData ? "" : " no"));
+    if (uHaveData)
+        KBCUpdateInterrupts(pThis->pParent);
+
+    PDMCritSectLeave(pThis->pCritSectR3);
+}
+
+/* Timer handler for emulating typematic keys. Note that only the last key
+ * held down repeats (if typematic).
+ */
+static DECLCALLBACK(void) ps2kTypematicTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+    RT_NOREF2(pDevIns, pTimer);
+    PPS2K pThis = (PS2K *)pvUser;
+    LogFlowFunc(("Typematic state=%d, key %08X\n", pThis->enmTypematicState, pThis->u32TypematicKey));
+
+    /* If the current typematic key is zero, the repeat was canceled just when
+     * the timer was about to run. In that case, do nothing.
+     */
+    if (pThis->u32TypematicKey)
+    {
+        if (pThis->enmTypematicState == KBD_TMS_DELAY)
+            pThis->enmTypematicState = KBD_TMS_REPEAT;
+
+        if (pThis->enmTypematicState == KBD_TMS_REPEAT)
+        {
+            ps2kProcessKeyEvent(pThis, pThis->u32TypematicKey, true /* Key down */ );
+            TMTimerSetMillies(pThis->CTX_SUFF(pKbdTypematicTimer), pThis->uTypematicRepeat);
+        }
+    }
+}
+
+/* The keyboard BAT is specified to take several hundred milliseconds. We need
+ * to delay sending the result to the host for at least a tiny little while.
+ */
+static DECLCALLBACK(void) ps2kDelayTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+    RT_NOREF2(pDevIns, pTimer);
+    PPS2K pThis = (PS2K *)pvUser;
+
+    LogFlowFunc(("Delay timer: cmd %02X\n", pThis->u8CurrCmd));
+
+    AssertMsg(pThis->u8CurrCmd == KCMD_RESET, ("u8CurrCmd=%02x\n", pThis->u8CurrCmd));
+    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_BAT_OK);
+    pThis->fScanning = true;    /* BAT completion enables scanning! */
+    pThis->u8CurrCmd = 0;
+
+    /// @todo Might want a PS2KCompleteCommand() to push last response, clear command, and kick the KBC...
+    /* Give the KBC a kick. */
+    KBCUpdateInterrupts(pThis->pParent);
+}
+
+/* Release any and all currently depressed keys. Used whenever the guest keyboard
+ * is likely to be out of sync with the host, such as when loading a saved state
+ * or resuming a suspended host.
+ */
+static void ps2kReleaseKeys(PPS2K pThis)
+{
+    LogFlowFunc(("Releasing keys...\n"));
+
+    for (unsigned uKey = 0; uKey < sizeof(pThis->abDepressedKeys); ++uKey)
+        if (pThis->abDepressedKeys[uKey])
+        {
+            ps2kProcessKeyEvent(pThis, RT_MAKE_U32(USB_HID_KB_PAGE, uKey), false /* key up */);
+            pThis->abDepressedKeys[uKey] = 0;
+        }
+    LogFlowFunc(("Done releasing keys\n"));
+}
+
+
+/**
+ * Debug device info handler. Prints basic keyboard state.
+ *
+ * @param   pDevIns     Device instance which registered the info.
+ * @param   pHlp        Callback functions for doing output.
+ * @param   pszArgs     Argument string. Optional and specific to the handler.
+ */
+static DECLCALLBACK(void) ps2kInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    PPS2K   pThis = KBDGetPS2KFromDevIns(pDevIns);
+    NOREF(pszArgs);
+
+    pHlp->pfnPrintf(pHlp, "PS/2 Keyboard: scan set %d, scanning %s\n",
+                    pThis->u8ScanSet, pThis->fScanning ? "enabled" : "disabled");
+    pHlp->pfnPrintf(pHlp, "Active command %02X\n", pThis->u8CurrCmd);
+    pHlp->pfnPrintf(pHlp, "LED state %02X, Num Lock %s\n", pThis->u8LEDs,
+                    pThis->fNumLockOn ? "on" : "off");
+    pHlp->pfnPrintf(pHlp, "Typematic delay %ums, repeat period %ums\n",
+                    pThis->uTypematicDelay, pThis->uTypematicRepeat);
+    pHlp->pfnPrintf(pHlp, "Command queue: %d items (%d max)\n",
+                    pThis->cmdQ.cUsed, pThis->cmdQ.cSize);
+    pHlp->pfnPrintf(pHlp, "Input queue  : %d items (%d max)\n",
+                    pThis->keyQ.cUsed, pThis->keyQ.cSize);
+    if (pThis->enmTypematicState != KBD_TMS_IDLE)
+        pHlp->pfnPrintf(pHlp, "Active typematic key %08X (%s)\n", pThis->u32TypematicKey,
+                        pThis->enmTypematicState == KBD_TMS_DELAY ? "delay" : "repeat");
+}
+
+/* -=-=-=-=-=- Keyboard: IBase  -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) ps2kQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PPS2K pThis = RT_FROM_MEMBER(pInterface, PS2K, Keyboard.IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Keyboard.IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDPORT, &pThis->Keyboard.IPort);
+    return NULL;
+}
+
+
+/* -=-=-=-=-=- Keyboard: IKeyboardPort  -=-=-=-=-=- */
+
+/**
+ * Keyboard event handler.
+ *
+ * @returns VBox status code.
+ * @param   pThis           The PS2 keyboard instance data.
+ * @param   u32Usage        USB HID usage code with key
+ *                          press/release flag.
+ */
+static int ps2kPutEventWorker(PPS2K pThis, uint32_t u32Usage)
+{
+    uint32_t        u32HidCode;
+    uint8_t         u8KeyCode;
+    uint8_t         u8HidPage;
+    bool            fKeyDown;
+    bool            fHaveEvent = true;
+    int             rc = VINF_SUCCESS;
+
+    /* Extract the usage page and ID and ensure it's valid. */
+    fKeyDown   = !(u32Usage & 0x80000000);
+    u32HidCode = u32Usage & 0xFFFFFF;
+    u8HidPage  = RT_LOBYTE(RT_HIWORD(u32Usage));
+    u8KeyCode  = RT_LOBYTE(u32Usage);
+    if (u8HidPage == USB_HID_KB_PAGE)
+        AssertReturn(u8KeyCode <= VBOX_USB_MAX_USAGE_CODE, VERR_INTERNAL_ERROR);
+    else
+        AssertReturn(u8HidPage == USB_HID_CC_PAGE, VERR_INTERNAL_ERROR);
+
+    if (fKeyDown)
+    {
+        /* Due to host key repeat, we can get key events for keys which are
+         * already depressed. We need to ignore those. */
+        if (pThis->abDepressedKeys[u8KeyCode])
+            fHaveEvent = false;
+        pThis->abDepressedKeys[u8KeyCode] = 1;
+    }
+    else
+    {
+        /* NB: We allow key release events for keys which aren't depressed.
+         * That is unlikely to happen and should not cause trouble.
+         */
+        pThis->abDepressedKeys[u8KeyCode] = 0;
+    }
+
+    /* Unless this is a new key press/release, don't even bother. */
+    if (fHaveEvent)
+    {
+        rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
+        AssertReleaseRC(rc);
+
+        rc = ps2kProcessKeyEvent(pThis, u32HidCode, fKeyDown);
+
+        PDMCritSectLeave(pThis->pCritSectR3);
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) ps2kPutEventWrapper(PPDMIKEYBOARDPORT pInterface, uint32_t u32UsageCode)
+{
+    PPS2K       pThis = RT_FROM_MEMBER(pInterface, PS2K, Keyboard.IPort);
+    int         rc;
+
+    LogRelFlowFunc(("key code %08X\n", u32UsageCode));
+
+    rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
+    AssertReleaseRC(rc);
+
+    /* The 'BAT fail' scancode is reused as a signal to release keys. No actual
+     * key is allowed to use this scancode.
+     */
+    if (RT_UNLIKELY(u32UsageCode == KRSP_BAT_FAIL))
+    {
+        ps2kReleaseKeys(pThis);
+    }
+    else
+    {
+        ps2kPutEventWorker(pThis, u32UsageCode);
+    }
+
+    PDMCritSectLeave(pThis->pCritSectR3);
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Attach command.
+ *
+ * This is called to let the device attach to a driver for a
+ * specified LUN.
+ *
+ * This is like plugging in the keyboard after turning on the
+ * system.
+ *
+ * @returns VBox status code.
+ * @param   pThis       The PS/2 keyboard instance data.
+ * @param   pDevIns     The device instance.
+ * @param   iLUN        The logical unit which is being detached.
+ * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ */
+int PS2KAttach(PPS2K pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+    int         rc;
+
+    /* The LUN must be 0, i.e. keyboard. */
+    Assert(iLUN == 0);
+    AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
+                    ("PS/2 keyboard does not support hotplugging\n"),
+                    VERR_INVALID_PARAMETER);
+
+    LogFlowFunc(("iLUN=%d\n", iLUN));
+
+    rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Keyboard.IBase, &pThis->Keyboard.pDrvBase, "Keyboard Port");
+    if (RT_SUCCESS(rc))
+    {
+        pThis->Keyboard.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Keyboard.pDrvBase, PDMIKEYBOARDCONNECTOR);
+        if (!pThis->Keyboard.pDrv)
+        {
+            AssertLogRelMsgFailed(("LUN #0 doesn't have a keyboard interface! rc=%Rrc\n", rc));
+            rc = VERR_PDM_MISSING_INTERFACE;
+        }
+    }
+    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+    {
+        Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
+        rc = VINF_SUCCESS;
+    }
+    else
+        AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
+
+    return rc;
+}
+
+void PS2KSaveState(PPS2K pThis, PSSMHANDLE pSSM)
+{
+    uint32_t    cPressed = 0;
+    uint32_t    cbTMSSize = 0;
+
+    LogFlowFunc(("Saving PS2K state\n"));
+
+    /* Save the basic keyboard state. */
+    SSMR3PutU8(pSSM, pThis->u8CurrCmd);
+    SSMR3PutU8(pSSM, pThis->u8LEDs);
+    SSMR3PutU8(pSSM, pThis->u8TypematicCfg);
+    SSMR3PutU8(pSSM, (uint8_t)pThis->u32TypematicKey);
+    SSMR3PutU8(pSSM, pThis->u8Modifiers);
+    SSMR3PutU8(pSSM, pThis->u8ScanSet);
+    SSMR3PutU8(pSSM, pThis->enmTypematicState);
+    SSMR3PutBool(pSSM, pThis->fNumLockOn);
+    SSMR3PutBool(pSSM, pThis->fScanning);
+
+    /* Save the command and keystroke queues. */
+    ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
+    ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->keyQ);
+
+    /* Save the command delay timer. Note that the typematic repeat
+     * timer is *not* saved.
+     */
+    TMR3TimerSave(pThis->CTX_SUFF(pKbdDelayTimer), pSSM);
+
+    /* Save any pressed keys. This is necessary to avoid "stuck"
+     * keys after a restore. Needs two passes.
+     */
+    for (unsigned i = 0; i < sizeof(pThis->abDepressedKeys); ++i)
+        if (pThis->abDepressedKeys[i])
+            ++cPressed;
+
+    SSMR3PutU32(pSSM, cPressed);
+
+    for (unsigned uKey = 0; uKey < sizeof(pThis->abDepressedKeys); ++uKey)
+        if (pThis->abDepressedKeys[uKey])
+            SSMR3PutU8(pSSM, uKey);
+
+    /* Save the typematic settings for Scan Set 3. */
+    SSMR3PutU32(pSSM, cbTMSSize);
+    /* Currently not implemented. */
+}
+
+int PS2KLoadState(PPS2K pThis, PSSMHANDLE pSSM, uint32_t uVersion)
+{
+    uint8_t     u8;
+    uint32_t    cPressed;
+    uint32_t    cbTMSSize;
+    int         rc;
+
+    NOREF(uVersion);
+    LogFlowFunc(("Loading PS2K state version %u\n", uVersion));
+
+    /* Load the basic keyboard state. */
+    SSMR3GetU8(pSSM, &pThis->u8CurrCmd);
+    SSMR3GetU8(pSSM, &pThis->u8LEDs);
+    SSMR3GetU8(pSSM, &pThis->u8TypematicCfg);
+    SSMR3GetU8(pSSM, &u8);
+    /* Reconstruct the 32-bit code from the 8-bit value in saved state. */
+    pThis->u32TypematicKey = u8 ? RT_MAKE_U32(USB_HID_KB_PAGE, u8) : 0;
+    SSMR3GetU8(pSSM, &pThis->u8Modifiers);
+    SSMR3GetU8(pSSM, &pThis->u8ScanSet);
+    SSMR3GetU8(pSSM, &u8);
+    pThis->enmTypematicState = (tmatic_state_t)u8;
+    SSMR3GetBool(pSSM, &pThis->fNumLockOn);
+    SSMR3GetBool(pSSM, &pThis->fScanning);
+
+    /* Load the command and keystroke queues. */
+    rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
+    AssertRCReturn(rc, rc);
+    rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->keyQ);
+    AssertRCReturn(rc, rc);
+
+    /* Load the command delay timer, just in case. */
+    rc = TMR3TimerLoad(pThis->CTX_SUFF(pKbdDelayTimer), pSSM);
+    AssertRCReturn(rc, rc);
+
+    /* Recalculate the typematic delay/rate. */
+    ps2kSetupTypematic(pThis, pThis->u8TypematicCfg);
+
+    /* Fake key up events for keys that were held down at the time the state was saved. */
+    rc = SSMR3GetU32(pSSM, &cPressed);
+    AssertRCReturn(rc, rc);
+
+    /* If any keys were down, load and then release them. */
+    if (cPressed)
+    {
+        for (unsigned i = 0; i < cPressed; ++i)
+        {
+            rc = SSMR3GetU8(pSSM, &u8);
+            AssertRCReturn(rc, rc);
+            pThis->abDepressedKeys[u8] = 1;
+        }
+    }
+
+    /* Load typematic settings for Scan Set 3. */
+    rc = SSMR3GetU32(pSSM, &cbTMSSize);
+    AssertRCReturn(rc, rc);
+
+    while (cbTMSSize--)
+    {
+        rc = SSMR3GetU8(pSSM, &u8);
+        AssertRCReturn(rc, rc);
+    }
+
+    return rc;
+}
+
+int PS2KLoadDone(PPS2K pThis, PSSMHANDLE pSSM)
+{
+    RT_NOREF1(pSSM);
+
+    /* This *must* be done after the inital load because it may trigger
+     * interrupts and change the interrupt controller state.
+     */
+    ps2kReleaseKeys(pThis);
+    ps2kNotifyLedsState(pThis, pThis->u8LEDs);
+    return VINF_SUCCESS;
+}
+
+void PS2KReset(PPS2K pThis)
+{
+    LogFlowFunc(("Resetting PS2K\n"));
+
+    pThis->fScanning         = true;
+    pThis->fThrottleActive   = false;
+    pThis->u8ScanSet         = 2;
+    pThis->u8CurrCmd         = 0;
+    pThis->u8Modifiers       = 0;
+    pThis->u32TypematicKey   = 0;
+    pThis->enmTypematicState = KBD_TMS_IDLE;
+
+    /* Clear queues and any pressed keys. */
+    memset(pThis->abDepressedKeys, 0, sizeof(pThis->abDepressedKeys));
+    ps2kClearQueue((GeneriQ *)&pThis->cmdQ);
+    ps2kSetDefaults(pThis);     /* Also clears keystroke queue. */
+
+    /* Activate the PS/2 keyboard by default. */
+    if (pThis->Keyboard.pDrv)
+        pThis->Keyboard.pDrv->pfnSetActive(pThis->Keyboard.pDrv, true);
+}
+
+void PS2KRelocate(PPS2K pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns)
+{
+    RT_NOREF1(pDevIns);
+    LogFlowFunc(("Relocating PS2K\n"));
+    pThis->pKbdDelayTimerRC     = TMTimerRCPtr(pThis->pKbdDelayTimerR3);
+    pThis->pKbdTypematicTimerRC = TMTimerRCPtr(pThis->pKbdTypematicTimerR3);
+    pThis->pThrottleTimerRC     = TMTimerRCPtr(pThis->pThrottleTimerR3);
+    NOREF(offDelta);
+}
+
+int PS2KConstruct(PPS2K pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance, PCFGMNODE pCfg)
+{
+    RT_NOREF2(pDevIns, iInstance);
+    LogFlowFunc(("iInstance=%d\n", iInstance));
+
+    pThis->pParent = pParent;
+
+    bool fThrottleEnabled;
+    int rc = CFGMR3QueryBoolDef(pCfg, "KbdThrottleEnabled", &fThrottleEnabled, true);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"KbdThrottleEnabled\" from the config"));
+    Log(("KbdThrottleEnabled=%u\n", fThrottleEnabled));
+    pThis->fThrottleEnabled = fThrottleEnabled;
+
+    /* Initialize the queues. */
+    pThis->keyQ.cSize = KBD_KEY_QUEUE_SIZE;
+    pThis->cmdQ.cSize = KBD_CMD_QUEUE_SIZE;
+
+    pThis->Keyboard.IBase.pfnQueryInterface = ps2kQueryInterface;
+    pThis->Keyboard.IPort.pfnPutEventHid    = ps2kPutEventWrapper;
+
+    /*
+     * Initialize the critical section pointer(s).
+     */
+    pThis->pCritSectR3 = pDevIns->pCritSectRoR3;
+
+    /*
+     * Create the input rate throttling timer.
+     */
+    PTMTIMER pTimer;
+    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ps2kThrottleTimer, pThis,
+                                TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2K Throttle Timer", &pTimer);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    pThis->pThrottleTimerR3 = pTimer;
+    pThis->pThrottleTimerR0 = TMTimerR0Ptr(pTimer);
+    pThis->pThrottleTimerRC = TMTimerRCPtr(pTimer);
+
+    /*
+     * Create the typematic delay/repeat timer.
+     */
+    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ps2kTypematicTimer, pThis,
+                                TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2K Typematic Timer", &pTimer);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    pThis->pKbdTypematicTimerR3 = pTimer;
+    pThis->pKbdTypematicTimerR0 = TMTimerR0Ptr(pTimer);
+    pThis->pKbdTypematicTimerRC = TMTimerRCPtr(pTimer);
+
+    /*
+     * Create the command delay timer.
+     */
+    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ps2kDelayTimer, pThis,
+                                TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2K Delay Timer", &pTimer);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    pThis->pKbdDelayTimerR3 = pTimer;
+    pThis->pKbdDelayTimerR0 = TMTimerR0Ptr(pTimer);
+    pThis->pKbdDelayTimerRC = TMTimerRCPtr(pTimer);
+
+    /*
+     * Register debugger info callbacks.
+     */
+    PDMDevHlpDBGFInfoRegister(pDevIns, "ps2k", "Display PS/2 keyboard state.", ps2kInfoState);
+
+    return rc;
+}
+
+#endif
+
+/// @todo The following should live with the KBC implementation.
+
+/* Table used by the keyboard controller to optionally translate the incoming
+ * keyboard data. Note that the translation is designed for essentially taking
+ * Scan Set 2 input and producing Scan Set 1 output, but can be turned on and
+ * off regardless of what the keyboard is sending.
+ */
+static uint8_t aAT2PC[128] = {
+    0xff,0x43,0x41,0x3f,0x3d,0x3b,0x3c,0x58,0x64,0x44,0x42,0x40,0x3e,0x0f,0x29,0x59,
+    0x65,0x38,0x2a,0x70,0x1d,0x10,0x02,0x5a,0x66,0x71,0x2c,0x1f,0x1e,0x11,0x03,0x5b,
+    0x67,0x2e,0x2d,0x20,0x12,0x05,0x04,0x5c,0x68,0x39,0x2f,0x21,0x14,0x13,0x06,0x5d,
+    0x69,0x31,0x30,0x23,0x22,0x15,0x07,0x5e,0x6a,0x72,0x32,0x24,0x16,0x08,0x09,0x5f,
+    0x6b,0x33,0x25,0x17,0x18,0x0b,0x0a,0x60,0x6c,0x34,0x35,0x26,0x27,0x19,0x0c,0x61,
+    0x6d,0x73,0x28,0x74,0x1a,0x0d,0x62,0x6e,0x3a,0x36,0x1c,0x1b,0x75,0x2b,0x63,0x76,
+    0x55,0x56,0x77,0x78,0x79,0x7a,0x0e,0x7b,0x7c,0x4f,0x7d,0x4b,0x47,0x7e,0x7f,0x6f,
+    0x52,0x53,0x50,0x4c,0x4d,0x48,0x01,0x45,0x57,0x4e,0x51,0x4a,0x37,0x49,0x46,0x54
+};
+
+/**
+ * Convert an AT (Scan Set 2) scancode to PC (Scan Set 1).
+ *
+ * @param state         Current state of the translator
+ *                      (xlat_state_t).
+ * @param scanIn        Incoming scan code.
+ * @param pScanOut      Pointer to outgoing scan code. The
+ *                      contents are only valid if returned
+ *                      state is not XS_BREAK.
+ *
+ * @return xlat_state_t New state of the translator.
+ */
+int32_t XlateAT2PC(int32_t state, uint8_t scanIn, uint8_t *pScanOut)
+{
+    uint8_t     scan_in;
+    uint8_t     scan_out;
+
+    Assert(pScanOut);
+    Assert(state == XS_IDLE || state == XS_BREAK || state == XS_HIBIT);
+
+    /* Preprocess the scan code for a 128-entry translation table. */
+    if (scanIn == 0x83)         /* Check for F7 key. */
+        scan_in = 0x02;
+    else if (scanIn == 0x84)    /* Check for SysRq key. */
+        scan_in = 0x7f;
+    else
+        scan_in = scanIn;
+
+    /* Values 0x80 and above are passed through, except for 0xF0
+     * which indicates a key release.
+     */
+    if (scan_in < 0x80)
+    {
+        scan_out = aAT2PC[scan_in];
+        /* Turn into break code if required. */
+        if (state == XS_BREAK || state == XS_HIBIT)
+            scan_out |= 0x80;
+
+        state = XS_IDLE;
+    }
+    else
+    {
+        /* NB: F0 E0 10 will be translated to E0 E5 (high bit set on last byte)! */
+        if (scan_in == 0xF0)        /* Check for break code. */
+            state = XS_BREAK;
+        else if (state == XS_BREAK)
+            state = XS_HIBIT;       /* Remember the break bit. */
+        scan_out = scan_in;
+    }
+    LogFlowFunc(("scan code %02X translated to %02X; new state is %d\n",
+                 scanIn, scan_out, state));
+
+    *pScanOut = scan_out;
+    return state;
+}
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Index: trunk/src/VBox/Devices/Input/DevPS2M.cpp
===================================================================
--- trunk/src/VBox/Devices/Input/DevPS2M.cpp	(revision 82170)
+++ trunk/src/VBox/Devices/Input/DevPS2M.cpp	(revision 82170)
@@ -0,0 +1,1414 @@
+/* $Id$ */
+/** @file
+ * PS2M - PS/2 auxiliary device (mouse) emulation.
+ */
+
+/*
+ * Copyright (C) 2007-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*
+ * References:
+ *
+ * The Undocumented PC (2nd Ed.), Frank van Gilluwe, Addison-Wesley, 1996.
+ * IBM TrackPoint System Version 4.0 Engineering Specification, 1999.
+ * ELAN Microelectronics eKM8025 USB & PS/2 Mouse Controller, 2006.
+ *
+ *
+ * Notes:
+ *
+ *  - The auxiliary device commands are very similar to keyboard commands.
+ *    Most keyboard commands which do not specifically deal with the keyboard
+ *    (enable, disable, reset) have identical counterparts.
+ *  - The code refers to 'auxiliary device' and 'mouse'; these terms are not
+ *    quite interchangeable. 'Auxiliary device' is used when referring to the
+ *    generic PS/2 auxiliary device interface and 'mouse' when referring to
+ *    a mouse attached to the auxiliary port.
+ *  - The basic modes of operation are reset, stream, and remote. Those are
+ *    mutually exclusive. Stream and remote modes can additionally have wrap
+ *    mode enabled.
+ *  - The auxiliary device sends unsolicited data to the host only when it is
+ *    both in stream mode and enabled. Otherwise it only responds to commands.
+ *
+ *
+ * There are three report packet formats supported by the emulated device. The
+ * standard three-byte PS/2 format (with middle button support), IntelliMouse
+ * four-byte format with added scroll wheel, and IntelliMouse Explorer four-byte
+ * format with reduced scroll wheel range but two additional buttons. Note that
+ * the first three bytes of the report are always the same.
+ *
+ * Upon reset, the mouse is always in the standard PS/2 mode. A special 'knock'
+ * sequence can be used to switch to ImPS/2 or ImEx mode. Three consecutive
+ * Set Sampling Rate (0F3h) commands with arguments 200, 100, 80 switch to ImPS/2
+ * mode. While in ImPS/2 or PS/2 mode, three consecutive Set Sampling Rate
+ * commands with arguments 200, 200, 80 switch to ImEx mode. The Read ID (0F2h)
+ * command will report the currently selected protocol.
+ *
+ * There is an extended ImEx mode with support for horizontal scrolling. It is
+ * entered from ImEx mode with a 200, 80, 40 sequence of Set Sampling Rate
+ * commands. It does not change the reported protocol (it remains 4, or ImEx)
+ * but changes the meaning of the 4th byte.
+ *
+ *
+ * Standard PS/2 pointing device three-byte report packet format:
+ *
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 1 | Y ovfl | X ovfl | Y sign | X sign |  Sync  | M btn  | R btn  | L btn  |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 2 |              X movement delta (two's complement)                      |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 3 |              Y movement delta (two's complement)                      |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ *
+ *   - The sync bit is always set. It allows software to synchronize data packets
+ *     as the X/Y position data typically does not have bit 4 set.
+ *   - The overflow bits are set if motion exceeds accumulator range. We use the
+ *     maximum range (effectively 9 bits) and do not set the overflow bits.
+ *   - Movement in the up/right direction is defined as having positive sign.
+ *
+ *
+ * IntelliMouse PS/2 (ImPS/2) fourth report packet byte:
+ *
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 4 |              Z movement delta (two's complement)                      |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ *
+ *   - The valid range for Z delta values is only -8/+7, i.e. 4 bits.
+ *
+ * IntelliMouse Explorer (ImEx) fourth report packet byte:
+ *
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 4 |   0    |   0    | Btn 5  | Btn 4  |  Z mov't delta (two's complement) |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ *
+ *   - The Z delta values are in practice only -1/+1; some mice (A4tech?) report
+ *     horizontal scrolling as -2/+2.
+ *
+ * IntelliMouse Explorer (ImEx) fourth report packet byte when scrolling:
+ *
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 4 |   V    |   H    |      Z or W movement delta (two's complement)       |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ *
+ *   - Buttons 4 and 5 are reported as with the regular ImEx protocol, but not when
+ *     scrolling. This is a departure from the usual logic because when the mouse
+ *     sends scroll events, the state of buttons 4/5 is not reported and the last
+ *     reported state should be assumed.
+ *
+ *   - When the V bit (bit 7) is set, vertical scroll (Z axis) is being reported.
+ *     When the H bit (bit 6) is set, horizontal scroll (W axis) is being reported.
+ *     The H and V bits are never set at the same time (also see below). When
+ *     the H and V bits are both clear, button 4/5 state is being reported.
+ *
+ *   - The Z/W delta is extended to 6 bits. Z (vertical) values are not restricted
+ *     to -1/+1, although W (horizontal) values are. Z values of at least -20/+20
+ *     can be seen in practice.
+ *
+ *   - Horizontal and vertical scroll is mutually exclusive. When the button is
+ *     tilted, no vertical scrolling is reported, i.e. horizontal scrolling
+ *     has priority over vertical.
+ *
+ *   - Positive values indicate down/right direction, negative values up/left.
+ *
+ *   - When the scroll button is tilted to engage horizontal scrolling, the mouse
+ *     keeps sending events at a rate of 4 or 5 per second as long as the button
+ *     is tilted.
+ *
+ * All report formats were verified with a real Microsoft IntelliMouse Explorer 4.0
+ * mouse attached through a PS/2 port.
+ *
+ * The button "accumulator" is necessary to avoid missing brief button presses.
+ * Without it, a very fast mouse button press + release might be lost if it
+ * happened between sending reports. The accumulator latches button presses to
+ * prevent that.
+ *
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP   LOG_GROUP_DEV_KBD
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include "VBoxDD.h"
+#define IN_PS2M
+#include "DevPS2.h"
+
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+/** @name Auxiliary device commands sent by the system.
+ * @{ */
+#define ACMD_SET_SCALE_11   0xE6    /* Set 1:1 scaling. */
+#define ACMD_SET_SCALE_21   0xE7    /* Set 2:1 scaling. */
+#define ACMD_SET_RES        0xE8    /* Set resolution. */
+#define ACMD_REQ_STATUS     0xE9    /* Get device status. */
+#define ACMD_SET_STREAM     0xEA    /* Set stream mode. */
+#define ACMD_READ_REMOTE    0xEB    /* Read remote data. */
+#define ACMD_RESET_WRAP     0xEC    /* Exit wrap mode. */
+#define ACMD_INVALID_1      0xED
+#define ACMD_SET_WRAP       0xEE    /* Set wrap (echo) mode. */
+#define ACMD_INVALID_2      0xEF
+#define ACMD_SET_REMOTE     0xF0    /* Set remote mode. */
+#define ACMD_INVALID_3      0xF1
+#define ACMD_READ_ID        0xF2    /* Read device ID. */
+#define ACMD_SET_SAMP_RATE  0xF3    /* Set sampling rate. */
+#define ACMD_ENABLE         0xF4    /* Enable (streaming mode). */
+#define ACMD_DISABLE        0xF5    /* Disable (streaming mode). */
+#define ACMD_SET_DEFAULT    0xF6    /* Set defaults. */
+#define ACMD_INVALID_4      0xF7
+#define ACMD_INVALID_5      0xF8
+#define ACMD_INVALID_6      0xF9
+#define ACMD_INVALID_7      0xFA
+#define ACMD_INVALID_8      0xFB
+#define ACMD_INVALID_9      0xFC
+#define ACMD_INVALID_10     0xFD
+#define ACMD_RESEND         0xFE    /* Resend response. */
+#define ACMD_RESET          0xFF    /* Reset device. */
+/** @} */
+
+/** @name Auxiliary device responses sent to the system.
+ * @{ */
+#define ARSP_ID             0x00
+#define ARSP_BAT_OK         0xAA    /* Self-test passed. */
+#define ARSP_ACK            0xFA    /* Command acknowledged. */
+#define ARSP_ERROR          0xFC    /* Bad command. */
+#define ARSP_RESEND         0xFE    /* Requesting resend. */
+/** @} */
+
+/** Define a simple PS/2 input device queue. */
+#define DEF_PS2Q_TYPE(name, size)   \
+     typedef struct {               \
+        uint32_t    rpos;           \
+        uint32_t    wpos;           \
+        uint32_t    cUsed;          \
+        uint32_t    cSize;          \
+        uint8_t     abQueue[size];  \
+     } name
+
+/* Internal mouse queue sizes. The input queue is relatively large,
+ * but the command queue only needs to handle a few bytes.
+ */
+#define AUX_EVT_QUEUE_SIZE        256
+#define AUX_CMD_QUEUE_SIZE          8
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+
+DEF_PS2Q_TYPE(AuxEvtQ, AUX_EVT_QUEUE_SIZE);
+DEF_PS2Q_TYPE(AuxCmdQ, AUX_CMD_QUEUE_SIZE);
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE /// @todo hack
+DEF_PS2Q_TYPE(GeneriQ, 1);
+#endif
+
+/* Auxiliary device special modes of operation. */
+typedef enum {
+    AUX_MODE_STD,           /* Standard operation. */
+    AUX_MODE_RESET,         /* Currently in reset. */
+    AUX_MODE_WRAP           /* Wrap mode (echoing input). */
+} PS2M_MODE;
+
+/* Auxiliary device operational state. */
+typedef enum {
+    AUX_STATE_RATE_ERR  = RT_BIT(0),    /* Invalid rate received. */
+    AUX_STATE_RES_ERR   = RT_BIT(1),    /* Invalid resolution received. */
+    AUX_STATE_SCALING   = RT_BIT(4),    /* 2:1 scaling in effect. */
+    AUX_STATE_ENABLED   = RT_BIT(5),    /* Reporting enabled in stream mode. */
+    AUX_STATE_REMOTE    = RT_BIT(6)     /* Remote mode (reports on request). */
+} PS2M_STATE;
+
+/* Externally visible state bits. */
+#define AUX_STATE_EXTERNAL  (AUX_STATE_SCALING | AUX_STATE_ENABLED | AUX_STATE_REMOTE)
+
+/* Protocols supported by the PS/2 mouse. */
+typedef enum {
+    PS2M_PROTO_PS2STD      = 0,  /* Standard PS/2 mouse protocol. */
+    PS2M_PROTO_IMPS2       = 3,  /* IntelliMouse PS/2 protocol. */
+    PS2M_PROTO_IMEX        = 4,  /* IntelliMouse Explorer protocol. */
+    PS2M_PROTO_IMEX_HORZ   = 5   /* IntelliMouse Explorer with horizontal reports. */
+} PS2M_PROTO;
+
+/* Protocol selection 'knock' states. */
+typedef enum {
+    PS2M_KNOCK_INITIAL,
+    PS2M_KNOCK_1ST,
+    PS2M_KNOCK_IMPS2_2ND,
+    PS2M_KNOCK_IMEX_2ND,
+    PS2M_KNOCK_IMEX_HORZ_2ND
+} PS2M_KNOCK_STATE;
+
+/**
+ * The PS/2 auxiliary device instance data.
+ */
+typedef struct PS2M
+{
+    /** Pointer to parent device (keyboard controller). */
+    R3PTRTYPE(void *)   pParent;
+    /** Operational state. */
+    uint8_t             u8State;
+    /** Configured sampling rate. */
+    uint8_t             u8SampleRate;
+    /** Configured resolution. */
+    uint8_t             u8Resolution;
+    /** Currently processed command (if any). */
+    uint8_t             u8CurrCmd;
+    /** Set if the throttle delay is active. */
+    bool                fThrottleActive;
+    /** Set if the throttle delay is active. */
+    bool                fDelayReset;
+    /** Operational mode. */
+    PS2M_MODE           enmMode;
+    /** Currently used protocol. */
+    PS2M_PROTO          enmProtocol;
+    /** Currently used protocol. */
+    PS2M_KNOCK_STATE    enmKnockState;
+    /** Buffer holding mouse events to be sent to the host. */
+    AuxEvtQ             evtQ;
+    /** Command response queue (priority). */
+    AuxCmdQ             cmdQ;
+    /** Accumulated horizontal movement. */
+    int32_t             iAccumX;
+    /** Accumulated vertical movement. */
+    int32_t             iAccumY;
+    /** Accumulated Z axis (vertical scroll) movement. */
+    int32_t             iAccumZ;
+    /** Accumulated W axis (horizontal scroll) movement. */
+    int32_t             iAccumW;
+    /** Accumulated button presses. */
+    uint32_t            fAccumB;
+    /** Instantaneous button data. */
+    uint32_t            fCurrB;
+    /** Button state last sent to the guest. */
+    uint32_t            fReportedB;
+    /** Throttling delay in milliseconds. */
+    uint32_t            uThrottleDelay;
+
+    /** The device critical section protecting everything - R3 Ptr */
+    R3PTRTYPE(PPDMCRITSECT) pCritSectR3;
+    /** Command delay timer - R3 Ptr. */
+    PTMTIMERR3          pDelayTimerR3;
+    /** Interrupt throttling timer - R3 Ptr. */
+    PTMTIMERR3          pThrottleTimerR3;
+    RTR3PTR             Alignment1;
+
+    /** Command delay timer - RC Ptr. */
+    PTMTIMERRC          pDelayTimerRC;
+    /** Interrupt throttling timer - RC Ptr. */
+    PTMTIMERRC          pThrottleTimerRC;
+
+    /** Command delay timer - R0 Ptr. */
+    PTMTIMERR0          pDelayTimerR0;
+    /** Interrupt throttling timer - R0 Ptr. */
+    PTMTIMERR0          pThrottleTimerR0;
+
+    /**
+     * Mouse port - LUN#1.
+     *
+     * @implements  PDMIBASE
+     * @implements  PDMIMOUSEPORT
+     */
+    struct
+    {
+        /** The base interface for the mouse port. */
+        PDMIBASE                            IBase;
+        /** The keyboard port base interface. */
+        PDMIMOUSEPORT                       IPort;
+
+        /** The base interface of the attached mouse driver. */
+        R3PTRTYPE(PPDMIBASE)                pDrvBase;
+        /** The keyboard interface of the attached mouse driver. */
+        R3PTRTYPE(PPDMIMOUSECONNECTOR)      pDrv;
+    } Mouse;
+} PS2M, *PPS2M;
+
+AssertCompile(PS2M_STRUCT_FILLER >= sizeof(PS2M));
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+/*********************************************************************************************************************************
+*   Test code function declarations                                                                                              *
+*********************************************************************************************************************************/
+#if defined(RT_STRICT) && defined(IN_RING3)
+static void ps2mTestAccumulation(void);
+#endif
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+
+
+/**
+ * Clear a queue.
+ *
+ * @param   pQ                  Pointer to the queue.
+ */
+static void ps2kClearQueue(GeneriQ *pQ)
+{
+    LogFlowFunc(("Clearing queue %p\n", pQ));
+    pQ->wpos  = pQ->rpos;
+    pQ->cUsed = 0;
+}
+
+
+/**
+ * Add a byte to a queue.
+ *
+ * @param   pQ                  Pointer to the queue.
+ * @param   val                 The byte to store.
+ */
+static void ps2kInsertQueue(GeneriQ *pQ, uint8_t val)
+{
+    /* Check if queue is full. */
+    if (pQ->cUsed >= pQ->cSize)
+    {
+        LogRelFlowFunc(("queue %p full (%d entries)\n", pQ, pQ->cUsed));
+        return;
+    }
+    /* Insert data and update circular buffer write position. */
+    pQ->abQueue[pQ->wpos] = val;
+    if (++pQ->wpos == pQ->cSize)
+        pQ->wpos = 0;   /* Roll over. */
+    ++pQ->cUsed;
+    LogRelFlowFunc(("inserted 0x%02X into queue %p\n", val, pQ));
+}
+
+#ifdef IN_RING3
+
+/**
+ * Save a queue state.
+ *
+ * @param   pSSM                SSM handle to write the state to.
+ * @param   pQ                  Pointer to the queue.
+ */
+static void ps2kSaveQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
+{
+    uint32_t    cItems = pQ->cUsed;
+    int         i;
+
+    /* Only save the number of items. Note that the read/write
+     * positions aren't saved as they will be rebuilt on load.
+     */
+    SSMR3PutU32(pSSM, cItems);
+
+    LogFlow(("Storing %d items from queue %p\n", cItems, pQ));
+
+    /* Save queue data - only the bytes actually used (typically zero). */
+    for (i = pQ->rpos; cItems-- > 0; i = (i + 1) % pQ->cSize)
+        SSMR3PutU8(pSSM, pQ->abQueue[i]);
+}
+
+/**
+ * Load a queue state.
+ *
+ * @param   pSSM                SSM handle to read the state from.
+ * @param   pQ                  Pointer to the queue.
+ *
+ * @return  int                 VBox status/error code.
+ */
+static int ps2kLoadQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
+{
+    int         rc;
+
+    /* On load, always put the read pointer at zero. */
+    SSMR3GetU32(pSSM, &pQ->cUsed);
+
+    LogFlow(("Loading %d items to queue %p\n", pQ->cUsed, pQ));
+
+    if (pQ->cUsed > pQ->cSize)
+    {
+        AssertMsgFailed(("Saved size=%u, actual=%u\n", pQ->cUsed, pQ->cSize));
+        return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+    }
+
+    /* Recalculate queue positions and load data in one go. */
+    pQ->rpos = 0;
+    pQ->wpos = pQ->cUsed;
+    rc = SSMR3GetMem(pSSM, pQ->abQueue, pQ->cUsed);
+
+    return rc;
+}
+
+/* Report a change in status down (or is it up?) the driver chain. */
+static void ps2mSetDriverState(PPS2M pThis, bool fEnabled)
+{
+    PPDMIMOUSECONNECTOR pDrv = pThis->Mouse.pDrv;
+    if (pDrv)
+        pDrv->pfnReportModes(pDrv, fEnabled, false, false);
+}
+
+/* Reset the pointing device. */
+static void ps2mReset(PPS2M pThis)
+{
+    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_BAT_OK);
+    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, 0);
+    pThis->enmMode   = AUX_MODE_STD;
+    pThis->u8CurrCmd = 0;
+
+    /// @todo move to its proper home!
+    ps2mSetDriverState(pThis, true);
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Retrieve a byte from a queue.
+ *
+ * @param   pQ                  Pointer to the queue.
+ * @param   pVal                Pointer to storage for the byte.
+ *
+ * @return  int                 VINF_TRY_AGAIN if queue is empty,
+ *                              VINF_SUCCESS if a byte was read.
+ */
+static int ps2kRemoveQueue(GeneriQ *pQ, uint8_t *pVal)
+{
+    int     rc = VINF_TRY_AGAIN;
+
+    Assert(pVal);
+    if (pQ->cUsed)
+    {
+        *pVal = pQ->abQueue[pQ->rpos];
+        if (++pQ->rpos == pQ->cSize)
+            pQ->rpos = 0;   /* Roll over. */
+        --pQ->cUsed;
+        rc = VINF_SUCCESS;
+        LogFlowFunc(("removed 0x%02X from queue %p\n", *pVal, pQ));
+    } else
+        LogFlowFunc(("queue %p empty\n", pQ));
+    return rc;
+}
+
+static void ps2mSetRate(PPS2M pThis, uint8_t rate)
+{
+    Assert(rate);
+    pThis->uThrottleDelay = rate ? 1000 / rate : 0;
+    pThis->u8SampleRate = rate;
+    LogFlowFunc(("Sampling rate %u, throttle delay %u ms\n", pThis->u8SampleRate, pThis->uThrottleDelay));
+}
+
+static void ps2mSetDefaults(PPS2M pThis)
+{
+    LogFlowFunc(("Set mouse defaults\n"));
+    /* Standard protocol, reporting disabled, resolution 2, 1:1 scaling. */
+    pThis->enmProtocol  = PS2M_PROTO_PS2STD;
+    pThis->u8State      = 0;
+    pThis->u8Resolution = 2;
+
+    /* Sample rate 100 reports per second. */
+    ps2mSetRate(pThis, 100);
+
+    /* Event queue, eccumulators, and button status bits are cleared. */
+    ps2kClearQueue((GeneriQ *)&pThis->evtQ);
+    pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = pThis->iAccumW = pThis->fAccumB = 0;
+}
+
+/* Handle the sampling rate 'knock' sequence which selects protocol. */
+static void ps2mRateProtocolKnock(PPS2M pThis, uint8_t rate)
+{
+    switch (pThis->enmKnockState)
+    {
+    case PS2M_KNOCK_INITIAL:
+        if (rate == 200)
+            pThis->enmKnockState = PS2M_KNOCK_1ST;
+        break;
+    case PS2M_KNOCK_1ST:
+        if (rate == 100)
+            pThis->enmKnockState = PS2M_KNOCK_IMPS2_2ND;
+        else if (rate == 200)
+            pThis->enmKnockState = PS2M_KNOCK_IMEX_2ND;
+        else if (rate == 80)
+            pThis->enmKnockState = PS2M_KNOCK_IMEX_HORZ_2ND;
+        else
+            pThis->enmKnockState = PS2M_KNOCK_INITIAL;
+        break;
+    case PS2M_KNOCK_IMPS2_2ND:
+        if (rate == 80)
+        {
+            pThis->enmProtocol = PS2M_PROTO_IMPS2;
+            LogRelFlow(("PS2M: Switching mouse to ImPS/2 protocol.\n"));
+        }
+        pThis->enmKnockState = PS2M_KNOCK_INITIAL;
+        break;
+    case PS2M_KNOCK_IMEX_2ND:
+        if (rate == 80)
+        {
+            pThis->enmProtocol = PS2M_PROTO_IMEX;
+            LogRelFlow(("PS2M: Switching mouse to ImEx protocol.\n"));
+        }
+        pThis->enmKnockState = PS2M_KNOCK_INITIAL;
+        break;
+    case PS2M_KNOCK_IMEX_HORZ_2ND:
+        if (rate == 40)
+        {
+            pThis->enmProtocol = PS2M_PROTO_IMEX_HORZ;
+            LogRelFlow(("PS2M: Switching mouse ImEx with horizontal scrolling.\n"));
+        }
+        RT_FALL_THRU();
+    default:
+        pThis->enmKnockState = PS2M_KNOCK_INITIAL;
+    }
+}
+
+/* Three-button event mask. */
+#define PS2M_STD_BTN_MASK   (RT_BIT(0) | RT_BIT(1) | RT_BIT(2))
+/* ImEx button 4/5 event mask. */
+#define PS2M_IMEX_BTN_MASK  (RT_BIT(3) | RT_BIT(4))
+
+/* Report accumulated movement and button presses, then clear the accumulators. */
+static void ps2mReportAccumulatedEvents(PPS2M pThis, GeneriQ *pQueue, bool fAccumBtns)
+{
+    uint32_t    fBtnState = fAccumBtns ? pThis->fAccumB : pThis->fCurrB;
+    uint8_t     val;
+    int         dX, dY, dZ, dW;
+
+    /* Clamp the accumulated delta values to the allowed range. */
+    dX = RT_MIN(RT_MAX(pThis->iAccumX, -255), 255);
+    dY = RT_MIN(RT_MAX(pThis->iAccumY, -255), 255);
+
+    /* Start with the sync bit and buttons 1-3. */
+    val = RT_BIT(3) | (fBtnState & PS2M_STD_BTN_MASK);
+    /* Set the X/Y sign bits. */
+    if (dX < 0)
+        val |= RT_BIT(4);
+    if (dY < 0)
+        val |= RT_BIT(5);
+
+    /* Send the standard 3-byte packet (always the same). */
+    ps2kInsertQueue(pQueue, val);
+    ps2kInsertQueue(pQueue, dX);
+    ps2kInsertQueue(pQueue, dY);
+
+    /* Add fourth byte if an extended protocol is in use. */
+    if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
+    {
+        /* Start out with 4-bit dZ range. */
+        dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -8), 7);
+
+        if (pThis->enmProtocol == PS2M_PROTO_IMPS2)
+        {
+            /* NB: Only uses 4-bit dZ range, despite using a full byte. */
+            ps2kInsertQueue(pQueue, dZ);
+            pThis->iAccumZ -= dZ;
+        }
+        else if (pThis->enmProtocol == PS2M_PROTO_IMEX)
+        {
+           /* Z value uses 4 bits; buttons 4/5 in bits 4 and 5. */
+           val  = (fBtnState & PS2M_IMEX_BTN_MASK) << 1;
+           val |= dZ & 0x0f;
+           pThis->iAccumZ -= dZ;
+           ps2kInsertQueue(pQueue, val);
+        }
+        else
+        {
+            Assert((pThis->enmProtocol == PS2M_PROTO_IMEX_HORZ));
+            /* With ImEx + horizontal reporting, prioritize buttons 4/5. */
+            if (pThis->iAccumZ || pThis->iAccumW)
+            {
+               /* ImEx + horizontal reporting Horizontal scroll has
+                * precedence over vertical. Buttons cannot be reported
+                * this way.
+                */
+               if (pThis->iAccumW)
+               {
+                  dW = RT_MIN(RT_MAX(pThis->iAccumW, -32), 31);
+                  val = (dW & 0x3F) | 0x40;
+                  pThis->iAccumW -= dW;
+               }
+               else
+               {
+                  Assert(pThis->iAccumZ);
+                  /* We can use 6-bit dZ range. Wow! */
+                  dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -32), 31);
+                  val = (dZ & 0x3F) | 0x80;
+                  pThis->iAccumZ -= dZ;
+               }
+            }
+            else
+            {
+               /* Just Buttons 4/5 in bits 4 and 5. No scrolling. */
+               val = (fBtnState & PS2M_IMEX_BTN_MASK) << 1;
+            }
+            ps2kInsertQueue(pQueue, val);
+        }
+    }
+
+    /* Clear the movement accumulators, but not necessarily button state. */
+    pThis->iAccumX = pThis->iAccumY = 0;
+    /* Clear accumulated button state only when it's being used. */
+    if (fAccumBtns)
+    {
+        pThis->fReportedB = pThis->fCurrB | pThis->fAccumB;
+        pThis->fAccumB    = 0;
+    }
+}
+
+
+/* Determine whether a reporting rate is one of the valid ones. */
+bool ps2mIsRateSupported(uint8_t rate)
+{
+    static uint8_t  aValidRates[] = { 10, 20, 40, 60, 80, 100, 200 };
+    size_t          i;
+    bool            fValid = false;
+
+    for (i = 0; i < RT_ELEMENTS(aValidRates); ++i)
+        if (aValidRates[i] == rate)
+        {
+            fValid = true;
+            break;
+        }
+
+   return fValid;
+}
+
+/**
+ * Receive and process a byte sent by the keyboard controller.
+ *
+ * @param   pThis               The PS/2 auxiliary device instance data.
+ * @param   cmd                 The command (or data) byte.
+ */
+int PS2MByteToAux(PPS2M pThis, uint8_t cmd)
+{
+    uint8_t u8Val;
+    bool    fHandled = true;
+
+    LogFlowFunc(("cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
+
+    if (pThis->enmMode == AUX_MODE_RESET)
+        /* In reset mode, do not respond at all. */
+        return VINF_SUCCESS;
+
+    /* If there's anything left in the command response queue, trash it. */
+    ps2kClearQueue((GeneriQ *)&pThis->cmdQ);
+
+    if (pThis->enmMode == AUX_MODE_WRAP)
+    {
+        /* In wrap mode, bounce most data right back.*/
+        if (cmd == ACMD_RESET || cmd == ACMD_RESET_WRAP)
+            ;   /* Handle as regular commands. */
+        else
+        {
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, cmd);
+            return VINF_SUCCESS;
+        }
+    }
+
+#ifndef IN_RING3
+    /* Reset, Enable, and Set Default commands must be run in R3. */
+    if (cmd == ACMD_RESET || cmd == ACMD_ENABLE || cmd == ACMD_SET_DEFAULT)
+        return VINF_IOM_R3_IOPORT_WRITE;
+#endif
+
+    switch (cmd)
+    {
+        case ACMD_SET_SCALE_11:
+            pThis->u8State &= ~AUX_STATE_SCALING;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_SET_SCALE_21:
+            pThis->u8State |= AUX_STATE_SCALING;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_REQ_STATUS:
+            /* Report current status, sample rate, and resolution. */
+            u8Val  = (pThis->u8State & AUX_STATE_EXTERNAL) | (pThis->fCurrB & PS2M_STD_BTN_MASK);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, u8Val);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8Resolution);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8SampleRate);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_SET_STREAM:
+            pThis->u8State &= ~AUX_STATE_REMOTE;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_READ_REMOTE:
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->cmdQ, false);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_RESET_WRAP:
+            pThis->enmMode = AUX_MODE_STD;
+            /* NB: Stream mode reporting remains disabled! */
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_SET_WRAP:
+            pThis->enmMode = AUX_MODE_WRAP;
+            pThis->u8State &= ~AUX_STATE_ENABLED;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_SET_REMOTE:
+            pThis->u8State |= AUX_STATE_REMOTE;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_READ_ID:
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            /* ImEx + horizontal is protocol 4, just like plain ImEx. */
+            u8Val = pThis->enmProtocol == PS2M_PROTO_IMEX_HORZ ? PS2M_PROTO_IMEX : pThis->enmProtocol;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, u8Val);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_ENABLE:
+            pThis->u8State |= AUX_STATE_ENABLED;
+#ifdef IN_RING3
+            ps2mSetDriverState(pThis, true);
+#else
+            AssertLogRelMsgFailed(("Invalid ACMD_ENABLE outside R3!\n"));
+#endif
+            ps2kClearQueue((GeneriQ *)&pThis->evtQ);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_DISABLE:
+            pThis->u8State &= ~AUX_STATE_ENABLED;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_SET_DEFAULT:
+            ps2mSetDefaults(pThis);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_RESEND:
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_RESET:
+            ps2mSetDefaults(pThis);
+            /// @todo reset more?
+            pThis->u8CurrCmd = cmd;
+            pThis->enmMode   = AUX_MODE_RESET;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            if (pThis->fDelayReset)
+                /* Slightly delay reset completion; it might take hundreds of ms. */
+                TMTimerSetMillies(pThis->CTX_SUFF(pDelayTimer), 1);
+            else
+#ifdef IN_RING3
+                ps2mReset(pThis);
+#else
+                AssertLogRelMsgFailed(("Invalid ACMD_RESET outside R3!\n"));
+#endif
+            break;
+        /* The following commands need a parameter. */
+        case ACMD_SET_RES:
+        case ACMD_SET_SAMP_RATE:
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = cmd;
+            break;
+        default:
+            /* Sending a command instead of a parameter starts the new command. */
+            switch (pThis->u8CurrCmd)
+            {
+                case ACMD_SET_RES:
+                    if (cmd < 4)    /* Valid resolutions are 0-3. */
+                    {
+                        pThis->u8Resolution = cmd;
+                        pThis->u8State &= ~AUX_STATE_RES_ERR;
+                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+                        pThis->u8CurrCmd = 0;
+                    }
+                    else
+                    {
+                        /* Bad resolution. Reply with Resend or Error. */
+                        if (pThis->u8State & AUX_STATE_RES_ERR)
+                        {
+                            pThis->u8State &= ~AUX_STATE_RES_ERR;
+                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ERROR);
+                            pThis->u8CurrCmd = 0;
+                        }
+                        else
+                        {
+                            pThis->u8State |= AUX_STATE_RES_ERR;
+                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
+                            /* NB: Current command remains unchanged. */
+                        }
+                    }
+                    break;
+                case ACMD_SET_SAMP_RATE:
+                    if (ps2mIsRateSupported(cmd))
+                    {
+                        pThis->u8State &= ~AUX_STATE_RATE_ERR;
+                        ps2mSetRate(pThis, cmd);
+                        ps2mRateProtocolKnock(pThis, cmd);
+                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+                        pThis->u8CurrCmd = 0;
+                    }
+                    else
+                    {
+                        /* Bad rate. Reply with Resend or Error. */
+                        if (pThis->u8State & AUX_STATE_RATE_ERR)
+                        {
+                            pThis->u8State &= ~AUX_STATE_RATE_ERR;
+                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ERROR);
+                            pThis->u8CurrCmd = 0;
+                        }
+                        else
+                        {
+                            pThis->u8State |= AUX_STATE_RATE_ERR;
+                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
+                            /* NB: Current command remains unchanged. */
+                        }
+                    }
+                    break;
+                default:
+                    fHandled = false;
+            }
+            /* Fall through only to handle unrecognized commands. */
+            if (fHandled)
+                break;
+            RT_FALL_THRU();
+
+        case ACMD_INVALID_1:
+        case ACMD_INVALID_2:
+        case ACMD_INVALID_3:
+        case ACMD_INVALID_4:
+        case ACMD_INVALID_5:
+        case ACMD_INVALID_6:
+        case ACMD_INVALID_7:
+        case ACMD_INVALID_8:
+        case ACMD_INVALID_9:
+        case ACMD_INVALID_10:
+            Log(("Unsupported command 0x%02X!\n", cmd));
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
+            pThis->u8CurrCmd = 0;
+            break;
+    }
+    LogFlowFunc(("Active cmd now 0x%02X; updating interrupts\n", pThis->u8CurrCmd));
+    return VINF_SUCCESS;
+}
+
+/**
+ * Send a byte (packet data or command response) to the keyboard controller.
+ *
+ * @returns VINF_SUCCESS or VINF_TRY_AGAIN.
+ * @param   pThis               The PS/2 auxiliary device instance data.
+ * @param   pb                  Where to return the byte we've read.
+ * @remarks Caller must have entered the device critical section.
+ */
+int PS2MByteFromAux(PPS2M pThis, uint8_t *pb)
+{
+    int         rc;
+
+    AssertPtr(pb);
+
+    /* Anything in the command queue has priority over data
+     * in the event queue. Additionally, packet data are
+     * blocked if a command is currently in progress, even if
+     * the command queue is empty.
+     */
+    /// @todo Probably should flush/not fill queue if stream mode reporting disabled?!
+    rc = ps2kRemoveQueue((GeneriQ *)&pThis->cmdQ, pb);
+    if (rc != VINF_SUCCESS && !pThis->u8CurrCmd && (pThis->u8State & AUX_STATE_ENABLED))
+        rc = ps2kRemoveQueue((GeneriQ *)&pThis->evtQ, pb);
+
+    LogFlowFunc(("mouse sends 0x%02x (%svalid data)\n", *pb, rc == VINF_SUCCESS ? "" : "not "));
+
+    return rc;
+}
+
+#ifdef IN_RING3
+
+/** Is there any state change to send as events to the guest? */
+static uint32_t ps2mHaveEvents(PPS2M pThis)
+{
+    return   pThis->iAccumX || pThis->iAccumY || pThis->iAccumZ || pThis->iAccumW
+           || ((pThis->fCurrB | pThis->fAccumB) != pThis->fReportedB);
+}
+
+/* Event rate throttling timer to emulate the auxiliary device sampling rate.
+ */
+static DECLCALLBACK(void) ps2mThrottleTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+    RT_NOREF2(pDevIns, pTimer);
+    PPS2M       pThis = (PS2M *)pvUser;
+    uint32_t    uHaveEvents;
+
+    /* Grab the lock to avoid races with PutEvent(). */
+    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
+    AssertReleaseRC(rc);
+
+    /* If more movement is accumulated, report it and restart the timer. */
+    uHaveEvents = ps2mHaveEvents(pThis);
+    LogFlowFunc(("Have%s events\n", uHaveEvents ? "" : " no"));
+
+    if (uHaveEvents)
+    {
+        /* Report accumulated data, poke the KBC, and start the timer. */
+        ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->evtQ, true);
+        KBCUpdateInterrupts(pThis->pParent);
+        TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
+    }
+    else
+        pThis->fThrottleActive = false;
+
+    PDMCritSectLeave(pThis->pCritSectR3);
+}
+
+/* The auxiliary device reset is specified to take up to about 500 milliseconds. We need
+ * to delay sending the result to the host for at least a tiny little while.
+ */
+static DECLCALLBACK(void) ps2mDelayTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+    RT_NOREF2(pDevIns, pTimer);
+    PPS2M pThis = (PS2M *)pvUser;
+
+    LogFlowFunc(("Delay timer: cmd %02X\n", pThis->u8CurrCmd));
+
+    Assert(pThis->u8CurrCmd == ACMD_RESET);
+    ps2mReset(pThis);
+
+    /// @todo Might want a PS2MCompleteCommand() to push last response, clear command, and kick the KBC...
+    /* Give the KBC a kick. */
+    KBCUpdateInterrupts(pThis->pParent);
+}
+
+
+/**
+ * Debug device info handler. Prints basic auxiliary device state.
+ *
+ * @param   pDevIns     Device instance which registered the info.
+ * @param   pHlp        Callback functions for doing output.
+ * @param   pszArgs     Argument string. Optional and specific to the handler.
+ */
+static DECLCALLBACK(void) ps2mInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    static const char   *pcszModes[] = { "normal", "reset", "wrap" };
+    static const char   *pcszProtocols[] = { "PS/2", NULL, NULL, "ImPS/2", "ImEx", "ImEx+horizontal" };
+    PPS2M   pThis = KBDGetPS2MFromDevIns(pDevIns);
+    NOREF(pszArgs);
+
+    Assert(pThis->enmMode <= RT_ELEMENTS(pcszModes));
+    Assert(pThis->enmProtocol <= RT_ELEMENTS(pcszProtocols));
+    pHlp->pfnPrintf(pHlp, "PS/2 mouse state: %s, %s mode, reporting %s\n",
+                    pcszModes[pThis->enmMode],
+                    pThis->u8State & AUX_STATE_REMOTE  ? "remote"  : "stream",
+                    pThis->u8State & AUX_STATE_ENABLED ? "enabled" : "disabled");
+    pHlp->pfnPrintf(pHlp, "Protocol: %s, scaling %u:1\n",
+                    pcszProtocols[pThis->enmProtocol],
+                    pThis->u8State & AUX_STATE_SCALING ? 2 : 1);
+    pHlp->pfnPrintf(pHlp, "Active command %02X\n", pThis->u8CurrCmd);
+    pHlp->pfnPrintf(pHlp, "Sampling rate %u reports/sec, resolution %u counts/mm\n",
+                    pThis->u8SampleRate, 1 << pThis->u8Resolution);
+    pHlp->pfnPrintf(pHlp, "Command queue: %d items (%d max)\n",
+                    pThis->cmdQ.cUsed, pThis->cmdQ.cSize);
+    pHlp->pfnPrintf(pHlp, "Event queue  : %d items (%d max)\n",
+                    pThis->evtQ.cUsed, pThis->evtQ.cSize);
+}
+
+/* -=-=-=-=-=- Mouse: IBase  -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) ps2mQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PPS2M pThis = RT_FROM_MEMBER(pInterface, PS2M, Mouse.IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Mouse.IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSEPORT, &pThis->Mouse.IPort);
+    return NULL;
+}
+
+
+/* -=-=-=-=-=- Mouse: IMousePort  -=-=-=-=-=- */
+
+/**
+ * Mouse event handler.
+ *
+ * @returns VBox status code.
+ * @param   pThis           The PS/2 auxiliary device instance data.
+ * @param   dx              X direction movement delta.
+ * @param   dy              Y direction movement delta.
+ * @param   dz              Z (vertical scroll) movement delta.
+ * @param   dw              W (horizontal scroll) movement delta.
+ * @param   fButtons        Depressed button mask.
+ */
+static int ps2mPutEventWorker(PPS2M pThis, int32_t dx, int32_t dy,
+                              int32_t dz, int32_t dw, uint32_t fButtons)
+{
+    int             rc = VINF_SUCCESS;
+
+    /* Update internal accumulators and button state. Ignore any buttons beyond 5. */
+    pThis->iAccumX += dx;
+    pThis->iAccumY += dy;
+    pThis->iAccumZ += dz;
+    pThis->iAccumW += dw;
+    pThis->fCurrB   = fButtons & (PS2M_STD_BTN_MASK | PS2M_IMEX_BTN_MASK);
+    pThis->fAccumB |= pThis->fCurrB;
+
+    /* Ditch accumulated data that can't be reported by the current protocol.
+     * This avoids sending phantom empty reports when un-reportable events
+     * are received.
+     */
+    if (pThis->enmProtocol < PS2M_PROTO_IMEX_HORZ)
+       pThis->iAccumW = 0; /* No horizontal scroll. */
+
+    if (pThis->enmProtocol < PS2M_PROTO_IMEX)
+    {
+       pThis->fAccumB &= PS2M_STD_BTN_MASK;   /* Only buttons 1-3. */
+       pThis->fCurrB  &= PS2M_STD_BTN_MASK;
+    }
+
+    if (pThis->enmProtocol < PS2M_PROTO_IMPS2)
+       pThis->iAccumZ = 0; /* No vertical scroll. */
+
+    /* Report the event (if any) and start the throttle timer unless it's already running. */
+    if (!pThis->fThrottleActive && ps2mHaveEvents(pThis))
+    {
+        ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->evtQ, true);
+        KBCUpdateInterrupts(pThis->pParent);
+        pThis->fThrottleActive = true;
+        TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
+    }
+
+    return rc;
+}
+
+/* -=-=-=-=-=- Mouse: IMousePort  -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIMOUSEPORT,pfnPutEvent}
+ */
+static DECLCALLBACK(int) ps2mPutEvent(PPDMIMOUSEPORT pInterface, int32_t dx, int32_t dy,
+                                      int32_t dz, int32_t dw, uint32_t fButtons)
+{
+    PPS2M       pThis = RT_FROM_MEMBER(pInterface, PS2M, Mouse.IPort);
+    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
+    AssertReleaseRC(rc);
+
+    LogRelFlowFunc(("dX=%d dY=%d dZ=%d dW=%d buttons=%02X\n", dx, dy, dz, dw, fButtons));
+    /* NB: The PS/2 Y axis direction is inverted relative to ours. */
+    ps2mPutEventWorker(pThis, dx, -dy, dz, dw, fButtons);
+
+    PDMCritSectLeave(pThis->pCritSectR3);
+    return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventAbs}
+ */
+static DECLCALLBACK(int) ps2mPutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t x, uint32_t y,
+                                         int32_t dz, int32_t dw, uint32_t fButtons)
+{
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+    NOREF(pInterface); NOREF(x); NOREF(y); NOREF(dz); NOREF(dw); NOREF(fButtons);
+}
+
+/**
+ * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventMultiTouch}
+ */
+static DECLCALLBACK(int) ps2mPutEventMT(PPDMIMOUSEPORT pInterface, uint8_t cContacts,
+                                        const uint64_t *pau64Contacts, uint32_t u32ScanTime)
+{
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+    NOREF(pInterface); NOREF(cContacts); NOREF(pau64Contacts); NOREF(u32ScanTime);
+}
+
+
+
+/**
+ * Attach command.
+ *
+ * This is called to let the device attach to a driver for a
+ * specified LUN.
+ *
+ * This is like plugging in the mouse after turning on the
+ * system.
+ *
+ * @returns VBox status code.
+ * @param   pThis       The PS/2 auxiliary device instance data.
+ * @param   pDevIns     The device instance.
+ * @param   iLUN        The logical unit which is being detached.
+ * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ */
+int PS2MAttach(PPS2M pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+    int         rc;
+
+    /* The LUN must be 1, i.e. mouse. */
+    Assert(iLUN == 1);
+    AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
+                    ("PS/2 mouse does not support hotplugging\n"),
+                    VERR_INVALID_PARAMETER);
+
+    LogFlowFunc(("iLUN=%d\n", iLUN));
+
+    rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Mouse.IBase, &pThis->Mouse.pDrvBase, "Mouse Port");
+    if (RT_SUCCESS(rc))
+    {
+        pThis->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
+        if (!pThis->Mouse.pDrv)
+        {
+            AssertLogRelMsgFailed(("LUN #1 doesn't have a mouse interface! rc=%Rrc\n", rc));
+            rc = VERR_PDM_MISSING_INTERFACE;
+        }
+    }
+    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+    {
+        Log(("%s/%d: warning: no driver attached to LUN #1!\n", pDevIns->pReg->szName, pDevIns->iInstance));
+        rc = VINF_SUCCESS;
+    }
+    else
+        AssertLogRelMsgFailed(("Failed to attach LUN #1! rc=%Rrc\n", rc));
+
+    return rc;
+}
+
+void PS2MSaveState(PPS2M pThis, PSSMHANDLE pSSM)
+{
+    LogFlowFunc(("Saving PS2M state\n"));
+
+    /* Save the core auxiliary device state. */
+    SSMR3PutU8(pSSM, pThis->u8State);
+    SSMR3PutU8(pSSM, pThis->u8SampleRate);
+    SSMR3PutU8(pSSM, pThis->u8Resolution);
+    SSMR3PutU8(pSSM, pThis->u8CurrCmd);
+    SSMR3PutU8(pSSM, pThis->enmMode);
+    SSMR3PutU8(pSSM, pThis->enmProtocol);
+    SSMR3PutU8(pSSM, pThis->enmKnockState);
+
+    /* Save the command and event queues. */
+    ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
+    ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->evtQ);
+
+    /* Save the command delay timer. Note that the rate throttling
+     * timer is *not* saved.
+     */
+    TMR3TimerSave(pThis->CTX_SUFF(pDelayTimer), pSSM);
+}
+
+int PS2MLoadState(PPS2M pThis, PSSMHANDLE pSSM, uint32_t uVersion)
+{
+    uint8_t     u8;
+    int         rc;
+
+    NOREF(uVersion);
+    LogFlowFunc(("Loading PS2M state version %u\n", uVersion));
+
+    /* Load the basic auxiliary device state. */
+    SSMR3GetU8(pSSM, &pThis->u8State);
+    SSMR3GetU8(pSSM, &pThis->u8SampleRate);
+    SSMR3GetU8(pSSM, &pThis->u8Resolution);
+    SSMR3GetU8(pSSM, &pThis->u8CurrCmd);
+    SSMR3GetU8(pSSM, &u8);
+    pThis->enmMode       = (PS2M_MODE)u8;
+    SSMR3GetU8(pSSM, &u8);
+    pThis->enmProtocol   = (PS2M_PROTO)u8;
+    SSMR3GetU8(pSSM, &u8);
+    pThis->enmKnockState = (PS2M_KNOCK_STATE)u8;
+
+    /* Load the command and event queues. */
+    rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
+    AssertRCReturn(rc, rc);
+    rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->evtQ);
+    AssertRCReturn(rc, rc);
+
+    /* Load the command delay timer, just in case. */
+    rc = TMR3TimerLoad(pThis->CTX_SUFF(pDelayTimer), pSSM);
+    AssertRCReturn(rc, rc);
+
+    /* Recalculate the throttling delay. */
+    ps2mSetRate(pThis, pThis->u8SampleRate);
+
+    ps2mSetDriverState(pThis, !!(pThis->u8State & AUX_STATE_ENABLED));
+
+    return rc;
+}
+
+void PS2MFixupState(PPS2M pThis, uint8_t u8State, uint8_t u8Rate, uint8_t u8Proto)
+{
+    LogFlowFunc(("Fixing up old PS2M state version\n"));
+
+    /* Load the basic auxiliary device state. */
+    pThis->u8State      = u8State;
+    pThis->u8SampleRate = u8Rate ? u8Rate : 40; /* In case it wasn't saved right. */
+    pThis->enmProtocol  = (PS2M_PROTO)u8Proto;
+
+    /* Recalculate the throttling delay. */
+    ps2mSetRate(pThis, pThis->u8SampleRate);
+
+    ps2mSetDriverState(pThis, !!(pThis->u8State & AUX_STATE_ENABLED));
+}
+
+void PS2MReset(PPS2M pThis)
+{
+    LogFlowFunc(("Resetting PS2M\n"));
+
+    pThis->u8CurrCmd         = 0;
+
+    /* Clear the queues. */
+    ps2kClearQueue((GeneriQ *)&pThis->cmdQ);
+    ps2mSetDefaults(pThis);     /* Also clears event queue. */
+}
+
+void PS2MRelocate(PPS2M pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns)
+{
+    RT_NOREF2(pDevIns, offDelta);
+    LogFlowFunc(("Relocating PS2M\n"));
+    pThis->pDelayTimerRC    = TMTimerRCPtr(pThis->pDelayTimerR3);
+    pThis->pThrottleTimerRC = TMTimerRCPtr(pThis->pThrottleTimerR3);
+}
+
+int PS2MConstruct(PPS2M pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance)
+{
+    RT_NOREF1(iInstance);
+
+    LogFlowFunc(("iInstance=%d\n", iInstance));
+
+#ifdef RT_STRICT
+    ps2mTestAccumulation();
+#endif
+
+    pThis->pParent = pParent;
+
+    /* Initialize the queues. */
+    pThis->evtQ.cSize = AUX_EVT_QUEUE_SIZE;
+    pThis->cmdQ.cSize = AUX_CMD_QUEUE_SIZE;
+
+    pThis->Mouse.IBase.pfnQueryInterface     = ps2mQueryInterface;
+    pThis->Mouse.IPort.pfnPutEvent           = ps2mPutEvent;
+    pThis->Mouse.IPort.pfnPutEventAbs        = ps2mPutEventAbs;
+    pThis->Mouse.IPort.pfnPutEventMultiTouch = ps2mPutEventMT;
+
+    /*
+     * Initialize the critical section pointer(s).
+     */
+    pThis->pCritSectR3 = pDevIns->pCritSectRoR3;
+
+    /*
+     * Create the input rate throttling timer. Does not use virtual time!
+     */
+    PTMTIMER pTimer;
+    int rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, ps2mThrottleTimer, pThis,
+                                    TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2M Throttle Timer", &pTimer);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    pThis->pThrottleTimerR3 = pTimer;
+    pThis->pThrottleTimerR0 = TMTimerR0Ptr(pTimer);
+    pThis->pThrottleTimerRC = TMTimerRCPtr(pTimer);
+
+    /*
+     * Create the command delay timer.
+     */
+    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ps2mDelayTimer, pThis,
+                                TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2M Delay Timer", &pTimer);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    pThis->pDelayTimerR3 = pTimer;
+    pThis->pDelayTimerR0 = TMTimerR0Ptr(pTimer);
+    pThis->pDelayTimerRC = TMTimerRCPtr(pTimer);
+
+    /*
+     * Register debugger info callbacks.
+     */
+    PDMDevHlpDBGFInfoRegister(pDevIns, "ps2m", "Display PS/2 mouse state.", ps2mInfoState);
+
+    /// @todo Where should we do this?
+    ps2mSetDriverState(pThis, true);
+    pThis->u8State = 0;
+    pThis->enmMode = AUX_MODE_STD;
+
+    return rc;
+}
+
+#endif
+
+#if defined(RT_STRICT) && defined(IN_RING3)
+/* -=-=-=-=-=- Test code  -=-=-=-=-=- */
+
+/** Test the event accumulation mechanism which we use to delay events going
+ * to the guest to one per 10ms (the default PS/2 mouse event rate).  This
+ * test depends on ps2mPutEventWorker() not touching the timer if
+ * This.fThrottleActive is true. */
+/** @todo if we add any more tests it might be worth using a table of test
+ * operations and checks. */
+static void ps2mTestAccumulation(void)
+{
+    PS2M This;
+    unsigned i;
+    int rc;
+    uint8_t b;
+
+    RT_ZERO(This);
+    This.evtQ.cSize = AUX_EVT_QUEUE_SIZE;
+    This.u8State = AUX_STATE_ENABLED;
+    This.fThrottleActive = true;
+    /* Certain Windows touch pad drivers report a double tap as a press, then
+     * a release-press-release all within a single 10ms interval.  Simulate
+     * this to check that it is handled right. */
+    ps2mPutEventWorker(&This, 0, 0, 0, 0, 1);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    ps2mPutEventWorker(&This, 0, 0, 0, 0, 0);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    ps2mPutEventWorker(&This, 0, 0, 0, 0, 1);
+    ps2mPutEventWorker(&This, 0, 0, 0, 0, 0);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    for (i = 0; i < 12; ++i)
+    {
+        const uint8_t abExpected[] = { 9, 0, 0, 8, 0, 0, 9, 0, 0, 8, 0, 0};
+
+        rc = PS2MByteFromAux(&This, &b);
+        AssertRCSuccess(rc);
+        Assert(b == abExpected[i]);
+    }
+    rc = PS2MByteFromAux(&This, &b);
+    Assert(rc != VINF_SUCCESS);
+    /* Button hold down during mouse drags was broken at some point during
+     * testing fixes for the previous issue.  Test that that works. */
+    ps2mPutEventWorker(&This, 0, 0, 0, 0, 1);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    for (i = 0; i < 3; ++i)
+    {
+        const uint8_t abExpected[] = { 9, 0, 0 };
+
+        rc = PS2MByteFromAux(&This, &b);
+        AssertRCSuccess(rc);
+        Assert(b == abExpected[i]);
+    }
+    rc = PS2MByteFromAux(&This, &b);
+    Assert(rc != VINF_SUCCESS);
+}
+#endif /* RT_STRICT && IN_RING3 */
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Index: trunk/src/VBox/Devices/Input/PS2Dev.h
===================================================================
--- trunk/src/VBox/Devices/Input/PS2Dev.h	(revision 81765)
+++ 	(revision )
@@ -1,94 +1,0 @@
-/* $Id$ */
-/** @file
- * PS/2 devices - Internal header file.
- */
-
-/*
- * Copyright (C) 2007-2019 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-#ifndef VBOX_INCLUDED_SRC_Input_PS2Dev_h
-#define VBOX_INCLUDED_SRC_Input_PS2Dev_h
-#ifndef RT_WITHOUT_PRAGMA_ONCE
-# pragma once
-#endif
-
-/** The size of the PS2K/PS2M structure fillers.
- * @note Must be at least as big as the real struct. Compile time assert
- *       makes sure this is so. */
-#define PS2K_STRUCT_FILLER  512
-#define PS2M_STRUCT_FILLER  512
-
-/* Hide the internal structure. */
-#if !(defined(IN_PS2K) || defined(VBOX_DEVICE_STRUCT_TESTCASE))
-typedef struct PS2K
-{
-    uint8_t     abFiller[PS2K_STRUCT_FILLER];
-} PS2K;
-#endif
-
-#if !(defined(IN_PS2M) || defined(VBOX_DEVICE_STRUCT_TESTCASE))
-typedef struct PS2M
-{
-    uint8_t     abFiller[PS2M_STRUCT_FILLER];
-} PS2M;
-#endif
-
-/* Internal PS/2 Keyboard interface. */
-typedef struct PS2K *PPS2K;
-
-int  PS2KByteToKbd(PPS2K pThis, uint8_t cmd);
-int  PS2KByteFromKbd(PPS2K pThis, uint8_t *pVal);
-
-int  PS2KConstruct(PPS2K pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance, PCFGMNODE pCfg);
-int  PS2KAttach(PPS2K pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags);
-void PS2KReset(PPS2K pThis);
-void PS2KRelocate(PPS2K pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns);
-void PS2KSaveState(PPS2K pThis, PSSMHANDLE pSSM);
-int  PS2KLoadState(PPS2K pThis, PSSMHANDLE pSSM, uint32_t uVersion);
-int  PS2KLoadDone(PPS2K pThis, PSSMHANDLE pSSM);
-
-PS2K *KBDGetPS2KFromDevIns(PPDMDEVINS pDevIns);
-
-
-/* Internal PS/2 Auxiliary device interface. */
-typedef struct PS2M *PPS2M;
-
-int  PS2MByteToAux(PPS2M pThis, uint8_t cmd);
-int  PS2MByteFromAux(PPS2M pThis, uint8_t *pVal);
-
-int  PS2MConstruct(PPS2M pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance);
-int  PS2MAttach(PPS2M pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags);
-void PS2MReset(PPS2M pThis);
-void PS2MRelocate(PPS2M pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns);
-void PS2MSaveState(PPS2M pThis, PSSMHANDLE pSSM);
-int  PS2MLoadState(PPS2M pThis, PSSMHANDLE pSSM, uint32_t uVersion);
-void PS2MFixupState(PPS2M pThis, uint8_t u8State, uint8_t u8Rate, uint8_t u8Proto);
-
-PS2M *KBDGetPS2MFromDevIns(PPDMDEVINS pDevIns);
-
-
-/* Shared keyboard/aux internal interface. */
-void KBCUpdateInterrupts(void *pKbc);
-
-
-///@todo: This should live with the KBC implementation.
-/** AT to PC scancode translator state.  */
-typedef enum
-{
-    XS_IDLE,    /**< Starting state. */
-    XS_BREAK,   /**< F0 break byte was received. */
-    XS_HIBIT    /**< Break code still active. */
-} xlat_state_t;
-
-int32_t XlateAT2PC(int32_t state, uint8_t scanIn, uint8_t *pScanOut);
-
-#endif /* !VBOX_INCLUDED_SRC_Input_PS2Dev_h */
Index: trunk/src/VBox/Devices/Input/PS2K.cpp
===================================================================
--- trunk/src/VBox/Devices/Input/PS2K.cpp	(revision 81765)
+++ 	(revision )
@@ -1,1744 +1,0 @@
-/* $Id$ */
-/** @file
- * PS2K - PS/2 keyboard emulation.
- */
-
-/*
- * Copyright (C) 2007-2019 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-/*
- * References:
- *
- * IBM PS/2 Technical Reference, Keyboards (101- and 102-Key), 1990
- * Keyboard Scan Code Specification, Microsoft, 2000
- *
- * Notes:
- *  - The keyboard never sends partial scan-code sequences; if there isn't enough
- *    room left in the buffer for the entire sequence, the keystroke is discarded
- *    and an overrun code is sent instead.
- *  - Command responses do not disturb stored keystrokes and always have priority.
- *  - Caps Lock and Scroll Lock are normal keys from the keyboard's point of view.
- *    However, Num Lock is not and the keyboard internally tracks its state.
- *  - The way Print Screen works in scan set 1/2 is totally insane.
- *  - A PS/2 keyboard can send at most 1,000 to 1,500 bytes per second. There is
- *    software which relies on that fact and assumes that a scan code can be
- *    read twice before the next scan code comes in.
- */
-
-
-/*********************************************************************************************************************************
-*   Header Files                                                                                                                 *
-*********************************************************************************************************************************/
-#define LOG_GROUP   LOG_GROUP_DEV_KBD
-#include <VBox/vmm/pdmdev.h>
-#include <VBox/err.h>
-#include <iprt/assert.h>
-#include <iprt/uuid.h>
-#include "VBoxDD.h"
-#define IN_PS2K
-#include "PS2Dev.h"
-
-
-/*********************************************************************************************************************************
-*   Defined Constants And Macros                                                                                                 *
-*********************************************************************************************************************************/
-/** @name Keyboard commands sent by the system.
- * @{ */
-#define KCMD_LEDS           0xED
-#define KCMD_ECHO           0xEE
-#define KCMD_INVALID_1      0xEF
-#define KCMD_SCANSET        0xF0
-#define KCMD_INVALID_2      0xF1
-#define KCMD_READ_ID        0xF2
-#define KCMD_RATE_DELAY     0xF3
-#define KCMD_ENABLE         0xF4
-#define KCMD_DFLT_DISABLE   0xF5
-#define KCMD_SET_DEFAULT    0xF6
-#define KCMD_ALL_TYPEMATIC  0xF7
-#define KCMD_ALL_MK_BRK     0xF8
-#define KCMD_ALL_MAKE       0xF9
-#define KCMD_ALL_TMB        0xFA
-#define KCMD_TYPE_MATIC     0xFB
-#define KCMD_TYPE_MK_BRK    0xFC
-#define KCMD_TYPE_MAKE      0xFD
-#define KCMD_RESEND         0xFE
-#define KCMD_RESET          0xFF
-/** @} */
-
-/** @name Keyboard responses sent to the system.
- * @{ */
-#define KRSP_ID1            0xAB
-#define KRSP_ID2            0x83
-#define KRSP_BAT_OK         0xAA
-#define KRSP_BAT_FAIL       0xFC    /* Also a 'release keys' signal. */
-#define KRSP_ECHO           0xEE
-#define KRSP_ACK            0xFA
-#define KRSP_RESEND         0xFE
-/** @} */
-
-/** @name HID modifier range.
- * @{ */
-#define HID_MODIFIER_FIRST  0xE0
-#define HID_MODIFIER_LAST   0xE8
-/** @} */
-
-/** @name USB HID additional constants
- * @{ */
-/** The highest USB usage code reported by VirtualBox. */
-#define VBOX_USB_MAX_USAGE_CODE     0xE7
-/** The size of an array needed to store all USB usage codes */
-#define VBOX_USB_USAGE_ARRAY_SIZE   (VBOX_USB_MAX_USAGE_CODE + 1)
-/** USB HID Keyboard Usage Page. */
-#define USB_HID_KB_PAGE             7
-/** USB HID Consumer Control Usage Page. */
-#define USB_HID_CC_PAGE             12
-/** @} */
-
-/** @name Modifier key states. Sorted in USB HID code order.
- * @{ */
-#define MOD_LCTRL           0x01
-#define MOD_LSHIFT          0x02
-#define MOD_LALT            0x04
-#define MOD_LGUI            0x08
-#define MOD_RCTRL           0x10
-#define MOD_RSHIFT          0x20
-#define MOD_RALT            0x40
-#define MOD_RGUI            0x80
-/** @} */
-
-/* Default typematic value. */
-#define KBD_DFL_RATE_DELAY  0x2B
-
-/* Input throttling delay in milliseconds. */
-#define KBD_THROTTLE_DELAY  1
-
-/** Define a simple PS/2 input device queue. */
-#define DEF_PS2Q_TYPE(name, size)   \
-     typedef struct {               \
-        uint32_t    rpos;           \
-        uint32_t    wpos;           \
-        uint32_t    cUsed;          \
-        uint32_t    cSize;          \
-        uint8_t     abQueue[size];  \
-     } name
-
-/* Internal keyboard queue sizes. The input queue doesn't need to be
- * extra huge and the command queue only needs to handle a few bytes.
- */
-#define KBD_KEY_QUEUE_SIZE         64
-#define KBD_CMD_QUEUE_SIZE          4
-
-
-/*********************************************************************************************************************************
-*   Structures and Typedefs                                                                                                      *
-*********************************************************************************************************************************/
-
-/** Typematic state. */
-typedef enum {
-    KBD_TMS_IDLE    = 0,    /* No typematic key active. */
-    KBD_TMS_DELAY   = 1,    /* In the initial delay period. */
-    KBD_TMS_REPEAT  = 2,    /* Key repeating at set rate. */
-    KBD_TMS_32BIT_HACK = 0x7fffffff
-} tmatic_state_t;
-
-
-DEF_PS2Q_TYPE(KbdKeyQ, KBD_KEY_QUEUE_SIZE);
-DEF_PS2Q_TYPE(KbdCmdQ, KBD_CMD_QUEUE_SIZE);
-DEF_PS2Q_TYPE(GeneriQ, 1);
-
-/**
- * The PS/2 keyboard instance data.
- */
-typedef struct PS2K
-{
-    /** Pointer to parent device (keyboard controller). */
-    R3PTRTYPE(void *)   pParent;
-    /** Set if keyboard is enabled ('scans' for input). */
-    bool                fScanning;
-    /** Set NumLock is on. */
-    bool                fNumLockOn;
-    /** Selected scan set. */
-    uint8_t             u8ScanSet;
-    /** Modifier key state. */
-    uint8_t             u8Modifiers;
-    /** Currently processed command (if any). */
-    uint8_t             u8CurrCmd;
-    /** Status indicator (LED) state. */
-    uint8_t             u8LEDs;
-    /** Selected typematic delay/rate. */
-    uint8_t             u8TypematicCfg;
-    /** Usage code of current typematic key, if any. */
-    uint32_t            u32TypematicKey;
-    /** Current typematic repeat state. */
-    tmatic_state_t      enmTypematicState;
-    /** Buffer holding scan codes to be sent to the host. */
-    KbdKeyQ             keyQ;
-    /** Command response queue (priority). */
-    KbdCmdQ             cmdQ;
-    /** Currently depressed keys. */
-    uint8_t             abDepressedKeys[VBOX_USB_USAGE_ARRAY_SIZE];
-    /** Typematic delay in milliseconds. */
-    unsigned            uTypematicDelay;
-    /** Typematic repeat period in milliseconds. */
-    unsigned            uTypematicRepeat;
-    /** Set if the throttle delay is currently active. */
-    bool                fThrottleActive;
-    /** Set if the input rate should be throttled. */
-    bool                fThrottleEnabled;
-
-    uint8_t             Alignment0[2];
-
-    /** Command delay timer - RC Ptr. */
-    PTMTIMERRC          pKbdDelayTimerRC;
-    /** Typematic timer - RC Ptr. */
-    PTMTIMERRC          pKbdTypematicTimerRC;
-    /** Input throttle timer - RC Ptr. */
-    PTMTIMERRC          pThrottleTimerRC;
-
-    /** The device critical section protecting everything - R3 Ptr */
-    R3PTRTYPE(PPDMCRITSECT) pCritSectR3;
-
-    /** Command delay timer - R3 Ptr. */
-    PTMTIMERR3          pKbdDelayTimerR3;
-    /** Typematic timer - R3 Ptr. */
-    PTMTIMERR3          pKbdTypematicTimerR3;
-    /** Input throttle timer - R3 Ptr. */
-    PTMTIMERR3          pThrottleTimerR3;
-
-    /** Command delay timer - R0 Ptr. */
-    PTMTIMERR0          pKbdDelayTimerR0;
-    /** Typematic timer - R0 Ptr. */
-    PTMTIMERR0          pKbdTypematicTimerR0;
-    /** Input throttle timer - R0 Ptr. */
-    PTMTIMERR0          pThrottleTimerR0;
-
-    /**
-     * Keyboard port - LUN#0.
-     *
-     * @implements  PDMIBASE
-     * @implements  PDMIKEYBOARDPORT
-     */
-    struct
-    {
-        /** The base interface for the keyboard port. */
-        PDMIBASE                            IBase;
-        /** The keyboard port base interface. */
-        PDMIKEYBOARDPORT                    IPort;
-
-        /** The base interface of the attached keyboard driver. */
-        R3PTRTYPE(PPDMIBASE)                pDrvBase;
-        /** The keyboard interface of the attached keyboard driver. */
-        R3PTRTYPE(PPDMIKEYBOARDCONNECTOR)   pDrv;
-    } Keyboard;
-} PS2K, *PPS2K;
-
-AssertCompile(PS2K_STRUCT_FILLER >= sizeof(PS2K));
-
-#ifndef VBOX_DEVICE_STRUCT_TESTCASE
-
-/* Key type flags. */
-#define KF_E0        0x01    /* E0 prefix. */
-#define KF_NB        0x02    /* No break code. */
-#define KF_GK        0x04    /* Gray navigation key. */
-#define KF_PS        0x08    /* Print Screen key. */
-#define KF_PB        0x10    /* Pause/Break key. */
-#define KF_NL        0x20    /* Num Lock key. */
-#define KF_NS        0x40    /* NumPad '/' key. */
-
-/* Scan Set 3 typematic defaults. */
-#define T_U          0x00    /* Unknown value. */
-#define T_T          0x01    /* Key is typematic. */
-#define T_M          0x02    /* Key is make only. */
-#define T_B          0x04    /* Key is make/break. */
-
-/* Special key values. */
-#define NONE         0x93    /* No PS/2 scan code returned. */
-#define UNAS         0x94    /* No PS/2 scan assigned to key. */
-#define RSVD         0x95    /* Reserved, do not use. */
-#define UNKN         0x96    /* Translation unknown. */
-
-/* Key definition structure. */
-typedef struct {
-    uint8_t makeS1;      /* Set 1 make code. */
-    uint8_t makeS2;      /* Set 2 make code. */
-    uint8_t makeS3;      /* Set 3 make code. */
-    uint8_t keyFlags;    /* Key flags. */
-    uint8_t keyMatic;    /* Set 3 typematic default. */
-} key_def;
-
-
-/*********************************************************************************************************************************
-*   Global Variables                                                                                                             *
-*********************************************************************************************************************************/
-#ifdef IN_RING3
-/* USB to PS/2 conversion table for regular keys (HID Usage Page 7). */
-static const   key_def   aPS2Keys[] = {
-    /* 00 */ {NONE, NONE, NONE, KF_NB, T_U }, /* Key N/A: No Event */
-    /* 01 */ {0xFF, 0x00, 0x00, KF_NB, T_U }, /* Key N/A: Overrun Error */
-    /* 02 */ {0xFC, 0xFC, 0xFC, KF_NB, T_U }, /* Key N/A: POST Fail */
-    /* 03 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key N/A: ErrorUndefined */
-    /* 04 */ {0x1E, 0x1C, 0x1C,     0, T_T }, /* Key  31: a A */
-    /* 05 */ {0x30, 0x32, 0x32,     0, T_T }, /* Key  50: b B */
-    /* 06 */ {0x2E, 0x21, 0x21,     0, T_T }, /* Key  48: c C */
-    /* 07 */ {0x20, 0x23, 0x23,     0, T_T }, /* Key  33: d D */
-    /* 08 */ {0x12, 0x24, 0x24,     0, T_T }, /* Key  19: e E */
-    /* 09 */ {0x21, 0x2B, 0x2B,     0, T_T }, /* Key  34: f F */
-    /* 0A */ {0x22, 0x34, 0x34,     0, T_T }, /* Key  35: g G */
-    /* 0B */ {0x23, 0x33, 0x33,     0, T_T }, /* Key  36: h H */
-    /* 0C */ {0x17, 0x43, 0x43,     0, T_T }, /* Key  24: i I */
-    /* 0D */ {0x24, 0x3B, 0x3B,     0, T_T }, /* Key  37: j J */
-    /* 0E */ {0x25, 0x42, 0x42,     0, T_T }, /* Key  38: k K */
-    /* 0F */ {0x26, 0x4B, 0x4B,     0, T_T }, /* Key  39: l L */
-    /* 10 */ {0x32, 0x3A, 0x3A,     0, T_T }, /* Key  52: m M */
-    /* 11 */ {0x31, 0x31, 0x31,     0, T_T }, /* Key  51: n N */
-    /* 12 */ {0x18, 0x44, 0x44,     0, T_T }, /* Key  25: o O */
-    /* 13 */ {0x19, 0x4D, 0x4D,     0, T_T }, /* Key  26: p P */
-    /* 14 */ {0x10, 0x15, 0x15,     0, T_T }, /* Key  17: q Q */
-    /* 15 */ {0x13, 0x2D, 0x2D,     0, T_T }, /* Key  20: r R */
-    /* 16 */ {0x1F, 0x1B, 0x1B,     0, T_T }, /* Key  32: s S */
-    /* 17 */ {0x14, 0x2C, 0x2C,     0, T_T }, /* Key  21: t T */
-    /* 18 */ {0x16, 0x3C, 0x3C,     0, T_T }, /* Key  23: u U */
-    /* 19 */ {0x2F, 0x2A, 0x2A,     0, T_T }, /* Key  49: v V */
-    /* 1A */ {0x11, 0x1D, 0x1D,     0, T_T }, /* Key  18: w W */
-    /* 1B */ {0x2D, 0x22, 0x22,     0, T_T }, /* Key  47: x X */
-    /* 1C */ {0x15, 0x35, 0x35,     0, T_T }, /* Key  22: y Y */
-    /* 1D */ {0x2C, 0x1A, 0x1A,     0, T_T }, /* Key  46: z Z */
-    /* 1E */ {0x02, 0x16, 0x16,     0, T_T }, /* Key   2: 1 ! */
-    /* 1F */ {0x03, 0x1E, 0x1E,     0, T_T }, /* Key   3: 2 @ */
-    /* 20 */ {0x04, 0x26, 0x26,     0, T_T }, /* Key   4: 3 # */
-    /* 21 */ {0x05, 0x25, 0x25,     0, T_T }, /* Key   5: 4 $ */
-    /* 22 */ {0x06, 0x2E, 0x2E,     0, T_T }, /* Key   6: 5 % */
-    /* 23 */ {0x07, 0x36, 0x36,     0, T_T }, /* Key   7: 6 ^ */
-    /* 24 */ {0x08, 0x3D, 0x3D,     0, T_T }, /* Key   8: 7 & */
-    /* 25 */ {0x09, 0x3E, 0x3E,     0, T_T }, /* Key   9: 8 * */
-    /* 26 */ {0x0A, 0x46, 0x46,     0, T_T }, /* Key  10: 9 ( */
-    /* 27 */ {0x0B, 0x45, 0x45,     0, T_T }, /* Key  11: 0 ) */
-    /* 28 */ {0x1C, 0x5A, 0x5A,     0, T_T }, /* Key  43: Return */
-    /* 29 */ {0x01, 0x76, 0x08,     0, T_M }, /* Key 110: Escape */
-    /* 2A */ {0x0E, 0x66, 0x66,     0, T_T }, /* Key  15: Backspace */
-    /* 2B */ {0x0F, 0x0D, 0x0D,     0, T_T }, /* Key  16: Tab */
-    /* 2C */ {0x39, 0x29, 0x29,     0, T_T }, /* Key  61: Space */
-    /* 2D */ {0x0C, 0x4E, 0x4E,     0, T_T }, /* Key  12: - _ */
-    /* 2E */ {0x0D, 0x55, 0x55,     0, T_T }, /* Key  13: = + */
-    /* 2F */ {0x1A, 0x54, 0x54,     0, T_T }, /* Key  27: [ { */
-    /* 30 */ {0x1B, 0x5B, 0x5B,     0, T_T }, /* Key  28: ] } */
-    /* 31 */ {0x2B, 0x5D, 0x5C,     0, T_T }, /* Key  29: \ | */
-    /* 32 */ {0x2B, 0x5D, 0x5D,     0, T_T }, /* Key  42: Europe 1 (Note 2) */
-    /* 33 */ {0x27, 0x4C, 0x4C,     0, T_T }, /* Key  40: ; : */
-    /* 34 */ {0x28, 0x52, 0x52,     0, T_T }, /* Key  41: ' " */
-    /* 35 */ {0x29, 0x0E, 0x0E,     0, T_T }, /* Key   1: ` ~ */
-    /* 36 */ {0x33, 0x41, 0x41,     0, T_T }, /* Key  53: , < */
-    /* 37 */ {0x34, 0x49, 0x49,     0, T_T }, /* Key  54: . > */
-    /* 38 */ {0x35, 0x4A, 0x4A,     0, T_T }, /* Key  55: / ? */
-    /* 39 */ {0x3A, 0x58, 0x14,     0, T_B }, /* Key  30: Caps Lock */
-    /* 3A */ {0x3B, 0x05, 0x07,     0, T_M }, /* Key 112: F1 */
-    /* 3B */ {0x3C, 0x06, 0x0F,     0, T_M }, /* Key 113: F2 */
-    /* 3C */ {0x3D, 0x04, 0x17,     0, T_M }, /* Key 114: F3 */
-    /* 3D */ {0x3E, 0x0C, 0x1F,     0, T_M }, /* Key 115: F4 */
-    /* 3E */ {0x3F, 0x03, 0x27,     0, T_M }, /* Key 116: F5 */
-    /* 3F */ {0x40, 0x0B, 0x2F,     0, T_M }, /* Key 117: F6 */
-    /* 40 */ {0x41, 0x83, 0x37,     0, T_M }, /* Key 118: F7 */
-    /* 41 */ {0x42, 0x0A, 0x3F,     0, T_M }, /* Key 119: F8 */
-    /* 42 */ {0x43, 0x01, 0x47,     0, T_M }, /* Key 120: F9 */
-    /* 43 */ {0x44, 0x09, 0x4F,     0, T_M }, /* Key 121: F10 */
-    /* 44 */ {0x57, 0x78, 0x56,     0, T_M }, /* Key 122: F11 */
-    /* 45 */ {0x58, 0x07, 0x5E,     0, T_M }, /* Key 123: F12 */
-    /* 46 */ {0x37, 0x7C, 0x57, KF_PS, T_M }, /* Key 124: Print Screen (Note 1) */
-    /* 47 */ {0x46, 0x7E, 0x5F,     0, T_M }, /* Key 125: Scroll Lock */
-    /* 48 */ {RSVD, RSVD, RSVD, KF_PB, T_M }, /* Key 126: Break (Ctrl-Pause) */
-    /* 49 */ {0x52, 0x70, 0x67, KF_GK, T_M }, /* Key  75: Insert (Note 1) */
-    /* 4A */ {0x47, 0x6C, 0x6E, KF_GK, T_M }, /* Key  80: Home (Note 1) */
-    /* 4B */ {0x49, 0x7D, 0x6F, KF_GK, T_M }, /* Key  85: Page Up (Note 1) */
-    /* 4C */ {0x53, 0x71, 0x64, KF_GK, T_T }, /* Key  76: Delete (Note 1) */
-    /* 4D */ {0x4F, 0x69, 0x65, KF_GK, T_M }, /* Key  81: End (Note 1) */
-    /* 4E */ {0x51, 0x7A, 0x6D, KF_GK, T_M }, /* Key  86: Page Down (Note 1) */
-    /* 4F */ {0x4D, 0x74, 0x6A, KF_GK, T_T }, /* Key  89: Right Arrow (Note 1) */
-    /* 50 */ {0x4B, 0x6B, 0x61, KF_GK, T_T }, /* Key  79: Left Arrow (Note 1) */
-    /* 51 */ {0x50, 0x72, 0x60, KF_GK, T_T }, /* Key  84: Down Arrow (Note 1) */
-    /* 52 */ {0x48, 0x75, 0x63, KF_GK, T_T }, /* Key  83: Up Arrow (Note 1) */
-    /* 53 */ {0x45, 0x77, 0x76, KF_NL, T_M }, /* Key  90: Num Lock */
-    /* 54 */ {0x35, 0x4A, 0x77, KF_NS, T_M }, /* Key  95: Keypad / (Note 1) */
-    /* 55 */ {0x37, 0x7C, 0x7E,     0, T_M }, /* Key 100: Keypad * */
-    /* 56 */ {0x4A, 0x7B, 0x84,     0, T_M }, /* Key 105: Keypad - */
-    /* 57 */ {0x4E, 0x79, 0x7C,     0, T_T }, /* Key 106: Keypad + */
-    /* 58 */ {0x1C, 0x5A, 0x79, KF_E0, T_M }, /* Key 108: Keypad Enter */
-    /* 59 */ {0x4F, 0x69, 0x69,     0, T_M }, /* Key  93: Keypad 1 End */
-    /* 5A */ {0x50, 0x72, 0x72,     0, T_M }, /* Key  98: Keypad 2 Down */
-    /* 5B */ {0x51, 0x7A, 0x7A,     0, T_M }, /* Key 103: Keypad 3 PageDn */
-    /* 5C */ {0x4B, 0x6B, 0x6B,     0, T_M }, /* Key  92: Keypad 4 Left */
-    /* 5D */ {0x4C, 0x73, 0x73,     0, T_M }, /* Key  97: Keypad 5 */
-    /* 5E */ {0x4D, 0x74, 0x74,     0, T_M }, /* Key 102: Keypad 6 Right */
-    /* 5F */ {0x47, 0x6C, 0x6C,     0, T_M }, /* Key  91: Keypad 7 Home */
-    /* 60 */ {0x48, 0x75, 0x75,     0, T_M }, /* Key  96: Keypad 8 Up */
-    /* 61 */ {0x49, 0x7D, 0x7D,     0, T_M }, /* Key 101: Keypad 9 PageUp */
-    /* 62 */ {0x52, 0x70, 0x70,     0, T_M }, /* Key  99: Keypad 0 Insert */
-    /* 63 */ {0x53, 0x71, 0x71,     0, T_M }, /* Key 104: Keypad . Delete */
-    /* 64 */ {0x56, 0x61, 0x13,     0, T_T }, /* Key  45: Europe 2 (Note 2) */
-    /* 65 */ {0x5D, 0x2F, UNKN, KF_E0, T_U }, /* Key 129: App */
-    /* 66 */ {0x5E, 0x37, UNKN, KF_E0, T_U }, /* Key Unk: Keyboard Power */
-    /* 67 */ {0x59, 0x0F, UNKN,     0, T_U }, /* Key Unk: Keypad = */
-    /* 68 */ {0x64, 0x08, UNKN,     0, T_U }, /* Key Unk: F13 */
-    /* 69 */ {0x65, 0x10, UNKN,     0, T_U }, /* Key Unk: F14 */
-    /* 6A */ {0x66, 0x18, UNKN,     0, T_U }, /* Key Unk: F15 */
-    /* 6B */ {0x67, 0x20, UNKN,     0, T_U }, /* Key Unk: F16 */
-    /* 6C */ {0x68, 0x28, UNKN,     0, T_U }, /* Key Unk: F17 */
-    /* 6D */ {0x69, 0x30, UNKN,     0, T_U }, /* Key Unk: F18 */
-    /* 6E */ {0x6A, 0x38, UNKN,     0, T_U }, /* Key Unk: F19 */
-    /* 6F */ {0x6B, 0x40, UNKN,     0, T_U }, /* Key Unk: F20 */
-    /* 70 */ {0x6C, 0x48, UNKN,     0, T_U }, /* Key Unk: F21 */
-    /* 71 */ {0x6D, 0x50, UNKN,     0, T_U }, /* Key Unk: F22 */
-    /* 72 */ {0x6E, 0x57, UNKN,     0, T_U }, /* Key Unk: F23 */
-    /* 73 */ {0x76, 0x5F, UNKN,     0, T_U }, /* Key Unk: F24 */
-    /* 74 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Execute */
-    /* 75 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Help */
-    /* 76 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Menu */
-    /* 77 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Select */
-    /* 78 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Stop */
-    /* 79 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Again */
-    /* 7A */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Undo */
-    /* 7B */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Cut */
-    /* 7C */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Copy */
-    /* 7D */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Paste */
-    /* 7E */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Find */
-    /* 7F */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Mute */
-    /* 80 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Volume Up */
-    /* 81 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Volume Dn */
-    /* 82 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Locking Caps Lock */
-    /* 83 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Locking Num Lock */
-    /* 84 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Locking Scroll Lock */
-    /* 85 */ {0x7E, 0x6D, UNKN,     0, T_U }, /* Key Unk: Keypad , (Brazilian Keypad .) */
-    /* 86 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Equal Sign */
-    /* 87 */ {0x73, 0x51, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl 1 (Ro) */
-    /* 88 */ {0x70, 0x13, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl2 (K'kana/H'gana) */
-    /* 89 */ {0x7D, 0x6A, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl 2 (Yen) */
-    /* 8A */ {0x79, 0x64, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl 4 (Henkan) */
-    /* 8B */ {0x7B, 0x67, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl 5 (Muhenkan) */
-    /* 8C */ {0x5C, 0x27, UNKN,     0, T_U }, /* Key Unk: Keyboard Intl 6 (PC9800 Pad ,) */
-    /* 8D */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Intl 7 */
-    /* 8E */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Intl 8 */
-    /* 8F */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Intl 9 */
-    /* 90 */ {0xF2, 0xF2, UNKN, KF_NB, T_U }, /* Key Unk: Keyboard Lang 1 (Hang'l/Engl) */
-    /* 91 */ {0xF1, 0xF1, UNKN, KF_NB, T_U }, /* Key Unk: Keyboard Lang 2 (Hanja) */
-    /* 92 */ {0x78, 0x63, UNKN,     0, T_U }, /* Key Unk: Keyboard Lang 3 (Katakana) */
-    /* 93 */ {0x77, 0x62, UNKN,     0, T_U }, /* Key Unk: Keyboard Lang 4 (Hiragana) */
-    /* 94 */ {0x76, 0x5F, UNKN,     0, T_U }, /* Key Unk: Keyboard Lang 5 (Zen/Han) */
-    /* 95 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Lang 6 */
-    /* 96 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Lang 7 */
-    /* 97 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Lang 8 */
-    /* 98 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Lang 9 */
-    /* 99 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Alternate Erase */
-    /* 9A */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard SysReq/Attention (Note 3) */
-    /* 9B */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Cancel */
-    /* 9C */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Clear */
-    /* 9D */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Prior */
-    /* 9E */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Return */
-    /* 9F */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Separator */
-    /* A0 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Out */
-    /* A1 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Oper */
-    /* A2 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard Clear/Again */
-    /* A3 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard CrSel/Props */
-    /* A4 */ {UNAS, UNAS, UNAS,     0, T_U }, /* Key Unk: Keyboard ExSel */
-};
-
-/*
- * Note 1: The behavior of these keys depends on the state of modifier keys
- * at the time the key was pressed.
- *
- * Note 2: The key label depends on the national version of the keyboard.
- *
- * Note 3: Certain keys which have their own PS/2 scancodes do not exist on
- * USB keyboards; the SysReq key is an example. The SysReq key scancode needs
- * to be translated to the Print Screen HID usage code. The HID usage to PS/2
- * scancode conversion then generates the correct sequence depending on the
- * keyboard state.
- */
-
-/* USB to PS/2 conversion table for modifier keys (HID Usage Page 7). */
-static const   key_def   aPS2ModKeys[] = {
-    /* E0 */ {0x1D, 0x14, 0x11,     0, T_B }, /* Key  58: Left Control */
-    /* E1 */ {0x2A, 0x12, 0x12,     0, T_B }, /* Key  44: Left Shift */
-    /* E2 */ {0x38, 0x11, 0x19,     0, T_B }, /* Key  60: Left Alt */
-    /* E3 */ {0x5B, 0x1F, UNKN, KF_E0, T_U }, /* Key 127: Left GUI */
-    /* E4 */ {0x1D, 0x14, 0x58, KF_E0, T_M }, /* Key  64: Right Control */
-    /* E5 */ {0x36, 0x59, 0x59,     0, T_B }, /* Key  57: Right Shift */
-    /* E6 */ {0x38, 0x11, 0x39, KF_E0, T_M }, /* Key  62: Right Alt */
-    /* E7 */ {0x5C, 0x27, UNKN, KF_E0, T_U }, /* Key 128: Right GUI */
-};
-
-/* Extended key definition for sparse mapping. */
-typedef struct {
-    uint16_t    usageId;
-    key_def     kdef;
-} ext_key_def;
-
-
-/* USB to PS/2 conversion table for consumer control keys (HID Usage Page 12). */
-/* This usage page is very sparse so we'll just search through it. */
-static const   ext_key_def  aPS2CCKeys[] = {
-    {0x00B5, {0x19, 0x4D, UNKN, KF_E0, T_U}},   /* Scan Next Track */
-    {0x00B6, {0x10, 0x15, UNKN, KF_E0, T_U}},   /* Scan Previous Track */
-    {0x00B7, {0x24, 0x3B, UNKN, KF_E0, T_U}},   /* Stop */
-    {0x00CD, {0x22, 0x34, UNKN, KF_E0, T_U}},   /* Play/Pause */
-    {0x00E2, {0x20, 0x23, UNKN, KF_E0, T_U}},   /* Mute */
-    {0x00E5, {UNAS, UNAS, UNAS,     0, T_U}},   /* Bass Boost */
-    {0x00E7, {UNAS, UNAS, UNAS,     0, T_U}},   /* Loudness */
-    {0x00E9, {0x30, 0x32, UNKN, KF_E0, T_U}},   /* Volume Up */
-    {0x00EA, {0x2E, 0x21, UNKN, KF_E0, T_U}},   /* Volume Down */
-    {0x0152, {UNAS, UNAS, UNAS,     0, T_U}},   /* Bass Up */
-    {0x0153, {UNAS, UNAS, UNAS,     0, T_U}},   /* Bass Down */
-    {0x0154, {UNAS, UNAS, UNAS,     0, T_U}},   /* Treble Up */
-    {0x0155, {UNAS, UNAS, UNAS,     0, T_U}},   /* Treble Down */
-    {0x0183, {0x6D, 0x50, UNKN, KF_E0, T_U}},   /* Media Select  */
-    {0x018A, {0x6C, 0x48, UNKN, KF_E0, T_U}},   /* Mail */
-    {0x0192, {0x21, 0x2B, UNKN, KF_E0, T_U}},   /* Calculator */
-    {0x0194, {0x6B, 0x40, UNKN, KF_E0, T_U}},   /* My Computer */
-    {0x0221, {0x65, 0x10, UNKN, KF_E0, T_U}},   /* WWW Search */
-    {0x0223, {0x32, 0x3A, UNKN, KF_E0, T_U}},   /* WWW Home */
-    {0x0224, {0x6A, 0x38, UNKN, KF_E0, T_U}},   /* WWW Back */
-    {0x0225, {0x69, 0x30, UNKN, KF_E0, T_U}},   /* WWW Forward */
-    {0x0226, {0x68, 0x28, UNKN, KF_E0, T_U}},   /* WWW Stop */
-    {0x0227, {0x67, 0x20, UNKN, KF_E0, T_U}},   /* WWW Refresh */
-    {0x022A, {0x66, 0x18, UNKN, KF_E0, T_U}},   /* WWW Favorites */
-};
-
-#endif /* IN_RING3 */
-
-
-
-/**
- * Clear a queue.
- *
- * @param   pQ                  Pointer to the queue.
- */
-static void ps2kClearQueue(GeneriQ *pQ)
-{
-    LogFlowFunc(("Clearing queue %p\n", pQ));
-    pQ->wpos  = pQ->rpos;
-    pQ->cUsed = 0;
-}
-
-
-/**
- * Add a byte to a queue.
- *
- * @param   pQ                  Pointer to the queue.
- * @param   val                 The byte to store.
- */
-static void ps2kInsertQueue(GeneriQ *pQ, uint8_t val)
-{
-    /* Check if queue is full. */
-    if (pQ->cUsed >= pQ->cSize)
-    {
-        LogRelFlowFunc(("queue %p full (%d entries)\n", pQ, pQ->cUsed));
-        return;
-    }
-    /* Insert data and update circular buffer write position. */
-    pQ->abQueue[pQ->wpos] = val;
-    if (++pQ->wpos == pQ->cSize)
-        pQ->wpos = 0;   /* Roll over. */
-    ++pQ->cUsed;
-    LogRelFlowFunc(("inserted 0x%02X into queue %p\n", val, pQ));
-}
-
-#ifdef IN_RING3
-
-/**
- * Add a null-terminated byte sequence to a queue if there is enough room.
- *
- * @param   pQ                  Pointer to the queue.
- * @param   pStr                Pointer to the bytes to store.
- * @param   uReserve            Number of bytes that must still remain
- *                              available in queue.
- * @return  int                 VBox status/error code.
- */
-static int ps2kInsertStrQueue(GeneriQ *pQ, const uint8_t *pStr, uint32_t uReserve)
-{
-    uint32_t    cbStr;
-    unsigned    i;
-
-    cbStr = (uint32_t)strlen((const char *)pStr);
-
-    /* Check if queue has enough room. */
-    if (pQ->cUsed + uReserve + cbStr >= pQ->cSize)
-    {
-        LogRelFlowFunc(("queue %p full (%u entries, want room for %u), cannot insert %u entries\n",
-                        pQ, pQ->cUsed, uReserve, cbStr));
-        return VERR_BUFFER_OVERFLOW;
-    }
-
-    /* Insert byte sequence and update circular buffer write position. */
-    for (i = 0; i < cbStr; ++i) {
-        pQ->abQueue[pQ->wpos] = pStr[i];
-        if (++pQ->wpos == pQ->cSize)
-            pQ->wpos = 0;   /* Roll over. */
-    }
-    pQ->cUsed += cbStr;
-    LogRelFlowFunc(("inserted %u bytes into queue %p\n", cbStr, pQ));
-    return VINF_SUCCESS;
-}
-
-/**
- * Save a queue state.
- *
- * @param   pSSM                SSM handle to write the state to.
- * @param   pQ                  Pointer to the queue.
- */
-static void ps2kSaveQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
-{
-    uint32_t    cItems = pQ->cUsed;
-    int         i;
-
-    /* Only save the number of items. Note that the read/write
-     * positions aren't saved as they will be rebuilt on load.
-     */
-    SSMR3PutU32(pSSM, cItems);
-
-    LogFlow(("Storing %d items from queue %p\n", cItems, pQ));
-
-    /* Save queue data - only the bytes actually used (typically zero). */
-    for (i = pQ->rpos; cItems-- > 0; i = (i + 1) % pQ->cSize)
-        SSMR3PutU8(pSSM, pQ->abQueue[i]);
-}
-
-/**
- * Load a queue state.
- *
- * @param   pSSM                SSM handle to read the state from.
- * @param   pQ                  Pointer to the queue.
- *
- * @return  int                 VBox status/error code.
- */
-static int ps2kLoadQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
-{
-    /* On load, always put the read pointer at zero. */
-    int rc = SSMR3GetU32(pSSM, &pQ->cUsed);
-    AssertRCReturn(rc, rc);
-    LogFlow(("Loading %u items to queue %p\n", pQ->cUsed, pQ));
-    AssertMsgReturn(pQ->cUsed <= pQ->cSize, ("Saved size=%u, actual=%u\n", pQ->cUsed, pQ->cSize),
-                    VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
-
-    /* Recalculate queue positions and load data in one go. */
-    pQ->rpos = 0;
-    pQ->wpos = pQ->cUsed;
-    rc = SSMR3GetMem(pSSM, pQ->abQueue, pQ->cUsed);
-
-    return rc;
-}
-
-/**
- * Notify listener about LEDs state change.
- *
- * @param   pThis           The PS/2 keyboard instance data.
- * @param   u8State         Bitfield which reflects LEDs state.
- */
-static void ps2kNotifyLedsState(PPS2K pThis, uint8_t u8State)
-{
-
-    PDMKEYBLEDS enmLeds = PDMKEYBLEDS_NONE;
-
-    if (u8State & 0x01)
-        enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_SCROLLLOCK);
-    if (u8State & 0x02)
-        enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_NUMLOCK);
-    if (u8State & 0x04)
-        enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_CAPSLOCK);
-
-    pThis->Keyboard.pDrv->pfnLedStatusChange(pThis->Keyboard.pDrv, enmLeds);
-
-}
-
-/**
- * Query the number of items currently in a queue.
- *
- * @param   pQ                  Pointer to the queue.
- *
- * @return  uint32_t            Number of items in queue.
- */
-static uint32_t ps2kInQueue(GeneriQ *pQ)
-{
-    return pQ->cUsed;
-}
-
-#endif /* IN_RING3 */
-
-/**
- * Retrieve a byte from a queue.
- *
- * @param   pQ                  Pointer to the queue.
- * @param   pVal                Pointer to storage for the byte.
- *
- * @return  int                 VINF_TRY_AGAIN if queue is empty,
- *                              VINF_SUCCESS if a byte was read.
- */
-static int ps2kRemoveQueue(GeneriQ *pQ, uint8_t *pVal)
-{
-    int     rc = VINF_TRY_AGAIN;
-
-    Assert(pVal);
-    if (pQ->cUsed)
-    {
-        *pVal = pQ->abQueue[pQ->rpos];
-        if (++pQ->rpos == pQ->cSize)
-            pQ->rpos = 0;   /* Roll over. */
-        --pQ->cUsed;
-        rc = VINF_SUCCESS;
-        LogFlowFunc(("removed 0x%02X from queue %p\n", *pVal, pQ));
-    } else
-        LogFlowFunc(("queue %p empty\n", pQ));
-    return rc;
-}
-
-/* Clears the currently active typematic key, if any. */
-static void ps2kStopTypematicRepeat(PPS2K pThis)
-{
-    if (pThis->u32TypematicKey)
-    {
-        LogFunc(("Typematic key %08X\n", pThis->u32TypematicKey));
-        pThis->enmTypematicState = KBD_TMS_IDLE;
-        pThis->u32TypematicKey = 0;
-        TMTimerStop(pThis->CTX_SUFF(pKbdTypematicTimer));
-    }
-}
-
-/* Convert encoded typematic value to milliseconds. Note that the values are rated
- * with +/- 20% accuracy, so there's no need for high precision.
- */
-static void ps2kSetupTypematic(PPS2K pThis, uint8_t val)
-{
-    int         A, B;
-    unsigned    period;
-
-    pThis->u8TypematicCfg = val;
-    /* The delay is easy: (1 + value) * 250 ms */
-    pThis->uTypematicDelay = (1 + ((val >> 5) & 3)) * 250;
-    /* The rate is more complicated: (8 + A) * 2^B * 4.17 ms */
-    A = val & 7;
-    B = (val >> 3) & 3;
-    period = (8 + A) * (1 << B) * 417 / 100;
-    pThis->uTypematicRepeat = period;
-    Log(("Typematic delay %u ms, repeat period %u ms\n",
-         pThis->uTypematicDelay, pThis->uTypematicRepeat));
-}
-
-static void ps2kSetDefaults(PPS2K pThis)
-{
-    LogFlowFunc(("Set keyboard defaults\n"));
-    ps2kClearQueue((GeneriQ *)&pThis->keyQ);
-    /* Set default Scan Set 3 typematic values. */
-    /* Set default typematic rate/delay. */
-    ps2kSetupTypematic(pThis, KBD_DFL_RATE_DELAY);
-    /* Clear last typematic key?? */
-    ps2kStopTypematicRepeat(pThis);
-}
-
-/**
- * Receive and process a byte sent by the keyboard controller.
- *
- * @param   pThis               The PS/2 keyboard instance data.
- * @param   cmd                 The command (or data) byte.
- */
-int PS2KByteToKbd(PPS2K pThis, uint8_t cmd)
-{
-    bool    fHandled = true;
-
-    LogFlowFunc(("new cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
-
-    if (pThis->u8CurrCmd == KCMD_RESET)
-        /* In reset mode, do not respond at all. */
-        return VINF_SUCCESS;
-
-    switch (cmd)
-    {
-        case KCMD_ECHO:
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ECHO);
-            pThis->u8CurrCmd = 0;
-            break;
-        case KCMD_READ_ID:
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ID1);
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ID2);
-            pThis->u8CurrCmd = 0;
-            break;
-        case KCMD_ENABLE:
-            pThis->fScanning = true;
-            ps2kClearQueue((GeneriQ *)&pThis->keyQ);
-            ps2kStopTypematicRepeat(pThis);
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case KCMD_DFLT_DISABLE:
-            pThis->fScanning = false;
-            ps2kSetDefaults(pThis); /* Also clears buffer/typematic state. */
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case KCMD_SET_DEFAULT:
-            ps2kSetDefaults(pThis);
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case KCMD_ALL_TYPEMATIC:
-        case KCMD_ALL_MK_BRK:
-        case KCMD_ALL_MAKE:
-        case KCMD_ALL_TMB:
-            /// @todo Set the key types here.
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case KCMD_RESEND:
-            pThis->u8CurrCmd = 0;
-            break;
-        case KCMD_RESET:
-            pThis->u8ScanSet = 2;
-            ps2kSetDefaults(pThis);
-            /// @todo reset more?
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
-            pThis->u8CurrCmd = cmd;
-            /* Delay BAT completion; the test may take hundreds of ms. */
-            TMTimerSetMillies(pThis->CTX_SUFF(pKbdDelayTimer), 2);
-            break;
-        /* The following commands need a parameter. */
-        case KCMD_LEDS:
-        case KCMD_SCANSET:
-        case KCMD_RATE_DELAY:
-        case KCMD_TYPE_MATIC:
-        case KCMD_TYPE_MK_BRK:
-        case KCMD_TYPE_MAKE:
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
-            pThis->u8CurrCmd = cmd;
-            break;
-        default:
-            /* Sending a command instead of a parameter starts the new command. */
-            switch (pThis->u8CurrCmd)
-            {
-                case KCMD_LEDS:
-#ifndef IN_RING3
-                    return VINF_IOM_R3_IOPORT_WRITE;
-#else
-                    {
-                        ps2kNotifyLedsState(pThis, cmd);
-                        pThis->fNumLockOn = !!(cmd & 0x02); /* Sync internal Num Lock state. */
-                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
-                        pThis->u8LEDs = cmd;
-                        pThis->u8CurrCmd = 0;
-                    }
-#endif
-                    break;
-                case KCMD_SCANSET:
-                    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
-                    if (cmd == 0)
-                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8ScanSet);
-                    else if (cmd < 4)
-                    {
-                        pThis->u8ScanSet = cmd;
-                        LogRel(("PS2K: Selected scan set %d\n", cmd));
-                    }
-                    /* Other values are simply ignored. */
-                    pThis->u8CurrCmd = 0;
-                    break;
-                case KCMD_RATE_DELAY:
-                    ps2kSetupTypematic(pThis, cmd);
-                    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
-                    pThis->u8CurrCmd = 0;
-                    break;
-                default:
-                    fHandled = false;
-            }
-            /* Fall through only to handle unrecognized commands. */
-            if (fHandled)
-                break;
-            RT_FALL_THRU();
-
-        case KCMD_INVALID_1:
-        case KCMD_INVALID_2:
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_RESEND);
-            pThis->u8CurrCmd = 0;
-            break;
-    }
-    LogFlowFunc(("Active cmd now 0x%02X; updating interrupts\n", pThis->u8CurrCmd));
-//    KBCUpdateInterrupts(pThis->pParent);
-    return VINF_SUCCESS;
-}
-
-/**
- * Send a byte (keystroke or command response) to the keyboard controller.
- *
- * @returns VINF_SUCCESS or VINF_TRY_AGAIN.
- * @param   pThis               The PS/2 keyboard instance data.
- * @param   pb                  Where to return the byte we've read.
- * @remarks Caller must have entered the device critical section.
- */
-int PS2KByteFromKbd(PPS2K pThis, uint8_t *pb)
-{
-    int         rc;
-
-    AssertPtr(pb);
-
-    /* Anything in the command queue has priority over data
-     * in the keystroke queue. Additionally, keystrokes are
-     * blocked if a command is currently in progress, even if
-     * the command queue is empty.
-     */
-    rc = ps2kRemoveQueue((GeneriQ *)&pThis->cmdQ, pb);
-    if (rc != VINF_SUCCESS && !pThis->u8CurrCmd && pThis->fScanning)
-        if (!pThis->fThrottleActive)
-        {
-            rc = ps2kRemoveQueue((GeneriQ *)&pThis->keyQ, pb);
-            if (pThis->fThrottleEnabled) {
-                pThis->fThrottleActive = true;
-                TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), KBD_THROTTLE_DELAY);
-            }
-        }
-
-    LogFlowFunc(("keyboard sends 0x%02x (%svalid data)\n", *pb, rc == VINF_SUCCESS ? "" : "not "));
-    return rc;
-}
-
-#ifdef IN_RING3
-
-static int ps2kProcessKeyEvent(PPS2K pThis, uint32_t u32HidCode, bool fKeyDown)
-{
-    key_def const   *pKeyDef;
-    uint8_t         abCodes[16];
-    char            *pCodes;
-    size_t          cbLeft;
-    uint8_t         abScan[2];
-    uint8_t         u8HidPage;
-    uint8_t         u8HidCode;
-    uint16_t        u16HidUsage;
-
-    u8HidPage   = RT_LOBYTE(RT_HIWORD(u32HidCode));
-    u16HidUsage = RT_LOWORD(u32HidCode);
-    /* For the keyboard usage page (7) we use a 8-bit code. For other pages we use the full 16-bit ID. */
-    u8HidCode   = (u8HidPage == USB_HID_KB_PAGE) ? RT_LOBYTE(u32HidCode) : 0;
-
-    LogFlowFunc(("key %s: page 0x%02x ID 0x%04x (set %d)\n", fKeyDown ? "down" : "up", u8HidPage, u16HidUsage, pThis->u8ScanSet));
-
-    /* Find the key definition in somewhat sparse storage. */
-    if (u8HidPage == USB_HID_KB_PAGE)
-        /* For the standard keyboard usage page, thre are just two arrays. */
-        pKeyDef = u8HidCode >= HID_MODIFIER_FIRST ? &aPS2ModKeys[u8HidCode - HID_MODIFIER_FIRST] : &aPS2Keys[u8HidCode];
-    else if (u8HidPage == USB_HID_CC_PAGE)
-    {
-        /* For the consumer control usage page, we need to search. */
-        unsigned    i;
-
-        pKeyDef = &aPS2Keys[0]; /* Dummy no-event key. */
-        for (i = 0; i < RT_ELEMENTS(aPS2CCKeys); ++i)
-        {
-            if (aPS2CCKeys[i].usageId == u16HidUsage)
-            {
-                pKeyDef = &aPS2CCKeys[i].kdef;
-                break;
-            }
-        }
-    }
-    else
-    {
-        LogFlow(("Unsupported HID usage page, ignoring key.\n"));
-        return VINF_SUCCESS;
-    }
-
-    /* Some keys are not processed at all; early return. */
-    if (pKeyDef->makeS1 == NONE)
-    {
-        LogFlow(("Skipping key processing.\n"));
-        return VINF_SUCCESS;
-    }
-
-    /* Handle modifier keys (Ctrl/Alt/Shift/GUI). We need to keep track
-     * of their state in addition to sending the scan code.
-     */
-    if (u8HidCode >= HID_MODIFIER_FIRST)
-    {
-        unsigned    mod_bit = 1 << (u8HidCode - HID_MODIFIER_FIRST);
-
-        Assert((u8HidPage == USB_HID_KB_PAGE));
-        Assert((u8HidCode <= HID_MODIFIER_LAST));
-        if (fKeyDown)
-            pThis->u8Modifiers |= mod_bit;
-        else
-            pThis->u8Modifiers &= ~mod_bit;
-    }
-
-    /* Toggle NumLock state. */
-    if ((pKeyDef->keyFlags & KF_NL) && fKeyDown)
-        pThis->fNumLockOn ^= true;
-
-    abCodes[0] = 0;
-    pCodes = (char *)abCodes;
-    cbLeft = sizeof(abCodes);
-
-    if (pThis->u8ScanSet == 1 || pThis->u8ScanSet == 2)
-    {
-        /* The basic scan set 1 and 2 logic is the same, only the scan codes differ.
-         * Since scan set 2 is used almost all the time, that case is handled first.
-         */
-        if (fKeyDown)
-        {
-            /* Process key down event. */
-            if (pKeyDef->keyFlags & KF_PB)
-            {
-                /* Pause/Break sends different data if either Ctrl is held. */
-                if (pThis->u8Modifiers & (MOD_LCTRL | MOD_RCTRL))
-                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                              "\xE0\x7E\xE0\xF0\x7E" : "\xE0\x46\xE0\xC6");
-                else
-                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                              "\xE1\x14\x77\xE1\xF0\x14\xF0\x77" : "\xE1\x1D\x45\xE1\x9D\xC5");
-            }
-            else if (pKeyDef->keyFlags & KF_PS)
-            {
-                /* Print Screen depends on all of Ctrl, Shift, *and* Alt! */
-                if (pThis->u8Modifiers & (MOD_LALT | MOD_RALT))
-                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                              "\x84" : "\x54");
-                else if (pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT))
-                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                              "\xE0\x7C" : "\xE0\x37");
-                else
-                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                              "\xE0\x12\xE0\x7C" : "\xE0\x2A\xE0\x37");
-            }
-            else if (pKeyDef->keyFlags & (KF_GK | KF_NS))
-            {
-                /* The numeric pad keys fake Shift presses or releases
-                 * depending on Num Lock and Shift key state. The '/'
-                 * key behaves in a similar manner but does not depend on
-                 * the Num Lock state.
-                 */
-                if (!pThis->fNumLockOn || (pKeyDef->keyFlags & KF_NS))
-                {
-                    if (pThis->u8Modifiers & MOD_LSHIFT)
-                        RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                                  "\xE0\xF0\x12" : "\xE0\xAA");
-                    if (pThis->u8Modifiers & MOD_RSHIFT)
-                        RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                                  "\xE0\xF0\x59" : "\xE0\xB6");
-                }
-                else
-                {
-                    Assert(pThis->fNumLockOn);  /* Not for KF_NS! */
-                    if ((pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT)) == 0)
-                        RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                                  "\xE0\x12" : "\xE0\x2A");
-                    /* Else Shift cancels NumLock, so no prefix! */
-                }
-            }
-
-            /* Standard processing for regular keys only. */
-            abScan[0] = pThis->u8ScanSet == 2 ? pKeyDef->makeS2 : pKeyDef->makeS1;
-            abScan[1] = '\0';
-            if (!(pKeyDef->keyFlags & (KF_PB | KF_PS)))
-            {
-                if (pKeyDef->keyFlags & (KF_E0 | KF_GK | KF_NS))
-                    RTStrCatP(&pCodes, &cbLeft, "\xE0");
-                RTStrCatP(&pCodes, &cbLeft, (const char *)abScan);
-            }
-
-            /* Feed the bytes to the queue if there is room. */
-            /// @todo Send overrun code if sequence won't fit?
-            ps2kInsertStrQueue((GeneriQ *)&pThis->keyQ, abCodes, 0);
-        }
-        else if (!(pKeyDef->keyFlags & (KF_NB | KF_PB)))
-        {
-            /* Process key up event except for keys which produce none. */
-
-            /* Handle Print Screen release. */
-            if (pKeyDef->keyFlags & KF_PS)
-            {
-                /* Undo faked Print Screen state as needed. */
-                if (pThis->u8Modifiers & (MOD_LALT | MOD_RALT))
-                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                              "\xF0\x84" : "\xD4");
-                else if (pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT))
-                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                              "\xE0\xF0\x7C" : "\xE0\xB7");
-                else
-                    RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                              "\xE0\xF0\x7C\xE0\xF0\x12" : "\xE0\xB7\xE0\xAA");
-            }
-            else
-            {
-                /* Process base scan code for less unusual keys. */
-                abScan[0] = pThis->u8ScanSet == 2 ? pKeyDef->makeS2 : pKeyDef->makeS1 | 0x80;
-                abScan[1] = '\0';
-                if (pKeyDef->keyFlags & (KF_E0 | KF_GK | KF_NS))
-                    RTStrCatP(&pCodes, &cbLeft, "\xE0");
-                if (pThis->u8ScanSet == 2)
-                    RTStrCatP(&pCodes, &cbLeft, "\xF0");
-                RTStrCatP(&pCodes, &cbLeft, (const char *)abScan);
-
-                /* Restore shift state for gray keys. */
-                if (pKeyDef->keyFlags & (KF_GK | KF_NS))
-                {
-                    if (!pThis->fNumLockOn || (pKeyDef->keyFlags & KF_NS))
-                    {
-                        if (pThis->u8Modifiers & MOD_LSHIFT)
-                            RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                                      "\xE0\x12" : "\xE0\x2A");
-                        if (pThis->u8Modifiers & MOD_RSHIFT)
-                            RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                                      "\xE0\x59" : "\xE0\x36");
-                    }
-                    else
-                    {
-                        Assert(pThis->fNumLockOn);  /* Not for KF_NS! */
-                        if ((pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT)) == 0)
-                            RTStrCatP(&pCodes, &cbLeft, pThis->u8ScanSet == 2 ?
-                                      "\xE0\xF0\x12" : "\xE0\xAA");
-                    }
-                }
-            }
-
-            /* Feed the bytes to the queue if there is room. */
-            /// @todo Send overrun code if sequence won't fit?
-            ps2kInsertStrQueue((GeneriQ *)&pThis->keyQ, abCodes, 0);
-        }
-    }
-    else
-    {
-        /* Handle Scan Set 3 - very straightforward. */
-        Assert(pThis->u8ScanSet == 3);
-        abScan[0] = pKeyDef->makeS3;
-        abScan[1] = '\0';
-        if (fKeyDown)
-        {
-            RTStrCatP(&pCodes, &cbLeft, (const char *)abScan);
-        }
-        else
-        {
-            /* Send a key release code unless it's a make only key. */
-            /// @todo Look up the current typematic setting, not the default!
-            if (pKeyDef->keyMatic != T_M)
-            {
-                RTStrCatP(&pCodes, &cbLeft, "\xF0");
-                RTStrCatP(&pCodes, &cbLeft, (const char *)abScan);
-            }
-        }
-        /* Feed the bytes to the queue if there is room. */
-        /// @todo Send overrun code if sequence won't fit?
-        ps2kInsertStrQueue((GeneriQ *)&pThis->keyQ, abCodes, 0);
-    }
-
-    /* Set up or cancel typematic key repeat. For keyboard usage page only. */
-    if (u8HidPage == USB_HID_KB_PAGE)
-    {
-        if (fKeyDown)
-        {
-            if (pThis->u32TypematicKey != u32HidCode)
-            {
-                pThis->enmTypematicState = KBD_TMS_DELAY;
-                pThis->u32TypematicKey   = u32HidCode;
-                TMTimerSetMillies(pThis->CTX_SUFF(pKbdTypematicTimer), pThis->uTypematicDelay);
-                Log(("Typematic delay %u ms, key %08X\n", pThis->uTypematicDelay, u32HidCode));
-            }
-        }
-        else
-        {
-            /* "Typematic operation stops when the last key pressed is released, even
-             * if other keys are still held down." (IBM PS/2 Tech Ref). The last key pressed
-             * is the one that's being repeated.
-             */
-            if (pThis->u32TypematicKey == u32HidCode)
-            {
-                /* This disables the typematic repeat. */
-                pThis->u32TypematicKey   = 0;
-                pThis->enmTypematicState = KBD_TMS_IDLE;
-                /* For good measure, we cancel the timer, too. */
-                TMTimerStop(pThis->CTX_SUFF(pKbdTypematicTimer));
-                Log(("Typematic action cleared for key %08X\n", u32HidCode));
-            }
-        }
-    }
-
-    /* Poke the KBC to update its state. */
-    KBCUpdateInterrupts(pThis->pParent);
-
-    return VINF_SUCCESS;
-}
-
-/* Throttling timer to emulate the finite keyboard communication speed. A PS/2 keyboard is
- * limited by the serial link speed and cannot send much more than 1,000 bytes per second.
- * Some software (notably Borland Pascal and programs built with its run-time) relies on
- * being able to read an incoming scan-code twice. Throttling the data rate enables such
- * software to function, while human typists cannot tell any difference.
- *
- * Note: The throttling is currently only done for keyboard data, not command responses.
- * The throttling could and perhaps should be done for any data (including command
- * response) scoming from PS/2 devices, both keyboard and auxiliary. That is not currently
- * done because it would needlessly slow things down.
- */
-static DECLCALLBACK(void) ps2kThrottleTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
-{
-    RT_NOREF2(pDevIns, pTimer);
-    PPS2K       pThis = (PS2K *)pvUser;
-    unsigned    uHaveData;
-
-    /* Grab the lock to avoid races with event delivery or EMTs. */
-    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
-    AssertReleaseRC(rc);
-
-    /* If data is available, poke the KBC. Once the data
-     * is actually read, the timer may be re-triggered.
-     */
-    pThis->fThrottleActive = false;
-    uHaveData = ps2kInQueue((GeneriQ *)&pThis->keyQ);
-    LogFlowFunc(("Have%s bytes\n", uHaveData ? "" : " no"));
-    if (uHaveData)
-        KBCUpdateInterrupts(pThis->pParent);
-
-    PDMCritSectLeave(pThis->pCritSectR3);
-}
-
-/* Timer handler for emulating typematic keys. Note that only the last key
- * held down repeats (if typematic).
- */
-static DECLCALLBACK(void) ps2kTypematicTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
-{
-    RT_NOREF2(pDevIns, pTimer);
-    PPS2K pThis = (PS2K *)pvUser;
-    LogFlowFunc(("Typematic state=%d, key %08X\n", pThis->enmTypematicState, pThis->u32TypematicKey));
-
-    /* If the current typematic key is zero, the repeat was canceled just when
-     * the timer was about to run. In that case, do nothing.
-     */
-    if (pThis->u32TypematicKey)
-    {
-        if (pThis->enmTypematicState == KBD_TMS_DELAY)
-            pThis->enmTypematicState = KBD_TMS_REPEAT;
-
-        if (pThis->enmTypematicState == KBD_TMS_REPEAT)
-        {
-            ps2kProcessKeyEvent(pThis, pThis->u32TypematicKey, true /* Key down */ );
-            TMTimerSetMillies(pThis->CTX_SUFF(pKbdTypematicTimer), pThis->uTypematicRepeat);
-        }
-    }
-}
-
-/* The keyboard BAT is specified to take several hundred milliseconds. We need
- * to delay sending the result to the host for at least a tiny little while.
- */
-static DECLCALLBACK(void) ps2kDelayTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
-{
-    RT_NOREF2(pDevIns, pTimer);
-    PPS2K pThis = (PS2K *)pvUser;
-
-    LogFlowFunc(("Delay timer: cmd %02X\n", pThis->u8CurrCmd));
-
-    AssertMsg(pThis->u8CurrCmd == KCMD_RESET, ("u8CurrCmd=%02x\n", pThis->u8CurrCmd));
-    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_BAT_OK);
-    pThis->fScanning = true;    /* BAT completion enables scanning! */
-    pThis->u8CurrCmd = 0;
-
-    /// @todo Might want a PS2KCompleteCommand() to push last response, clear command, and kick the KBC...
-    /* Give the KBC a kick. */
-    KBCUpdateInterrupts(pThis->pParent);
-}
-
-/* Release any and all currently depressed keys. Used whenever the guest keyboard
- * is likely to be out of sync with the host, such as when loading a saved state
- * or resuming a suspended host.
- */
-static void ps2kReleaseKeys(PPS2K pThis)
-{
-    LogFlowFunc(("Releasing keys...\n"));
-
-    for (unsigned uKey = 0; uKey < sizeof(pThis->abDepressedKeys); ++uKey)
-        if (pThis->abDepressedKeys[uKey])
-        {
-            ps2kProcessKeyEvent(pThis, RT_MAKE_U32(USB_HID_KB_PAGE, uKey), false /* key up */);
-            pThis->abDepressedKeys[uKey] = 0;
-        }
-    LogFlowFunc(("Done releasing keys\n"));
-}
-
-
-/**
- * Debug device info handler. Prints basic keyboard state.
- *
- * @param   pDevIns     Device instance which registered the info.
- * @param   pHlp        Callback functions for doing output.
- * @param   pszArgs     Argument string. Optional and specific to the handler.
- */
-static DECLCALLBACK(void) ps2kInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
-{
-    PPS2K   pThis = KBDGetPS2KFromDevIns(pDevIns);
-    NOREF(pszArgs);
-
-    pHlp->pfnPrintf(pHlp, "PS/2 Keyboard: scan set %d, scanning %s\n",
-                    pThis->u8ScanSet, pThis->fScanning ? "enabled" : "disabled");
-    pHlp->pfnPrintf(pHlp, "Active command %02X\n", pThis->u8CurrCmd);
-    pHlp->pfnPrintf(pHlp, "LED state %02X, Num Lock %s\n", pThis->u8LEDs,
-                    pThis->fNumLockOn ? "on" : "off");
-    pHlp->pfnPrintf(pHlp, "Typematic delay %ums, repeat period %ums\n",
-                    pThis->uTypematicDelay, pThis->uTypematicRepeat);
-    pHlp->pfnPrintf(pHlp, "Command queue: %d items (%d max)\n",
-                    pThis->cmdQ.cUsed, pThis->cmdQ.cSize);
-    pHlp->pfnPrintf(pHlp, "Input queue  : %d items (%d max)\n",
-                    pThis->keyQ.cUsed, pThis->keyQ.cSize);
-    if (pThis->enmTypematicState != KBD_TMS_IDLE)
-        pHlp->pfnPrintf(pHlp, "Active typematic key %08X (%s)\n", pThis->u32TypematicKey,
-                        pThis->enmTypematicState == KBD_TMS_DELAY ? "delay" : "repeat");
-}
-
-/* -=-=-=-=-=- Keyboard: IBase  -=-=-=-=-=- */
-
-/**
- * @interface_method_impl{PDMIBASE,pfnQueryInterface}
- */
-static DECLCALLBACK(void *) ps2kQueryInterface(PPDMIBASE pInterface, const char *pszIID)
-{
-    PPS2K pThis = RT_FROM_MEMBER(pInterface, PS2K, Keyboard.IBase);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Keyboard.IBase);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDPORT, &pThis->Keyboard.IPort);
-    return NULL;
-}
-
-
-/* -=-=-=-=-=- Keyboard: IKeyboardPort  -=-=-=-=-=- */
-
-/**
- * Keyboard event handler.
- *
- * @returns VBox status code.
- * @param   pThis           The PS2 keyboard instance data.
- * @param   u32Usage        USB HID usage code with key
- *                          press/release flag.
- */
-static int ps2kPutEventWorker(PPS2K pThis, uint32_t u32Usage)
-{
-    uint32_t        u32HidCode;
-    uint8_t         u8KeyCode;
-    uint8_t         u8HidPage;
-    bool            fKeyDown;
-    bool            fHaveEvent = true;
-    int             rc = VINF_SUCCESS;
-
-    /* Extract the usage page and ID and ensure it's valid. */
-    fKeyDown   = !(u32Usage & 0x80000000);
-    u32HidCode = u32Usage & 0xFFFFFF;
-    u8HidPage  = RT_LOBYTE(RT_HIWORD(u32Usage));
-    u8KeyCode  = RT_LOBYTE(u32Usage);
-    if (u8HidPage == USB_HID_KB_PAGE)
-        AssertReturn(u8KeyCode <= VBOX_USB_MAX_USAGE_CODE, VERR_INTERNAL_ERROR);
-    else
-        AssertReturn(u8HidPage == USB_HID_CC_PAGE, VERR_INTERNAL_ERROR);
-
-    if (fKeyDown)
-    {
-        /* Due to host key repeat, we can get key events for keys which are
-         * already depressed. We need to ignore those. */
-        if (pThis->abDepressedKeys[u8KeyCode])
-            fHaveEvent = false;
-        pThis->abDepressedKeys[u8KeyCode] = 1;
-    }
-    else
-    {
-        /* NB: We allow key release events for keys which aren't depressed.
-         * That is unlikely to happen and should not cause trouble.
-         */
-        pThis->abDepressedKeys[u8KeyCode] = 0;
-    }
-
-    /* Unless this is a new key press/release, don't even bother. */
-    if (fHaveEvent)
-    {
-        rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
-        AssertReleaseRC(rc);
-
-        rc = ps2kProcessKeyEvent(pThis, u32HidCode, fKeyDown);
-
-        PDMCritSectLeave(pThis->pCritSectR3);
-    }
-
-    return rc;
-}
-
-static DECLCALLBACK(int) ps2kPutEventWrapper(PPDMIKEYBOARDPORT pInterface, uint32_t u32UsageCode)
-{
-    PPS2K       pThis = RT_FROM_MEMBER(pInterface, PS2K, Keyboard.IPort);
-    int         rc;
-
-    LogRelFlowFunc(("key code %08X\n", u32UsageCode));
-
-    rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
-    AssertReleaseRC(rc);
-
-    /* The 'BAT fail' scancode is reused as a signal to release keys. No actual
-     * key is allowed to use this scancode.
-     */
-    if (RT_UNLIKELY(u32UsageCode == KRSP_BAT_FAIL))
-    {
-        ps2kReleaseKeys(pThis);
-    }
-    else
-    {
-        ps2kPutEventWorker(pThis, u32UsageCode);
-    }
-
-    PDMCritSectLeave(pThis->pCritSectR3);
-
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Attach command.
- *
- * This is called to let the device attach to a driver for a
- * specified LUN.
- *
- * This is like plugging in the keyboard after turning on the
- * system.
- *
- * @returns VBox status code.
- * @param   pThis       The PS/2 keyboard instance data.
- * @param   pDevIns     The device instance.
- * @param   iLUN        The logical unit which is being detached.
- * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
- */
-int PS2KAttach(PPS2K pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
-{
-    int         rc;
-
-    /* The LUN must be 0, i.e. keyboard. */
-    Assert(iLUN == 0);
-    AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
-                    ("PS/2 keyboard does not support hotplugging\n"),
-                    VERR_INVALID_PARAMETER);
-
-    LogFlowFunc(("iLUN=%d\n", iLUN));
-
-    rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Keyboard.IBase, &pThis->Keyboard.pDrvBase, "Keyboard Port");
-    if (RT_SUCCESS(rc))
-    {
-        pThis->Keyboard.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Keyboard.pDrvBase, PDMIKEYBOARDCONNECTOR);
-        if (!pThis->Keyboard.pDrv)
-        {
-            AssertLogRelMsgFailed(("LUN #0 doesn't have a keyboard interface! rc=%Rrc\n", rc));
-            rc = VERR_PDM_MISSING_INTERFACE;
-        }
-    }
-    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
-    {
-        Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
-        rc = VINF_SUCCESS;
-    }
-    else
-        AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
-
-    return rc;
-}
-
-void PS2KSaveState(PPS2K pThis, PSSMHANDLE pSSM)
-{
-    uint32_t    cPressed = 0;
-    uint32_t    cbTMSSize = 0;
-
-    LogFlowFunc(("Saving PS2K state\n"));
-
-    /* Save the basic keyboard state. */
-    SSMR3PutU8(pSSM, pThis->u8CurrCmd);
-    SSMR3PutU8(pSSM, pThis->u8LEDs);
-    SSMR3PutU8(pSSM, pThis->u8TypematicCfg);
-    SSMR3PutU8(pSSM, (uint8_t)pThis->u32TypematicKey);
-    SSMR3PutU8(pSSM, pThis->u8Modifiers);
-    SSMR3PutU8(pSSM, pThis->u8ScanSet);
-    SSMR3PutU8(pSSM, pThis->enmTypematicState);
-    SSMR3PutBool(pSSM, pThis->fNumLockOn);
-    SSMR3PutBool(pSSM, pThis->fScanning);
-
-    /* Save the command and keystroke queues. */
-    ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
-    ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->keyQ);
-
-    /* Save the command delay timer. Note that the typematic repeat
-     * timer is *not* saved.
-     */
-    TMR3TimerSave(pThis->CTX_SUFF(pKbdDelayTimer), pSSM);
-
-    /* Save any pressed keys. This is necessary to avoid "stuck"
-     * keys after a restore. Needs two passes.
-     */
-    for (unsigned i = 0; i < sizeof(pThis->abDepressedKeys); ++i)
-        if (pThis->abDepressedKeys[i])
-            ++cPressed;
-
-    SSMR3PutU32(pSSM, cPressed);
-
-    for (unsigned uKey = 0; uKey < sizeof(pThis->abDepressedKeys); ++uKey)
-        if (pThis->abDepressedKeys[uKey])
-            SSMR3PutU8(pSSM, uKey);
-
-    /* Save the typematic settings for Scan Set 3. */
-    SSMR3PutU32(pSSM, cbTMSSize);
-    /* Currently not implemented. */
-}
-
-int PS2KLoadState(PPS2K pThis, PSSMHANDLE pSSM, uint32_t uVersion)
-{
-    uint8_t     u8;
-    uint32_t    cPressed;
-    uint32_t    cbTMSSize;
-    int         rc;
-
-    NOREF(uVersion);
-    LogFlowFunc(("Loading PS2K state version %u\n", uVersion));
-
-    /* Load the basic keyboard state. */
-    SSMR3GetU8(pSSM, &pThis->u8CurrCmd);
-    SSMR3GetU8(pSSM, &pThis->u8LEDs);
-    SSMR3GetU8(pSSM, &pThis->u8TypematicCfg);
-    SSMR3GetU8(pSSM, &u8);
-    /* Reconstruct the 32-bit code from the 8-bit value in saved state. */
-    pThis->u32TypematicKey = u8 ? RT_MAKE_U32(USB_HID_KB_PAGE, u8) : 0;
-    SSMR3GetU8(pSSM, &pThis->u8Modifiers);
-    SSMR3GetU8(pSSM, &pThis->u8ScanSet);
-    SSMR3GetU8(pSSM, &u8);
-    pThis->enmTypematicState = (tmatic_state_t)u8;
-    SSMR3GetBool(pSSM, &pThis->fNumLockOn);
-    SSMR3GetBool(pSSM, &pThis->fScanning);
-
-    /* Load the command and keystroke queues. */
-    rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
-    AssertRCReturn(rc, rc);
-    rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->keyQ);
-    AssertRCReturn(rc, rc);
-
-    /* Load the command delay timer, just in case. */
-    rc = TMR3TimerLoad(pThis->CTX_SUFF(pKbdDelayTimer), pSSM);
-    AssertRCReturn(rc, rc);
-
-    /* Recalculate the typematic delay/rate. */
-    ps2kSetupTypematic(pThis, pThis->u8TypematicCfg);
-
-    /* Fake key up events for keys that were held down at the time the state was saved. */
-    rc = SSMR3GetU32(pSSM, &cPressed);
-    AssertRCReturn(rc, rc);
-
-    /* If any keys were down, load and then release them. */
-    if (cPressed)
-    {
-        for (unsigned i = 0; i < cPressed; ++i)
-        {
-            rc = SSMR3GetU8(pSSM, &u8);
-            AssertRCReturn(rc, rc);
-            pThis->abDepressedKeys[u8] = 1;
-        }
-    }
-
-    /* Load typematic settings for Scan Set 3. */
-    rc = SSMR3GetU32(pSSM, &cbTMSSize);
-    AssertRCReturn(rc, rc);
-
-    while (cbTMSSize--)
-    {
-        rc = SSMR3GetU8(pSSM, &u8);
-        AssertRCReturn(rc, rc);
-    }
-
-    return rc;
-}
-
-int PS2KLoadDone(PPS2K pThis, PSSMHANDLE pSSM)
-{
-    RT_NOREF1(pSSM);
-
-    /* This *must* be done after the inital load because it may trigger
-     * interrupts and change the interrupt controller state.
-     */
-    ps2kReleaseKeys(pThis);
-    ps2kNotifyLedsState(pThis, pThis->u8LEDs);
-    return VINF_SUCCESS;
-}
-
-void PS2KReset(PPS2K pThis)
-{
-    LogFlowFunc(("Resetting PS2K\n"));
-
-    pThis->fScanning         = true;
-    pThis->fThrottleActive   = false;
-    pThis->u8ScanSet         = 2;
-    pThis->u8CurrCmd         = 0;
-    pThis->u8Modifiers       = 0;
-    pThis->u32TypematicKey   = 0;
-    pThis->enmTypematicState = KBD_TMS_IDLE;
-
-    /* Clear queues and any pressed keys. */
-    memset(pThis->abDepressedKeys, 0, sizeof(pThis->abDepressedKeys));
-    ps2kClearQueue((GeneriQ *)&pThis->cmdQ);
-    ps2kSetDefaults(pThis);     /* Also clears keystroke queue. */
-
-    /* Activate the PS/2 keyboard by default. */
-    if (pThis->Keyboard.pDrv)
-        pThis->Keyboard.pDrv->pfnSetActive(pThis->Keyboard.pDrv, true);
-}
-
-void PS2KRelocate(PPS2K pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns)
-{
-    RT_NOREF1(pDevIns);
-    LogFlowFunc(("Relocating PS2K\n"));
-    pThis->pKbdDelayTimerRC     = TMTimerRCPtr(pThis->pKbdDelayTimerR3);
-    pThis->pKbdTypematicTimerRC = TMTimerRCPtr(pThis->pKbdTypematicTimerR3);
-    pThis->pThrottleTimerRC     = TMTimerRCPtr(pThis->pThrottleTimerR3);
-    NOREF(offDelta);
-}
-
-int PS2KConstruct(PPS2K pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance, PCFGMNODE pCfg)
-{
-    RT_NOREF2(pDevIns, iInstance);
-    LogFlowFunc(("iInstance=%d\n", iInstance));
-
-    pThis->pParent = pParent;
-
-    bool fThrottleEnabled;
-    int rc = CFGMR3QueryBoolDef(pCfg, "KbdThrottleEnabled", &fThrottleEnabled, true);
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"KbdThrottleEnabled\" from the config"));
-    Log(("KbdThrottleEnabled=%u\n", fThrottleEnabled));
-    pThis->fThrottleEnabled = fThrottleEnabled;
-
-    /* Initialize the queues. */
-    pThis->keyQ.cSize = KBD_KEY_QUEUE_SIZE;
-    pThis->cmdQ.cSize = KBD_CMD_QUEUE_SIZE;
-
-    pThis->Keyboard.IBase.pfnQueryInterface = ps2kQueryInterface;
-    pThis->Keyboard.IPort.pfnPutEventHid    = ps2kPutEventWrapper;
-
-    /*
-     * Initialize the critical section pointer(s).
-     */
-    pThis->pCritSectR3 = pDevIns->pCritSectRoR3;
-
-    /*
-     * Create the input rate throttling timer.
-     */
-    PTMTIMER pTimer;
-    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ps2kThrottleTimer, pThis,
-                                TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2K Throttle Timer", &pTimer);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    pThis->pThrottleTimerR3 = pTimer;
-    pThis->pThrottleTimerR0 = TMTimerR0Ptr(pTimer);
-    pThis->pThrottleTimerRC = TMTimerRCPtr(pTimer);
-
-    /*
-     * Create the typematic delay/repeat timer.
-     */
-    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ps2kTypematicTimer, pThis,
-                                TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2K Typematic Timer", &pTimer);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    pThis->pKbdTypematicTimerR3 = pTimer;
-    pThis->pKbdTypematicTimerR0 = TMTimerR0Ptr(pTimer);
-    pThis->pKbdTypematicTimerRC = TMTimerRCPtr(pTimer);
-
-    /*
-     * Create the command delay timer.
-     */
-    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ps2kDelayTimer, pThis,
-                                TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2K Delay Timer", &pTimer);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    pThis->pKbdDelayTimerR3 = pTimer;
-    pThis->pKbdDelayTimerR0 = TMTimerR0Ptr(pTimer);
-    pThis->pKbdDelayTimerRC = TMTimerRCPtr(pTimer);
-
-    /*
-     * Register debugger info callbacks.
-     */
-    PDMDevHlpDBGFInfoRegister(pDevIns, "ps2k", "Display PS/2 keyboard state.", ps2kInfoState);
-
-    return rc;
-}
-
-#endif
-
-/// @todo The following should live with the KBC implementation.
-
-/* Table used by the keyboard controller to optionally translate the incoming
- * keyboard data. Note that the translation is designed for essentially taking
- * Scan Set 2 input and producing Scan Set 1 output, but can be turned on and
- * off regardless of what the keyboard is sending.
- */
-static uint8_t aAT2PC[128] = {
-    0xff,0x43,0x41,0x3f,0x3d,0x3b,0x3c,0x58,0x64,0x44,0x42,0x40,0x3e,0x0f,0x29,0x59,
-    0x65,0x38,0x2a,0x70,0x1d,0x10,0x02,0x5a,0x66,0x71,0x2c,0x1f,0x1e,0x11,0x03,0x5b,
-    0x67,0x2e,0x2d,0x20,0x12,0x05,0x04,0x5c,0x68,0x39,0x2f,0x21,0x14,0x13,0x06,0x5d,
-    0x69,0x31,0x30,0x23,0x22,0x15,0x07,0x5e,0x6a,0x72,0x32,0x24,0x16,0x08,0x09,0x5f,
-    0x6b,0x33,0x25,0x17,0x18,0x0b,0x0a,0x60,0x6c,0x34,0x35,0x26,0x27,0x19,0x0c,0x61,
-    0x6d,0x73,0x28,0x74,0x1a,0x0d,0x62,0x6e,0x3a,0x36,0x1c,0x1b,0x75,0x2b,0x63,0x76,
-    0x55,0x56,0x77,0x78,0x79,0x7a,0x0e,0x7b,0x7c,0x4f,0x7d,0x4b,0x47,0x7e,0x7f,0x6f,
-    0x52,0x53,0x50,0x4c,0x4d,0x48,0x01,0x45,0x57,0x4e,0x51,0x4a,0x37,0x49,0x46,0x54
-};
-
-/**
- * Convert an AT (Scan Set 2) scancode to PC (Scan Set 1).
- *
- * @param state         Current state of the translator
- *                      (xlat_state_t).
- * @param scanIn        Incoming scan code.
- * @param pScanOut      Pointer to outgoing scan code. The
- *                      contents are only valid if returned
- *                      state is not XS_BREAK.
- *
- * @return xlat_state_t New state of the translator.
- */
-int32_t XlateAT2PC(int32_t state, uint8_t scanIn, uint8_t *pScanOut)
-{
-    uint8_t     scan_in;
-    uint8_t     scan_out;
-
-    Assert(pScanOut);
-    Assert(state == XS_IDLE || state == XS_BREAK || state == XS_HIBIT);
-
-    /* Preprocess the scan code for a 128-entry translation table. */
-    if (scanIn == 0x83)         /* Check for F7 key. */
-        scan_in = 0x02;
-    else if (scanIn == 0x84)    /* Check for SysRq key. */
-        scan_in = 0x7f;
-    else
-        scan_in = scanIn;
-
-    /* Values 0x80 and above are passed through, except for 0xF0
-     * which indicates a key release.
-     */
-    if (scan_in < 0x80)
-    {
-        scan_out = aAT2PC[scan_in];
-        /* Turn into break code if required. */
-        if (state == XS_BREAK || state == XS_HIBIT)
-            scan_out |= 0x80;
-
-        state = XS_IDLE;
-    }
-    else
-    {
-        /* NB: F0 E0 10 will be translated to E0 E5 (high bit set on last byte)! */
-        if (scan_in == 0xF0)        /* Check for break code. */
-            state = XS_BREAK;
-        else if (state == XS_BREAK)
-            state = XS_HIBIT;       /* Remember the break bit. */
-        scan_out = scan_in;
-    }
-    LogFlowFunc(("scan code %02X translated to %02X; new state is %d\n",
-                 scanIn, scan_out, state));
-
-    *pScanOut = scan_out;
-    return state;
-}
-
-#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Index: trunk/src/VBox/Devices/Input/PS2M.cpp
===================================================================
--- trunk/src/VBox/Devices/Input/PS2M.cpp	(revision 81765)
+++ 	(revision )
@@ -1,1414 +1,0 @@
-/* $Id$ */
-/** @file
- * PS2M - PS/2 auxiliary device (mouse) emulation.
- */
-
-/*
- * Copyright (C) 2007-2019 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-/*
- * References:
- *
- * The Undocumented PC (2nd Ed.), Frank van Gilluwe, Addison-Wesley, 1996.
- * IBM TrackPoint System Version 4.0 Engineering Specification, 1999.
- * ELAN Microelectronics eKM8025 USB & PS/2 Mouse Controller, 2006.
- *
- *
- * Notes:
- *
- *  - The auxiliary device commands are very similar to keyboard commands.
- *    Most keyboard commands which do not specifically deal with the keyboard
- *    (enable, disable, reset) have identical counterparts.
- *  - The code refers to 'auxiliary device' and 'mouse'; these terms are not
- *    quite interchangeable. 'Auxiliary device' is used when referring to the
- *    generic PS/2 auxiliary device interface and 'mouse' when referring to
- *    a mouse attached to the auxiliary port.
- *  - The basic modes of operation are reset, stream, and remote. Those are
- *    mutually exclusive. Stream and remote modes can additionally have wrap
- *    mode enabled.
- *  - The auxiliary device sends unsolicited data to the host only when it is
- *    both in stream mode and enabled. Otherwise it only responds to commands.
- *
- *
- * There are three report packet formats supported by the emulated device. The
- * standard three-byte PS/2 format (with middle button support), IntelliMouse
- * four-byte format with added scroll wheel, and IntelliMouse Explorer four-byte
- * format with reduced scroll wheel range but two additional buttons. Note that
- * the first three bytes of the report are always the same.
- *
- * Upon reset, the mouse is always in the standard PS/2 mode. A special 'knock'
- * sequence can be used to switch to ImPS/2 or ImEx mode. Three consecutive
- * Set Sampling Rate (0F3h) commands with arguments 200, 100, 80 switch to ImPS/2
- * mode. While in ImPS/2 or PS/2 mode, three consecutive Set Sampling Rate
- * commands with arguments 200, 200, 80 switch to ImEx mode. The Read ID (0F2h)
- * command will report the currently selected protocol.
- *
- * There is an extended ImEx mode with support for horizontal scrolling. It is
- * entered from ImEx mode with a 200, 80, 40 sequence of Set Sampling Rate
- * commands. It does not change the reported protocol (it remains 4, or ImEx)
- * but changes the meaning of the 4th byte.
- *
- *
- * Standard PS/2 pointing device three-byte report packet format:
- *
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- * | Byte 1 | Y ovfl | X ovfl | Y sign | X sign |  Sync  | M btn  | R btn  | L btn  |
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- * | Byte 2 |              X movement delta (two's complement)                      |
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- * | Byte 3 |              Y movement delta (two's complement)                      |
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- *
- *   - The sync bit is always set. It allows software to synchronize data packets
- *     as the X/Y position data typically does not have bit 4 set.
- *   - The overflow bits are set if motion exceeds accumulator range. We use the
- *     maximum range (effectively 9 bits) and do not set the overflow bits.
- *   - Movement in the up/right direction is defined as having positive sign.
- *
- *
- * IntelliMouse PS/2 (ImPS/2) fourth report packet byte:
- *
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- * | Byte 4 |              Z movement delta (two's complement)                      |
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- *
- *   - The valid range for Z delta values is only -8/+7, i.e. 4 bits.
- *
- * IntelliMouse Explorer (ImEx) fourth report packet byte:
- *
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- * | Byte 4 |   0    |   0    | Btn 5  | Btn 4  |  Z mov't delta (two's complement) |
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- *
- *   - The Z delta values are in practice only -1/+1; some mice (A4tech?) report
- *     horizontal scrolling as -2/+2.
- *
- * IntelliMouse Explorer (ImEx) fourth report packet byte when scrolling:
- *
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- * | Byte 4 |   V    |   H    |      Z or W movement delta (two's complement)       |
- * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
- *
- *   - Buttons 4 and 5 are reported as with the regular ImEx protocol, but not when
- *     scrolling. This is a departure from the usual logic because when the mouse
- *     sends scroll events, the state of buttons 4/5 is not reported and the last
- *     reported state should be assumed.
- *
- *   - When the V bit (bit 7) is set, vertical scroll (Z axis) is being reported.
- *     When the H bit (bit 6) is set, horizontal scroll (W axis) is being reported.
- *     The H and V bits are never set at the same time (also see below). When
- *     the H and V bits are both clear, button 4/5 state is being reported.
- *
- *   - The Z/W delta is extended to 6 bits. Z (vertical) values are not restricted
- *     to -1/+1, although W (horizontal) values are. Z values of at least -20/+20
- *     can be seen in practice.
- *
- *   - Horizontal and vertical scroll is mutually exclusive. When the button is
- *     tilted, no vertical scrolling is reported, i.e. horizontal scrolling
- *     has priority over vertical.
- *
- *   - Positive values indicate down/right direction, negative values up/left.
- *
- *   - When the scroll button is tilted to engage horizontal scrolling, the mouse
- *     keeps sending events at a rate of 4 or 5 per second as long as the button
- *     is tilted.
- *
- * All report formats were verified with a real Microsoft IntelliMouse Explorer 4.0
- * mouse attached through a PS/2 port.
- *
- * The button "accumulator" is necessary to avoid missing brief button presses.
- * Without it, a very fast mouse button press + release might be lost if it
- * happened between sending reports. The accumulator latches button presses to
- * prevent that.
- *
- */
-
-
-/*********************************************************************************************************************************
-*   Header Files                                                                                                                 *
-*********************************************************************************************************************************/
-#define LOG_GROUP   LOG_GROUP_DEV_KBD
-#include <VBox/vmm/pdmdev.h>
-#include <VBox/err.h>
-#include <iprt/assert.h>
-#include <iprt/uuid.h>
-#include "VBoxDD.h"
-#define IN_PS2M
-#include "PS2Dev.h"
-
-
-/*********************************************************************************************************************************
-*   Defined Constants And Macros                                                                                                 *
-*********************************************************************************************************************************/
-/** @name Auxiliary device commands sent by the system.
- * @{ */
-#define ACMD_SET_SCALE_11   0xE6    /* Set 1:1 scaling. */
-#define ACMD_SET_SCALE_21   0xE7    /* Set 2:1 scaling. */
-#define ACMD_SET_RES        0xE8    /* Set resolution. */
-#define ACMD_REQ_STATUS     0xE9    /* Get device status. */
-#define ACMD_SET_STREAM     0xEA    /* Set stream mode. */
-#define ACMD_READ_REMOTE    0xEB    /* Read remote data. */
-#define ACMD_RESET_WRAP     0xEC    /* Exit wrap mode. */
-#define ACMD_INVALID_1      0xED
-#define ACMD_SET_WRAP       0xEE    /* Set wrap (echo) mode. */
-#define ACMD_INVALID_2      0xEF
-#define ACMD_SET_REMOTE     0xF0    /* Set remote mode. */
-#define ACMD_INVALID_3      0xF1
-#define ACMD_READ_ID        0xF2    /* Read device ID. */
-#define ACMD_SET_SAMP_RATE  0xF3    /* Set sampling rate. */
-#define ACMD_ENABLE         0xF4    /* Enable (streaming mode). */
-#define ACMD_DISABLE        0xF5    /* Disable (streaming mode). */
-#define ACMD_SET_DEFAULT    0xF6    /* Set defaults. */
-#define ACMD_INVALID_4      0xF7
-#define ACMD_INVALID_5      0xF8
-#define ACMD_INVALID_6      0xF9
-#define ACMD_INVALID_7      0xFA
-#define ACMD_INVALID_8      0xFB
-#define ACMD_INVALID_9      0xFC
-#define ACMD_INVALID_10     0xFD
-#define ACMD_RESEND         0xFE    /* Resend response. */
-#define ACMD_RESET          0xFF    /* Reset device. */
-/** @} */
-
-/** @name Auxiliary device responses sent to the system.
- * @{ */
-#define ARSP_ID             0x00
-#define ARSP_BAT_OK         0xAA    /* Self-test passed. */
-#define ARSP_ACK            0xFA    /* Command acknowledged. */
-#define ARSP_ERROR          0xFC    /* Bad command. */
-#define ARSP_RESEND         0xFE    /* Requesting resend. */
-/** @} */
-
-/** Define a simple PS/2 input device queue. */
-#define DEF_PS2Q_TYPE(name, size)   \
-     typedef struct {               \
-        uint32_t    rpos;           \
-        uint32_t    wpos;           \
-        uint32_t    cUsed;          \
-        uint32_t    cSize;          \
-        uint8_t     abQueue[size];  \
-     } name
-
-/* Internal mouse queue sizes. The input queue is relatively large,
- * but the command queue only needs to handle a few bytes.
- */
-#define AUX_EVT_QUEUE_SIZE        256
-#define AUX_CMD_QUEUE_SIZE          8
-
-
-/*********************************************************************************************************************************
-*   Structures and Typedefs                                                                                                      *
-*********************************************************************************************************************************/
-
-DEF_PS2Q_TYPE(AuxEvtQ, AUX_EVT_QUEUE_SIZE);
-DEF_PS2Q_TYPE(AuxCmdQ, AUX_CMD_QUEUE_SIZE);
-#ifndef VBOX_DEVICE_STRUCT_TESTCASE /// @todo hack
-DEF_PS2Q_TYPE(GeneriQ, 1);
-#endif
-
-/* Auxiliary device special modes of operation. */
-typedef enum {
-    AUX_MODE_STD,           /* Standard operation. */
-    AUX_MODE_RESET,         /* Currently in reset. */
-    AUX_MODE_WRAP           /* Wrap mode (echoing input). */
-} PS2M_MODE;
-
-/* Auxiliary device operational state. */
-typedef enum {
-    AUX_STATE_RATE_ERR  = RT_BIT(0),    /* Invalid rate received. */
-    AUX_STATE_RES_ERR   = RT_BIT(1),    /* Invalid resolution received. */
-    AUX_STATE_SCALING   = RT_BIT(4),    /* 2:1 scaling in effect. */
-    AUX_STATE_ENABLED   = RT_BIT(5),    /* Reporting enabled in stream mode. */
-    AUX_STATE_REMOTE    = RT_BIT(6)     /* Remote mode (reports on request). */
-} PS2M_STATE;
-
-/* Externally visible state bits. */
-#define AUX_STATE_EXTERNAL  (AUX_STATE_SCALING | AUX_STATE_ENABLED | AUX_STATE_REMOTE)
-
-/* Protocols supported by the PS/2 mouse. */
-typedef enum {
-    PS2M_PROTO_PS2STD      = 0,  /* Standard PS/2 mouse protocol. */
-    PS2M_PROTO_IMPS2       = 3,  /* IntelliMouse PS/2 protocol. */
-    PS2M_PROTO_IMEX        = 4,  /* IntelliMouse Explorer protocol. */
-    PS2M_PROTO_IMEX_HORZ   = 5   /* IntelliMouse Explorer with horizontal reports. */
-} PS2M_PROTO;
-
-/* Protocol selection 'knock' states. */
-typedef enum {
-    PS2M_KNOCK_INITIAL,
-    PS2M_KNOCK_1ST,
-    PS2M_KNOCK_IMPS2_2ND,
-    PS2M_KNOCK_IMEX_2ND,
-    PS2M_KNOCK_IMEX_HORZ_2ND
-} PS2M_KNOCK_STATE;
-
-/**
- * The PS/2 auxiliary device instance data.
- */
-typedef struct PS2M
-{
-    /** Pointer to parent device (keyboard controller). */
-    R3PTRTYPE(void *)   pParent;
-    /** Operational state. */
-    uint8_t             u8State;
-    /** Configured sampling rate. */
-    uint8_t             u8SampleRate;
-    /** Configured resolution. */
-    uint8_t             u8Resolution;
-    /** Currently processed command (if any). */
-    uint8_t             u8CurrCmd;
-    /** Set if the throttle delay is active. */
-    bool                fThrottleActive;
-    /** Set if the throttle delay is active. */
-    bool                fDelayReset;
-    /** Operational mode. */
-    PS2M_MODE           enmMode;
-    /** Currently used protocol. */
-    PS2M_PROTO          enmProtocol;
-    /** Currently used protocol. */
-    PS2M_KNOCK_STATE    enmKnockState;
-    /** Buffer holding mouse events to be sent to the host. */
-    AuxEvtQ             evtQ;
-    /** Command response queue (priority). */
-    AuxCmdQ             cmdQ;
-    /** Accumulated horizontal movement. */
-    int32_t             iAccumX;
-    /** Accumulated vertical movement. */
-    int32_t             iAccumY;
-    /** Accumulated Z axis (vertical scroll) movement. */
-    int32_t             iAccumZ;
-    /** Accumulated W axis (horizontal scroll) movement. */
-    int32_t             iAccumW;
-    /** Accumulated button presses. */
-    uint32_t            fAccumB;
-    /** Instantaneous button data. */
-    uint32_t            fCurrB;
-    /** Button state last sent to the guest. */
-    uint32_t            fReportedB;
-    /** Throttling delay in milliseconds. */
-    uint32_t            uThrottleDelay;
-
-    /** The device critical section protecting everything - R3 Ptr */
-    R3PTRTYPE(PPDMCRITSECT) pCritSectR3;
-    /** Command delay timer - R3 Ptr. */
-    PTMTIMERR3          pDelayTimerR3;
-    /** Interrupt throttling timer - R3 Ptr. */
-    PTMTIMERR3          pThrottleTimerR3;
-    RTR3PTR             Alignment1;
-
-    /** Command delay timer - RC Ptr. */
-    PTMTIMERRC          pDelayTimerRC;
-    /** Interrupt throttling timer - RC Ptr. */
-    PTMTIMERRC          pThrottleTimerRC;
-
-    /** Command delay timer - R0 Ptr. */
-    PTMTIMERR0          pDelayTimerR0;
-    /** Interrupt throttling timer - R0 Ptr. */
-    PTMTIMERR0          pThrottleTimerR0;
-
-    /**
-     * Mouse port - LUN#1.
-     *
-     * @implements  PDMIBASE
-     * @implements  PDMIMOUSEPORT
-     */
-    struct
-    {
-        /** The base interface for the mouse port. */
-        PDMIBASE                            IBase;
-        /** The keyboard port base interface. */
-        PDMIMOUSEPORT                       IPort;
-
-        /** The base interface of the attached mouse driver. */
-        R3PTRTYPE(PPDMIBASE)                pDrvBase;
-        /** The keyboard interface of the attached mouse driver. */
-        R3PTRTYPE(PPDMIMOUSECONNECTOR)      pDrv;
-    } Mouse;
-} PS2M, *PPS2M;
-
-AssertCompile(PS2M_STRUCT_FILLER >= sizeof(PS2M));
-
-#ifndef VBOX_DEVICE_STRUCT_TESTCASE
-
-
-/*********************************************************************************************************************************
-*   Test code function declarations                                                                                              *
-*********************************************************************************************************************************/
-#if defined(RT_STRICT) && defined(IN_RING3)
-static void ps2mTestAccumulation(void);
-#endif
-
-
-/*********************************************************************************************************************************
-*   Global Variables                                                                                                             *
-*********************************************************************************************************************************/
-
-
-/*********************************************************************************************************************************
-*   Internal Functions                                                                                                           *
-*********************************************************************************************************************************/
-
-
-/**
- * Clear a queue.
- *
- * @param   pQ                  Pointer to the queue.
- */
-static void ps2kClearQueue(GeneriQ *pQ)
-{
-    LogFlowFunc(("Clearing queue %p\n", pQ));
-    pQ->wpos  = pQ->rpos;
-    pQ->cUsed = 0;
-}
-
-
-/**
- * Add a byte to a queue.
- *
- * @param   pQ                  Pointer to the queue.
- * @param   val                 The byte to store.
- */
-static void ps2kInsertQueue(GeneriQ *pQ, uint8_t val)
-{
-    /* Check if queue is full. */
-    if (pQ->cUsed >= pQ->cSize)
-    {
-        LogRelFlowFunc(("queue %p full (%d entries)\n", pQ, pQ->cUsed));
-        return;
-    }
-    /* Insert data and update circular buffer write position. */
-    pQ->abQueue[pQ->wpos] = val;
-    if (++pQ->wpos == pQ->cSize)
-        pQ->wpos = 0;   /* Roll over. */
-    ++pQ->cUsed;
-    LogRelFlowFunc(("inserted 0x%02X into queue %p\n", val, pQ));
-}
-
-#ifdef IN_RING3
-
-/**
- * Save a queue state.
- *
- * @param   pSSM                SSM handle to write the state to.
- * @param   pQ                  Pointer to the queue.
- */
-static void ps2kSaveQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
-{
-    uint32_t    cItems = pQ->cUsed;
-    int         i;
-
-    /* Only save the number of items. Note that the read/write
-     * positions aren't saved as they will be rebuilt on load.
-     */
-    SSMR3PutU32(pSSM, cItems);
-
-    LogFlow(("Storing %d items from queue %p\n", cItems, pQ));
-
-    /* Save queue data - only the bytes actually used (typically zero). */
-    for (i = pQ->rpos; cItems-- > 0; i = (i + 1) % pQ->cSize)
-        SSMR3PutU8(pSSM, pQ->abQueue[i]);
-}
-
-/**
- * Load a queue state.
- *
- * @param   pSSM                SSM handle to read the state from.
- * @param   pQ                  Pointer to the queue.
- *
- * @return  int                 VBox status/error code.
- */
-static int ps2kLoadQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
-{
-    int         rc;
-
-    /* On load, always put the read pointer at zero. */
-    SSMR3GetU32(pSSM, &pQ->cUsed);
-
-    LogFlow(("Loading %d items to queue %p\n", pQ->cUsed, pQ));
-
-    if (pQ->cUsed > pQ->cSize)
-    {
-        AssertMsgFailed(("Saved size=%u, actual=%u\n", pQ->cUsed, pQ->cSize));
-        return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
-    }
-
-    /* Recalculate queue positions and load data in one go. */
-    pQ->rpos = 0;
-    pQ->wpos = pQ->cUsed;
-    rc = SSMR3GetMem(pSSM, pQ->abQueue, pQ->cUsed);
-
-    return rc;
-}
-
-/* Report a change in status down (or is it up?) the driver chain. */
-static void ps2mSetDriverState(PPS2M pThis, bool fEnabled)
-{
-    PPDMIMOUSECONNECTOR pDrv = pThis->Mouse.pDrv;
-    if (pDrv)
-        pDrv->pfnReportModes(pDrv, fEnabled, false, false);
-}
-
-/* Reset the pointing device. */
-static void ps2mReset(PPS2M pThis)
-{
-    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_BAT_OK);
-    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, 0);
-    pThis->enmMode   = AUX_MODE_STD;
-    pThis->u8CurrCmd = 0;
-
-    /// @todo move to its proper home!
-    ps2mSetDriverState(pThis, true);
-}
-
-#endif /* IN_RING3 */
-
-/**
- * Retrieve a byte from a queue.
- *
- * @param   pQ                  Pointer to the queue.
- * @param   pVal                Pointer to storage for the byte.
- *
- * @return  int                 VINF_TRY_AGAIN if queue is empty,
- *                              VINF_SUCCESS if a byte was read.
- */
-static int ps2kRemoveQueue(GeneriQ *pQ, uint8_t *pVal)
-{
-    int     rc = VINF_TRY_AGAIN;
-
-    Assert(pVal);
-    if (pQ->cUsed)
-    {
-        *pVal = pQ->abQueue[pQ->rpos];
-        if (++pQ->rpos == pQ->cSize)
-            pQ->rpos = 0;   /* Roll over. */
-        --pQ->cUsed;
-        rc = VINF_SUCCESS;
-        LogFlowFunc(("removed 0x%02X from queue %p\n", *pVal, pQ));
-    } else
-        LogFlowFunc(("queue %p empty\n", pQ));
-    return rc;
-}
-
-static void ps2mSetRate(PPS2M pThis, uint8_t rate)
-{
-    Assert(rate);
-    pThis->uThrottleDelay = rate ? 1000 / rate : 0;
-    pThis->u8SampleRate = rate;
-    LogFlowFunc(("Sampling rate %u, throttle delay %u ms\n", pThis->u8SampleRate, pThis->uThrottleDelay));
-}
-
-static void ps2mSetDefaults(PPS2M pThis)
-{
-    LogFlowFunc(("Set mouse defaults\n"));
-    /* Standard protocol, reporting disabled, resolution 2, 1:1 scaling. */
-    pThis->enmProtocol  = PS2M_PROTO_PS2STD;
-    pThis->u8State      = 0;
-    pThis->u8Resolution = 2;
-
-    /* Sample rate 100 reports per second. */
-    ps2mSetRate(pThis, 100);
-
-    /* Event queue, eccumulators, and button status bits are cleared. */
-    ps2kClearQueue((GeneriQ *)&pThis->evtQ);
-    pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = pThis->iAccumW = pThis->fAccumB = 0;
-}
-
-/* Handle the sampling rate 'knock' sequence which selects protocol. */
-static void ps2mRateProtocolKnock(PPS2M pThis, uint8_t rate)
-{
-    switch (pThis->enmKnockState)
-    {
-    case PS2M_KNOCK_INITIAL:
-        if (rate == 200)
-            pThis->enmKnockState = PS2M_KNOCK_1ST;
-        break;
-    case PS2M_KNOCK_1ST:
-        if (rate == 100)
-            pThis->enmKnockState = PS2M_KNOCK_IMPS2_2ND;
-        else if (rate == 200)
-            pThis->enmKnockState = PS2M_KNOCK_IMEX_2ND;
-        else if (rate == 80)
-            pThis->enmKnockState = PS2M_KNOCK_IMEX_HORZ_2ND;
-        else
-            pThis->enmKnockState = PS2M_KNOCK_INITIAL;
-        break;
-    case PS2M_KNOCK_IMPS2_2ND:
-        if (rate == 80)
-        {
-            pThis->enmProtocol = PS2M_PROTO_IMPS2;
-            LogRelFlow(("PS2M: Switching mouse to ImPS/2 protocol.\n"));
-        }
-        pThis->enmKnockState = PS2M_KNOCK_INITIAL;
-        break;
-    case PS2M_KNOCK_IMEX_2ND:
-        if (rate == 80)
-        {
-            pThis->enmProtocol = PS2M_PROTO_IMEX;
-            LogRelFlow(("PS2M: Switching mouse to ImEx protocol.\n"));
-        }
-        pThis->enmKnockState = PS2M_KNOCK_INITIAL;
-        break;
-    case PS2M_KNOCK_IMEX_HORZ_2ND:
-        if (rate == 40)
-        {
-            pThis->enmProtocol = PS2M_PROTO_IMEX_HORZ;
-            LogRelFlow(("PS2M: Switching mouse ImEx with horizontal scrolling.\n"));
-        }
-        RT_FALL_THRU();
-    default:
-        pThis->enmKnockState = PS2M_KNOCK_INITIAL;
-    }
-}
-
-/* Three-button event mask. */
-#define PS2M_STD_BTN_MASK   (RT_BIT(0) | RT_BIT(1) | RT_BIT(2))
-/* ImEx button 4/5 event mask. */
-#define PS2M_IMEX_BTN_MASK  (RT_BIT(3) | RT_BIT(4))
-
-/* Report accumulated movement and button presses, then clear the accumulators. */
-static void ps2mReportAccumulatedEvents(PPS2M pThis, GeneriQ *pQueue, bool fAccumBtns)
-{
-    uint32_t    fBtnState = fAccumBtns ? pThis->fAccumB : pThis->fCurrB;
-    uint8_t     val;
-    int         dX, dY, dZ, dW;
-
-    /* Clamp the accumulated delta values to the allowed range. */
-    dX = RT_MIN(RT_MAX(pThis->iAccumX, -255), 255);
-    dY = RT_MIN(RT_MAX(pThis->iAccumY, -255), 255);
-
-    /* Start with the sync bit and buttons 1-3. */
-    val = RT_BIT(3) | (fBtnState & PS2M_STD_BTN_MASK);
-    /* Set the X/Y sign bits. */
-    if (dX < 0)
-        val |= RT_BIT(4);
-    if (dY < 0)
-        val |= RT_BIT(5);
-
-    /* Send the standard 3-byte packet (always the same). */
-    ps2kInsertQueue(pQueue, val);
-    ps2kInsertQueue(pQueue, dX);
-    ps2kInsertQueue(pQueue, dY);
-
-    /* Add fourth byte if an extended protocol is in use. */
-    if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
-    {
-        /* Start out with 4-bit dZ range. */
-        dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -8), 7);
-
-        if (pThis->enmProtocol == PS2M_PROTO_IMPS2)
-        {
-            /* NB: Only uses 4-bit dZ range, despite using a full byte. */
-            ps2kInsertQueue(pQueue, dZ);
-            pThis->iAccumZ -= dZ;
-        }
-        else if (pThis->enmProtocol == PS2M_PROTO_IMEX)
-        {
-           /* Z value uses 4 bits; buttons 4/5 in bits 4 and 5. */
-           val  = (fBtnState & PS2M_IMEX_BTN_MASK) << 1;
-           val |= dZ & 0x0f;
-           pThis->iAccumZ -= dZ;
-           ps2kInsertQueue(pQueue, val);
-        }
-        else
-        {
-            Assert((pThis->enmProtocol == PS2M_PROTO_IMEX_HORZ));
-            /* With ImEx + horizontal reporting, prioritize buttons 4/5. */
-            if (pThis->iAccumZ || pThis->iAccumW)
-            {
-               /* ImEx + horizontal reporting Horizontal scroll has
-                * precedence over vertical. Buttons cannot be reported
-                * this way.
-                */
-               if (pThis->iAccumW)
-               {
-                  dW = RT_MIN(RT_MAX(pThis->iAccumW, -32), 31);
-                  val = (dW & 0x3F) | 0x40;
-                  pThis->iAccumW -= dW;
-               }
-               else
-               {
-                  Assert(pThis->iAccumZ);
-                  /* We can use 6-bit dZ range. Wow! */
-                  dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -32), 31);
-                  val = (dZ & 0x3F) | 0x80;
-                  pThis->iAccumZ -= dZ;
-               }
-            }
-            else
-            {
-               /* Just Buttons 4/5 in bits 4 and 5. No scrolling. */
-               val = (fBtnState & PS2M_IMEX_BTN_MASK) << 1;
-            }
-            ps2kInsertQueue(pQueue, val);
-        }
-    }
-
-    /* Clear the movement accumulators, but not necessarily button state. */
-    pThis->iAccumX = pThis->iAccumY = 0;
-    /* Clear accumulated button state only when it's being used. */
-    if (fAccumBtns)
-    {
-        pThis->fReportedB = pThis->fCurrB | pThis->fAccumB;
-        pThis->fAccumB    = 0;
-    }
-}
-
-
-/* Determine whether a reporting rate is one of the valid ones. */
-bool ps2mIsRateSupported(uint8_t rate)
-{
-    static uint8_t  aValidRates[] = { 10, 20, 40, 60, 80, 100, 200 };
-    size_t          i;
-    bool            fValid = false;
-
-    for (i = 0; i < RT_ELEMENTS(aValidRates); ++i)
-        if (aValidRates[i] == rate)
-        {
-            fValid = true;
-            break;
-        }
-
-   return fValid;
-}
-
-/**
- * Receive and process a byte sent by the keyboard controller.
- *
- * @param   pThis               The PS/2 auxiliary device instance data.
- * @param   cmd                 The command (or data) byte.
- */
-int PS2MByteToAux(PPS2M pThis, uint8_t cmd)
-{
-    uint8_t u8Val;
-    bool    fHandled = true;
-
-    LogFlowFunc(("cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
-
-    if (pThis->enmMode == AUX_MODE_RESET)
-        /* In reset mode, do not respond at all. */
-        return VINF_SUCCESS;
-
-    /* If there's anything left in the command response queue, trash it. */
-    ps2kClearQueue((GeneriQ *)&pThis->cmdQ);
-
-    if (pThis->enmMode == AUX_MODE_WRAP)
-    {
-        /* In wrap mode, bounce most data right back.*/
-        if (cmd == ACMD_RESET || cmd == ACMD_RESET_WRAP)
-            ;   /* Handle as regular commands. */
-        else
-        {
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, cmd);
-            return VINF_SUCCESS;
-        }
-    }
-
-#ifndef IN_RING3
-    /* Reset, Enable, and Set Default commands must be run in R3. */
-    if (cmd == ACMD_RESET || cmd == ACMD_ENABLE || cmd == ACMD_SET_DEFAULT)
-        return VINF_IOM_R3_IOPORT_WRITE;
-#endif
-
-    switch (cmd)
-    {
-        case ACMD_SET_SCALE_11:
-            pThis->u8State &= ~AUX_STATE_SCALING;
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_SET_SCALE_21:
-            pThis->u8State |= AUX_STATE_SCALING;
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_REQ_STATUS:
-            /* Report current status, sample rate, and resolution. */
-            u8Val  = (pThis->u8State & AUX_STATE_EXTERNAL) | (pThis->fCurrB & PS2M_STD_BTN_MASK);
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, u8Val);
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8Resolution);
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8SampleRate);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_SET_STREAM:
-            pThis->u8State &= ~AUX_STATE_REMOTE;
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_READ_REMOTE:
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->cmdQ, false);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_RESET_WRAP:
-            pThis->enmMode = AUX_MODE_STD;
-            /* NB: Stream mode reporting remains disabled! */
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_SET_WRAP:
-            pThis->enmMode = AUX_MODE_WRAP;
-            pThis->u8State &= ~AUX_STATE_ENABLED;
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_SET_REMOTE:
-            pThis->u8State |= AUX_STATE_REMOTE;
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_READ_ID:
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            /* ImEx + horizontal is protocol 4, just like plain ImEx. */
-            u8Val = pThis->enmProtocol == PS2M_PROTO_IMEX_HORZ ? PS2M_PROTO_IMEX : pThis->enmProtocol;
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, u8Val);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_ENABLE:
-            pThis->u8State |= AUX_STATE_ENABLED;
-#ifdef IN_RING3
-            ps2mSetDriverState(pThis, true);
-#else
-            AssertLogRelMsgFailed(("Invalid ACMD_ENABLE outside R3!\n"));
-#endif
-            ps2kClearQueue((GeneriQ *)&pThis->evtQ);
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_DISABLE:
-            pThis->u8State &= ~AUX_STATE_ENABLED;
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_SET_DEFAULT:
-            ps2mSetDefaults(pThis);
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_RESEND:
-            pThis->u8CurrCmd = 0;
-            break;
-        case ACMD_RESET:
-            ps2mSetDefaults(pThis);
-            /// @todo reset more?
-            pThis->u8CurrCmd = cmd;
-            pThis->enmMode   = AUX_MODE_RESET;
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            if (pThis->fDelayReset)
-                /* Slightly delay reset completion; it might take hundreds of ms. */
-                TMTimerSetMillies(pThis->CTX_SUFF(pDelayTimer), 1);
-            else
-#ifdef IN_RING3
-                ps2mReset(pThis);
-#else
-                AssertLogRelMsgFailed(("Invalid ACMD_RESET outside R3!\n"));
-#endif
-            break;
-        /* The following commands need a parameter. */
-        case ACMD_SET_RES:
-        case ACMD_SET_SAMP_RATE:
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-            pThis->u8CurrCmd = cmd;
-            break;
-        default:
-            /* Sending a command instead of a parameter starts the new command. */
-            switch (pThis->u8CurrCmd)
-            {
-                case ACMD_SET_RES:
-                    if (cmd < 4)    /* Valid resolutions are 0-3. */
-                    {
-                        pThis->u8Resolution = cmd;
-                        pThis->u8State &= ~AUX_STATE_RES_ERR;
-                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-                        pThis->u8CurrCmd = 0;
-                    }
-                    else
-                    {
-                        /* Bad resolution. Reply with Resend or Error. */
-                        if (pThis->u8State & AUX_STATE_RES_ERR)
-                        {
-                            pThis->u8State &= ~AUX_STATE_RES_ERR;
-                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ERROR);
-                            pThis->u8CurrCmd = 0;
-                        }
-                        else
-                        {
-                            pThis->u8State |= AUX_STATE_RES_ERR;
-                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
-                            /* NB: Current command remains unchanged. */
-                        }
-                    }
-                    break;
-                case ACMD_SET_SAMP_RATE:
-                    if (ps2mIsRateSupported(cmd))
-                    {
-                        pThis->u8State &= ~AUX_STATE_RATE_ERR;
-                        ps2mSetRate(pThis, cmd);
-                        ps2mRateProtocolKnock(pThis, cmd);
-                        ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
-                        pThis->u8CurrCmd = 0;
-                    }
-                    else
-                    {
-                        /* Bad rate. Reply with Resend or Error. */
-                        if (pThis->u8State & AUX_STATE_RATE_ERR)
-                        {
-                            pThis->u8State &= ~AUX_STATE_RATE_ERR;
-                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ERROR);
-                            pThis->u8CurrCmd = 0;
-                        }
-                        else
-                        {
-                            pThis->u8State |= AUX_STATE_RATE_ERR;
-                            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
-                            /* NB: Current command remains unchanged. */
-                        }
-                    }
-                    break;
-                default:
-                    fHandled = false;
-            }
-            /* Fall through only to handle unrecognized commands. */
-            if (fHandled)
-                break;
-            RT_FALL_THRU();
-
-        case ACMD_INVALID_1:
-        case ACMD_INVALID_2:
-        case ACMD_INVALID_3:
-        case ACMD_INVALID_4:
-        case ACMD_INVALID_5:
-        case ACMD_INVALID_6:
-        case ACMD_INVALID_7:
-        case ACMD_INVALID_8:
-        case ACMD_INVALID_9:
-        case ACMD_INVALID_10:
-            Log(("Unsupported command 0x%02X!\n", cmd));
-            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
-            pThis->u8CurrCmd = 0;
-            break;
-    }
-    LogFlowFunc(("Active cmd now 0x%02X; updating interrupts\n", pThis->u8CurrCmd));
-    return VINF_SUCCESS;
-}
-
-/**
- * Send a byte (packet data or command response) to the keyboard controller.
- *
- * @returns VINF_SUCCESS or VINF_TRY_AGAIN.
- * @param   pThis               The PS/2 auxiliary device instance data.
- * @param   pb                  Where to return the byte we've read.
- * @remarks Caller must have entered the device critical section.
- */
-int PS2MByteFromAux(PPS2M pThis, uint8_t *pb)
-{
-    int         rc;
-
-    AssertPtr(pb);
-
-    /* Anything in the command queue has priority over data
-     * in the event queue. Additionally, packet data are
-     * blocked if a command is currently in progress, even if
-     * the command queue is empty.
-     */
-    /// @todo Probably should flush/not fill queue if stream mode reporting disabled?!
-    rc = ps2kRemoveQueue((GeneriQ *)&pThis->cmdQ, pb);
-    if (rc != VINF_SUCCESS && !pThis->u8CurrCmd && (pThis->u8State & AUX_STATE_ENABLED))
-        rc = ps2kRemoveQueue((GeneriQ *)&pThis->evtQ, pb);
-
-    LogFlowFunc(("mouse sends 0x%02x (%svalid data)\n", *pb, rc == VINF_SUCCESS ? "" : "not "));
-
-    return rc;
-}
-
-#ifdef IN_RING3
-
-/** Is there any state change to send as events to the guest? */
-static uint32_t ps2mHaveEvents(PPS2M pThis)
-{
-    return   pThis->iAccumX || pThis->iAccumY || pThis->iAccumZ || pThis->iAccumW
-           || ((pThis->fCurrB | pThis->fAccumB) != pThis->fReportedB);
-}
-
-/* Event rate throttling timer to emulate the auxiliary device sampling rate.
- */
-static DECLCALLBACK(void) ps2mThrottleTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
-{
-    RT_NOREF2(pDevIns, pTimer);
-    PPS2M       pThis = (PS2M *)pvUser;
-    uint32_t    uHaveEvents;
-
-    /* Grab the lock to avoid races with PutEvent(). */
-    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
-    AssertReleaseRC(rc);
-
-    /* If more movement is accumulated, report it and restart the timer. */
-    uHaveEvents = ps2mHaveEvents(pThis);
-    LogFlowFunc(("Have%s events\n", uHaveEvents ? "" : " no"));
-
-    if (uHaveEvents)
-    {
-        /* Report accumulated data, poke the KBC, and start the timer. */
-        ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->evtQ, true);
-        KBCUpdateInterrupts(pThis->pParent);
-        TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
-    }
-    else
-        pThis->fThrottleActive = false;
-
-    PDMCritSectLeave(pThis->pCritSectR3);
-}
-
-/* The auxiliary device reset is specified to take up to about 500 milliseconds. We need
- * to delay sending the result to the host for at least a tiny little while.
- */
-static DECLCALLBACK(void) ps2mDelayTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
-{
-    RT_NOREF2(pDevIns, pTimer);
-    PPS2M pThis = (PS2M *)pvUser;
-
-    LogFlowFunc(("Delay timer: cmd %02X\n", pThis->u8CurrCmd));
-
-    Assert(pThis->u8CurrCmd == ACMD_RESET);
-    ps2mReset(pThis);
-
-    /// @todo Might want a PS2MCompleteCommand() to push last response, clear command, and kick the KBC...
-    /* Give the KBC a kick. */
-    KBCUpdateInterrupts(pThis->pParent);
-}
-
-
-/**
- * Debug device info handler. Prints basic auxiliary device state.
- *
- * @param   pDevIns     Device instance which registered the info.
- * @param   pHlp        Callback functions for doing output.
- * @param   pszArgs     Argument string. Optional and specific to the handler.
- */
-static DECLCALLBACK(void) ps2mInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
-{
-    static const char   *pcszModes[] = { "normal", "reset", "wrap" };
-    static const char   *pcszProtocols[] = { "PS/2", NULL, NULL, "ImPS/2", "ImEx", "ImEx+horizontal" };
-    PPS2M   pThis = KBDGetPS2MFromDevIns(pDevIns);
-    NOREF(pszArgs);
-
-    Assert(pThis->enmMode <= RT_ELEMENTS(pcszModes));
-    Assert(pThis->enmProtocol <= RT_ELEMENTS(pcszProtocols));
-    pHlp->pfnPrintf(pHlp, "PS/2 mouse state: %s, %s mode, reporting %s\n",
-                    pcszModes[pThis->enmMode],
-                    pThis->u8State & AUX_STATE_REMOTE  ? "remote"  : "stream",
-                    pThis->u8State & AUX_STATE_ENABLED ? "enabled" : "disabled");
-    pHlp->pfnPrintf(pHlp, "Protocol: %s, scaling %u:1\n",
-                    pcszProtocols[pThis->enmProtocol],
-                    pThis->u8State & AUX_STATE_SCALING ? 2 : 1);
-    pHlp->pfnPrintf(pHlp, "Active command %02X\n", pThis->u8CurrCmd);
-    pHlp->pfnPrintf(pHlp, "Sampling rate %u reports/sec, resolution %u counts/mm\n",
-                    pThis->u8SampleRate, 1 << pThis->u8Resolution);
-    pHlp->pfnPrintf(pHlp, "Command queue: %d items (%d max)\n",
-                    pThis->cmdQ.cUsed, pThis->cmdQ.cSize);
-    pHlp->pfnPrintf(pHlp, "Event queue  : %d items (%d max)\n",
-                    pThis->evtQ.cUsed, pThis->evtQ.cSize);
-}
-
-/* -=-=-=-=-=- Mouse: IBase  -=-=-=-=-=- */
-
-/**
- * @interface_method_impl{PDMIBASE,pfnQueryInterface}
- */
-static DECLCALLBACK(void *) ps2mQueryInterface(PPDMIBASE pInterface, const char *pszIID)
-{
-    PPS2M pThis = RT_FROM_MEMBER(pInterface, PS2M, Mouse.IBase);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Mouse.IBase);
-    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSEPORT, &pThis->Mouse.IPort);
-    return NULL;
-}
-
-
-/* -=-=-=-=-=- Mouse: IMousePort  -=-=-=-=-=- */
-
-/**
- * Mouse event handler.
- *
- * @returns VBox status code.
- * @param   pThis           The PS/2 auxiliary device instance data.
- * @param   dx              X direction movement delta.
- * @param   dy              Y direction movement delta.
- * @param   dz              Z (vertical scroll) movement delta.
- * @param   dw              W (horizontal scroll) movement delta.
- * @param   fButtons        Depressed button mask.
- */
-static int ps2mPutEventWorker(PPS2M pThis, int32_t dx, int32_t dy,
-                              int32_t dz, int32_t dw, uint32_t fButtons)
-{
-    int             rc = VINF_SUCCESS;
-
-    /* Update internal accumulators and button state. Ignore any buttons beyond 5. */
-    pThis->iAccumX += dx;
-    pThis->iAccumY += dy;
-    pThis->iAccumZ += dz;
-    pThis->iAccumW += dw;
-    pThis->fCurrB   = fButtons & (PS2M_STD_BTN_MASK | PS2M_IMEX_BTN_MASK);
-    pThis->fAccumB |= pThis->fCurrB;
-
-    /* Ditch accumulated data that can't be reported by the current protocol.
-     * This avoids sending phantom empty reports when un-reportable events
-     * are received.
-     */
-    if (pThis->enmProtocol < PS2M_PROTO_IMEX_HORZ)
-       pThis->iAccumW = 0; /* No horizontal scroll. */
-
-    if (pThis->enmProtocol < PS2M_PROTO_IMEX)
-    {
-       pThis->fAccumB &= PS2M_STD_BTN_MASK;   /* Only buttons 1-3. */
-       pThis->fCurrB  &= PS2M_STD_BTN_MASK;
-    }
-
-    if (pThis->enmProtocol < PS2M_PROTO_IMPS2)
-       pThis->iAccumZ = 0; /* No vertical scroll. */
-
-    /* Report the event (if any) and start the throttle timer unless it's already running. */
-    if (!pThis->fThrottleActive && ps2mHaveEvents(pThis))
-    {
-        ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->evtQ, true);
-        KBCUpdateInterrupts(pThis->pParent);
-        pThis->fThrottleActive = true;
-        TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
-    }
-
-    return rc;
-}
-
-/* -=-=-=-=-=- Mouse: IMousePort  -=-=-=-=-=- */
-
-/**
- * @interface_method_impl{PDMIMOUSEPORT,pfnPutEvent}
- */
-static DECLCALLBACK(int) ps2mPutEvent(PPDMIMOUSEPORT pInterface, int32_t dx, int32_t dy,
-                                      int32_t dz, int32_t dw, uint32_t fButtons)
-{
-    PPS2M       pThis = RT_FROM_MEMBER(pInterface, PS2M, Mouse.IPort);
-    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
-    AssertReleaseRC(rc);
-
-    LogRelFlowFunc(("dX=%d dY=%d dZ=%d dW=%d buttons=%02X\n", dx, dy, dz, dw, fButtons));
-    /* NB: The PS/2 Y axis direction is inverted relative to ours. */
-    ps2mPutEventWorker(pThis, dx, -dy, dz, dw, fButtons);
-
-    PDMCritSectLeave(pThis->pCritSectR3);
-    return VINF_SUCCESS;
-}
-
-/**
- * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventAbs}
- */
-static DECLCALLBACK(int) ps2mPutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t x, uint32_t y,
-                                         int32_t dz, int32_t dw, uint32_t fButtons)
-{
-    AssertFailedReturn(VERR_NOT_SUPPORTED);
-    NOREF(pInterface); NOREF(x); NOREF(y); NOREF(dz); NOREF(dw); NOREF(fButtons);
-}
-
-/**
- * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventMultiTouch}
- */
-static DECLCALLBACK(int) ps2mPutEventMT(PPDMIMOUSEPORT pInterface, uint8_t cContacts,
-                                        const uint64_t *pau64Contacts, uint32_t u32ScanTime)
-{
-    AssertFailedReturn(VERR_NOT_SUPPORTED);
-    NOREF(pInterface); NOREF(cContacts); NOREF(pau64Contacts); NOREF(u32ScanTime);
-}
-
-
-
-/**
- * Attach command.
- *
- * This is called to let the device attach to a driver for a
- * specified LUN.
- *
- * This is like plugging in the mouse after turning on the
- * system.
- *
- * @returns VBox status code.
- * @param   pThis       The PS/2 auxiliary device instance data.
- * @param   pDevIns     The device instance.
- * @param   iLUN        The logical unit which is being detached.
- * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
- */
-int PS2MAttach(PPS2M pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
-{
-    int         rc;
-
-    /* The LUN must be 1, i.e. mouse. */
-    Assert(iLUN == 1);
-    AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
-                    ("PS/2 mouse does not support hotplugging\n"),
-                    VERR_INVALID_PARAMETER);
-
-    LogFlowFunc(("iLUN=%d\n", iLUN));
-
-    rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Mouse.IBase, &pThis->Mouse.pDrvBase, "Mouse Port");
-    if (RT_SUCCESS(rc))
-    {
-        pThis->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
-        if (!pThis->Mouse.pDrv)
-        {
-            AssertLogRelMsgFailed(("LUN #1 doesn't have a mouse interface! rc=%Rrc\n", rc));
-            rc = VERR_PDM_MISSING_INTERFACE;
-        }
-    }
-    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
-    {
-        Log(("%s/%d: warning: no driver attached to LUN #1!\n", pDevIns->pReg->szName, pDevIns->iInstance));
-        rc = VINF_SUCCESS;
-    }
-    else
-        AssertLogRelMsgFailed(("Failed to attach LUN #1! rc=%Rrc\n", rc));
-
-    return rc;
-}
-
-void PS2MSaveState(PPS2M pThis, PSSMHANDLE pSSM)
-{
-    LogFlowFunc(("Saving PS2M state\n"));
-
-    /* Save the core auxiliary device state. */
-    SSMR3PutU8(pSSM, pThis->u8State);
-    SSMR3PutU8(pSSM, pThis->u8SampleRate);
-    SSMR3PutU8(pSSM, pThis->u8Resolution);
-    SSMR3PutU8(pSSM, pThis->u8CurrCmd);
-    SSMR3PutU8(pSSM, pThis->enmMode);
-    SSMR3PutU8(pSSM, pThis->enmProtocol);
-    SSMR3PutU8(pSSM, pThis->enmKnockState);
-
-    /* Save the command and event queues. */
-    ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
-    ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->evtQ);
-
-    /* Save the command delay timer. Note that the rate throttling
-     * timer is *not* saved.
-     */
-    TMR3TimerSave(pThis->CTX_SUFF(pDelayTimer), pSSM);
-}
-
-int PS2MLoadState(PPS2M pThis, PSSMHANDLE pSSM, uint32_t uVersion)
-{
-    uint8_t     u8;
-    int         rc;
-
-    NOREF(uVersion);
-    LogFlowFunc(("Loading PS2M state version %u\n", uVersion));
-
-    /* Load the basic auxiliary device state. */
-    SSMR3GetU8(pSSM, &pThis->u8State);
-    SSMR3GetU8(pSSM, &pThis->u8SampleRate);
-    SSMR3GetU8(pSSM, &pThis->u8Resolution);
-    SSMR3GetU8(pSSM, &pThis->u8CurrCmd);
-    SSMR3GetU8(pSSM, &u8);
-    pThis->enmMode       = (PS2M_MODE)u8;
-    SSMR3GetU8(pSSM, &u8);
-    pThis->enmProtocol   = (PS2M_PROTO)u8;
-    SSMR3GetU8(pSSM, &u8);
-    pThis->enmKnockState = (PS2M_KNOCK_STATE)u8;
-
-    /* Load the command and event queues. */
-    rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
-    AssertRCReturn(rc, rc);
-    rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->evtQ);
-    AssertRCReturn(rc, rc);
-
-    /* Load the command delay timer, just in case. */
-    rc = TMR3TimerLoad(pThis->CTX_SUFF(pDelayTimer), pSSM);
-    AssertRCReturn(rc, rc);
-
-    /* Recalculate the throttling delay. */
-    ps2mSetRate(pThis, pThis->u8SampleRate);
-
-    ps2mSetDriverState(pThis, !!(pThis->u8State & AUX_STATE_ENABLED));
-
-    return rc;
-}
-
-void PS2MFixupState(PPS2M pThis, uint8_t u8State, uint8_t u8Rate, uint8_t u8Proto)
-{
-    LogFlowFunc(("Fixing up old PS2M state version\n"));
-
-    /* Load the basic auxiliary device state. */
-    pThis->u8State      = u8State;
-    pThis->u8SampleRate = u8Rate ? u8Rate : 40; /* In case it wasn't saved right. */
-    pThis->enmProtocol  = (PS2M_PROTO)u8Proto;
-
-    /* Recalculate the throttling delay. */
-    ps2mSetRate(pThis, pThis->u8SampleRate);
-
-    ps2mSetDriverState(pThis, !!(pThis->u8State & AUX_STATE_ENABLED));
-}
-
-void PS2MReset(PPS2M pThis)
-{
-    LogFlowFunc(("Resetting PS2M\n"));
-
-    pThis->u8CurrCmd         = 0;
-
-    /* Clear the queues. */
-    ps2kClearQueue((GeneriQ *)&pThis->cmdQ);
-    ps2mSetDefaults(pThis);     /* Also clears event queue. */
-}
-
-void PS2MRelocate(PPS2M pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns)
-{
-    RT_NOREF2(pDevIns, offDelta);
-    LogFlowFunc(("Relocating PS2M\n"));
-    pThis->pDelayTimerRC    = TMTimerRCPtr(pThis->pDelayTimerR3);
-    pThis->pThrottleTimerRC = TMTimerRCPtr(pThis->pThrottleTimerR3);
-}
-
-int PS2MConstruct(PPS2M pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance)
-{
-    RT_NOREF1(iInstance);
-
-    LogFlowFunc(("iInstance=%d\n", iInstance));
-
-#ifdef RT_STRICT
-    ps2mTestAccumulation();
-#endif
-
-    pThis->pParent = pParent;
-
-    /* Initialize the queues. */
-    pThis->evtQ.cSize = AUX_EVT_QUEUE_SIZE;
-    pThis->cmdQ.cSize = AUX_CMD_QUEUE_SIZE;
-
-    pThis->Mouse.IBase.pfnQueryInterface     = ps2mQueryInterface;
-    pThis->Mouse.IPort.pfnPutEvent           = ps2mPutEvent;
-    pThis->Mouse.IPort.pfnPutEventAbs        = ps2mPutEventAbs;
-    pThis->Mouse.IPort.pfnPutEventMultiTouch = ps2mPutEventMT;
-
-    /*
-     * Initialize the critical section pointer(s).
-     */
-    pThis->pCritSectR3 = pDevIns->pCritSectRoR3;
-
-    /*
-     * Create the input rate throttling timer. Does not use virtual time!
-     */
-    PTMTIMER pTimer;
-    int rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, ps2mThrottleTimer, pThis,
-                                    TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2M Throttle Timer", &pTimer);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    pThis->pThrottleTimerR3 = pTimer;
-    pThis->pThrottleTimerR0 = TMTimerR0Ptr(pTimer);
-    pThis->pThrottleTimerRC = TMTimerRCPtr(pTimer);
-
-    /*
-     * Create the command delay timer.
-     */
-    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ps2mDelayTimer, pThis,
-                                TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2M Delay Timer", &pTimer);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    pThis->pDelayTimerR3 = pTimer;
-    pThis->pDelayTimerR0 = TMTimerR0Ptr(pTimer);
-    pThis->pDelayTimerRC = TMTimerRCPtr(pTimer);
-
-    /*
-     * Register debugger info callbacks.
-     */
-    PDMDevHlpDBGFInfoRegister(pDevIns, "ps2m", "Display PS/2 mouse state.", ps2mInfoState);
-
-    /// @todo Where should we do this?
-    ps2mSetDriverState(pThis, true);
-    pThis->u8State = 0;
-    pThis->enmMode = AUX_MODE_STD;
-
-    return rc;
-}
-
-#endif
-
-#if defined(RT_STRICT) && defined(IN_RING3)
-/* -=-=-=-=-=- Test code  -=-=-=-=-=- */
-
-/** Test the event accumulation mechanism which we use to delay events going
- * to the guest to one per 10ms (the default PS/2 mouse event rate).  This
- * test depends on ps2mPutEventWorker() not touching the timer if
- * This.fThrottleActive is true. */
-/** @todo if we add any more tests it might be worth using a table of test
- * operations and checks. */
-static void ps2mTestAccumulation(void)
-{
-    PS2M This;
-    unsigned i;
-    int rc;
-    uint8_t b;
-
-    RT_ZERO(This);
-    This.evtQ.cSize = AUX_EVT_QUEUE_SIZE;
-    This.u8State = AUX_STATE_ENABLED;
-    This.fThrottleActive = true;
-    /* Certain Windows touch pad drivers report a double tap as a press, then
-     * a release-press-release all within a single 10ms interval.  Simulate
-     * this to check that it is handled right. */
-    ps2mPutEventWorker(&This, 0, 0, 0, 0, 1);
-    if (ps2mHaveEvents(&This))
-        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
-    ps2mPutEventWorker(&This, 0, 0, 0, 0, 0);
-    if (ps2mHaveEvents(&This))
-        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
-    ps2mPutEventWorker(&This, 0, 0, 0, 0, 1);
-    ps2mPutEventWorker(&This, 0, 0, 0, 0, 0);
-    if (ps2mHaveEvents(&This))
-        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
-    if (ps2mHaveEvents(&This))
-        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
-    for (i = 0; i < 12; ++i)
-    {
-        const uint8_t abExpected[] = { 9, 0, 0, 8, 0, 0, 9, 0, 0, 8, 0, 0};
-
-        rc = PS2MByteFromAux(&This, &b);
-        AssertRCSuccess(rc);
-        Assert(b == abExpected[i]);
-    }
-    rc = PS2MByteFromAux(&This, &b);
-    Assert(rc != VINF_SUCCESS);
-    /* Button hold down during mouse drags was broken at some point during
-     * testing fixes for the previous issue.  Test that that works. */
-    ps2mPutEventWorker(&This, 0, 0, 0, 0, 1);
-    if (ps2mHaveEvents(&This))
-        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
-    if (ps2mHaveEvents(&This))
-        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
-    for (i = 0; i < 3; ++i)
-    {
-        const uint8_t abExpected[] = { 9, 0, 0 };
-
-        rc = PS2MByteFromAux(&This, &b);
-        AssertRCSuccess(rc);
-        Assert(b == abExpected[i]);
-    }
-    rc = PS2MByteFromAux(&This, &b);
-    Assert(rc != VINF_SUCCESS);
-}
-#endif /* RT_STRICT && IN_RING3 */
-
-#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
