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.cpp1103
1 files changed, 1103 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..41d756a1
--- /dev/null
+++ b/src/VBox/Devices/Input/DevPS2.cpp
@@ -0,0 +1,1103 @@
+/* $Id: DevPS2.cpp $ */
+/** @file
+ * DevPS2 - PS/2 keyboard & mouse controller device.
+ */
+
+/*
+ * Copyright (C) 2006-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.
+ * --------------------------------------------------------------------
+ *
+ * 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 <iprt/assert.h>
+#include <iprt/uuid.h>
+
+#include "VBoxDD.h"
+#include "PS2Dev.h"
+
+/* 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
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
+PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
+PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
+PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
+RT_C_DECLS_END
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
+
+/* 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
+
+
+/**
+ * The keyboard controller/device state.
+ *
+ * @note We use the default critical section for serialize data access.
+ */
+typedef struct KBDState
+{
+ uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
+ uint8_t status;
+ uint8_t mode;
+ uint8_t dbbout; /* data buffer byte */
+ /* keyboard state */
+ int32_t translate;
+ int32_t xlat_state;
+
+ /** Pointer to the device instance - RC. */
+ PPDMDEVINSRC pDevInsRC;
+ /** Pointer to the device instance - R3 . */
+ PPDMDEVINSR3 pDevInsR3;
+ /** Pointer to the device instance. */
+ PPDMDEVINSR0 pDevInsR0;
+
+ /** Keyboard state (implemented in separate PS2K module). */
+#ifdef VBOX_DEVICE_STRUCT_TESTCASE
+ uint8_t KbdFiller[PS2K_STRUCT_FILLER];
+#else
+ PS2K Kbd;
+#endif
+
+ /** Mouse state (implemented in separate PS2M module). */
+#ifdef VBOX_DEVICE_STRUCT_TESTCASE
+ uint8_t AuxFiller[PS2M_STRUCT_FILLER];
+#else
+ PS2M Aux;
+#endif
+} KBDState;
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+/* update irq and KBD_STAT_[MOUSE_]OBF */
+static void kbd_update_irq(KBDState *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(&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 = XlateAT2PC(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(&s->Kbd, &val) == VINF_SUCCESS)
+ {
+ s->xlat_state = XlateAT2PC(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(s->CTX_SUFF(pDevIns), 1, irq1_level);
+ PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, irq12_level);
+}
+
+void KBCUpdateInterrupts(void *pKbc)
+{
+ KBDState *s = (KBDState *)pKbc;
+ kbd_update_irq(s);
+}
+
+static void kbc_dbb_out(void *opaque, uint8_t val)
+{
+ KBDState *s = (KBDState*)opaque;
+
+ s->dbbout = val;
+ /* Set the OBF and raise IRQ. */
+ s->status |= KBD_STAT_OBF;
+ if (s->mode & KBD_MODE_KBD_INT)
+ PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, 1);
+}
+
+static void kbc_dbb_out_aux(void *opaque, uint8_t val)
+{
+ KBDState *s = (KBDState*)opaque;
+
+ 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(s->CTX_SUFF(pDevIns), 12, PDM_IRQ_LEVEL_HIGH);
+}
+
+static uint32_t kbd_read_status(void *opaque, uint32_t addr)
+{
+ KBDState *s = (KBDState*)opaque;
+ int val = s->status;
+ NOREF(addr);
+
+#if defined(DEBUG_KBD)
+ Log(("kbd: read status=0x%02x\n", val));
+#endif
+ return val;
+}
+
+static int kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
+{
+ int rc = VINF_SUCCESS;
+ KBDState *s = (KBDState*)opaque;
+ NOREF(addr);
+
+#ifdef DEBUG_KBD
+ Log(("kbd: write cmd=0x%02x\n", val));
+#endif
+ switch(val) {
+ case KBD_CCMD_READ_MODE:
+ kbc_dbb_out(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;
+ break;
+ case KBD_CCMD_MOUSE_ENABLE:
+ s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+ /* Check for queued input. */
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_TEST_MOUSE:
+ kbc_dbb_out(s, 0x00);
+ break;
+ case KBD_CCMD_SELF_TEST:
+ /* Enable the A20 line - that is the power-on state(!). */
+# ifndef IN_RING3
+ if (!PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
+ {
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+ break;
+ }
+# else /* IN_RING3 */
+ PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), true);
+# endif /* IN_RING3 */
+ s->status |= KBD_STAT_SELFTEST;
+ s->mode |= KBD_MODE_DISABLE_KBD;
+ kbc_dbb_out(s, 0x55);
+ break;
+ case KBD_CCMD_KBD_TEST:
+ kbc_dbb_out(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(s);
+ break;
+ case KBD_CCMD_READ_INPORT:
+ kbc_dbb_out(s, 0xBF);
+ break;
+ case KBD_CCMD_READ_OUTPORT:
+ /* XXX: check that */
+#ifdef TARGET_I386
+ val = 0x01 | (PDMDevHlpA20IsEnabled(s->CTX_SUFF(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(s, val);
+ break;
+#ifdef TARGET_I386
+ case KBD_CCMD_ENABLE_A20:
+# ifndef IN_RING3
+ if (!PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+# else /* IN_RING3 */
+ PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), true);
+# endif /* IN_RING3 */
+ break;
+ case KBD_CCMD_DISABLE_A20:
+# ifndef IN_RING3
+ if (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+# else /* IN_RING3 */
+ PDMDevHlpA20Set(s->CTX_SUFF(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(s, val);
+ break;
+ case KBD_CCMD_RESET:
+ case KBD_CCMD_RESET_ALT:
+#ifndef IN_RING3
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#else /* IN_RING3 */
+ LogRel(("Reset initiated by keyboard controller\n"));
+ rc = PDMDevHlpVMReset(s->CTX_SUFF(pDevIns), PDMVMRESET_F_KBD);
+#endif /* !IN_RING3 */
+ break;
+ 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(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 rc;
+}
+
+static uint32_t kbd_read_data(void *opaque, uint32_t addr)
+{
+ KBDState *s = (KBDState*)opaque;
+ uint32_t val;
+ NOREF(addr);
+
+ /* Return the current DBB contents. */
+ val = s->dbbout;
+
+ /* Reading the DBB deasserts IRQs... */
+ if (s->status & KBD_STAT_MOUSE_OBF)
+ PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, 0);
+ else
+ PDMDevHlpISASetIrq(s->CTX_SUFF(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(s);
+#ifdef DEBUG_KBD
+ Log(("kbd: read data=0x%02x\n", val));
+#endif
+ return val;
+}
+
+PS2K *KBDGetPS2KFromDevIns(PPDMDEVINS pDevIns)
+{
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+ return &pThis->Kbd;
+}
+
+PS2M *KBDGetPS2MFromDevIns(PPDMDEVINS pDevIns)
+{
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+ return &pThis->Aux;
+}
+
+static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
+{
+ int rc = VINF_SUCCESS;
+ KBDState *s = (KBDState*)opaque;
+ NOREF(addr);
+
+#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(&s->Kbd, val);
+ if (rc == VINF_SUCCESS)
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_WRITE_MODE:
+ s->mode = val;
+ s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_WRITE_OBUF:
+ kbc_dbb_out(s, val);
+ break;
+ case KBD_CCMD_WRITE_AUX_OBUF:
+ kbc_dbb_out_aux(s, val);
+ break;
+ case KBD_CCMD_WRITE_OUTPORT:
+#ifdef TARGET_I386
+# ifndef IN_RING3
+ if (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)) != !!(val & 2))
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+# else /* IN_RING3 */
+ PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), !!(val & 2));
+# endif /* !IN_RING3 */
+#endif
+ if (!(val & 1)) {
+# ifndef IN_RING3
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+# else
+ rc = PDMDevHlpVMReset(s->CTX_SUFF(pDevIns), PDMVMRESET_F_KBD);
+# endif
+ }
+ break;
+ case KBD_CCMD_WRITE_MOUSE:
+ /* Automatically enables aux interface. */
+ s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+ rc = PS2MByteToAux(&s->Aux, val);
+ if (rc == VINF_SUCCESS)
+ kbd_update_irq(s);
+ break;
+ default:
+ break;
+ }
+ if (rc != VINF_IOM_R3_IOPORT_WRITE)
+ s->write_cmd = 0;
+ return rc;
+}
+
+#ifdef IN_RING3
+
+static void kbd_reset(void *opaque)
+{
+ KBDState *s = (KBDState*)opaque;
+ s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
+ s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
+ /* Resetting everything, keyword was not working right on NT4 reboot. */
+ s->write_cmd = 0;
+ s->translate = 0;
+}
+
+static void kbd_save(PSSMHANDLE pSSM, KBDState *s)
+{
+ SSMR3PutU8(pSSM, s->write_cmd);
+ SSMR3PutU8(pSSM, s->status);
+ SSMR3PutU8(pSSM, s->mode);
+ SSMR3PutU8(pSSM, s->dbbout);
+
+ /* terminator */
+ SSMR3PutU32(pSSM, UINT32_MAX);
+}
+
+static int kbd_load(PSSMHANDLE pSSM, KBDState *s, 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;
+ SSMR3GetU8(pSSM, &s->write_cmd);
+ SSMR3GetU8(pSSM, &s->status);
+ SSMR3GetU8(pSSM, &s->mode);
+ if (version_id <= 5)
+ {
+ SSMR3GetU32(pSSM, (uint32_t *)&u32Dummy);
+ SSMR3GetU32(pSSM, (uint32_t *)&u32Dummy);
+ }
+ else
+ {
+ SSMR3GetU8(pSSM, &s->dbbout);
+ }
+ if (version_id <= 7)
+ {
+ int32_t i32Dummy;
+ uint8_t u8State;
+ uint8_t u8Rate;
+ uint8_t u8Proto;
+
+ SSMR3GetU32(pSSM, &u32Dummy);
+ SSMR3GetU8(pSSM, &u8State);
+ SSMR3GetU8(pSSM, &u8Dummy);
+ SSMR3GetU8(pSSM, &u8Rate);
+ SSMR3GetU8(pSSM, &u8Dummy);
+ SSMR3GetU8(pSSM, &u8Proto);
+ SSMR3GetU8(pSSM, &u8Dummy);
+ SSMR3GetS32(pSSM, &i32Dummy);
+ SSMR3GetS32(pSSM, &i32Dummy);
+ SSMR3GetS32(pSSM, &i32Dummy);
+ if (version_id > 2)
+ {
+ SSMR3GetS32(pSSM, &i32Dummy);
+ SSMR3GetS32(pSSM, &i32Dummy);
+ }
+ rc = SSMR3GetU8(pSSM, &u8Dummy);
+ if (version_id == 4)
+ {
+ SSMR3GetU32(pSSM, &u32Dummy);
+ rc = SSMR3GetU32(pSSM, &u32Dummy);
+ }
+ if (version_id > 3)
+ rc = SSMR3GetU8(pSSM, &u8Dummy);
+ if (version_id == 4)
+ rc = SSMR3GetU8(pSSM, &u8Dummy);
+ AssertLogRelRCReturn(rc, rc);
+
+ PS2MFixupState(&s->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 = SSMR3GetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ for (i = 0; i < u32; i++)
+ {
+ rc = SSMR3GetU8(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 = SSMR3GetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ for (i = 0; i < u32; i++)
+ {
+ rc = SSMR3GetU8(pSSM, &u8Dummy);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ Log(("kbd_load: %d mouse event queue items discarded from old saved state\n", u32));
+
+ rc = SSMR3GetU32(pSSM, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ for (i = 0; i < u32; i++)
+ {
+ rc = SSMR3GetU8(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 = SSMR3GetU32(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 -=-=-=-=-=- */
+
+/**
+ * Port I/O Handler for keyboard data IN operations.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pvUser User argument - ignored.
+ * @param Port Port number used for the IN operation.
+ * @param pu32 Where to store the result.
+ * @param cb Number of bytes read.
+ */
+PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ uint32_t fluff = 0;
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+
+ NOREF(pvUser);
+ switch (cb) {
+ case 4:
+ fluff |= 0xffff0000; /* Crazy Apple (Darwin 6.0.2 and earlier). */
+ RT_FALL_THRU();
+ case 2:
+ fluff |= 0x0000ff00;
+ RT_FALL_THRU();
+ case 1:
+ *pu32 = fluff | kbd_read_data(pThis, Port);
+ Log2(("kbdIOPortDataRead: Port=%#x cb=%d *pu32=%#x\n", Port, cb, *pu32));
+ return VINF_SUCCESS;
+ default:
+ AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
+ return VERR_IOM_IOPORT_UNUSED;
+ }
+}
+
+/**
+ * Port I/O Handler for keyboard data OUT operations.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pvUser User argument - ignored.
+ * @param Port Port number used for the IN operation.
+ * @param u32 The value to output.
+ * @param cb The value size in bytes.
+ */
+PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ int rc = VINF_SUCCESS;
+ NOREF(pvUser);
+ if (cb == 1 || cb == 2)
+ {
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+ rc = kbd_write_data(pThis, Port, (uint8_t)u32);
+ Log2(("kbdIOPortDataWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
+ }
+ else
+ AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
+ return rc;
+}
+
+/**
+ * Port I/O Handler for keyboard status IN operations.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pvUser User argument - ignored.
+ * @param Port Port number used for the IN operation.
+ * @param pu32 Where to store the result.
+ * @param cb Number of bytes read.
+ */
+PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ uint32_t fluff = 0;
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+
+ NOREF(pvUser);
+ switch (cb) {
+ case 4:
+ fluff |= 0xffff0000; /* Crazy Apple (Darwin 6.0.2 and earlier). */
+ RT_FALL_THRU();
+ case 2:
+ fluff |= 0x0000ff00;
+ RT_FALL_THRU();
+ case 1:
+ *pu32 = fluff | kbd_read_status(pThis, Port);
+ Log2(("kbdIOPortStatusRead: Port=%#x cb=%d -> *pu32=%#x\n", Port, cb, *pu32));
+ return VINF_SUCCESS;
+ default:
+ AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
+ return VERR_IOM_IOPORT_UNUSED;
+ }
+}
+
+/**
+ * Port I/O Handler for keyboard command OUT operations.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pvUser User argument - ignored.
+ * @param Port Port number used for the IN operation.
+ * @param u32 The value to output.
+ * @param cb The value size in bytes.
+ */
+PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ int rc = VINF_SUCCESS;
+ NOREF(pvUser);
+ if (cb == 1 || cb == 2)
+ {
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+ rc = kbd_write_command(pThis, Port, (uint8_t)u32);
+ Log2(("kbdIOPortCommandWrite: Port=%#x cb=%d u32=%#x rc=%Rrc\n", Port, cb, u32, rc));
+ }
+ else
+ AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
+ return rc;
+}
+
+#ifdef IN_RING3
+
+/**
+ * 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) kbdSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+ kbd_save(pSSM, pThis);
+ PS2KSaveState(&pThis->Kbd, pSSM);
+ PS2MSaveState(&pThis->Aux, pSSM);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Loads a saved keyboard device state.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ * @param pSSM The handle to the saved state.
+ * @param uVersion The data unit version number.
+ * @param uPass The data pass.
+ */
+static DECLCALLBACK(int) kbdLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+ int rc;
+
+ Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
+ rc = kbd_load(pSSM, pThis, uVersion);
+ if (uVersion >= 6)
+ rc = PS2KLoadState(&pThis->Kbd, pSSM, uVersion);
+ if (uVersion >= 8)
+ rc = PS2MLoadState(&pThis->Aux, pSSM, uVersion);
+ return rc;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADDONE, Key state fix-up after loading}
+ */
+static DECLCALLBACK(int) kbdLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+ int rc;
+
+ rc = PS2KLoadDone(&pThis->Kbd, pSSM);
+ return rc;
+}
+
+/**
+ * Reset notification.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance data.
+ */
+static DECLCALLBACK(void) kbdReset(PPDMDEVINS pDevIns)
+{
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+
+ kbd_reset(pThis);
+ PS2KReset(&pThis->Kbd);
+ PS2MReset(&pThis->Aux);
+}
+
+
+/* -=-=-=-=-=- real code -=-=-=-=-=- */
+
+
+/**
+ * Attach command.
+ *
+ * This is called to let the device attach to a driver for a specified LUN
+ * during runtime. This is not called during VM construction, the device
+ * constructor have to attach to all the available drivers.
+ *
+ * This is like plugging in the keyboard or mouse after turning on the PC.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ * @param iLUN The logical unit which is being detached.
+ * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ * @remark The keyboard controller doesn't support this action, this is just
+ * implemented to try out the driver<->device structure.
+ */
+static DECLCALLBACK(int) kbdAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ int rc;
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+
+ 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 = PS2KAttach(&pThis->Kbd, pDevIns, iLUN, fFlags);
+ if (RT_FAILURE(rc))
+ return rc;
+ break;
+
+ /* LUN #1: aux/mouse */
+ case 1:
+ rc = PS2MAttach(&pThis->Aux, pDevIns, iLUN, fFlags);
+ break;
+
+ default:
+ AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
+ return VERR_PDM_NO_SUCH_LUN;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Detach notification.
+ *
+ * This is called when a driver is detaching itself from a LUN of the device.
+ * The device should adjust it's state to reflect this.
+ *
+ * This is like unplugging the network cable to use it for the laptop or
+ * something while the PC is still running.
+ *
+ * @param pDevIns The device instance.
+ * @param iLUN The logical unit which is being detached.
+ * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ * @remark The keyboard controller doesn't support this action, this is just
+ * implemented to try out the driver<->device structure.
+ */
+static DECLCALLBACK(void) kbdDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+#if 0
+ /*
+ * Reset the interfaces and update the controller state.
+ */
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+ switch (iLUN)
+ {
+ /* LUN #0: keyboard */
+ case 0:
+ pThis->Keyboard.pDrv = NULL;
+ pThis->Keyboard.pDrvBase = NULL;
+ break;
+
+ /* LUN #1: aux/mouse */
+ case 1:
+ pThis->Mouse.pDrv = NULL;
+ pThis->Mouse.pDrvBase = NULL;
+ break;
+
+ default:
+ AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
+ break;
+ }
+#else
+ NOREF(pDevIns); NOREF(iLUN); NOREF(fFlags);
+#endif
+}
+
+
+/**
+ * @copydoc FNPDMDEVRELOCATE
+ */
+static DECLCALLBACK(void) kbdRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+ pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ PS2KRelocate(&pThis->Kbd, offDelta, pDevIns);
+ PS2MRelocate(&pThis->Aux, offDelta, pDevIns);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) kbdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+ int rc;
+ bool fGCEnabled;
+ bool fR0Enabled;
+ Assert(iInstance == 0);
+
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+
+ /*
+ * Validate and read the configuration.
+ */
+ if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0R0Enabled\0KbdThrottleEnabled\0"))
+ return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
+ rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"GCEnabled\" from the config"));
+ rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"R0Enabled\" from the config"));
+ Log(("pckbd: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
+
+
+ /*
+ * Initialize the interfaces.
+ */
+ pThis->pDevInsR3 = pDevIns;
+ pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+
+ rc = PS2KConstruct(&pThis->Kbd, pDevIns, pThis, iInstance, pCfg);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = PS2MConstruct(&pThis->Aux, pDevIns, pThis, iInstance);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Register I/O ports, save state, keyboard event handler and mouse event handlers.
+ */
+ rc = PDMDevHlpIOPortRegister(pDevIns, 0x60, 1, NULL, kbdIOPortDataWrite, kbdIOPortDataRead, NULL, NULL, "PC Keyboard - Data");
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIOPortRegister(pDevIns, 0x64, 1, NULL, kbdIOPortCommandWrite, kbdIOPortStatusRead, NULL, NULL, "PC Keyboard - Command / Status");
+ if (RT_FAILURE(rc))
+ return rc;
+ if (fGCEnabled)
+ {
+ rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ if (fR0Enabled)
+ {
+ rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, PCKBD_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
+ NULL, NULL, NULL,
+ NULL, kbdSaveExec, NULL,
+ NULL, kbdLoadExec, kbdLoadDone);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Attach to the keyboard and mouse drivers.
+ */
+ rc = kbdAttach(pDevIns, 0 /* keyboard LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = kbdAttach(pDevIns, 1 /* aux/mouse LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Initialize the device state.
+ */
+ kbdReset(pDevIns);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DevicePS2KeyboardMouse =
+{
+ /* u32Version */
+ PDM_DEVREG_VERSION,
+ /* szName */
+ "pckbd",
+ /* szRCMod */
+ "VBoxDDRC.rc",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "PS/2 Keyboard and Mouse device. Emulates both the keyboard, mouse and the keyboard controller. "
+ "LUN #0 is the keyboard connector. "
+ "LUN #1 is the aux/mouse connector.",
+ /* fFlags */
+ PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
+ /* fClass */
+ PDM_DEVREG_CLASS_INPUT,
+ /* cMaxInstances */
+ 1,
+ /* cbInstance */
+ sizeof(KBDState),
+ /* pfnConstruct */
+ kbdConstruct,
+ /* pfnDestruct */
+ NULL,
+ /* pfnRelocate */
+ kbdRelocate,
+ /* pfnMemSetup */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ kbdReset,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ kbdAttach,
+ /* pfnDetach */
+ kbdDetach,
+ /* pfnQueryInterface. */
+ NULL,
+ /* pfnInitComplete */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32VersionEnd */
+ PDM_DEVREG_VERSION
+};
+
+#endif /* IN_RING3 */
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
+