summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Input/DevPS2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Input/DevPS2.cpp')
-rw-r--r--src/VBox/Devices/Input/DevPS2.cpp1264
1 files changed, 1264 insertions, 0 deletions
diff --git a/src/VBox/Devices/Input/DevPS2.cpp b/src/VBox/Devices/Input/DevPS2.cpp
new file mode 100644
index 00000000..5e9c1746
--- /dev/null
+++ b/src/VBox/Devices/Input/DevPS2.cpp
@@ -0,0 +1,1264 @@
+/* $Id: DevPS2.cpp $ */
+/** @file
+ * DevPS2 - PS/2 keyboard & mouse controller device.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ * --------------------------------------------------------------------
+ *
+ * This code is based on:
+ *
+ * QEMU PC keyboard emulation (revision 1.12)
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_KBD
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/AssertGuest.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+
+#include "VBoxDD.h"
+#include "DevPS2.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* Do not remove this (unless eliminating the corresponding ifdefs), it will
+ * cause instant triple faults when booting Windows VMs. */
+#define TARGET_I386
+
+#define PCKBD_SAVED_STATE_VERSION 8
+
+/* debug PC keyboard */
+#define DEBUG_KBD
+
+/* debug PC keyboard : only mouse */
+#define DEBUG_MOUSE
+
+/* Keyboard Controller Commands */
+#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
+#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
+#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
+#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
+#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
+#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
+#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
+#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
+#define KBD_CCMD_WRITE_OBUF 0xD2
+#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
+ initiated by the auxiliary device */
+#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
+#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
+#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
+#define KBD_CCMD_READ_TSTINP 0xE0 /* Read test inputs T0, T1 */
+#define KBD_CCMD_RESET_ALT 0xF0
+#define KBD_CCMD_RESET 0xFE
+
+/* Status Register Bits */
+#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
+#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
+#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
+#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
+#define KBD_STAT_PERR 0x80 /* Parity error */
+
+/* Controller Mode Register Bits */
+#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
+#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS 0x04 /* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
+#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
+#define KBD_MODE_RFU 0x80
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** 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;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/* 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 const g_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.
+ */
+static int32_t kbcXlateAT2PC(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 = g_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;
+}
+
+
+/** update irq and KBD_STAT_[MOUSE_]OBF */
+static void kbd_update_irq(PPDMDEVINS pDevIns, PKBDSTATE s)
+{
+ int irq12_level, irq1_level;
+ uint8_t val;
+
+ irq1_level = 0;
+ irq12_level = 0;
+
+ /* Determine new OBF state, but only if OBF is clear. If OBF was already
+ * set, we cannot risk changing the event type after an ISR potentially
+ * started executing! Only kbd_read_data() clears the OBF bits.
+ */
+ if (!(s->status & KBD_STAT_OBF)) {
+ s->status &= ~KBD_STAT_MOUSE_OBF;
+ /* Keyboard data has priority if both kbd and aux data is available. */
+ if (!(s->mode & KBD_MODE_DISABLE_KBD) && PS2KByteFromKbd(pDevIns, &s->Kbd, &val) == VINF_SUCCESS)
+ {
+ bool fHaveData = true;
+
+ /* If scancode translation is on (it usually is), there's more work to do. */
+ if (s->translate)
+ {
+ uint8_t xlated_val;
+
+ s->xlat_state = kbcXlateAT2PC(s->xlat_state, val, &xlated_val);
+ val = xlated_val;
+
+ /* If the translation state is XS_BREAK, there's nothing to report
+ * and we keep going until the state changes or there's no more data.
+ */
+ while (s->xlat_state == XS_BREAK && PS2KByteFromKbd(pDevIns, &s->Kbd, &val) == VINF_SUCCESS)
+ {
+ s->xlat_state = kbcXlateAT2PC(s->xlat_state, val, &xlated_val);
+ val = xlated_val;
+ }
+ /* This can happen if the last byte in the queue is F0... */
+ if (s->xlat_state == XS_BREAK)
+ fHaveData = false;
+ }
+ if (fHaveData)
+ {
+ s->dbbout = val;
+ s->status |= KBD_STAT_OBF;
+ }
+ }
+ else if (!(s->mode & KBD_MODE_DISABLE_MOUSE) && PS2MByteFromAux(&s->Aux, &val) == VINF_SUCCESS)
+ {
+ s->dbbout = val;
+ s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
+ }
+ }
+ /* Determine new IRQ state. */
+ if (s->status & KBD_STAT_OBF) {
+ if (s->status & KBD_STAT_MOUSE_OBF)
+ {
+ if (s->mode & KBD_MODE_MOUSE_INT)
+ irq12_level = 1;
+ }
+ else
+ { /* KBD_STAT_OBF set but KBD_STAT_MOUSE_OBF isn't. */
+ if (s->mode & KBD_MODE_KBD_INT)
+ irq1_level = 1;
+ }
+ }
+ PDMDevHlpISASetIrq(pDevIns, 1, irq1_level);
+ PDMDevHlpISASetIrq(pDevIns, 12, irq12_level);
+}
+
+void KBCUpdateInterrupts(PPDMDEVINS pDevIns)
+{
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ kbd_update_irq(pDevIns, pThis);
+}
+
+static void kbc_dbb_out(PPDMDEVINS pDevIns, PKBDSTATE s, uint8_t val)
+{
+ s->dbbout = val;
+ /* Set the OBF and raise IRQ. */
+ s->status |= KBD_STAT_OBF;
+ if (s->mode & KBD_MODE_KBD_INT)
+ PDMDevHlpISASetIrq(pDevIns, 1, 1);
+}
+
+static void kbc_dbb_out_aux(PPDMDEVINS pDevIns, PKBDSTATE s, uint8_t val)
+{
+ s->dbbout = val;
+ /* Set the aux OBF and raise IRQ. */
+ s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
+ if (s->mode & KBD_MODE_MOUSE_INT)
+ PDMDevHlpISASetIrq(pDevIns, 12, PDM_IRQ_LEVEL_HIGH);
+}
+
+static VBOXSTRICTRC kbd_write_command(PPDMDEVINS pDevIns, PKBDSTATE s, uint32_t val)
+{
+#ifdef DEBUG_KBD
+ Log(("kbd: write cmd=0x%02x\n", val));
+#endif
+ switch(val) {
+ case KBD_CCMD_READ_MODE:
+ kbc_dbb_out(pDevIns, s, s->mode);
+ break;
+ case KBD_CCMD_WRITE_MODE:
+ case KBD_CCMD_WRITE_OBUF:
+ case KBD_CCMD_WRITE_AUX_OBUF:
+ case KBD_CCMD_WRITE_MOUSE:
+ case KBD_CCMD_WRITE_OUTPORT:
+ s->write_cmd = val;
+ break;
+ case KBD_CCMD_MOUSE_DISABLE:
+ s->mode |= KBD_MODE_DISABLE_MOUSE;
+ PS2MLineDisable(&s->Aux);
+ break;
+ case KBD_CCMD_MOUSE_ENABLE:
+ PS2MLineEnable(&s->Aux);
+ s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+ /* Check for queued input. */
+ /// @todo Can there actually be any?
+ kbd_update_irq(pDevIns, s);
+ break;
+ case KBD_CCMD_TEST_MOUSE:
+ kbc_dbb_out(pDevIns, s, 0x00);
+ break;
+ case KBD_CCMD_SELF_TEST:
+ /* Enable the A20 line - that is the power-on state(!). */
+# ifndef IN_RING3
+ if (!PDMDevHlpA20IsEnabled(pDevIns))
+ return VINF_IOM_R3_IOPORT_WRITE;
+# else /* IN_RING3 */
+ PDMDevHlpA20Set(pDevIns, true);
+# endif /* IN_RING3 */
+ s->status |= KBD_STAT_SELFTEST;
+ s->mode |= KBD_MODE_DISABLE_KBD;
+ kbc_dbb_out(pDevIns, s, 0x55);
+ break;
+ case KBD_CCMD_KBD_TEST:
+ kbc_dbb_out(pDevIns, s, 0x00);
+ break;
+ case KBD_CCMD_KBD_DISABLE:
+ s->mode |= KBD_MODE_DISABLE_KBD;
+ break;
+ case KBD_CCMD_KBD_ENABLE:
+ s->mode &= ~KBD_MODE_DISABLE_KBD;
+ /* Check for queued input. */
+ kbd_update_irq(pDevIns, s);
+ break;
+ case KBD_CCMD_READ_INPORT:
+ kbc_dbb_out(pDevIns, s, 0xBF);
+ break;
+ case KBD_CCMD_READ_OUTPORT:
+ /* XXX: check that */
+#ifdef TARGET_I386
+ val = 0x01 | (PDMDevHlpA20IsEnabled(pDevIns) << 1);
+#else
+ val = 0x01;
+#endif
+ if (s->status & KBD_STAT_OBF)
+ val |= 0x10;
+ if (s->status & KBD_STAT_MOUSE_OBF)
+ val |= 0x20;
+ kbc_dbb_out(pDevIns, s, val);
+ break;
+#ifdef TARGET_I386
+ case KBD_CCMD_ENABLE_A20:
+# ifndef IN_RING3
+ if (!PDMDevHlpA20IsEnabled(pDevIns))
+ return VINF_IOM_R3_IOPORT_WRITE;
+# else /* IN_RING3 */
+ PDMDevHlpA20Set(pDevIns, true);
+# endif /* IN_RING3 */
+ break;
+ case KBD_CCMD_DISABLE_A20:
+# ifndef IN_RING3
+ if (PDMDevHlpA20IsEnabled(pDevIns))
+ return VINF_IOM_R3_IOPORT_WRITE;
+# else /* IN_RING3 */
+ PDMDevHlpA20Set(pDevIns, false);
+# endif /* IN_RING3 */
+ break;
+#endif
+ case KBD_CCMD_READ_TSTINP:
+ /* Keyboard clock line is zero IFF keyboard is disabled */
+ val = (s->mode & KBD_MODE_DISABLE_KBD) ? 0 : 1;
+ kbc_dbb_out(pDevIns, s, val);
+ break;
+ case KBD_CCMD_RESET:
+ case KBD_CCMD_RESET_ALT:
+#ifndef IN_RING3
+ return VINF_IOM_R3_IOPORT_WRITE;
+#else /* IN_RING3 */
+ LogRel(("Reset initiated by keyboard controller\n"));
+ return PDMDevHlpVMReset(pDevIns, PDMVMRESET_F_KBD);
+#endif /* IN_RING3 */
+ case 0xff:
+ /* ignore that - I don't know what is its use */
+ break;
+ /* Make OS/2 happy. */
+ /* The 8042 RAM is readable using commands 0x20 thru 0x3f, and writable
+ by 0x60 thru 0x7f. Now days only the first byte, the mode, is used.
+ We'll ignore the writes (0x61..7f) and return 0 for all the reads
+ just to make some OS/2 debug stuff a bit happier. */
+ case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
+ case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
+ case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+ kbc_dbb_out(pDevIns, s, 0);
+ Log(("kbd: reading non-standard RAM addr %#x\n", val & 0x1f));
+ break;
+ default:
+ Log(("kbd: unsupported keyboard cmd=0x%02x\n", val));
+ break;
+ }
+ return VINF_SUCCESS;
+}
+
+static uint32_t kbd_read_data(PPDMDEVINS pDevIns, PKBDSTATE s)
+{
+ uint32_t val;
+
+ /* Return the current DBB contents. */
+ val = s->dbbout;
+
+ /* Reading the DBB deasserts IRQs... */
+ if (s->status & KBD_STAT_MOUSE_OBF)
+ PDMDevHlpISASetIrq(pDevIns, 12, 0);
+ else
+ PDMDevHlpISASetIrq(pDevIns, 1, 0);
+ /* ...and clears the OBF bits. */
+ s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
+
+ /* Check if more data is available. */
+ kbd_update_irq(pDevIns, s);
+#ifdef DEBUG_KBD
+ Log(("kbd: read data=0x%02x\n", val));
+#endif
+ return val;
+}
+
+static VBOXSTRICTRC kbd_write_data(PPDMDEVINS pDevIns, PKBDSTATE s, uint32_t val)
+{
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+
+#ifdef DEBUG_KBD
+ Log(("kbd: write data=0x%02x\n", val));
+#endif
+
+ switch(s->write_cmd) {
+ case 0:
+ /* Automatically enables keyboard interface. */
+ s->mode &= ~KBD_MODE_DISABLE_KBD;
+ rc = PS2KByteToKbd(pDevIns, &s->Kbd, val);
+ if (rc == VINF_SUCCESS)
+ kbd_update_irq(pDevIns, s);
+ break;
+ case KBD_CCMD_WRITE_MODE:
+ s->mode = val;
+ s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
+ kbd_update_irq(pDevIns, s);
+ break;
+ case KBD_CCMD_WRITE_OBUF:
+ kbc_dbb_out(pDevIns, s, val);
+ break;
+ case KBD_CCMD_WRITE_AUX_OBUF:
+ kbc_dbb_out_aux(pDevIns, s, val);
+ break;
+ case KBD_CCMD_WRITE_OUTPORT:
+#ifdef TARGET_I386
+# ifndef IN_RING3
+ if (PDMDevHlpA20IsEnabled(pDevIns) != !!(val & 2))
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+# else /* IN_RING3 */
+ PDMDevHlpA20Set(pDevIns, !!(val & 2));
+# endif /* IN_RING3 */
+#endif
+ if (!(val & 1)) {
+# ifndef IN_RING3
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+# else
+ rc = PDMDevHlpVMReset(pDevIns, PDMVMRESET_F_KBD);
+# endif
+ }
+ break;
+ case KBD_CCMD_WRITE_MOUSE:
+ /* Automatically enables aux interface. */
+ if (s->mode & KBD_MODE_DISABLE_MOUSE)
+ {
+ PS2MLineEnable(&s->Aux);
+ s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+ }
+ rc = PS2MByteToAux(pDevIns, &s->Aux, val);
+ if (rc == VINF_SUCCESS)
+ kbd_update_irq(pDevIns, s);
+ break;
+ default:
+ break;
+ }
+ if (rc != VINF_IOM_R3_IOPORT_WRITE)
+ s->write_cmd = 0;
+ return rc;
+}
+
+#ifdef IN_RING3
+
+static int kbd_load(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PKBDSTATE s, PKBDSTATER3 pThisCC, uint32_t version_id)
+{
+ uint32_t u32, i;
+ uint8_t u8Dummy;
+ uint32_t u32Dummy;
+ int rc;
+
+#if 0
+ /** @todo enable this and remove the "if (version_id == 4)" code at some
+ * later time */
+ /* Version 4 was never created by any publicly released version of VBox */
+ AssertReturn(version_id != 4, VERR_NOT_SUPPORTED);
+#endif
+ if (version_id < 2 || version_id > PCKBD_SAVED_STATE_VERSION)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+ pHlp->pfnSSMGetU8(pSSM, &s->write_cmd);
+ pHlp->pfnSSMGetU8(pSSM, &s->status);
+ pHlp->pfnSSMGetU8(pSSM, &s->mode);
+ if (version_id <= 5)
+ {
+ pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
+ pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
+ }
+ else
+ {
+ pHlp->pfnSSMGetU8(pSSM, &s->dbbout);
+ }
+ if (version_id <= 7)
+ {
+ int32_t i32Dummy;
+ uint8_t u8State;
+ uint8_t u8Rate;
+ uint8_t u8Proto;
+
+ pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
+ pHlp->pfnSSMGetU8(pSSM, &u8State);
+ pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
+ pHlp->pfnSSMGetU8(pSSM, &u8Rate);
+ pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
+ pHlp->pfnSSMGetU8(pSSM, &u8Proto);
+ pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
+ pHlp->pfnSSMGetS32(pSSM, &i32Dummy);
+ pHlp->pfnSSMGetS32(pSSM, &i32Dummy);
+ pHlp->pfnSSMGetS32(pSSM, &i32Dummy);
+ if (version_id > 2)
+ {
+ pHlp->pfnSSMGetS32(pSSM, &i32Dummy);
+ pHlp->pfnSSMGetS32(pSSM, &i32Dummy);
+ }
+ rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
+ if (version_id == 4)
+ {
+ pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
+ }
+ if (version_id > 3)
+ rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
+ if (version_id == 4)
+ rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
+ AssertLogRelRCReturn(rc, rc);
+
+ PS2MR3FixupState(&s->Aux, &pThisCC->Aux, u8State, u8Rate, u8Proto);
+ }
+
+ /* Determine the translation state. */
+ s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
+
+ /*
+ * Load the queues
+ */
+ if (version_id <= 5)
+ {
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ for (i = 0; i < u32; i++)
+ {
+ rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ Log(("kbd_load: %d keyboard queue items discarded from old saved state\n", u32));
+ }
+
+ if (version_id <= 7)
+ {
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ for (i = 0; i < u32; i++)
+ {
+ rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ Log(("kbd_load: %d mouse event queue items discarded from old saved state\n", u32));
+
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ for (i = 0; i < u32; i++)
+ {
+ rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ Log(("kbd_load: %d mouse command queue items discarded from old saved state\n", u32));
+ }
+
+ /* terminator */
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (u32 != ~0U)
+ {
+ AssertMsgFailed(("u32=%#x\n", u32));
+ return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ }
+ return 0;
+}
+
+#endif /* IN_RING3 */
+
+
+/* VirtualBox code start */
+
+/* -=-=-=-=-=- wrappers -=-=-=-=-=- */
+
+/** Fluff bits indexed by size (1,2,4). */
+static uint32_t const g_afFluff[5] =
+{
+ /* [0] = */ 0,
+ /* [1] = */ 0,
+ /* [2] = */ UINT32_C(0xff00),
+ /* [3] = */ 0,
+ /* [4] = */ UINT32_C(0xffffff00) /* Crazy Apple (Darwin 6.0.2 and earlier). */
+};
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWIN,
+ * Port I/O Handler for keyboard data IN operations.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
+{
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ RT_NOREF(pvUser, offPort);
+ Assert(offPort == 0);
+ Assert(cb == 1 || cb == 2 || cb == 4);
+
+ *pu32 = kbd_read_data(pDevIns, pThis) | g_afFluff[cb];
+ Log2(("kbdIOPortDataRead: cb=%u *pu32=%#x\n", cb, *pu32));
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWOUT,
+ * Port I/O Handler for keyboard data OUT operations.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
+{
+ RT_NOREF(offPort, pvUser);
+ Assert(offPort == 0);
+
+ if (cb == 1 || cb == 2)
+ {
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ VBOXSTRICTRC rc = kbd_write_data(pDevIns, pThis, (uint8_t)u32);
+ Log2(("kbdIOPortDataWrite: Port=0x60+%x cb=%d u32=%#x rc=%Rrc\n", offPort, cb, u32, VBOXSTRICTRC_VAL(rc)));
+ return rc;
+ }
+ Assert(cb == 4);
+ ASSERT_GUEST_MSG_FAILED(("Port=0x60+%x cb=%d\n", offPort, cb));
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWIN,
+ * Port I/O Handler for keyboard status IN operations.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
+{
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ RT_NOREF(offPort, pvUser);
+ Assert(offPort == 0);
+ Assert(cb == 1 || cb == 2 || cb == 4);
+
+ *pu32 = pThis->status | g_afFluff[cb];
+ Log2(("kbdIOPortStatusRead: cb=%u -> *pu32=%#x\n", cb, *pu32));
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWIN,
+ * Port I/O Handler for keyboard command OUT operations.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
+{
+ RT_NOREF(offPort, pvUser);
+ Assert(offPort == 0);
+
+ if (cb == 1 || cb == 2)
+ {
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ VBOXSTRICTRC rc = kbd_write_command(pDevIns, pThis, (uint8_t)u32);
+ Log2(("kbdIOPortCommandWrite: cb=%d u32=%#x rc=%Rrc\n", cb, u32, VBOXSTRICTRC_VAL(rc)));
+ return rc;
+ }
+ Assert(cb == 4);
+ ASSERT_GUEST_MSG_FAILED(("offPort=0x64+%x cb=%d\n", offPort, cb));
+ return VINF_SUCCESS;
+}
+
+/**
+ * Clear a queue.
+ *
+ * @param pQHdr The queue header.
+ * @param cElements The queue size.
+ */
+void PS2CmnClearQueue(PPS2QHDR pQHdr, size_t cElements)
+{
+ Assert(cElements > 0);
+ LogFlowFunc(("Clearing %s queue %p\n", R3STRING(pQHdr->pszDescR3), pQHdr));
+ pQHdr->wpos = pQHdr->rpos = pQHdr->rpos % cElements;
+ pQHdr->cUsed = 0;
+}
+
+
+/**
+ * Add a byte to a queue.
+ *
+ * @param pQHdr The queue header.
+ * @param cElements The queue size.
+ * @param pbElements The queue element array.
+ * @param bValue The byte to store.
+ */
+void PS2CmnInsertQueue(PPS2QHDR pQHdr, size_t cElements, uint8_t *pbElements, uint8_t bValue)
+{
+ Assert(cElements > 0);
+
+ /* Check that the queue is not full. */
+ uint32_t cUsed = pQHdr->cUsed;
+ if (cUsed < cElements)
+ {
+ /* Insert data and update circular buffer write position. */
+ uint32_t wpos = pQHdr->wpos % cElements;
+ pbElements[wpos] = bValue;
+
+ wpos += 1;
+ if (wpos < cElements)
+ pQHdr->wpos = wpos;
+ else
+ pQHdr->wpos = 0; /* Roll over. */
+ pQHdr->cUsed = cUsed + 1;
+
+ LogRelFlowFunc(("inserted %#04x into %s queue %p\n", bValue, R3STRING(pQHdr->pszDescR3), pQHdr));
+ }
+ else
+ {
+ Assert(cUsed == cElements);
+ LogRelFlowFunc(("%s queue %p full (%zu entries)\n", R3STRING(pQHdr->pszDescR3), pQHdr, cElements));
+ }
+}
+
+/**
+ * Retrieve a byte from a queue.
+ *
+ * @param pQHdr The queue header.
+ * @param cElements The queue size.
+ * @param pbElements The queue element array.
+ * @param pbValue Where to return the byte on success.
+ *
+ * @retval VINF_TRY_AGAIN if queue is empty,
+ * @retval VINF_SUCCESS if a byte was read.
+ */
+int PS2CmnRemoveQueue(PPS2QHDR pQHdr, size_t cElements, uint8_t const *pbElements, uint8_t *pbValue)
+{
+ int rc;
+
+ Assert(cElements > 0);
+ Assert(pbValue);
+
+ uint32_t cUsed = (uint32_t)RT_MIN(pQHdr->cUsed, cElements);
+ if (cUsed > 0)
+ {
+ uint32_t rpos = pQHdr->rpos % cElements;
+ *pbValue = pbElements[rpos];
+
+ rpos += 1;
+ if (rpos < cElements)
+ pQHdr->rpos = rpos;
+ else
+ pQHdr->rpos = 0; /* Roll over. */
+ pQHdr->cUsed = cUsed - 1;
+
+ LogFlowFunc(("removed 0x%02X from %s queue %p\n", *pbValue, R3STRING(pQHdr->pszDescR3), pQHdr));
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogFlowFunc(("%s queue %p empty\n", R3STRING(pQHdr->pszDescR3), pQHdr));
+ rc = VINF_TRY_AGAIN;
+ }
+ return rc;
+}
+
+#ifdef IN_RING3
+
+/**
+ * Save a queue state.
+ *
+ * @param pHlp The device helpers.
+ * @param pSSM SSM handle to write the state to.
+ * @param pQHdr The queue header.
+ * @param cElements The queue size.
+ * @param pbElements The queue element array.
+ */
+void PS2CmnR3SaveQueue(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PPS2QHDR pQHdr, size_t cElements, uint8_t const *pbElements)
+{
+ uint32_t cItems = (uint32_t)RT_MIN(pQHdr->cUsed, cElements);
+
+ /* Only save the number of items. Note that the read/write
+ * positions aren't saved as they will be rebuilt on load.
+ */
+ pHlp->pfnSSMPutU32(pSSM, cItems);
+
+ LogFlow(("Storing %u items from %s queue %p\n", cItems, pQHdr->pszDescR3, pQHdr));
+
+ /* Save queue data - only the bytes actually used (typically zero). */
+ for (uint32_t i = pQHdr->rpos % cElements; cItems-- > 0; i = (i + 1) % cElements)
+ pHlp->pfnSSMPutU8(pSSM, pbElements[i]);
+}
+
+/**
+ * Load a queue state.
+ *
+ * @param pHlp The device helpers.
+ * @param pSSM SSM handle to read the state from.
+ * @param pQHdr The queue header.
+ * @param cElements The queue size.
+ * @param pbElements The queue element array.
+ *
+ * @returns VBox status/error code.
+ */
+int PS2CmnR3LoadQueue(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PPS2QHDR pQHdr, size_t cElements, uint8_t *pbElements)
+{
+ /* On load, always put the read pointer at zero. */
+ uint32_t cUsed;
+ int rc = pHlp->pfnSSMGetU32(pSSM, &cUsed);
+ AssertRCReturn(rc, rc);
+
+ LogFlow(("Loading %u items to %s queue %p\n", cUsed, pQHdr->pszDescR3, pQHdr));
+
+ AssertMsgReturn(cUsed <= cElements, ("Saved size=%u, actual=%zu\n", cUsed, cElements),
+ VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+
+ /* Recalculate queue positions and load data in one go. */
+ pQHdr->rpos = 0;
+ pQHdr->wpos = cUsed;
+ pQHdr->cUsed = cUsed;
+ return pHlp->pfnSSMGetMem(pSSM, pbElements, cUsed);
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC, Saves a state of the keyboard device.}
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ * @param pSSM The handle to save the state to.
+ */
+static DECLCALLBACK(int) kbdR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ pHlp->pfnSSMPutU8(pSSM, pThis->write_cmd);
+ pHlp->pfnSSMPutU8(pSSM, pThis->status);
+ pHlp->pfnSSMPutU8(pSSM, pThis->mode);
+ pHlp->pfnSSMPutU8(pSSM, pThis->dbbout);
+ /* terminator */
+ pHlp->pfnSSMPutU32(pSSM, UINT32_MAX);
+
+ PS2KR3SaveState(pDevIns, &pThis->Kbd, pSSM);
+ PS2MR3SaveState(pDevIns, &pThis->Aux, pSSM);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC, Loads a saved keyboard device state.}
+ */
+static DECLCALLBACK(int) kbdR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ PKBDSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PKBDSTATER3);
+ int rc;
+
+ Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
+ rc = kbd_load(pDevIns->pHlpR3, pSSM, pThis, pThisCC, uVersion);
+ AssertRCReturn(rc, rc);
+
+ if (uVersion >= 6)
+ rc = PS2KR3LoadState(pDevIns, &pThis->Kbd, pSSM, uVersion);
+ AssertRCReturn(rc, rc);
+
+ if (uVersion >= 8)
+ rc = PS2MR3LoadState(pDevIns, &pThis->Aux, &pThisCC->Aux, pSSM, uVersion);
+ AssertRCReturn(rc, rc);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADDONE, Key state fix-up after loading}
+ */
+static DECLCALLBACK(int) kbdR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ PKBDSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PKBDSTATER3);
+ RT_NOREF(pSSM);
+ if (pThis->mode & KBD_MODE_DISABLE_MOUSE)
+ PS2MLineDisable(&pThis->Aux);
+ if (pThis->mode & KBD_MODE_DISABLE_KBD)
+ PS2KLineDisable(&pThis->Kbd);
+ return PS2KR3LoadDone(pDevIns, &pThis->Kbd, &pThisCC->Kbd);
+}
+
+
+/**
+ * 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) kbdR3InfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ NOREF(pszArgs);
+
+ pHlp->pfnPrintf(pHlp, "Keyboard controller: Active command %02X, DBB out %02X, translation %s\n",
+ pThis->write_cmd, pThis->dbbout, pThis->translate ? "on" : "off");
+
+ pHlp->pfnPrintf(pHlp, "Mode: %02X ( ", pThis->mode);
+ if (pThis->mode & KBD_MODE_DISABLE_KBD)
+ pHlp->pfnPrintf(pHlp, "DISABLE_KBD ");
+ if (pThis->mode & KBD_MODE_KBD_INT)
+ pHlp->pfnPrintf(pHlp, "KBD_INT ");
+ if (pThis->mode & KBD_MODE_MOUSE_INT)
+ pHlp->pfnPrintf(pHlp, "AUX_INT ");
+ if (pThis->mode & KBD_MODE_SYS)
+ pHlp->pfnPrintf(pHlp, "SYS ");
+ if (pThis->mode & KBD_MODE_NO_KEYLOCK)
+ pHlp->pfnPrintf(pHlp, "NO_KEYLOCK ");
+ if (pThis->mode & KBD_MODE_DISABLE_KBD)
+ pHlp->pfnPrintf(pHlp, "DISABLE_KBD ");
+ if (pThis->mode & KBD_MODE_DISABLE_MOUSE)
+ pHlp->pfnPrintf(pHlp, "DISABLE_AUX ");
+ if (pThis->mode & KBD_MODE_KCC)
+ pHlp->pfnPrintf(pHlp, "KCC ");
+ if (pThis->mode & KBD_MODE_RFU)
+ pHlp->pfnPrintf(pHlp, "RFU ");
+ pHlp->pfnPrintf(pHlp, " )\n");
+
+ pHlp->pfnPrintf(pHlp, "Status: %02X ( ", pThis->status);
+ if (pThis->status & KBD_STAT_OBF)
+ pHlp->pfnPrintf(pHlp, "OBF ");
+ if (pThis->status & KBD_STAT_IBF)
+ pHlp->pfnPrintf(pHlp, "IBF ");
+ if (pThis->status & KBD_STAT_SELFTEST)
+ pHlp->pfnPrintf(pHlp, "SELFTEST ");
+ if (pThis->status & KBD_STAT_CMD)
+ pHlp->pfnPrintf(pHlp, "CMD ");
+ if (pThis->status & KBD_STAT_UNLOCKED)
+ pHlp->pfnPrintf(pHlp, "UNLOCKED ");
+ if (pThis->status & KBD_STAT_MOUSE_OBF)
+ pHlp->pfnPrintf(pHlp, "AUX_OBF ");
+ if (pThis->status & KBD_STAT_GTO)
+ pHlp->pfnPrintf(pHlp, "GTO ");
+ if (pThis->status & KBD_STAT_PERR)
+ pHlp->pfnPrintf(pHlp, "PERR ");
+ pHlp->pfnPrintf(pHlp, " )\n");
+}
+
+
+/* -=-=-=-=-=- real code -=-=-=-=-=- */
+
+/**
+ * Reset notification.
+ *
+ * @param pDevIns The device instance data.
+ */
+static DECLCALLBACK(void) kbdR3Reset(PPDMDEVINS pDevIns)
+{
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ PKBDSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PKBDSTATER3);
+
+ pThis->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
+ pThis->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
+ /* Resetting everything, keyword was not working right on NT4 reboot. */
+ pThis->write_cmd = 0;
+ pThis->translate = 0;
+
+ PS2KR3Reset(pDevIns, &pThis->Kbd, &pThisCC->Kbd);
+ PS2MR3Reset(&pThis->Aux);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnAttach}
+ *
+ * @remark The keyboard controller doesn't support this action, this is just
+ * implemented to try out the driver<->device structure.
+ */
+static DECLCALLBACK(int) kbdR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PKBDSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PKBDSTATER3);
+ int rc;
+
+ AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
+ ("PS/2 device does not support hotplugging\n"),
+ VERR_INVALID_PARAMETER);
+
+ switch (iLUN)
+ {
+ /* LUN #0: keyboard */
+ case 0:
+ rc = PS2KR3Attach(pDevIns, &pThisCC->Kbd, iLUN, fFlags);
+ break;
+
+ /* LUN #1: aux/mouse */
+ case 1:
+ rc = PS2MR3Attach(pDevIns, &pThisCC->Aux, iLUN, fFlags);
+ break;
+
+ default:
+ AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
+ return VERR_PDM_NO_SUCH_LUN;
+ }
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnDetach}
+ * @remark The keyboard controller doesn't support this action, this is just
+ * implemented to try out the driver<->device structure.
+ */
+static DECLCALLBACK(void) kbdR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+#if 0
+ /*
+ * Reset the interfaces and update the controller state.
+ */
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ switch (iLUN)
+ {
+ /* LUN #0: keyboard */
+ case 0:
+ pThisCC->Keyboard.pDrv = NULL;
+ pThisCC->Keyboard.pDrvBase = NULL;
+ break;
+
+ /* LUN #1: aux/mouse */
+ case 1:
+ pThisCC->Mouse.pDrv = NULL;
+ pThisCC->Mouse.pDrvBase = NULL;
+ break;
+
+ default:
+ AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
+ break;
+ }
+#else
+ NOREF(pDevIns); NOREF(iLUN); NOREF(fFlags);
+#endif
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
+ */
+static DECLCALLBACK(int) kbdR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+ PKBDSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PKBDSTATER3);
+ int rc;
+ RT_NOREF(iInstance);
+
+ Assert(iInstance == 0);
+
+ /*
+ * Validate and read the configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "KbdThrottleEnabled", "");
+ Log(("pckbd: fRCEnabled=%RTbool fR0Enabled=%RTbool\n", pDevIns->fRCEnabled, pDevIns->fR0Enabled));
+
+ /*
+ * Initialize the sub-components.
+ */
+ rc = PS2KR3Construct(pDevIns, &pThis->Kbd, &pThisCC->Kbd, pCfg);
+ AssertRCReturn(rc, rc);
+
+ rc = PS2MR3Construct(pDevIns, &pThis->Aux, &pThisCC->Aux);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Register I/O ports.
+ */
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x60 /*uPort*/, 1 /*cPorts*/, kbdIOPortDataWrite, kbdIOPortDataRead,
+ "PC Keyboard - Data", NULL /*pExtDescs*/, &pThis->hIoPortData);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x64 /*uPort*/, 1 /*cPorts*/, kbdIOPortCommandWrite, kbdIOPortStatusRead,
+ "PC Keyboard - Command / Status", NULL /*pExtDescs*/, &pThis->hIoPortCmdStatus);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Saved state.
+ */
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, PCKBD_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
+ NULL, NULL, NULL,
+ NULL, kbdR3SaveExec, NULL,
+ NULL, kbdR3LoadExec, kbdR3LoadDone);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Register debugger info callbacks.
+ */
+ PDMDevHlpDBGFInfoRegister(pDevIns, "ps2c", "Display keyboard/mouse controller state.", kbdR3InfoState);
+
+ /*
+ * Attach to the keyboard and mouse drivers.
+ */
+ rc = kbdR3Attach(pDevIns, 0 /* keyboard LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
+ AssertRCReturn(rc, rc);
+ rc = kbdR3Attach(pDevIns, 1 /* aux/mouse LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Initialize the device state.
+ */
+ kbdR3Reset(pDevIns);
+
+ return VINF_SUCCESS;
+}
+
+#else /* !IN_RING3 */
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) kbdRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
+
+ int rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortData, kbdIOPortDataWrite, kbdIOPortDataRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortCmdStatus, kbdIOPortCommandWrite, kbdIOPortStatusRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_RING3 */
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DevicePS2KeyboardMouse =
+{
+ /* .u32Version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "pckbd",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_INPUT,
+ /* .cMaxInstances = */ 1,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(KBDSTATE),
+ /* .cbInstanceCC = */ CTX_EXPR(sizeof(KBDSTATER3), 0, 0),
+ /* .cbInstanceRC = */ 0,
+ /* .cMaxPciDevices = */ 0,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "PS/2 Keyboard and Mouse device. Emulates both the keyboard, mouse and the keyboard controller.\n"
+ "LUN #0 is the keyboard connector.\n"
+ "LUN #1 is the aux/mouse connector.",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ kbdR3Construct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnRelocate = */ NULL,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ kbdR3Reset,
+ /* .pfnSuspend = */ NULL,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ kbdR3Attach,
+ /* .pfnDetach = */ kbdR3Detach,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ NULL,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ kbdRZConstruct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+ /* .pfnRequest = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ kbdRZConstruct,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION
+};
+