summaryrefslogtreecommitdiff
path: root/src/VBox/Devices/Input
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Input')
-rw-r--r--src/VBox/Devices/Input/DevPS2.cpp277
-rw-r--r--src/VBox/Devices/Input/DrvKeyboardQueue.cpp2
-rw-r--r--src/VBox/Devices/Input/DrvMouseQueue.cpp112
-rw-r--r--src/VBox/Devices/Input/PS2Dev.h55
-rw-r--r--src/VBox/Devices/Input/PS2K.cpp615
-rw-r--r--src/VBox/Devices/Input/PS2M.cpp1151
-rw-r--r--src/VBox/Devices/Input/UsbKbd.cpp82
-rw-r--r--src/VBox/Devices/Input/UsbMouse.cpp1270
-rw-r--r--src/VBox/Devices/Input/testcase/Makefile.kmk34
-rw-r--r--src/VBox/Devices/Input/testcase/tstUsbMouse.cpp329
10 files changed, 3399 insertions, 528 deletions
diff --git a/src/VBox/Devices/Input/DevPS2.cpp b/src/VBox/Devices/Input/DevPS2.cpp
index d63bfa5c..12d58bf2 100644
--- a/src/VBox/Devices/Input/DevPS2.cpp
+++ b/src/VBox/Devices/Input/DevPS2.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -53,7 +53,7 @@
#include "VBoxDD.h"
#include "PS2Dev.h"
-#define PCKBD_SAVED_STATE_VERSION 6
+#define PCKBD_SAVED_STATE_VERSION 7
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
@@ -95,6 +95,7 @@ RT_C_DECLS_END
#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 */
@@ -117,6 +118,7 @@ RT_C_DECLS_END
#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
#define KBD_MODE_RFU 0x80
+#ifndef VBOX_WITH_NEW_PS2M
/* Mouse Commands */
#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
@@ -164,14 +166,26 @@ typedef struct {
#define MOUSE_EVENT_QUEUE_SIZE 256
-typedef struct {
+typedef struct
+{
uint8_t data[MOUSE_EVENT_QUEUE_SIZE];
- int rptr, wptr, count;
+ int rptr;
+ int wptr;
+ int count;
} MouseEventQueue;
+#endif
-typedef struct KBDState {
+/**
+ * The keyboard controller/device state.
+ *
+ * @note We use the default critical section for serialize data access.
+ */
+typedef struct KBDState
+{
+#ifndef VBOX_WITH_NEW_PS2M
MouseCmdQueue mouse_command_queue;
MouseEventQueue mouse_event_queue;
+#endif
uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
uint8_t status;
uint8_t mode;
@@ -179,6 +193,7 @@ typedef struct KBDState {
/* keyboard state */
int32_t translate;
int32_t xlat_state;
+#ifndef VBOX_WITH_NEW_PS2M
/* mouse state */
int32_t mouse_write_cmd;
uint8_t mouse_status;
@@ -194,6 +209,7 @@ typedef struct KBDState {
int32_t mouse_flags;
uint8_t mouse_buttons;
uint8_t mouse_buttons_reported;
+#endif
uint32_t Alignment0;
@@ -204,9 +220,6 @@ typedef struct KBDState {
/** Pointer to the device instance. */
PPDMDEVINSR0 pDevInsR0;
- /** Critical section protecting the state. */
- PDMCRITSECT CritSect;
-
/** Keyboard state (implemented in separate PS2K module). */
#ifdef VBOX_DEVICE_STRUCT_TESTCASE
uint8_t KbdFiller[PS2K_STRUCT_FILLER];
@@ -214,6 +227,14 @@ typedef struct KBDState {
PS2K Kbd;
#endif
+#ifdef VBOX_WITH_NEW_PS2M
+ /** Mouse state (implemented in separate PS2M module). */
+#ifdef VBOX_DEVICE_STRUCT_TESTCASE
+ uint8_t AuxFiller[PS2M_STRUCT_FILLER];
+#else
+ PS2M Aux;
+#endif
+#else
/**
* Mouse port - LUN#1.
*
@@ -232,6 +253,7 @@ typedef struct KBDState {
/** The mouse interface of the attached mouse driver. */
R3PTRTYPE(PPDMIMOUSECONNECTOR) pDrv;
} Mouse;
+#endif
} KBDState;
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
@@ -239,8 +261,10 @@ typedef struct KBDState {
/* update irq and KBD_STAT_[MOUSE_]OBF */
static void kbd_update_irq(KBDState *s)
{
+#ifndef VBOX_WITH_NEW_PS2M
MouseCmdQueue *mcq = &s->mouse_command_queue;
MouseEventQueue *meq = &s->mouse_event_queue;
+#endif
int irq12_level, irq1_level;
uint8_t val;
@@ -284,6 +308,13 @@ static void kbd_update_irq(KBDState *s)
s->status |= KBD_STAT_OBF;
}
}
+#ifdef VBOX_WITH_NEW_PS2M
+ 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;
+ }
+#else
else if ((mcq->count || meq->count) && !(s->mode & KBD_MODE_DISABLE_MOUSE))
{
s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
@@ -302,6 +333,7 @@ static void kbd_update_irq(KBDState *s)
meq->count--;
}
}
+#endif
}
/* Determine new IRQ state. */
if (s->status & KBD_STAT_OBF) {
@@ -326,6 +358,7 @@ void KBCUpdateInterrupts(void *pKbc)
kbd_update_irq(s);
}
+#ifndef VBOX_WITH_NEW_PS2M
static void kbd_queue(KBDState *s, int b, int aux)
{
MouseCmdQueue *mcq = &s->mouse_command_queue;
@@ -367,6 +400,7 @@ static void kbd_queue(KBDState *s, int b, int aux)
}
kbd_update_irq(s);
}
+#endif
static void kbc_dbb_out(void *opaque, uint8_t val)
{
@@ -379,6 +413,17 @@ static void kbc_dbb_out(void *opaque, uint8_t val)
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;
@@ -488,6 +533,7 @@ static int kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
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 */
@@ -542,12 +588,23 @@ static uint32_t kbd_read_data(void *opaque, uint32_t addr)
return val;
}
-PS2K *GetPS2KFromDevIns(PPDMDEVINS pDevIns)
+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 *);
+#ifdef VBOX_WITH_NEW_PS2M
+ return &pThis->Aux;
+#else
+ return NULL;
+#endif
+}
+
+#ifndef VBOX_WITH_NEW_PS2M
static void kbd_mouse_set_reported_buttons(KBDState *s, unsigned fButtons, unsigned fButtonMask)
{
s->mouse_buttons_reported |= (fButtons & fButtonMask);
@@ -698,7 +755,8 @@ static void kbd_mouse_update_downstream_status(KBDState *pThis)
{
PPDMIMOUSECONNECTOR pDrv = pThis->Mouse.pDrv;
bool fEnabled = !!(pThis->mouse_status & MOUSE_STATUS_ENABLED);
- pDrv->pfnReportModes(pDrv, fEnabled, false);
+ if (pDrv)
+ pDrv->pfnReportModes(pDrv, fEnabled, false, false);
}
#endif /* IN_RING3 */
@@ -904,6 +962,7 @@ static int kbd_write_mouse(KBDState *s, int val)
}
return rc;
}
+#endif
static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
{
@@ -932,7 +991,7 @@ static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
kbc_dbb_out(s, val);
break;
case KBD_CCMD_WRITE_AUX_OBUF:
- kbd_queue(s, val, 1);
+ kbc_dbb_out_aux(s, val);
break;
case KBD_CCMD_WRITE_OUTPORT:
#ifdef TARGET_I386
@@ -954,7 +1013,13 @@ static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
case KBD_CCMD_WRITE_MOUSE:
/* Automatically enables aux interface. */
s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+#ifdef VBOX_WITH_NEW_PS2M
+ rc = PS2MByteToAux(&s->Aux, val);
+ if (rc == VINF_SUCCESS)
+ kbd_update_irq(s);
+#else
rc = kbd_write_mouse(s, val);
+#endif
break;
default:
break;
@@ -969,15 +1034,18 @@ static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
static void kbd_reset(void *opaque)
{
KBDState *s = (KBDState*)opaque;
+#ifndef VBOX_WITH_NEW_PS2M
MouseCmdQueue *mcq;
MouseEventQueue *meq;
s->mouse_write_cmd = -1;
+#endif
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;
+#ifndef VBOX_WITH_NEW_PS2M
if (s->mouse_status)
{
s->mouse_status = 0;
@@ -1003,18 +1071,22 @@ static void kbd_reset(void *opaque)
meq->rptr = 0;
meq->wptr = 0;
meq->count = 0;
+#endif
}
static void kbd_save(QEMUFile* f, void* opaque)
{
+#ifndef VBOX_WITH_NEW_PS2M
uint32_t cItems;
int i;
+#endif
KBDState *s = (KBDState*)opaque;
qemu_put_8s(f, &s->write_cmd);
qemu_put_8s(f, &s->status);
qemu_put_8s(f, &s->mode);
qemu_put_8s(f, &s->dbbout);
+#ifndef VBOX_WITH_NEW_PS2M
qemu_put_be32s(f, &s->mouse_write_cmd);
qemu_put_8s(f, &s->mouse_status);
qemu_put_8s(f, &s->mouse_resolution);
@@ -1041,6 +1113,7 @@ static void kbd_save(QEMUFile* f, void* opaque)
for (i = s->mouse_event_queue.rptr; cItems-- > 0; i = (i + 1) % RT_ELEMENTS(s->mouse_event_queue.data))
SSMR3PutU8(f, s->mouse_event_queue.data[i]);
Log(("kbd_save: %d mouse event queue items stored\n", s->mouse_event_queue.count));
+#endif
/* terminator */
SSMR3PutU32(f, ~0);
@@ -1074,6 +1147,7 @@ static int kbd_load(QEMUFile* f, void* opaque, int version_id)
{
qemu_get_8s(f, &s->dbbout);
}
+#ifndef VBOX_WITH_NEW_PS2M
qemu_get_be32s(f, (uint32_t *)&s->mouse_write_cmd);
qemu_get_8s(f, &s->mouse_status);
qemu_get_8s(f, &s->mouse_resolution);
@@ -1105,6 +1179,7 @@ static int kbd_load(QEMUFile* f, void* opaque, int version_id)
s->mouse_event_queue.count = 0;
s->mouse_event_queue.rptr = 0;
s->mouse_event_queue.wptr = 0;
+#endif
/* Determine the translation state. */
s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
@@ -1126,6 +1201,7 @@ static int kbd_load(QEMUFile* f, void* opaque, int version_id)
Log(("kbd_load: %d keyboard queue items discarded from old saved state\n", u32));
}
+#ifndef VBOX_WITH_NEW_PS2M
rc = SSMR3GetU32(f, &u32);
if (RT_FAILURE(rc))
return rc;
@@ -1161,6 +1237,21 @@ static int kbd_load(QEMUFile* f, void* opaque, int version_id)
s->mouse_event_queue.wptr = u32 % RT_ELEMENTS(s->mouse_event_queue.data);
s->mouse_event_queue.count = u32;
Log(("kbd_load: %d mouse event queue items loaded\n", u32));
+#else
+ if (version_id <= 6)
+ {
+ rc = SSMR3GetU32(f, &u32);
+ if (RT_FAILURE(rc))
+ return rc;
+ for (i = 0; i < u32; i++)
+ {
+ rc = SSMR3GetU8(f, &u8Dummy);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ Log(("kbd_load: %d mouse event queue items discarded from old saved state\n", u32));
+ }
+#endif
/* terminator */
rc = SSMR3GetU32(f, &u32);
@@ -1171,8 +1262,10 @@ static int kbd_load(QEMUFile* f, void* opaque, int version_id)
AssertMsgFailed(("u32=%#x\n", u32));
return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
}
+#ifndef VBOX_WITH_NEW_PS2M
/* Resend a notification to Main if the device is active */
kbd_mouse_update_downstream_status(s);
+#endif
return 0;
}
#endif /* IN_RING3 */
@@ -1199,14 +1292,9 @@ PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT
if (cb == 1)
{
KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
- int rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_R3_IOPORT_READ);
- if (RT_LIKELY(rc == VINF_SUCCESS))
- {
- *pu32 = kbd_read_data(pThis, Port);
- PDMCritSectLeave(&pThis->CritSect);
- Log2(("kbdIOPortDataRead: Port=%#x cb=%d *pu32=%#x\n", Port, cb, *pu32));
- }
- return rc;
+ *pu32 = kbd_read_data(pThis, Port);
+ Log2(("kbdIOPortDataRead: Port=%#x cb=%d *pu32=%#x\n", Port, cb, *pu32));
+ return VINF_SUCCESS;
}
AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
return VERR_IOM_IOPORT_UNUSED;
@@ -1230,13 +1318,8 @@ PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT
if (cb == 1)
{
KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
- rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_R3_IOPORT_WRITE);
- if (RT_LIKELY(rc == VINF_SUCCESS))
- {
- rc = kbd_write_data(pThis, Port, u32);
- PDMCritSectLeave(&pThis->CritSect);
- Log2(("kbdIOPortDataWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
- }
+ rc = kbd_write_data(pThis, Port, u32);
+ Log2(("kbdIOPortDataWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
}
else
AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
@@ -1260,14 +1343,9 @@ PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPOR
if (cb == 1)
{
KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
- int rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_R3_IOPORT_READ);
- if (RT_LIKELY(rc == VINF_SUCCESS))
- {
- *pu32 = kbd_read_status(pThis, Port);
- PDMCritSectLeave(&pThis->CritSect);
- Log2(("kbdIOPortStatusRead: Port=%#x cb=%d -> *pu32=%#x\n", Port, cb, *pu32));
- }
- return rc;
+ *pu32 = kbd_read_status(pThis, Port);
+ Log2(("kbdIOPortStatusRead: Port=%#x cb=%d -> *pu32=%#x\n", Port, cb, *pu32));
+ return VINF_SUCCESS;
}
AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
return VERR_IOM_IOPORT_UNUSED;
@@ -1291,13 +1369,8 @@ PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOP
if (cb == 1)
{
KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
- rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_R3_IOPORT_WRITE);
- if (RT_LIKELY(rc == VINF_SUCCESS))
- {
- rc = kbd_write_command(pThis, Port, u32);
- PDMCritSectLeave(&pThis->CritSect);
- Log2(("kbdIOPortCommandWrite: Port=%#x cb=%d u32=%#x rc=%Rrc\n", Port, cb, u32, rc));
- }
+ rc = kbd_write_command(pThis, Port, 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));
@@ -1311,13 +1384,16 @@ PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOP
*
* @returns VBox status code.
* @param pDevIns The device instance.
- * @param pSSMHandle The handle to save the state to.
+ * @param pSSM The handle to save the state to.
*/
-static DECLCALLBACK(int) kbdSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
+static DECLCALLBACK(int) kbdSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
- kbd_save(pSSMHandle, pThis);
- PS2KSaveState(pSSMHandle, &pThis->Kbd);
+ kbd_save(pSSM, pThis);
+ PS2KSaveState(&pThis->Kbd, pSSM);
+#ifdef VBOX_WITH_NEW_PS2M
+ PS2MSaveState(&pThis->Aux, pSSM);
+#endif
return VINF_SUCCESS;
}
@@ -1327,19 +1403,35 @@ static DECLCALLBACK(int) kbdSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
*
* @returns VBox status code.
* @param pDevIns The device instance.
- * @param pSSMHandle The handle to the saved state.
+ * @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 pSSMHandle, uint32_t uVersion, uint32_t uPass)
+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(pSSMHandle, pThis, uVersion);
+ rc = kbd_load(pSSM, pThis, uVersion);
if (uVersion >= 6)
- rc = PS2KLoadState(pSSMHandle, &pThis->Kbd, uVersion);
+ rc = PS2KLoadState(&pThis->Kbd, pSSM, uVersion);
+#ifdef VBOX_WITH_NEW_PS2M
+ if (uVersion >= 7)
+ rc = PS2MLoadState(&pThis->Aux, pSSM, uVersion);
+#endif
+ 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;
}
@@ -1355,9 +1447,13 @@ static DECLCALLBACK(void) kbdReset(PPDMDEVINS pDevIns)
kbd_reset(pThis);
PS2KReset(&pThis->Kbd);
+#ifdef VBOX_WITH_NEW_PS2M
+ PS2MReset(&pThis->Aux);
+#endif
}
+#ifndef VBOX_WITH_NEW_PS2M
/* -=-=-=-=-=- Mouse: IBase -=-=-=-=-=- */
/**
@@ -1377,28 +1473,43 @@ static DECLCALLBACK(void *) kbdMouseQueryInterface(PPDMIBASE pInterface, const
/**
* @interface_method_impl{PDMIMOUSEPORT, pfnPutEvent}
*/
-static DECLCALLBACK(int) kbdMousePutEvent(PPDMIMOUSEPORT pInterface, int32_t iDeltaX, int32_t iDeltaY,
- int32_t iDeltaZ, int32_t iDeltaW, uint32_t fButtonStates)
+static DECLCALLBACK(int) kbdMousePutEvent(PPDMIMOUSEPORT pInterface, int32_t dx,
+ int32_t dy, int32_t dz, int32_t dw,
+ uint32_t fButtons)
{
KBDState *pThis = RT_FROM_MEMBER(pInterface, KBDState, Mouse.IPort);
- int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
+ int rc = PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_SEM_BUSY);
AssertReleaseRC(rc);
- pc_kbd_mouse_event(pThis, iDeltaX, iDeltaY, iDeltaZ, iDeltaW, fButtonStates);
+ pc_kbd_mouse_event(pThis, dx, dy, dz, dw, fButtons);
- PDMCritSectLeave(&pThis->CritSect);
+ PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMIMOUSEPORT, pfnPutEventAbs}
*/
-static DECLCALLBACK(int) kbdMousePutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t uX, uint32_t uY, int32_t iDeltaZ, int32_t iDeltaW, uint32_t fButtons)
+static DECLCALLBACK(int) kbdMousePutEventAbs(PPDMIMOUSEPORT pInterface,
+ uint32_t x, uint32_t y, int32_t dz,
+ int32_t dw, uint32_t fButtons)
{
AssertFailedReturn(VERR_NOT_SUPPORTED);
- NOREF(pInterface); NOREF(uX); NOREF(uY); NOREF(iDeltaZ); NOREF(iDeltaW); NOREF(fButtons);
+ NOREF(pInterface); NOREF(x); NOREF(y); NOREF(dz); NOREF(dw); NOREF(fButtons);
}
+/**
+ * @interface_method_impl{PDMIMOUSEPORT, pfnPutEventMultiTouch}
+ */
+static DECLCALLBACK(int) kbdMousePutEventMultiTouch(PPDMIMOUSEPORT pInterface,
+ uint8_t cContacts,
+ const uint64_t *pau64Contacts,
+ uint32_t u32ScanTime)
+{
+ AssertFailedReturn(VERR_NOT_SUPPORTED);
+ NOREF(pInterface); NOREF(cContacts); NOREF(pau64Contacts); NOREF(u32ScanTime);
+}
+#endif
/* -=-=-=-=-=- real code -=-=-=-=-=- */
@@ -1432,13 +1543,16 @@ static DECLCALLBACK(int) kbdAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t
{
/* LUN #0: keyboard */
case 0:
- rc = PS2KAttach(pDevIns, &pThis->Kbd, iLUN, fFlags);
+ rc = PS2KAttach(&pThis->Kbd, pDevIns, iLUN, fFlags);
if (RT_FAILURE(rc))
return rc;
break;
/* LUN #1: aux/mouse */
case 1:
+#ifdef VBOX_WITH_NEW_PS2M
+ rc = PS2MAttach(&pThis->Aux, pDevIns, iLUN, fFlags);
+#else
rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Mouse.IBase, &pThis->Mouse.pDrvBase, "Aux (Mouse) Port");
if (RT_SUCCESS(rc))
{
@@ -1456,6 +1570,7 @@ static DECLCALLBACK(int) kbdAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t
}
else
AssertLogRelMsgFailed(("Failed to attach LUN #1! rc=%Rrc\n", rc));
+#endif
break;
default:
@@ -1520,24 +1635,10 @@ 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);
-}
-
-
-/**
- * Destruct a device instance for a VM.
- *
- * @returns VBox status.
- * @param pDevIns The device instance data.
- */
-static DECLCALLBACK(int) kbdDestruct(PPDMDEVINS pDevIns)
-{
- KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
- PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
-
- PDMR3CritSectDelete(&pThis->CritSect);
-
- return VINF_SUCCESS;
+ PS2KRelocate(&pThis->Kbd, offDelta, pDevIns);
+#ifdef VBOX_WITH_NEW_PS2M
+ PS2MRelocate(&pThis->Aux, offDelta, pDevIns);
+#endif
}
@@ -1565,6 +1666,7 @@ static DECLCALLBACK(int) kbdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNO
rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"R0Enabled\" from the config"));
+fGCEnabled = fR0Enabled = false;
Log(("pckbd: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
@@ -1575,20 +1677,20 @@ static DECLCALLBACK(int) kbdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNO
pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
- rc = PS2KConstruct(pDevIns, &pThis->Kbd, pThis, iInstance);
+ rc = PS2KConstruct(&pThis->Kbd, pDevIns, pThis, iInstance);
if (RT_FAILURE(rc))
return rc;
+#ifdef VBOX_WITH_NEW_PS2M
+ rc = PS2MConstruct(&pThis->Aux, pDevIns, pThis, iInstance);
+ if (RT_FAILURE(rc))
+ return rc;
+#else
pThis->Mouse.IBase.pfnQueryInterface = kbdMouseQueryInterface;
pThis->Mouse.IPort.pfnPutEvent = kbdMousePutEvent;
pThis->Mouse.IPort.pfnPutEventAbs = kbdMousePutEventAbs;
-
- /*
- * Initialize the critical section.
- */
- rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "PS2KM#%u", iInstance);
- if (RT_FAILURE(rc))
- return rc;
+ pThis->Mouse.IPort.pfnPutEventMultiTouch = kbdMousePutEventMultiTouch;
+#endif
/*
* Register I/O ports, save state, keyboard event handler and mouse event handlers.
@@ -1617,7 +1719,10 @@ static DECLCALLBACK(int) kbdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNO
if (RT_FAILURE(rc))
return rc;
}
- rc = PDMDevHlpSSMRegister(pDevIns, PCKBD_SAVED_STATE_VERSION, sizeof(*pThis), kbdSaveExec, kbdLoadExec);
+ 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;
@@ -1668,10 +1773,10 @@ const PDMDEVREG g_DevicePS2KeyboardMouse =
/* pfnConstruct */
kbdConstruct,
/* pfnDestruct */
- kbdDestruct,
+ NULL,
/* pfnRelocate */
kbdRelocate,
- /* pfnIOCtl */
+ /* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
diff --git a/src/VBox/Devices/Input/DrvKeyboardQueue.cpp b/src/VBox/Devices/Input/DrvKeyboardQueue.cpp
index e0ceb231..379a339d 100644
--- a/src/VBox/Devices/Input/DrvKeyboardQueue.cpp
+++ b/src/VBox/Devices/Input/DrvKeyboardQueue.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Devices/Input/DrvMouseQueue.cpp b/src/VBox/Devices/Input/DrvMouseQueue.cpp
index 19a84310..9d0a8d26 100644
--- a/src/VBox/Devices/Input/DrvMouseQueue.cpp
+++ b/src/VBox/Devices/Input/DrvMouseQueue.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -58,20 +58,38 @@ typedef struct DRVMOUSEQUEUE
/**
+ * Event type for @a DRVMOUSEQUEUEITEM
+ */
+enum EVENTTYPE { RELATIVE, ABSOLUTE };
+
+/**
* Mouse queue item.
*/
typedef struct DRVMOUSEQUEUEITEM
{
/** The core part owned by the queue manager. */
PDMQUEUEITEMCORE Core;
- uint32_t fAbs;
- int32_t iDeltaX;
- int32_t iDeltaY;
- int32_t iDeltaZ;
- int32_t iDeltaW;
- uint32_t fButtonStates;
- uint32_t uX;
- uint32_t uY;
+ enum EVENTTYPE enmType;
+ union
+ {
+ uint32_t padding[5];
+ struct
+ {
+ uint32_t fButtons;
+ int32_t dx;
+ int32_t dy;
+ int32_t dz;
+ int32_t dw;
+ } Relative;
+ struct
+ {
+ uint32_t fButtons;
+ uint32_t x;
+ uint32_t y;
+ int32_t dz;
+ int32_t dw;
+ } Absolute;
+ } u;
} DRVMOUSEQUEUEITEM, *PDRVMOUSEQUEUEITEM;
@@ -101,7 +119,10 @@ static DECLCALLBACK(void *) drvMouseQueueQueryInterface(PPDMIBASE pInterface, c
/**
* @interface_method_impl{PDMIMOUSEPORT,pfnPutEvent}
*/
-static DECLCALLBACK(int) drvMouseQueuePutEvent(PPDMIMOUSEPORT pInterface, int32_t iDeltaX, int32_t iDeltaY, int32_t iDeltaZ, int32_t iDeltaW, uint32_t fButtonStates)
+static DECLCALLBACK(int) drvMouseQueuePutEvent(PPDMIMOUSEPORT pInterface,
+ int32_t dx, int32_t dy,
+ int32_t dz, int32_t dw,
+ uint32_t fButtons)
{
PDRVMOUSEQUEUE pDrv = IMOUSEPORT_2_DRVMOUSEQUEUE(pInterface);
if (pDrv->fInactive)
@@ -110,14 +131,13 @@ static DECLCALLBACK(int) drvMouseQueuePutEvent(PPDMIMOUSEPORT pInterface, int32_
PDRVMOUSEQUEUEITEM pItem = (PDRVMOUSEQUEUEITEM)PDMQueueAlloc(pDrv->pQueue);
if (pItem)
{
- pItem->fAbs = 0;
- pItem->iDeltaX = iDeltaX;
- pItem->iDeltaY = iDeltaY;
- pItem->iDeltaZ = iDeltaZ;
- pItem->iDeltaW = iDeltaW;
- pItem->fButtonStates = fButtonStates;
- pItem->uX = 0;
- pItem->uY = 0;
+ RT_ZERO(pItem->u.padding);
+ pItem->enmType = RELATIVE;
+ pItem->u.Relative.dx = dx;
+ pItem->u.Relative.dy = dy;
+ pItem->u.Relative.dz = dz;
+ pItem->u.Relative.dw = dw;
+ pItem->u.Relative.fButtons = fButtons;
PDMQueueInsert(pDrv->pQueue, &pItem->Core);
return VINF_SUCCESS;
}
@@ -127,7 +147,10 @@ static DECLCALLBACK(int) drvMouseQueuePutEvent(PPDMIMOUSEPORT pInterface, int32_
/**
* @interface_method_impl{PDMIMOUSEPORT,pfnPutEventAbs}
*/
-static DECLCALLBACK(int) drvMouseQueuePutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t uX, uint32_t uY, int32_t iDeltaZ, int32_t iDeltaW, uint32_t fButtonStates)
+static DECLCALLBACK(int) drvMouseQueuePutEventAbs(PPDMIMOUSEPORT pInterface,
+ uint32_t x, uint32_t y,
+ int32_t dz, int32_t dw,
+ uint32_t fButtons)
{
PDRVMOUSEQUEUE pDrv = IMOUSEPORT_2_DRVMOUSEQUEUE(pInterface);
if (pDrv->fInactive)
@@ -136,14 +159,13 @@ static DECLCALLBACK(int) drvMouseQueuePutEventAbs(PPDMIMOUSEPORT pInterface, uin
PDRVMOUSEQUEUEITEM pItem = (PDRVMOUSEQUEUEITEM)PDMQueueAlloc(pDrv->pQueue);
if (pItem)
{
- pItem->fAbs = 1;
- pItem->iDeltaX = 0;
- pItem->iDeltaY = 0;
- pItem->iDeltaZ = iDeltaZ;
- pItem->iDeltaW = iDeltaW;
- pItem->fButtonStates = fButtonStates;
- pItem->uX = uX;
- pItem->uY = uY;
+ RT_ZERO(pItem->u.padding);
+ pItem->enmType = ABSOLUTE;
+ pItem->u.Absolute.x = x;
+ pItem->u.Absolute.y = y;
+ pItem->u.Absolute.dz = dz;
+ pItem->u.Absolute.dw = dw;
+ pItem->u.Absolute.fButtons = fButtons;
PDMQueueInsert(pDrv->pQueue, &pItem->Core);
return VINF_SUCCESS;
}
@@ -151,6 +173,15 @@ static DECLCALLBACK(int) drvMouseQueuePutEventAbs(PPDMIMOUSEPORT pInterface, uin
}
+static DECLCALLBACK(int) drvMouseQueuePutEventMultiTouch(PPDMIMOUSEPORT pInterface,
+ uint8_t cContacts,
+ const uint64_t *pau64Contacts,
+ uint32_t u32ScanTime)
+{
+ PDRVMOUSEQUEUE pThis = IMOUSEPORT_2_DRVMOUSEQUEUE(pInterface);
+ return pThis->pUpPort->pfnPutEventMultiTouch(pThis->pUpPort, cContacts, pau64Contacts, u32ScanTime);
+}
+
/* -=-=-=-=- IConnector -=-=-=-=- */
#define PPDMIMOUSECONNECTOR_2_DRVMOUSEQUEUE(pInterface) ( (PDRVMOUSEQUEUE)((char *)(pInterface) - RT_OFFSETOF(DRVMOUSEQUEUE, IConnector)) )
@@ -161,12 +192,14 @@ static DECLCALLBACK(int) drvMouseQueuePutEventAbs(PPDMIMOUSEPORT pInterface, uin
* driver.
*
* @param pInterface Pointer to the mouse connector interface structure.
- * @param fAbs The new absolute mode state.
+ * @param fRel Is relative reporting supported?
+ * @param fAbs Is absolute reporting supported?
+ * @param fMT Is multi-touch reporting supported?
*/
-static DECLCALLBACK(void) drvMousePassThruReportModes(PPDMIMOUSECONNECTOR pInterface, bool fRel, bool fAbs)
+static DECLCALLBACK(void) drvMousePassThruReportModes(PPDMIMOUSECONNECTOR pInterface, bool fRel, bool fAbs, bool fMT)
{
PDRVMOUSEQUEUE pDrv = PPDMIMOUSECONNECTOR_2_DRVMOUSEQUEUE(pInterface);
- pDrv->pDownConnector->pfnReportModes(pDrv->pDownConnector, fRel, fAbs);
+ pDrv->pDownConnector->pfnReportModes(pDrv->pDownConnector, fRel, fAbs, fMT);
}
@@ -186,10 +219,22 @@ static DECLCALLBACK(bool) drvMouseQueueConsumer(PPDMDRVINS pDrvIns, PPDMQUEUEITE
PDRVMOUSEQUEUE pThis = PDMINS_2_DATA(pDrvIns, PDRVMOUSEQUEUE);
PDRVMOUSEQUEUEITEM pItem = (PDRVMOUSEQUEUEITEM)pItemCore;
int rc;
- if (!pItem->fAbs)
- rc = pThis->pUpPort->pfnPutEvent(pThis->pUpPort, pItem->iDeltaX, pItem->iDeltaY, pItem->iDeltaZ, pItem->iDeltaW, pItem->fButtonStates);
+ if (pItem->enmType == RELATIVE)
+ rc = pThis->pUpPort->pfnPutEvent(pThis->pUpPort,
+ pItem->u.Relative.dx,
+ pItem->u.Relative.dy,
+ pItem->u.Relative.dz,
+ pItem->u.Relative.dw,
+ pItem->u.Relative.fButtons);
+ else if (pItem->enmType == ABSOLUTE)
+ rc = pThis->pUpPort->pfnPutEventAbs(pThis->pUpPort,
+ pItem->u.Absolute.x,
+ pItem->u.Absolute.y,
+ pItem->u.Absolute.dz,
+ pItem->u.Absolute.dw,
+ pItem->u.Absolute.fButtons);
else
- rc = pThis->pUpPort->pfnPutEventAbs(pThis->pUpPort, pItem->uX, pItem->uY, pItem->iDeltaZ, pItem->iDeltaW, pItem->fButtonStates);
+ return false;
return RT_SUCCESS(rc);
}
@@ -288,6 +333,7 @@ static DECLCALLBACK(int) drvMouseQueueConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pC
/* IMousePort. */
pDrv->IPort.pfnPutEvent = drvMouseQueuePutEvent;
pDrv->IPort.pfnPutEventAbs = drvMouseQueuePutEventAbs;
+ pDrv->IPort.pfnPutEventMultiTouch = drvMouseQueuePutEventMultiTouch;
/*
* Get the IMousePort interface of the above driver/device.
diff --git a/src/VBox/Devices/Input/PS2Dev.h b/src/VBox/Devices/Input/PS2Dev.h
index 392095a7..bdb32430 100644
--- a/src/VBox/Devices/Input/PS2Dev.h
+++ b/src/VBox/Devices/Input/PS2Dev.h
@@ -17,35 +17,68 @@
#ifndef PS2DEV_H
#define PS2DEV_H
-/* Must be at least as big as the real struct. */
-#define PS2K_STRUCT_FILLER 768
+/** 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 {
+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(PPDMDEVINS pDevIns, PPS2K pThis, void *pParent, int iInstance);
-int PS2KAttach(PPDMDEVINS pDevIns, PPS2K pThis, unsigned iLUN, uint32_t fFlags);
+int PS2KConstruct(PPS2K pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance);
+int PS2KAttach(PPS2K pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags);
void PS2KReset(PPS2K pThis);
-void PS2KRelocate(PPS2K pThis, RTGCINTPTR offDelta);
-void PS2KSaveState(PSSMHANDLE pSSM, PPS2K pThis);
-int PS2KLoadState(PSSMHANDLE pSSM, PPS2K pThis, uint32_t uVersion);
+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);
+
+PS2M *KBDGetPS2MFromDevIns(PPDMDEVINS pDevIns);
+
+/* Shared keyboard/aux internal interface. */
void KBCUpdateInterrupts(void *pKbc);
-PS2K *GetPS2KFromDevIns(PPDMDEVINS pDevIns);
-//@todo: This should live with the KBC implementation.
+///@todo: This should live with the KBC implementation.
/** AT to PC scancode translator state. */
-typedef enum {
+typedef enum
+{
XS_IDLE, /**< Starting state. */
XS_BREAK, /**< F0 break byte was received. */
XS_HIBIT /**< Break code still active. */
diff --git a/src/VBox/Devices/Input/PS2K.cpp b/src/VBox/Devices/Input/PS2K.cpp
index 1df1ddb2..e933fbc4 100644
--- a/src/VBox/Devices/Input/PS2K.cpp
+++ b/src/VBox/Devices/Input/PS2K.cpp
@@ -74,7 +74,7 @@
#define KRSP_ID1 0xAB
#define KRSP_ID2 0x83
#define KRSP_BAT_OK 0xAA
-#define KRSP_BAT_FAIL 0xFC
+#define KRSP_BAT_FAIL 0xFC /* Also a 'release keys' signal. */
#define KRSP_ECHO 0xEE
#define KRSP_ACK 0xFA
#define KRSP_RESEND 0xFE
@@ -187,22 +187,26 @@ typedef struct PS2K
#if HC_ARCH_BITS == 32
uint32_t Alignment0;
#endif
- /** Critical section protecting the state. */
- PDMCRITSECT KbdCritSect;
+
/** Command delay timer - RC Ptr. */
PTMTIMERRC pKbdDelayTimerRC;
/** Typematic timer - RC Ptr. */
PTMTIMERRC pKbdTypematicTimerRC;
+
+ /** The device critical section protecting everything - R3 Ptr */
+ R3PTRTYPE(PPDMCRITSECT) pCritSectR3;
/** Command delay timer - R3 Ptr. */
PTMTIMERR3 pKbdDelayTimerR3;
/** Typematic timer - R3 Ptr. */
PTMTIMERR3 pKbdTypematicTimerR3;
+ RTR3PTR Alignment2;
+
/** Command delay timer - R0 Ptr. */
PTMTIMERR0 pKbdDelayTimerR0;
/** Typematic timer - R0 Ptr. */
PTMTIMERR0 pKbdTypematicTimerR0;
- scan_state_t XlatState; //@todo: temporary
+ scan_state_t XlatState; ///@todo: temporary
uint32_t Alignment1;
/**
@@ -415,7 +419,7 @@ static const key_def aPS2Keys[] = {
/* 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 */
+ /* 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 */
@@ -428,6 +432,19 @@ static const key_def aPS2Keys[] = {
/* 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. */
static const key_def aPS2ModKeys[] = {
/* E0 */ {0x1D, 0x14, 0x11, 0, T_B }, /* Key 58: Left Control */
@@ -467,7 +484,7 @@ static uint8_t aScancode2Hid[] =
0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, /* 38-3F */
0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, /* 40-47 */
0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, /* 48-4F */
- 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x64, 0x44, /* 50-57 */
+ 0x5a, 0x5b, 0x62, 0x63, 0x46, 0x00, 0x64, 0x44, /* 50-57 */
0x45, 0x67, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, /* 58-5F */
0x00, 0x00, 0x00, 0x00, 0x68, 0x69, 0x6a, 0x6b, /* 60-67 */
0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x00, /* 68-6F */
@@ -560,7 +577,7 @@ static scan_state_t ScancodeToHidUsage(scan_state_t state, uint8_t scanCode, uin
*
* @param pQ Pointer to the queue.
*/
-static void PS2ClearQueue(GeneriQ *pQ)
+static void ps2kClearQueue(GeneriQ *pQ)
{
LogFlowFunc(("Clearing queue %p\n", pQ));
pQ->wpos = pQ->rpos;
@@ -574,7 +591,7 @@ static void PS2ClearQueue(GeneriQ *pQ)
* @param pQ Pointer to the queue.
* @param val The byte to store.
*/
-static void PS2InsertQueue(GeneriQ *pQ, uint8_t val)
+static void ps2kInsertQueue(GeneriQ *pQ, uint8_t val)
{
/* Check if queue is full. */
if (pQ->cUsed >= pQ->cSize)
@@ -598,7 +615,7 @@ static void PS2InsertQueue(GeneriQ *pQ, uint8_t val)
* @param pSSM SSM handle to write the state to.
* @param pQ Pointer to the queue.
*/
-static void PS2SaveQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
+static void ps2kSaveQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
{
uint32_t cItems = pQ->cUsed;
int i;
@@ -623,7 +640,7 @@ static void PS2SaveQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
*
* @return int VBox status/error code.
*/
-static int PS2LoadQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
+static int ps2kLoadQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
{
int rc;
@@ -646,7 +663,7 @@ static int PS2LoadQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
return rc;
}
-#endif
+#endif /* IN_RING3 */
/**
* Retrieve a byte from a queue.
@@ -657,7 +674,7 @@ static int PS2LoadQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
* @return int VINF_TRY_AGAIN if queue is empty,
* VINF_SUCCESS if a byte was read.
*/
-int PS2RemoveQueue(GeneriQ *pQ, uint8_t *pVal)
+static int ps2kRemoveQueue(GeneriQ *pQ, uint8_t *pVal)
{
int rc = VINF_TRY_AGAIN;
@@ -678,7 +695,7 @@ int PS2RemoveQueue(GeneriQ *pQ, uint8_t *pVal)
/* 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)
+static void ps2kSetupTypematic(PPS2K pThis, uint8_t val)
{
int A, B;
unsigned period;
@@ -692,23 +709,23 @@ static void PS2KSetupTypematic(PPS2K pThis, uint8_t val)
period = (8 + A) * (1 << B) * 417 / 100;
pThis->uTypematicRepeat = period;
Log(("Typematic delay %u ms, repeat period %u ms\n",
- pThis->uTypematicDelay, pThis->uTypematicRepeat));
+ pThis->uTypematicDelay, pThis->uTypematicRepeat));
}
-static void PS2KSetDefaults(PPS2K pThis)
+static void ps2kSetDefaults(PPS2K pThis)
{
LogFlowFunc(("Set keyboard defaults\n"));
- PS2ClearQueue((GeneriQ *)&pThis->keyQ);
+ ps2kClearQueue((GeneriQ *)&pThis->keyQ);
/* Set default Scan Set 3 typematic values. */
/* Set default typematic rate/delay. */
- PS2KSetupTypematic(pThis, KBD_DFL_RATE_DELAY);
+ ps2kSetupTypematic(pThis, KBD_DFL_RATE_DELAY);
/* Clear last typematic key?? */
}
/**
* Receive and process a byte sent by the keyboard controller.
*
- * @param pThis The keyboard.
+ * @param pThis The PS/2 keyboard instance data.
* @param cmd The command (or data) byte.
*/
int PS2KByteToKbd(PPS2K pThis, uint8_t cmd)
@@ -717,118 +734,120 @@ int PS2KByteToKbd(PPS2K pThis, uint8_t cmd)
LogFlowFunc(("new cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
- switch (cmd) {
- case KCMD_ECHO:
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ECHO);
- pThis->u8CurrCmd = 0;
- break;
- case KCMD_READ_ID:
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ID1);
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ID2);
- pThis->u8CurrCmd = 0;
- break;
- case KCMD_ENABLE:
- pThis->fScanning = true;
- PS2ClearQueue((GeneriQ *)&pThis->keyQ);
- /* Clear last typematic key?? */
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
- pThis->u8CurrCmd = 0;
- break;
- case KCMD_DFLT_DISABLE:
- pThis->fScanning = false;
- PS2KSetDefaults(pThis);
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
- pThis->u8CurrCmd = 0;
- break;
- case KCMD_SET_DEFAULT:
- PS2KSetDefaults(pThis);
- PS2InsertQueue((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.
- PS2InsertQueue((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?
- PS2InsertQueue((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:
- PS2InsertQueue((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
- {
- PDMKEYBLEDS enmLeds = PDMKEYBLEDS_NONE;
-
- if (cmd & 0x01)
- enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_SCROLLLOCK);
- if (cmd & 0x02)
- enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_NUMLOCK);
- if (cmd & 0x04)
- enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_CAPSLOCK);
- pThis->Keyboard.pDrv->pfnLedStatusChange(pThis->Keyboard.pDrv, enmLeds);
- pThis->fNumLockOn = !!(cmd & 0x02); /* Sync internal Num Lock state. */
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
- pThis->u8LEDs = cmd;
- pThis->u8CurrCmd = 0;
- }
-#endif
+ switch (cmd)
+ {
+ case KCMD_ECHO:
+ ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ECHO);
+ pThis->u8CurrCmd = 0;
break;
- case KCMD_SCANSET:
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
- if (cmd == 0)
- PS2InsertQueue((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. */
+ 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_RATE_DELAY:
- PS2KSetupTypematic(pThis, cmd);
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
+ case KCMD_ENABLE:
+ pThis->fScanning = true;
+ ps2kClearQueue((GeneriQ *)&pThis->keyQ);
+ /* Clear last typematic key?? */
+ ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_ACK);
pThis->u8CurrCmd = 0;
break;
- default:
- fHandled = false;
- }
- /* Fall through only to handle unrecognized commands. */
- if (fHandled)
+ case KCMD_DFLT_DISABLE:
+ pThis->fScanning = false;
+ ps2kSetDefaults(pThis);
+ 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
+ {
+ PDMKEYBLEDS enmLeds = PDMKEYBLEDS_NONE;
+
+ if (cmd & 0x01)
+ enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_SCROLLLOCK);
+ if (cmd & 0x02)
+ enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_NUMLOCK);
+ if (cmd & 0x04)
+ enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_CAPSLOCK);
+ pThis->Keyboard.pDrv->pfnLedStatusChange(pThis->Keyboard.pDrv, enmLeds);
+ 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;
- case KCMD_INVALID_1:
- case KCMD_INVALID_2:
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_RESEND);
- pThis->u8CurrCmd = 0;
- break;
+ 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);
@@ -838,34 +857,38 @@ int PS2KByteToKbd(PPS2K pThis, uint8_t cmd)
/**
* Send a byte (keystroke or command response) to the keyboard controller.
*
- * @param pThis The keyboard.
+ * @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 *pVal)
+int PS2KByteFromKbd(PPS2K pThis, uint8_t *pb)
{
int rc;
- Assert(pVal);
+ 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 = PS2RemoveQueue((GeneriQ *)&pThis->cmdQ, pVal);
+ rc = ps2kRemoveQueue((GeneriQ *)&pThis->cmdQ, pb);
if (rc != VINF_SUCCESS && !pThis->u8CurrCmd && pThis->fScanning)
- rc = PS2RemoveQueue((GeneriQ *)&pThis->keyQ, pVal);
+ rc = ps2kRemoveQueue((GeneriQ *)&pThis->keyQ, pb);
- LogFlowFunc(("keyboard sends 0x%02x (%svalid data)\n", *pVal, rc == VINF_SUCCESS ? "" : "not "));
+ LogFlowFunc(("keyboard sends 0x%02x (%svalid data)\n", *pb, rc == VINF_SUCCESS ? "" : "not "));
return rc;
}
#ifdef IN_RING3
-static int PS2KProcessKeyEvent(PPS2K pThis, uint8_t u8HidCode, bool fKeyDown)
+static int psk2ProcessKeyEvent(PPS2K pThis, uint8_t u8HidCode, bool fKeyDown)
{
unsigned int i = 0;
key_def const *pKeyDef;
uint8_t abCodes[16];
+ uint8_t u8MakeCode;
LogFlowFunc(("key %s: 0x%02x (set %d)\n", fKeyDown ? "down" : "up", u8HidCode, pThis->u8ScanSet));
@@ -897,9 +920,11 @@ static int PS2KProcessKeyEvent(PPS2K pThis, uint8_t u8HidCode, bool fKeyDown)
if ((pKeyDef->keyFlags & KF_NL) && fKeyDown)
pThis->fNumLockOn ^= true;
- if (pThis->u8ScanSet == 2)
+ if (pThis->u8ScanSet == 1 || pThis->u8ScanSet == 2)
{
- /* Handle Scan Set 2 - used almost all the time. */
+ /* 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.
+ */
abCodes[0] = 0;
if (fKeyDown)
{
@@ -908,47 +933,63 @@ static int PS2KProcessKeyEvent(PPS2K pThis, uint8_t u8HidCode, bool fKeyDown)
{
/* Pause/Break sends different data if either Ctrl is held. */
if (pThis->u8Modifiers & (MOD_LCTRL | MOD_RCTRL))
- strcpy((char *)abCodes, "\xE0\x7E\xE0\xF0\x7E");
+ strcpy((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\x7E\xE0\xF0\x7E" : "\xE0\x46\xE0\xC6");
else
- strcpy((char *)abCodes, "\xE1\x14\x77\xE1\xF0\x14\xF0\x77");
+ strcpy((char *)abCodes, 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 Ctrl, Shift, *and* Alt! */
+ /* Print Screen depends on all of Ctrl, Shift, *and* Alt! */
if (pThis->u8Modifiers & (MOD_LALT | MOD_RALT))
- strcpy((char *)abCodes, "\x84");
+ strcpy((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\x84" : "\x54");
else if (pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT))
- strcpy((char *)abCodes, "\xE0\x7C");
+ strcpy((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\x7C" : "\xE0\x37");
else
- strcpy((char *)abCodes, "\xE0\x12\xE0\x7C");
+ strcpy((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\x12\xE0\x7C" : "\xE0\x2A\xE0\x37");
}
- else if (pKeyDef->keyFlags & KF_GK)
+ else if (pKeyDef->keyFlags & (KF_GK | KF_NS))
{
- if (pThis->fNumLockOn)
+ /* 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 | MOD_RSHIFT)) == 0)
- strcpy((char *)abCodes, "\xE0\x12");
+ if (pThis->u8Modifiers & MOD_LSHIFT)
+ strcat((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\xF0\x12" : "\xE0\xAA");
+ if (pThis->u8Modifiers & MOD_RSHIFT)
+ strcat((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\xF0\x59" : "\xE0\xB6");
}
else
{
- if (pThis->u8Modifiers & MOD_LSHIFT)
- strcat((char *)abCodes, "\xE0\xF0\x12");
- if (pThis->u8Modifiers & MOD_RSHIFT)
- strcat((char *)abCodes, "\xE0\xF0\x59");
+ Assert(pThis->fNumLockOn); /* Not for KF_NS! */
+ if ((pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT)) == 0)
+ strcpy((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\x12" : "\xE0\x2A");
+ /* Else Shift cancels NumLock, so no prefix! */
}
}
/* Feed the bytes to the queue if there is room. */
- //@todo: check empty space!
+ ///@todo check empty space!
while (abCodes[i])
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, abCodes[i++]);
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, abCodes[i++]);
Assert(i < sizeof(abCodes));
/* Standard processing for regular keys only. */
+ u8MakeCode = pThis->u8ScanSet == 2 ? pKeyDef->makeS2 : pKeyDef->makeS1;
if (!(pKeyDef->keyFlags & (KF_PB | KF_PS)))
{
if (pKeyDef->keyFlags & (KF_E0 | KF_GK | KF_NS))
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, 0xE0);
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, pKeyDef->makeS2);
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, 0xE0);
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, u8MakeCode);
}
}
else if (!(pKeyDef->keyFlags & (KF_NB | KF_PB)))
@@ -960,75 +1001,73 @@ static int PS2KProcessKeyEvent(PPS2K pThis, uint8_t u8HidCode, bool fKeyDown)
{
/* Undo faked Print Screen state as needed. */
if (pThis->u8Modifiers & (MOD_LALT | MOD_RALT))
- strcpy((char *)abCodes, "\xF0\x84");
+ strcpy((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xF0\x84" : "\xD4");
else if (pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT))
- strcpy((char *)abCodes, "\xE0\xF0\x7C");
+ strcpy((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\xF0\x7C" : "\xE0\xB7");
else
- strcpy((char *)abCodes, "\xE0\xF0\x7C\xE0\xF0\x12");
+ strcpy((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\xF0\x7C\xE0\xF0\x12" : "\xE0\xB7\xE0\xAA");
}
else
{
/* Process base scan code for less unusual keys. */
if (pKeyDef->keyFlags & (KF_E0 | KF_GK | KF_NS))
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, 0xE0);
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, 0xF0);
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, pKeyDef->makeS2);
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, 0xE0);
+ if (pThis->u8ScanSet == 2) {
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, 0xF0);
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, pKeyDef->makeS2);
+ } else {
+ Assert(pThis->u8ScanSet == 1);
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, pKeyDef->makeS1 | 0x80);
+ }
/* Restore shift state for gray keys. */
- if (pKeyDef->keyFlags & KF_GK)
+ if (pKeyDef->keyFlags & (KF_GK | KF_NS))
{
- if (pThis->fNumLockOn)
+ if (!pThis->fNumLockOn || (pKeyDef->keyFlags & KF_NS))
{
- if ((pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT)) == 0)
- strcpy((char *)abCodes, "\xE0\xF0\x12");
+ if (pThis->u8Modifiers & MOD_LSHIFT)
+ strcat((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\x12" : "\xE0\x2A");
+ if (pThis->u8Modifiers & MOD_RSHIFT)
+ strcat((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\x59" : "\xE0\x36");
}
else
{
- if (pThis->u8Modifiers & MOD_RSHIFT)
- strcat((char *)abCodes, "\xE0\x59");
- if (pThis->u8Modifiers & MOD_LSHIFT)
- strcat((char *)abCodes, "\xE0\x12");
+ Assert(pThis->fNumLockOn); /* Not for KF_NS! */
+ if ((pThis->u8Modifiers & (MOD_LSHIFT | MOD_RSHIFT)) == 0)
+ strcpy((char *)abCodes, pThis->u8ScanSet == 2 ?
+ "\xE0\xF0\x12" : "\xE0\xAA");
}
}
}
/* Feed any additional bytes to the queue if there is room. */
- //@todo: check empty space!
+ ///@todo check empty space!
while (abCodes[i])
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, abCodes[i++]);
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, abCodes[i++]);
Assert(i < sizeof(abCodes));
}
}
- else if (pThis->u8ScanSet == 1)
- {
- /* Handle Scan Set 1 - similar in complexity to Set 2. */
- if (fKeyDown)
- {
- if (pKeyDef->keyFlags & (KF_E0 | KF_GK | KF_NS | KF_PS))
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, 0xE0);
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, pKeyDef->makeS1);
- }
- else if (!(pKeyDef->keyFlags & (KF_NB | KF_PB))) {
- if (pKeyDef->keyFlags & (KF_E0 | KF_GK | KF_NS | KF_PS))
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, 0xE0);
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, pKeyDef->makeS1 | 0x80);
- }
- }
else
{
/* Handle Scan Set 3 - very straightforward. */
+ Assert(pThis->u8ScanSet == 3);
if (fKeyDown)
{
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, pKeyDef->makeS3);
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, pKeyDef->makeS3);
}
else
{
/* Send a key release code unless it's a make only key. */
- //@todo: Look up the current typematic setting, not the default!
+ ///@todo Look up the current typematic setting, not the default!
if (pKeyDef->keyMatic != T_M)
{
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, 0xF0);
- PS2InsertQueue((GeneriQ *)&pThis->keyQ, pKeyDef->makeS3);
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, 0xF0);
+ ps2kInsertQueue((GeneriQ *)&pThis->keyQ, pKeyDef->makeS3);
}
}
}
@@ -1048,8 +1087,8 @@ static int PS2KProcessKeyEvent(PPS2K pThis, uint8_t u8HidCode, bool fKeyDown)
{
pThis->u8TypematicKey = 0;
pThis->enmTypematicState = KBD_TMS_IDLE;
- //@todo: Cancel timer right away?
- //@todo: Cancel timer before pushing key up code!?
+ ///@todo Cancel timer right away?
+ ///@todo Cancel timer before pushing key up code!?
}
/* Poke the KBC to update its state. */
@@ -1061,12 +1100,9 @@ static int PS2KProcessKeyEvent(PPS2K pThis, uint8_t u8HidCode, bool fKeyDown)
/* 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)
+static DECLCALLBACK(void) ps2kTypematicTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
- PPS2K pThis = (PS2K *)pvUser; //PDMINS_2_DATA(pDevIns, PS2K *);
- int rc = PDMCritSectEnter(&pThis->KbdCritSect, VERR_SEM_BUSY);
- AssertReleaseRC(rc);
-
+ PPS2K pThis = (PS2K *)pvUser; NOREF(pDevIns);
LogFlowFunc(("Typematic state=%d, key %02X\n", pThis->enmTypematicState, pThis->u8TypematicKey));
/* If the current typematic key is zero, the repeat was canceled just when
@@ -1079,35 +1115,48 @@ static DECLCALLBACK(void) PS2KTypematicTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer
if (pThis->enmTypematicState == KBD_TMS_REPEAT)
{
- PS2KProcessKeyEvent(pThis, pThis->u8TypematicKey, true /* Key down */ );
+ psk2ProcessKeyEvent(pThis, pThis->u8TypematicKey, true /* Key down */ );
TMTimerSetMillies(pThis->CTX_SUFF(pKbdTypematicTimer), pThis->uTypematicRepeat);
}
}
-
- PDMCritSectLeave(&pThis->KbdCritSect);
}
/* 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)
+static DECLCALLBACK(void) ps2kDelayTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
- PPS2K pThis = GetPS2KFromDevIns(pDevIns);
- int rc = PDMCritSectEnter(&pThis->KbdCritSect, VERR_SEM_BUSY);
- AssertReleaseRC(rc);
+ PPS2K pThis = (PS2K *)pvUser; NOREF(pDevIns);
LogFlowFunc(("Delay timer: cmd %02X\n", pThis->u8CurrCmd));
Assert(pThis->u8CurrCmd == KCMD_RESET);
- PS2InsertQueue((GeneriQ *)&pThis->cmdQ, KRSP_BAT_OK);
+ 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...
+ ///@todo Might want a PS2KCompleteCommand() to push last response, clear command, and kick the KBC...
/* Give the KBC a kick. */
KBCUpdateInterrupts(pThis->pParent);
+}
- PDMCritSectLeave(&pThis->KbdCritSect);
+/* 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"));
+ LogRel(("Releasing keys...\n"));
+
+ for (unsigned uKey = 0; uKey < sizeof(pThis->abDepressedKeys); ++uKey)
+ if (pThis->abDepressedKeys[uKey])
+ {
+ LogRel(("Releasing key %02X\n", uKey));
+ psk2ProcessKeyEvent(pThis, uKey, false /* key up */);
+ pThis->abDepressedKeys[uKey] = 0;
+ }
+ LogFlowFunc(("Done releasing keys\n"));
}
@@ -1118,9 +1167,9 @@ static DECLCALLBACK(void) PS2KDelayTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, vo
* @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)
+static DECLCALLBACK(void) ps2kInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
- PPS2K pThis = GetPS2KFromDevIns(pDevIns);
+ PPS2K pThis = KBDGetPS2KFromDevIns(pDevIns);
NOREF(pszArgs);
pHlp->pfnPrintf(pHlp, "PS/2 Keyboard: scan set %d, scanning %s\n",
@@ -1144,7 +1193,7 @@ static DECLCALLBACK(void) PS2KInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp,
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
-static DECLCALLBACK(void *) PS2KQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+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);
@@ -1159,13 +1208,12 @@ static DECLCALLBACK(void *) PS2KQueryInterface(PPDMIBASE pInterface, const char
* Keyboard event handler.
*
* @returns VBox status code.
- * @param pInterface Pointer to the keyboard port interface (KBDState::Keyboard.IPort).
+ * @param pThis The PS2 keyboard instance data.
* @param u32Usage USB HID usage code with key
* press/release flag.
*/
-static DECLCALLBACK(int) PS2KPutEvent(PPDMIKEYBOARDPORT pInterface, uint32_t u32Usage)
+static int ps2kPutEventWorker(PPS2K pThis, uint32_t u32Usage)
{
- PPS2K pThis = RT_FROM_MEMBER(pInterface, PS2K, Keyboard.IPort);
uint8_t u8HidCode;
bool fKeyDown;
bool fHaveEvent = true;
@@ -1195,28 +1243,49 @@ static DECLCALLBACK(int) PS2KPutEvent(PPDMIKEYBOARDPORT pInterface, uint32_t u32
/* Unless this is a new key press/release, don't even bother. */
if (fHaveEvent)
{
- rc = PDMCritSectEnter(&pThis->KbdCritSect, VERR_SEM_BUSY);
+ rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
AssertReleaseRC(rc);
- rc = PS2KProcessKeyEvent(pThis, u8HidCode, fKeyDown);
+ rc = psk2ProcessKeyEvent(pThis, u8HidCode, fKeyDown);
- PDMCritSectLeave(&pThis->KbdCritSect);
+ PDMCritSectLeave(pThis->pCritSectR3);
}
return rc;
}
-static DECLCALLBACK(int) PS2KPutEventWrapper(PPDMIKEYBOARDPORT pInterface, uint8_t u8KeyCode)
+static DECLCALLBACK(int) ps2kPutEventWrapper(PPDMIKEYBOARDPORT pInterface, uint8_t u8KeyCode)
{
PPS2K pThis = RT_FROM_MEMBER(pInterface, PS2K, Keyboard.IPort);
uint32_t u32Usage = 0;
+ int rc;
+
+ LogFlowFunc(("key code %02X\n", u8KeyCode));
- LogFlowFunc(("key code %02X\n", u8KeyCode ));
- pThis->XlatState = ScancodeToHidUsage(pThis->XlatState, u8KeyCode, &u32Usage);
+ /* The 'BAT fail' scancode is reused as a signal to release keys. No actual
+ * key is allowed to use this scancode.
+ */
+ if (RT_UNLIKELY(u8KeyCode == KRSP_BAT_FAIL))
+ {
+ rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
+ AssertReleaseRC(rc);
+
+ ps2kReleaseKeys(pThis);
- if (pThis->XlatState == SS_IDLE)
+ PDMCritSectLeave(pThis->pCritSectR3);
+ }
+ else
{
- PS2KPutEvent(pInterface, u32Usage);
+ pThis->XlatState = ScancodeToHidUsage(pThis->XlatState, u8KeyCode, &u32Usage);
+
+ if (pThis->XlatState == SS_IDLE)
+ {
+ /* Stupid Korean key hack: convert a lone break key into a press/release sequence. */
+ if (u32Usage == 0x80000090 || u32Usage == 0x80000091)
+ ps2kPutEventWorker(pThis, u32Usage & ~0x80000000);
+
+ ps2kPutEventWorker(pThis, u32Usage);
+ }
}
return VINF_SUCCESS;
@@ -1233,11 +1302,12 @@ static DECLCALLBACK(int) PS2KPutEventWrapper(PPDMIKEYBOARDPORT pInterface, uint8
* 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(PPDMDEVINS pDevIns, PPS2K pThis, unsigned iLUN, uint32_t fFlags)
+int PS2KAttach(PPS2K pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
int rc;
@@ -1270,7 +1340,7 @@ int PS2KAttach(PPDMDEVINS pDevIns, PPS2K pThis, unsigned iLUN, uint32_t fFlags)
return rc;
}
-void PS2KSaveState(PSSMHANDLE pSSM, PPS2K pThis)
+void PS2KSaveState(PPS2K pThis, PSSMHANDLE pSSM)
{
uint32_t cPressed = 0;
uint32_t cbTMSSize = 0;
@@ -1289,8 +1359,8 @@ void PS2KSaveState(PSSMHANDLE pSSM, PPS2K pThis)
SSMR3PutBool(pSSM, pThis->fScanning);
/* Save the command and keystroke queues. */
- PS2SaveQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
- PS2SaveQueue(pSSM, (GeneriQ *)&pThis->keyQ);
+ ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
+ ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->keyQ);
/* Save the command delay timer. Note that the typematic repeat
* timer is *not* saved.
@@ -1306,16 +1376,16 @@ void PS2KSaveState(PSSMHANDLE pSSM, PPS2K pThis)
SSMR3PutU32(pSSM, cPressed);
- for (unsigned i = 0; i < sizeof(pThis->abDepressedKeys); ++i)
- if (pThis->abDepressedKeys[i])
- SSMR3PutU8(pSSM, pThis->abDepressedKeys[i]);
+ 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(PSSMHANDLE pSSM, PPS2K pThis, uint32_t uVersion)
+int PS2KLoadState(PPS2K pThis, PSSMHANDLE pSSM, uint32_t uVersion)
{
uint8_t u8;
uint32_t cPressed;
@@ -1337,43 +1407,56 @@ int PS2KLoadState(PSSMHANDLE pSSM, PPS2K pThis, uint32_t uVersion)
SSMR3GetBool(pSSM, &pThis->fNumLockOn);
SSMR3GetBool(pSSM, &pThis->fScanning);
- do {
- /* Load the command and keystroke queues. */
- rc = PS2LoadQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
- if (RT_FAILURE(rc)) break;
- rc = PS2LoadQueue(pSSM, (GeneriQ *)&pThis->keyQ);
- if (RT_FAILURE(rc)) break;
+ /* 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);
- if (RT_FAILURE(rc)) break;
+ /* Load the command delay timer, just in case. */
+ rc = TMR3TimerLoad(pThis->CTX_SUFF(pKbdDelayTimer), pSSM);
+ AssertRCReturn(rc, rc);
- /* Fake key up events for keys that were held down at the time the state was saved. */
- rc = SSMR3GetU32(pSSM, &cPressed);
- if (RT_FAILURE(rc)) break;
+ /* Recalculate the typematic delay/rate. */
+ ps2kSetupTypematic(pThis, pThis->u8Typematic);
- while (cPressed--)
+ /* 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);
- if (RT_FAILURE(rc)) break;
- PS2KProcessKeyEvent(pThis, u8, false /* key up */);
+ AssertRCReturn(rc, rc);
+ pThis->abDepressedKeys[u8] = 1;
}
- if (RT_FAILURE(rc)) break;
+ }
- /* Load typematic settings for Scan Set 3. */
- rc = SSMR3GetU32(pSSM, &cbTMSSize);
- if (RT_FAILURE(rc)) break;
+ /* Load typematic settings for Scan Set 3. */
+ rc = SSMR3GetU32(pSSM, &cbTMSSize);
+ AssertRCReturn(rc, rc);
- while (cbTMSSize--)
- {
- rc = SSMR3GetU8(pSSM, &u8);
- if (RT_FAILURE(rc)) break;
- }
- } while (0);
+ while (cbTMSSize--)
+ {
+ rc = SSMR3GetU8(pSSM, &u8);
+ AssertRCReturn(rc, rc);
+ }
return rc;
}
+int PS2KLoadDone(PPS2K pThis, PSSMHANDLE pSSM)
+{
+ /* This *must* be done after the inital load because it may trigger
+ * interrupts and change the interrupt controller state.
+ */
+ ps2kReleaseKeys(pThis);
+ return VINF_SUCCESS;
+}
+
void PS2KReset(PPS2K pThis)
{
LogFlowFunc(("Resetting PS2K\n"));
@@ -1387,15 +1470,15 @@ void PS2KReset(PPS2K pThis)
/* Clear queues and any pressed keys. */
memset(pThis->abDepressedKeys, 0, sizeof(pThis->abDepressedKeys));
- PS2ClearQueue((GeneriQ *)&pThis->cmdQ);
- PS2KSetDefaults(pThis); /* Also clears keystroke queue. */
+ 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)
+void PS2KRelocate(PPS2K pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns)
{
LogFlowFunc(("Relocating PS2K\n"));
pThis->pKbdDelayTimerRC = TMTimerRCPtr(pThis->pKbdDelayTimerR3);
@@ -1403,7 +1486,7 @@ void PS2KRelocate(PPS2K pThis, RTGCINTPTR offDelta)
NOREF(offDelta);
}
-int PS2KConstruct(PPDMDEVINS pDevIns, PPS2K pThis, void *pParent, int iInstance)
+int PS2KConstruct(PPS2K pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance)
{
int rc;
@@ -1415,23 +1498,21 @@ int PS2KConstruct(PPDMDEVINS pDevIns, PPS2K pThis, void *pParent, int iInstance)
pThis->keyQ.cSize = KBD_KEY_QUEUE_SIZE;
pThis->cmdQ.cSize = KBD_CMD_QUEUE_SIZE;
- pThis->Keyboard.IBase.pfnQueryInterface = PS2KQueryInterface;
- pThis->Keyboard.IPort.pfnPutEvent = PS2KPutEventWrapper;
+ pThis->Keyboard.IBase.pfnQueryInterface = ps2kQueryInterface;
+ pThis->Keyboard.IPort.pfnPutEvent = ps2kPutEventWrapper;
/*
- * Initialize the critical section.
+ * Initialize the critical section pointer(s).
*/
- rc = PDMDevHlpCritSectInit(pDevIns, &pThis->KbdCritSect, RT_SRC_POS, "PS2K#%u", iInstance);
- if (RT_FAILURE(rc))
- return rc;
+ pThis->pCritSectR3 = pDevIns->pCritSectRoR3;
/*
* Create the typematic delay/repeat timer. Does not use virtual time!
*/
PTMTIMER pTimer;
- rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, PS2KTypematicTimer, pThis,
- TMTIMER_FLAGS_NO_CRIT_SECT, "PS2K Typematic Timer", &pTimer);
- if (RT_FAILURE (rc))
+ rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, ps2kTypematicTimer, pThis,
+ TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2K Typematic Timer", &pTimer);
+ if (RT_FAILURE(rc))
return rc;
pThis->pKbdTypematicTimerR3 = pTimer;
@@ -1441,9 +1522,9 @@ int PS2KConstruct(PPDMDEVINS pDevIns, PPS2K pThis, void *pParent, int iInstance)
/*
* Create the command delay timer.
*/
- rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, PS2KDelayTimer, pThis,
- TMTIMER_FLAGS_NO_CRIT_SECT, "PS2K Delay Timer", &pTimer);
- if (RT_FAILURE (rc))
+ 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;
@@ -1453,14 +1534,14 @@ int PS2KConstruct(PPDMDEVINS pDevIns, PPS2K pThis, void *pParent, int iInstance)
/*
* Register debugger info callbacks.
*/
- PDMDevHlpDBGFInfoRegister(pDevIns, "ps2k", "Display PS/2 keyboard state.", PS2KInfoState);
+ PDMDevHlpDBGFInfoRegister(pDevIns, "ps2k", "Display PS/2 keyboard state.", ps2kInfoState);
return rc;
}
#endif
-//@todo: The following should live with the KBC implementation.
+///@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
diff --git a/src/VBox/Devices/Input/PS2M.cpp b/src/VBox/Devices/Input/PS2M.cpp
new file mode 100644
index 00000000..6de654b7
--- /dev/null
+++ b/src/VBox/Devices/Input/PS2M.cpp
@@ -0,0 +1,1151 @@
+/** @file
+ * PS2M - PS/2 auxiliary device (mouse) emulation.
+ */
+
+/*
+ * Copyright (C) 2007-2013 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 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.
+ *
+ *
+ * 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) |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ *
+ */
+
+
+/*******************************************************************************
+* 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_DFLT_DISABLE 0xF5 /* Disable and set defaults. */
+#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_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;
+
+/* 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;
+
+/* Protocol selection 'knock' states. */
+typedef enum {
+ PS2M_KNOCK_INITIAL,
+ PS2M_KNOCK_IMPS2_1ST,
+ PS2M_KNOCK_IMPS2_2ND,
+ PS2M_KNOCK_IMEX_1ST,
+ PS2M_KNOCK_IMEX_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;
+ /** 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 movement. */
+ int32_t iAccumZ;
+ /** Accumulated button presses. */
+ uint32_t fAccumB;
+ /** Throttling delay in milliseconds. */
+ unsigned uThrottleDelay;
+#if HC_ARCH_BITS == 32
+ uint32_t Alignment0;
+#endif
+
+ /** 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
+
+/* 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. */
+
+/*******************************************************************************
+* 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)
+ {
+ LogFlowFunc(("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;
+ LogFlowFunc(("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);
+}
+
+#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)
+{
+ pThis->uThrottleDelay = 1000 / rate;
+ 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);
+
+ /* Accumulators and button status bits are cleared. */
+ ps2kClearQueue((GeneriQ *)&pThis->evtQ);
+ //@todo accumulators/current state
+}
+
+/* Handle the sampling rate 'knock' sequence which selects protocol. */
+static void ps2mRateProtocolKnock(PPS2M pThis, uint8_t rate)
+{
+ if (pThis->enmProtocol == PS2M_PROTO_PS2STD)
+ {
+ switch (pThis->enmKnockState)
+ {
+ case PS2M_KNOCK_INITIAL:
+ if (rate == 200)
+ pThis->enmKnockState = PS2M_KNOCK_IMPS2_1ST;
+ break;
+ case PS2M_KNOCK_IMPS2_1ST:
+ if (rate == 100)
+ pThis->enmKnockState = PS2M_KNOCK_IMPS2_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"));
+ }
+ default:
+ pThis->enmKnockState = PS2M_KNOCK_INITIAL;
+ }
+ }
+ else if (pThis->enmProtocol == PS2M_PROTO_IMPS2)
+ {
+ switch (pThis->enmKnockState)
+ {
+ case PS2M_KNOCK_INITIAL:
+ if (rate == 200)
+ pThis->enmKnockState = PS2M_KNOCK_IMEX_1ST;
+ break;
+ case PS2M_KNOCK_IMEX_1ST:
+ if (rate == 200)
+ pThis->enmKnockState = PS2M_KNOCK_IMEX_2ND;
+ else
+ 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"));
+ }
+ default:
+ pThis->enmKnockState = PS2M_KNOCK_INITIAL;
+ }
+ }
+}
+
+/**
+ * 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));
+//LogRel(("aux: 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;
+ }
+ else 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;
+ }
+ }
+
+ 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. */
+ //@todo: buttons
+ u8Val = pThis->u8State;
+ 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_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);
+ ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->enmProtocol);
+ pThis->u8CurrCmd = 0;
+ break;
+ case ACMD_ENABLE:
+ pThis->u8State |= AUX_STATE_ENABLED;
+ //@todo: R3 only!
+#ifdef IN_RING3
+ ps2mSetDriverState(pThis, true);
+#endif
+ ps2kClearQueue((GeneriQ *)&pThis->evtQ);
+ ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+ pThis->u8CurrCmd = 0;
+ break;
+ case ACMD_DFLT_DISABLE:
+ ps2mSetDefaults(pThis);
+ 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);
+ /* Slightly delay reset completion; it might take hundreds of ms. */
+ TMTimerSetMillies(pThis->CTX_SUFF(pDelayTimer), 1);
+ 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:
+ //@todo reject unsupported resolutions
+ pThis->u8Resolution = cmd;
+ ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+ pThis->u8CurrCmd = 0;
+ break;
+ case ACMD_SET_SAMP_RATE:
+ //@todo reject unsupported rates
+ ps2mSetRate(pThis, cmd);
+ ps2mRateProtocolKnock(pThis, cmd);
+ ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+ pThis->u8CurrCmd = 0;
+ break;
+ default:
+ fHandled = false;
+ }
+ /* Fall through only to handle unrecognized commands. */
+ if (fHandled)
+ break;
+
+ 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));
+// 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 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, keystrokes are //@todo: true?
+ * 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 "));
+//if (rc == VINF_SUCCESS) LogRel(("aux: sends 0x%02X\n", *pb));
+
+ return rc;
+}
+
+#ifdef IN_RING3
+
+/* Three-button event mask. */
+#define PS2M_STD_BTN_MASK (RT_BIT(0) | RT_BIT(1) | RT_BIT(2))
+
+/* Report accumulated movement and button presses, then clear the accumulators. */
+static void ps2mReportAccumulatedEvents(PPS2M pThis)
+{
+ uint8_t val;
+ int8_t dX, dY, dZ;
+
+ /* Clamp the accumulated delta values to the allowed range. */
+ dX = RT_MIN(RT_MAX(pThis->iAccumX, -256), 255);
+ dY = RT_MIN(RT_MAX(pThis->iAccumY, -256), 255);
+ dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -8), 7);
+
+ /* Start with the sync bit and buttons 1-3. */
+ val = RT_BIT(3) | (pThis->fAccumB & 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((GeneriQ *)&pThis->evtQ, val);
+ ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dX);
+ ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dY);
+
+ /* Add fourth byte if extended protocol is in use. */
+ if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
+ {
+ if (pThis->enmProtocol == PS2M_PROTO_IMPS2)
+ ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dZ);
+ else
+ {
+ Assert(pThis->enmProtocol == PS2M_PROTO_IMEX);
+ /* Z value uses 4 bits; buttons 4/5 in bits 4 and 5. */
+ val = dZ & 0x0f;
+ val |= (pThis->fAccumB << 1) & (RT_BIT(4) | RT_BIT(5));
+ ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dZ);
+ }
+ }
+
+ /* Clear the accumulators. */
+ pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = pThis->fAccumB = 0;
+
+ /* Poke the KBC to update its state. */
+ KBCUpdateInterrupts(pThis->pParent);
+}
+
+/* Event rate throttling timer to emulate the auxiliary device sampling rate.
+ */
+static DECLCALLBACK(void) ps2mThrottleTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+ PPS2M pThis = (PS2M *)pvUser; NOREF(pDevIns);
+ uint32_t uHaveEvents;
+
+ /* Grab the lock to avoid races with PutEvent(). */
+ int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
+ AssertReleaseRC(rc);
+
+#if 0
+ /* If the input queue is not empty, restart the timer. */
+#else
+ /* If more movement is accumulated, report it and restart the timer. */
+ uHaveEvents = pThis->iAccumX | pThis->iAccumY | pThis->iAccumZ | pThis->fAccumB;
+ LogFlowFunc(("Have%s events\n", uHaveEvents ? "" : " no"));
+
+ if (uHaveEvents)
+#endif
+ {
+ ps2mReportAccumulatedEvents(pThis);
+ TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
+ }
+ else
+ pThis->fThrottleActive = false;
+
+ PDMCritSectLeave(pThis->pCritSectR3);
+}
+
+/* The auxiliary device 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)
+{
+ PPS2M pThis = (PS2M *)pvUser; NOREF(pDevIns);
+
+ LogFlowFunc(("Delay timer: cmd %02X\n", pThis->u8CurrCmd));
+
+ Assert(pThis->u8CurrCmd == ACMD_RESET);
+ ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_BAT_OK);
+ ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, 0);
+ pThis->enmMode = AUX_MODE_STD;
+ pThis->u8CurrCmd = 0;
+
+ ///@todo Might want a PS2MCompleteCommand() to push last response, clear command, and kick the KBC...
+ /* Give the KBC a kick. */
+ KBCUpdateInterrupts(pThis->pParent);
+
+ //@todo: move to its proper home!
+ ps2mSetDriverState(pThis, true);
+}
+
+
+/**
+ * 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" };
+ 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. */
+ pThis->iAccumX += dx;
+ pThis->iAccumY += dy;
+ pThis->iAccumZ += dz;
+ pThis->fAccumB |= fButtons & PS2M_STD_BTN_MASK; //@todo: accumulate based on current protocol?
+
+#if 1
+ /* Report the event and the throttle timer unless it's already running. */
+ if (!pThis->fThrottleActive)
+ {
+ ps2mReportAccumulatedEvents(pThis);
+ pThis->fThrottleActive = true;
+ TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
+ }
+#else
+ /* Clamp the delta values to the allowed range. */
+ dx = RT_MIN(RT_MAX(dx, -256), 255);
+ dy = RT_MIN(RT_MAX(dy, -256), 255);
+
+ /* Start with the sync bit. */
+ val = RT_BIT(3);
+ /* Add buttons 1-3. */
+ val |= fButtons & PS2M_STD_BTN_MASK;
+ /* Set the X/Y sign bits. */
+ if (dx < 0)
+ val |= RT_BIT(4);
+ if (dy < 0)
+ val |= RT_BIT(5);
+
+ ps2kInsertQueue((GeneriQ *)&pThis->evtQ, val);
+ ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dx);
+ ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dy);
+ if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
+ {
+ ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dz);
+ }
+#endif
+
+ 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);
+
+ LogFlowFunc(("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)
+{
+ uint32_t cPressed = 0;
+
+ 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);
+
+ //@todo: Is this the right place/logic?
+ ps2mSetDriverState(pThis, !!(pThis->u8State & AUX_STATE_ENABLED));
+
+ return rc;
+}
+
+void PS2MReset(PPS2M pThis)
+{
+ LogFlowFunc(("Resetting PS2M\n"));
+
+ pThis->u8CurrCmd = 0;
+
+ /* Clear the queues. */
+ ps2kClearQueue((GeneriQ *)&pThis->cmdQ);
+ ps2mSetDefaults(pThis); /* Also clears event queue. */
+
+ /* Activate the PS/2 mouse by default. */
+// if (pThis->Mouse.pDrv)
+// pThis->Mouse.pDrv->pfnSetActive(pThis->Mouse.pDrv, true);
+}
+
+void PS2MRelocate(PPS2M pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns)
+{
+ LogFlowFunc(("Relocating PS2M\n"));
+ pThis->pDelayTimerRC = TMTimerRCPtr(pThis->pDelayTimerR3);
+ pThis->pThrottleTimerRC = TMTimerRCPtr(pThis->pThrottleTimerR3);
+ NOREF(offDelta);
+}
+
+int PS2MConstruct(PPS2M pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance)
+{
+ int rc;
+
+ LogFlowFunc(("iInstance=%d\n", iInstance));
+
+ 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;
+ rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, ps2mThrottleTimer, pThis,
+ TMTIMER_FLAGS_NO_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_NO_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
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
diff --git a/src/VBox/Devices/Input/UsbKbd.cpp b/src/VBox/Devices/Input/UsbKbd.cpp
index e2c18e44..37ff8104 100644
--- a/src/VBox/Devices/Input/UsbKbd.cpp
+++ b/src/VBox/Devices/Input/UsbKbd.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2007-2010 Oracle Corporation
+ * Copyright (C) 2007-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -67,6 +67,10 @@
/** The size of an array needed to store all USB usage codes */
#define VBOX_USB_USAGE_ARRAY_SIZE (VBOX_USB_MAX_USAGE_CODE + 1)
#define USBHID_USAGE_ROLL_OVER 1
+/** The usage code of the first modifier key. */
+#define USBHID_MODIFIER_FIRST 0xE0
+/** The usage code of the last modifier key. */
+#define USBHID_MODIFIER_LAST 0xE7
/** @} */
/*******************************************************************************
@@ -298,7 +302,9 @@ static const VUSBDESCINTERFACEEX g_UsbHidInterfaceDesc =
/* .pvMore = */ NULL,
/* .pvClass = */ &g_UsbHidIfHidDesc,
/* .cbClass = */ sizeof(g_UsbHidIfHidDesc),
- &g_aUsbHidEndpointDescs[0]
+ &g_aUsbHidEndpointDescs[0],
+ /* .pIAD = */ NULL,
+ /* .cbIAD = */ 0
};
static const VUSBINTERFACE g_aUsbHidInterfaces[] =
@@ -379,7 +385,7 @@ static uint8_t aScancode2Hid[] =
0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, /* 38-3F */
0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, /* 40-47 */
0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, /* 48-4F */
- 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x64, 0x44, /* 50-57 */
+ 0x5a, 0x5b, 0x62, 0x63, 0x46, 0x00, 0x64, 0x44, /* 50-57 */
0x45, 0x67, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, /* 58-5F */
/* Sun keys: Props Undo Front Copy */
0x00, 0x00, 0x00, 0x00, 0x76, 0x7a, 0x77, 0x7c, /* 60-67 */
@@ -685,7 +691,7 @@ static void usbHidComputePressed(PUSBHIDK_REPORT pReport, char* pszBuf, unsigned
*/
static bool usbHidUsageCodeIsModifier(uint8_t u8Usage)
{
- return u8Usage >= 0xe0 && u8Usage <= 0xe7;
+ return u8Usage >= USBHID_MODIFIER_FIRST && u8Usage <= USBHID_MODIFIER_LAST;
}
/**
@@ -740,6 +746,12 @@ static int usbHidFillReport(PUSBHIDK_REPORT pReport,
if (iKey == 0x90 || iKey == 0x91)
rc = true;
}
+ /* Avoid "hanging" keys: If a key is unreported but no longer
+ * depressed, we'll need to report the key-up state, too.
+ */
+ if (pabUnreportedKeys[iKey] && !pabDepressedKeys[iKey])
+ rc = true;
+
pabUnreportedKeys[iKey] = 0;
}
}
@@ -848,6 +860,34 @@ static testUsbHidFillReport gsTestUsbHidFillReport;
#endif
/**
+ * Handles a SET_REPORT request sent to the default control pipe. Note
+ * that unrecognized requests are ignored without reporting an error.
+ */
+static void usbHidSetReport(PUSBHID pThis, PVUSBURB pUrb)
+{
+ PVUSBSETUP pSetup = (PVUSBSETUP)&pUrb->abData[0];
+ Assert(pSetup->bRequest == HID_REQ_SET_REPORT);
+
+ /* The LED report is the 3rd report, ID 0 (-> wValue 0x200). */
+ if (pSetup->wIndex == 0 && pSetup->wLength == 1 && pSetup->wValue == 0x200)
+ {
+ PDMKEYBLEDS enmLeds = PDMKEYBLEDS_NONE;
+ uint8_t u8LEDs = pUrb->abData[sizeof(*pSetup)];
+ LogFlowFunc(("Setting keybooard LEDs to u8LEDs=%02X\n", u8LEDs));
+
+ /* Translate LED state to PDM format and send upstream. */
+ if (u8LEDs & 0x01)
+ enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_NUMLOCK);
+ if (u8LEDs & 0x02)
+ enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_CAPSLOCK);
+ if (u8LEDs & 0x04)
+ enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_SCROLLLOCK);
+
+ pThis->Lun0.pDrv->pfnLedStatusChange(pThis->Lun0.pDrv, enmLeds);
+ }
+}
+
+/**
* Sends a state report to the host if there is a pending URB.
*/
static int usbHidSendReport(PUSBHID pThis)
@@ -918,7 +958,25 @@ static DECLCALLBACK(int) usbHidKeyboardPutEvent(PPDMIKEYBOARDPORT pInterface, ui
/* Due to host key repeat, we can get key events for keys which are
* already depressed. */
if (!pThis->abDepressedKeys[u8HidCode])
+ {
pThis->abUnreportedKeys[u8HidCode] = 1;
+
+ /* If a non-modifier key is being marked as unreported, also set
+ * all currently depressed modifer keys as unreported. This avoids
+ * problems where a simulated key sequence is sent too fast and
+ * by the time the key is reported, some previously reported
+ * modifiers are already released. This helps ensure that the guest
+ * sees the entire modifier(s)+key sequence in a single report.
+ */
+ if (!usbHidUsageCodeIsModifier(u8HidCode))
+ {
+ int iModKey;
+
+ for (iModKey = USBHID_MODIFIER_FIRST; iModKey <= USBHID_MODIFIER_LAST; ++iModKey)
+ if (pThis->abDepressedKeys[iModKey])
+ pThis->abUnreportedKeys[iModKey] = 1;
+ }
+ }
else
fHaveEvent = false;
pThis->abDepressedKeys[u8HidCode] = 1;
@@ -1038,7 +1096,7 @@ static int usbHidHandleIntrDevToHost(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb
case USBHIDREQSTATE_READY:
usbHidQueueAddTail(&pThis->ToHostQueue, pUrb);
- /* If device was not set idle, sent the current report right away. */
+ /* If device was not set idle, send the current report right away. */
if (pThis->bIdle != 0 || pThis->fHasPendingChanges)
usbHidSendReport(pThis);
LogFlow(("usbHidHandleIntrDevToHost: Sent report via %p:%s\n", pUrb, pUrb->pszDesc));
@@ -1229,6 +1287,20 @@ static int usbHidHandleDefaultPipe(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
}
break;
}
+ case HID_REQ_SET_REPORT:
+ {
+ switch (pSetup->bmRequestType)
+ {
+ case VUSB_TO_INTERFACE | VUSB_REQ_CLASS | VUSB_DIR_TO_DEVICE:
+ {
+ Log(("usbHid: SET_REPORT wValue=%#x wIndex=%#x wLength=%#x\n", pSetup->wValue, pSetup->wIndex, pSetup->wLength));
+ usbHidSetReport(pThis, pUrb);
+ return usbHidCompleteOk(pThis, pUrb, 0);
+ }
+ break;
+ }
+ break;
+ }
}
Log(("usbHid: Unimplemented class request: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
diff --git a/src/VBox/Devices/Input/UsbMouse.cpp b/src/VBox/Devices/Input/UsbMouse.cpp
index c85da4da..9a657839 100644
--- a/src/VBox/Devices/Input/UsbMouse.cpp
+++ b/src/VBox/Devices/Input/UsbMouse.cpp
@@ -3,7 +3,7 @@
*/
/*
- * Copyright (C) 2007-2010 Oracle Corporation
+ * Copyright (C) 2007-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -17,7 +17,7 @@
/*******************************************************************************
* Header Files *
*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_USB_MSD
+#define LOG_GROUP LOG_GROUP_USB_MOUSE
#include <VBox/vmm/pdmusb.h>
#include <VBox/log.h>
#include <VBox/err.h>
@@ -38,6 +38,7 @@
#define USBHID_STR_ID_MANUFACTURER 1
#define USBHID_STR_ID_PRODUCT_M 2
#define USBHID_STR_ID_PRODUCT_T 3
+#define USBHID_STR_ID_PRODUCT_MT 4
/** @} */
/** @name USB HID specific descriptor types
@@ -51,6 +52,7 @@
#define VBOX_USB_VENDOR 0x80EE
#define USBHID_PID_MOUSE 0x0020
#define USBHID_PID_TABLET 0x0021
+#define USBHID_PID_MULTI_TOUCH 0x0022
/** @} */
/*******************************************************************************
@@ -74,6 +76,20 @@ typedef enum USBHIDREQSTATE
USBHIDREQSTATE_END
} USBHIDREQSTATE;
+/**
+ * The device reporting mode.
+ * @todo Use an interface instead of an enum and switches.
+ */
+typedef enum USBHIDMODE
+{
+ /** Relative. */
+ USBHIDMODE_RELATIVE = 0,
+ /** Absolute. */
+ USBHIDMODE_ABSOLUTE,
+ /** Multi-touch. */
+ USBHIDMODE_MULTI_TOUCH
+} USBHIDMODE;
+
/**
* Endpoint status data.
@@ -107,12 +123,48 @@ typedef USBHIDURBQUEUE const *PCUSBHIDURBQUEUE;
*/
typedef struct USBHIDM_ACCUM
{
- uint32_t btn;
- int32_t dX;
- int32_t dY;
- int32_t dZ;
+ union
+ {
+ struct
+ {
+ uint32_t fButtons;
+ int32_t dx;
+ int32_t dy;
+ int32_t dz;
+ } Relative;
+ struct
+ {
+ uint32_t fButtons;
+ int32_t dz;
+ int32_t dw;
+ uint32_t x;
+ uint32_t y;
+ } Absolute;
+ } u;
} USBHIDM_ACCUM, *PUSBHIDM_ACCUM;
+#define MT_CONTACTS_PER_REPORT 5
+
+#define MT_CONTACT_MAX_COUNT 10
+
+#define MT_CONTACT_F_IN_CONTACT 0x01
+#define MT_CONTACT_F_IN_RANGE 0x02
+
+#define MT_CONTACT_S_ACTIVE 0x01 /* Contact must be reported to the guest. */
+#define MT_CONTACT_S_CANCELLED 0x02 /* Contact loss must be reported to the guest. */
+#define MT_CONTACT_S_REUSED 0x04 /* Report contact loss for the oldId and then new contact for the id. */
+#define MT_CONTACT_S_DIRTY 0x08 /* Temporary flag used to track already processed elements. */
+
+typedef struct MTCONTACT
+{
+ uint16_t x;
+ uint16_t y;
+ uint8_t id;
+ uint8_t flags;
+ uint8_t status;
+ uint8_t oldId; /* Valid only if MT_CONTACT_S_REUSED is set. */
+} MTCONTACT;
+
/**
* The USB HID instance data.
@@ -150,9 +202,9 @@ typedef struct USBHID
/** Someone is waiting on the done queue. */
bool fHaveDoneQueueWaiter;
/** If device has pending changes. */
- bool fHasPendingChanges;
- /** Is this an absolute pointing device (tablet)? Relative (mouse) otherwise. */
- bool isAbsolute;
+ bool fHasPendingChanges;
+ /** Is this a relative, absolute or multi-touch pointing device? */
+ USBHIDMODE enmMode;
/** Tablet coordinate shift factor for old and broken operating systems. */
uint8_t u8CoordShift;
@@ -175,50 +227,86 @@ typedef struct USBHID
R3PTRTYPE(PPDMIMOUSECONNECTOR) pDrv;
} Lun0;
+ MTCONTACT aCurrentContactState[MT_CONTACT_MAX_COUNT];
+ MTCONTACT aReportingContactState[MT_CONTACT_MAX_COUNT];
+ uint32_t u32LastTouchScanTime;
+ bool fTouchReporting;
+ bool fTouchStateUpdated;
} USBHID;
/** Pointer to the USB HID instance data. */
typedef USBHID *PUSBHID;
+#pragma pack(1)
/**
* The USB HID report structure for relative device.
*/
typedef struct USBHIDM_REPORT
{
- uint8_t btn;
+ uint8_t fButtons;
int8_t dx;
int8_t dy;
int8_t dz;
} USBHIDM_REPORT, *PUSBHIDM_REPORT;
/**
- * The USB HID report structure for relative device.
+ * The USB HID report structure for absolute device.
*/
typedef struct USBHIDT_REPORT
{
- uint8_t btn;
+ uint8_t fButtons;
int8_t dz;
- uint16_t cx;
- uint16_t cy;
+ int8_t dw;
+ uint8_t padding;
+ uint16_t x;
+ uint16_t y;
} USBHIDT_REPORT, *PUSBHIDT_REPORT;
/**
- * The combined USB HID report union for relative and absolute device.
+ * The combined USB HID report union for relative and absolute
+ * devices.
*/
typedef union USBHIDTM_REPORT
{
- USBHIDT_REPORT t;
USBHIDM_REPORT m;
+ USBHIDT_REPORT t;
} USBHIDTM_REPORT, *PUSBHIDTM_REPORT;
+/**
+ * The USB HID report structure for the multi-touch device.
+ */
+typedef struct USBHIDMT_REPORT
+{
+ uint8_t idReport;
+ uint8_t cContacts;
+ struct
+ {
+ uint8_t fContact;
+ uint8_t cContact;
+ uint16_t x;
+ uint16_t y;
+ } aContacts[MT_CONTACTS_PER_REPORT];
+ uint32_t u32ScanTime;
+} USBHIDMT_REPORT, *PUSBHIDMT_REPORT;
+
+typedef struct USBHIDMT_REPORT_POINTER
+{
+ uint8_t idReport;
+ uint8_t fButtons;
+ uint16_t x;
+ uint16_t y;
+} USBHIDMT_REPORT_POINTER;
+#pragma pack()
+
/*******************************************************************************
* Global Variables *
*******************************************************************************/
static const PDMUSBDESCCACHESTRING g_aUsbHidStrings_en_US[] =
{
- { USBHID_STR_ID_MANUFACTURER, "VirtualBox" },
- { USBHID_STR_ID_PRODUCT_M, "USB Mouse" },
- { USBHID_STR_ID_PRODUCT_T, "USB Tablet" },
+ { USBHID_STR_ID_MANUFACTURER, "VirtualBox" },
+ { USBHID_STR_ID_PRODUCT_M, "USB Mouse" },
+ { USBHID_STR_ID_PRODUCT_T, "USB Tablet" },
+ { USBHID_STR_ID_PRODUCT_MT, "USB Multi-Touch" },
};
static const PDMUSBDESCCACHELANG g_aUsbHidLanguages[] =
@@ -251,7 +339,24 @@ static const VUSBDESCENDPOINTEX g_aUsbHidTEndpointDescs[] =
/* .bDescriptorType = */ VUSB_DT_ENDPOINT,
/* .bEndpointAddress = */ 0x81 /* ep=1, in */,
/* .bmAttributes = */ 3 /* interrupt */,
- /* .wMaxPacketSize = */ 6,
+ /* .wMaxPacketSize = */ 8,
+ /* .bInterval = */ 10,
+ },
+ /* .pvMore = */ NULL,
+ /* .pvClass = */ NULL,
+ /* .cbClass = */ 0
+ },
+};
+
+static const VUSBDESCENDPOINTEX g_aUsbHidMTEndpointDescs[] =
+{
+ {
+ {
+ /* .bLength = */ sizeof(VUSBDESCENDPOINT),
+ /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
+ /* .bEndpointAddress = */ 0x81 /* ep=1, in */,
+ /* .bmAttributes = */ 3 /* interrupt */,
+ /* .wMaxPacketSize = */ 64,
/* .bInterval = */ 10,
},
/* .pvMore = */ NULL,
@@ -299,6 +404,7 @@ static const uint8_t g_UsbHidMReportDesc[] =
* as OS X shows no such problem. When X/Y is reported last, Windows behaves
* properly.
*/
+
static const uint8_t g_UsbHidTReportDesc[] =
{
/* Usage Page */ 0x05, 0x01, /* Generic Desktop */
@@ -324,6 +430,13 @@ static const uint8_t g_UsbHidTReportDesc[] =
/* Report Size */ 0x75, 0x08, /* 8 */
/* Report Count */ 0x95, 0x01, /* 1 */
/* Input */ 0x81, 0x06, /* Data, Value, Relative, Bit field */
+ /* Usage Page */ 0x05, 0x0C, /* Consumer Devices */
+ /* Usage */ 0x0A, 0x38, 0x02,/* AC Pan (horizontal wheel) */
+ /* Report Count */ 0x95, 0x01, /* 1 */
+ /* Input */ 0x81, 0x06, /* Data, Value, Relative, Bit field */
+ /* Report Size */ 0x75, 0x08, /* 8 (padding byte) */
+ /* Report Count */ 0x95, 0x01, /* 1 */
+ /* Input */ 0x81, 0x03, /* Constant, Value, Absolute, Bit field */
/* Usage Page */ 0x05, 0x01, /* Generic Desktop */
/* Usage */ 0x09, 0x30, /* X */
/* Usage */ 0x09, 0x31, /* Y */
@@ -338,6 +451,280 @@ static const uint8_t g_UsbHidTReportDesc[] =
/* End Collection */ 0xC0,
};
+/*
+ * Multi-touch device implementation based on "Windows Pointer Device Data Delivery Protocol"
+ * specification.
+ */
+
+#define REPORTID_TOUCH_POINTER 1
+#define REPORTID_TOUCH_EVENT 2
+#define REPORTID_TOUCH_MAX_COUNT 3
+#define REPORTID_TOUCH_QABLOB 4
+#define REPORTID_TOUCH_DEVCONFIG 5
+
+static const uint8_t g_UsbHidMTReportDesc[] =
+{
+/* Usage Page (Digitizer) */ 0x05, 0x0D,
+/* Usage (Touch Screen) */ 0x09, 0x04,
+/* Collection (Application) */ 0xA1, 0x01,
+/* Report ID */ 0x85, REPORTID_TOUCH_EVENT,
+/* Usage Page (Digitizer) */ 0x05, 0x0D,
+/* Usage (Contact count) */ 0x09, 0x54,
+/* Report Size (8) */ 0x75, 0x08,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (12) */ 0x25, 0x0C,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+
+/* MT_CONTACTS_PER_REPORT structs u8TipSwitch, u8ContactIdentifier, u16X, u16Y */
+/* 1 of 5 */
+/* Usage (Finger) */ 0x09, 0x22,
+/* Collection (Logical) */ 0xA1, 0x02,
+/* Usage (Tip Switch) */ 0x09, 0x42,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Size (1) */ 0x75, 0x01,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+
+/* Usage (In Range) */ 0x09, 0x32,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Size (1) */ 0x75, 0x01,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+
+/* Report Count (6) */ 0x95, 0x06,
+/* Input (Cnst,Var) */ 0x81, 0x03,
+
+/* Report Size (8) */ 0x75, 0x08,
+/* Usage (Contact identifier) */ 0x09, 0x51,
+/* Report Count (1) */ 0x95, 0x01,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (32) */ 0x25, 0x20,
+/* Input (Var) */ 0x81, 0x02,
+
+/* Usage Page (Generic Desktop) */ 0x05, 0x01,
+/* Logical Maximum (32K) */ 0x26, 0xFF, 0x7F,
+/* Report Size (16) */ 0x75, 0x10,
+/* Usage (X) */ 0x09, 0x30,
+/* Input (Var) */ 0x81, 0x02,
+
+/* Usage (Y) */ 0x09, 0x31,
+/* Input (Var) */ 0x81, 0x02,
+/* End Collection */ 0xC0,
+/* 2 of 5 */
+/* Usage Page (Digitizer) */ 0x05, 0x0D,
+/* Usage (Finger) */ 0x09, 0x22,
+/* Collection (Logical) */ 0xA1, 0x02,
+/* Usage (Tip Switch) */ 0x09, 0x42,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Size (1) */ 0x75, 0x01,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage (In Range) */ 0x09, 0x32,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Size (1) */ 0x75, 0x01,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+/* Report Count (6) */ 0x95, 0x06,
+/* Input (Cnst,Var) */ 0x81, 0x03,
+/* Report Size (8) */ 0x75, 0x08,
+/* Usage (Contact identifier) */ 0x09, 0x51,
+/* Report Count (1) */ 0x95, 0x01,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (32) */ 0x25, 0x20,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage Page (Generic Desktop) */ 0x05, 0x01,
+/* Logical Maximum (32K) */ 0x26, 0xFF, 0x7F,
+/* Report Size (16) */ 0x75, 0x10,
+/* Usage (X) */ 0x09, 0x30,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage (Y) */ 0x09, 0x31,
+/* Input (Var) */ 0x81, 0x02,
+/* End Collection */ 0xC0,
+/* 3 of 5 */
+/* Usage Page (Digitizer) */ 0x05, 0x0D,
+/* Usage (Finger) */ 0x09, 0x22,
+/* Collection (Logical) */ 0xA1, 0x02,
+/* Usage (Tip Switch) */ 0x09, 0x42,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Size (1) */ 0x75, 0x01,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage (In Range) */ 0x09, 0x32,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Size (1) */ 0x75, 0x01,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+/* Report Count (6) */ 0x95, 0x06,
+/* Input (Cnst,Var) */ 0x81, 0x03,
+/* Report Size (8) */ 0x75, 0x08,
+/* Usage (Contact identifier) */ 0x09, 0x51,
+/* Report Count (1) */ 0x95, 0x01,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (32) */ 0x25, 0x20,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage Page (Generic Desktop) */ 0x05, 0x01,
+/* Logical Maximum (32K) */ 0x26, 0xFF, 0x7F,
+/* Report Size (16) */ 0x75, 0x10,
+/* Usage (X) */ 0x09, 0x30,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage (Y) */ 0x09, 0x31,
+/* Input (Var) */ 0x81, 0x02,
+/* End Collection */ 0xC0,
+/* 4 of 5 */
+/* Usage Page (Digitizer) */ 0x05, 0x0D,
+/* Usage (Finger) */ 0x09, 0x22,
+/* Collection (Logical) */ 0xA1, 0x02,
+/* Usage (Tip Switch) */ 0x09, 0x42,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Size (1) */ 0x75, 0x01,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage (In Range) */ 0x09, 0x32,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Size (1) */ 0x75, 0x01,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+/* Report Count (6) */ 0x95, 0x06,
+/* Input (Cnst,Var) */ 0x81, 0x03,
+/* Report Size (8) */ 0x75, 0x08,
+/* Usage (Contact identifier) */ 0x09, 0x51,
+/* Report Count (1) */ 0x95, 0x01,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (32) */ 0x25, 0x20,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage Page (Generic Desktop) */ 0x05, 0x01,
+/* Logical Maximum (32K) */ 0x26, 0xFF, 0x7F,
+/* Report Size (16) */ 0x75, 0x10,
+/* Usage (X) */ 0x09, 0x30,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage (Y) */ 0x09, 0x31,
+/* Input (Var) */ 0x81, 0x02,
+/* End Collection */ 0xC0,
+/* 5 of 5 */
+/* Usage Page (Digitizer) */ 0x05, 0x0D,
+/* Usage (Finger) */ 0x09, 0x22,
+/* Collection (Logical) */ 0xA1, 0x02,
+/* Usage (Tip Switch) */ 0x09, 0x42,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Size (1) */ 0x75, 0x01,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage (In Range) */ 0x09, 0x32,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Size (1) */ 0x75, 0x01,
+/* Report Count (1) */ 0x95, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+/* Report Count (6) */ 0x95, 0x06,
+/* Input (Cnst,Var) */ 0x81, 0x03,
+/* Report Size (8) */ 0x75, 0x08,
+/* Usage (Contact identifier) */ 0x09, 0x51,
+/* Report Count (1) */ 0x95, 0x01,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (32) */ 0x25, 0x20,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage Page (Generic Desktop) */ 0x05, 0x01,
+/* Logical Maximum (32K) */ 0x26, 0xFF, 0x7F,
+/* Report Size (16) */ 0x75, 0x10,
+/* Usage (X) */ 0x09, 0x30,
+/* Input (Var) */ 0x81, 0x02,
+/* Usage (Y) */ 0x09, 0x31,
+/* Input (Var) */ 0x81, 0x02,
+/* End Collection */ 0xC0,
+
+/* Note: "Scan time" usage is required for all touch devices (in 100microseconds units). */
+/* Usage Page (Digitizer) */ 0x05, 0x0D,
+/* Logical Minimum (0) */ 0x17, 0x00, 0x00, 0x00, 0x00,
+/* Logical Maximum (2147483647) */ 0x27, 0xFF, 0xFF, 0xFF, 0x7F,
+/* Report Size (32) */ 0x75, 0x20,
+/* Report Count (1) */ 0x95, 0x01,
+/* Unit Exponent (0) */ 0x55, 0x00,
+/* Unit (None) */ 0x65, 0x00,
+/* Usage (Scan time) */ 0x09, 0x56,
+/* Input (Var) */ 0x81, 0x02,
+
+/* Report ID */ 0x85, REPORTID_TOUCH_MAX_COUNT,
+/* Usage (Contact count maximum) */ 0x09, 0x55,
+/* Usage (Device identifier) */ 0x09, 0x53,
+/* Report Size (8) */ 0x75, 0x08,
+/* Report Count (2) */ 0x95, 0x02,
+/* Logical Maximum (255) */ 0x26, 0xFF, 0x00,
+/* Feature (Var) */ 0xB1, 0x02,
+
+/* Usage Page (Vendor-Defined 1) */ 0x06, 0x00, 0xFF,
+/* Usage (QA blob) */ 0x09, 0xC5,
+/* Report ID */ 0x85, REPORTID_TOUCH_QABLOB,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (255) */ 0x26, 0xFF, 0x00,
+/* Report Size (8) */ 0x75, 0x08,
+/* Report Count (256) */ 0x96, 0x00, 0x01,
+/* Feature (Var) */ 0xB1, 0x02,
+/* End Collection */ 0xC0,
+
+/* Note: the pointer report is required by specification:
+ * "The report descriptor for a multiple input device must include at least
+ * one top-level collection for the primary device and a separate top-level
+ * collection for the mouse."
+ */
+/* Usage Page (Generic Desktop) */ 0x05, 0x01,
+/* Usage (Pointer) */ 0x09, 0x01,
+/* Collection (Application) */ 0xA1, 0x01,
+/* Report ID */ 0x85, REPORTID_TOUCH_POINTER,
+/* Usage (Pointer) */ 0x09, 0x01,
+/* Collection (Logical) */ 0xA1, 0x02,
+/* Usage Page (Button) */ 0x05, 0x09,
+/* Usage Minimum (Button 1) */ 0x19, 0x01,
+/* Usage Maximum (Button 2) */ 0x29, 0x02,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (1) */ 0x25, 0x01,
+/* Report Count (2) */ 0x95, 0x02,
+/* Report Size (1) */ 0x75, 0x01,
+/* Input (Var) */ 0x81, 0x02,
+/* Report Count (1) */ 0x95, 0x01,
+/* Report Size (6) */ 0x75, 0x06,
+/* Input (Cnst,Ary,Abs) */ 0x81, 0x01,
+/* Usage Page (Generic Desktop) */ 0x05, 0x01,
+/* Usage (X) */ 0x09, 0x30,
+/* Usage (Y) */ 0x09, 0x31,
+/* Logical Minimum (0) */ 0x16, 0x00, 0x00,
+/* Logical Maximum (32K) */ 0x26, 0xFF, 0x7F,
+/* Physical Minimum (0) */ 0x36, 0x00, 0x00,
+/* Physical Maximum (32K) */ 0x46, 0xFF, 0x7F,
+/* Unit (None) */ 0x66, 0x00, 0x00,
+/* Report Size (16) */ 0x75, 0x10,
+/* Report Count (2) */ 0x95, 0x02,
+/* Input (Var) */ 0x81, 0x02,
+/* End Collection */ 0xC0,
+/* End Collection */ 0xC0,
+
+/* Usage Page (Digitizer) */ 0x05, 0x0D,
+/* Usage (Device configuration) */ 0x09, 0x0E,
+/* Collection (Application) */ 0xA1, 0x01,
+/* Report ID */ 0x85, REPORTID_TOUCH_DEVCONFIG,
+/* Usage (Device settings) */ 0x09, 0x23,
+/* Collection (Logical) */ 0xA1, 0x02,
+/* Usage (Device mode) */ 0x09, 0x52,
+/* Usage (Device identifier) */ 0x09, 0x53,
+/* Logical Minimum (0) */ 0x15, 0x00,
+/* Logical Maximum (10) */ 0x25, 0x0A,
+/* Report Size (8) */ 0x75, 0x08,
+/* Report Count (2) */ 0x95, 0x02,
+/* Feature (Var) */ 0xB1, 0x02,
+/* End Collection */ 0xC0,
+/* End Collection */ 0xC0
+};
+
+/** @todo Do these really have to all be duplicated three times? */
/* Additional HID class interface descriptor. */
static const uint8_t g_UsbHidMIfHidDesc[] =
{
@@ -362,6 +749,19 @@ static const uint8_t g_UsbHidTIfHidDesc[] =
/* .wDescriptorLength = */ sizeof(g_UsbHidTReportDesc), 0x00
};
+/* Additional HID class interface descriptor. */
+static const uint8_t g_UsbHidMTIfHidDesc[] =
+{
+ /* .bLength = */ 0x09,
+ /* .bDescriptorType = */ 0x21, /* HID */
+ /* .bcdHID = */ 0x10, 0x02, /* 2.1 */
+ /* .bCountryCode = */ 0,
+ /* .bNumDescriptors = */ 1,
+ /* .bDescriptorType = */ 0x22, /* Report */
+ /* .wDescriptorLength = */ (uint8_t)(sizeof(g_UsbHidMTReportDesc) & 0xFF),
+ (uint8_t)((sizeof(g_UsbHidMTReportDesc) >> 8) & 0xFF)
+};
+
static const VUSBDESCINTERFACEEX g_UsbHidMInterfaceDesc =
{
{
@@ -378,7 +778,9 @@ static const VUSBDESCINTERFACEEX g_UsbHidMInterfaceDesc =
/* .pvMore = */ NULL,
/* .pvClass = */ &g_UsbHidMIfHidDesc,
/* .cbClass = */ sizeof(g_UsbHidMIfHidDesc),
- &g_aUsbHidMEndpointDescs[0]
+ &g_aUsbHidMEndpointDescs[0],
+ /* .pIAD = */ NULL,
+ /* .cbIAD = */ 0
};
static const VUSBDESCINTERFACEEX g_UsbHidTInterfaceDesc =
@@ -397,7 +799,30 @@ static const VUSBDESCINTERFACEEX g_UsbHidTInterfaceDesc =
/* .pvMore = */ NULL,
/* .pvClass = */ &g_UsbHidTIfHidDesc,
/* .cbClass = */ sizeof(g_UsbHidTIfHidDesc),
- &g_aUsbHidTEndpointDescs[0]
+ &g_aUsbHidTEndpointDescs[0],
+ /* .pIAD = */ NULL,
+ /* .cbIAD = */ 0
+};
+
+static const VUSBDESCINTERFACEEX g_UsbHidMTInterfaceDesc =
+{
+ {
+ /* .bLength = */ sizeof(VUSBDESCINTERFACE),
+ /* .bDescriptorType = */ VUSB_DT_INTERFACE,
+ /* .bInterfaceNumber = */ 0,
+ /* .bAlternateSetting = */ 0,
+ /* .bNumEndpoints = */ 1,
+ /* .bInterfaceClass = */ 3 /* HID */,
+ /* .bInterfaceSubClass = */ 0 /* No subclass - no boot interface. */,
+ /* .bInterfaceProtocol = */ 0 /* No protocol - no boot interface. */,
+ /* .iInterface = */ 0
+ },
+ /* .pvMore = */ NULL,
+ /* .pvClass = */ &g_UsbHidMTIfHidDesc,
+ /* .cbClass = */ sizeof(g_UsbHidMTIfHidDesc),
+ &g_aUsbHidMTEndpointDescs[0],
+ /* .pIAD = */ NULL,
+ /* .cbIAD = */ 0
};
static const VUSBINTERFACE g_aUsbHidMInterfaces[] =
@@ -410,6 +835,11 @@ static const VUSBINTERFACE g_aUsbHidTInterfaces[] =
{ &g_UsbHidTInterfaceDesc, /* .cSettings = */ 1 },
};
+static const VUSBINTERFACE g_aUsbHidMTInterfaces[] =
+{
+ { &g_UsbHidMTInterfaceDesc, /* .cSettings = */ 1 },
+};
+
static const VUSBDESCCONFIGEX g_UsbHidMConfigDesc =
{
{
@@ -444,6 +874,23 @@ static const VUSBDESCCONFIGEX g_UsbHidTConfigDesc =
NULL /* pvOriginal */
};
+static const VUSBDESCCONFIGEX g_UsbHidMTConfigDesc =
+{
+ {
+ /* .bLength = */ sizeof(VUSBDESCCONFIG),
+ /* .bDescriptorType = */ VUSB_DT_CONFIG,
+ /* .wTotalLength = */ 0 /* recalculated on read */,
+ /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbHidMTInterfaces),
+ /* .bConfigurationValue =*/ 1,
+ /* .iConfiguration = */ 0,
+ /* .bmAttributes = */ RT_BIT(7),
+ /* .MaxPower = */ 50 /* 100mA */
+ },
+ NULL, /* pvMore */
+ &g_aUsbHidMTInterfaces[0],
+ NULL /* pvOriginal */
+};
+
static const VUSBDESCDEVICE g_UsbHidMDeviceDesc =
{
/* .bLength = */ sizeof(g_UsbHidMDeviceDesc),
@@ -480,6 +927,24 @@ static const VUSBDESCDEVICE g_UsbHidTDeviceDesc =
/* .bNumConfigurations = */ 1
};
+static const VUSBDESCDEVICE g_UsbHidMTDeviceDesc =
+{
+ /* .bLength = */ sizeof(g_UsbHidMTDeviceDesc),
+ /* .bDescriptorType = */ VUSB_DT_DEVICE,
+ /* .bcdUsb = */ 0x110, /* 1.1 */
+ /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
+ /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
+ /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
+ /* .bMaxPacketSize0 = */ 8,
+ /* .idVendor = */ VBOX_USB_VENDOR,
+ /* .idProduct = */ USBHID_PID_MULTI_TOUCH,
+ /* .bcdDevice = */ 0x0100, /* 1.0 */
+ /* .iManufacturer = */ USBHID_STR_ID_MANUFACTURER,
+ /* .iProduct = */ USBHID_STR_ID_PRODUCT_MT,
+ /* .iSerialNumber = */ 0,
+ /* .bNumConfigurations = */ 1
+};
+
static const PDMUSBDESCCACHE g_UsbHidMDescCache =
{
/* .pDevice = */ &g_UsbHidMDeviceDesc,
@@ -500,6 +965,16 @@ static const PDMUSBDESCCACHE g_UsbHidTDescCache =
/* .fUseCachedStringsDescriptors = */ true
};
+static const PDMUSBDESCCACHE g_UsbHidMTDescCache =
+{
+ /* .pDevice = */ &g_UsbHidMTDeviceDesc,
+ /* .paConfigs = */ &g_UsbHidMTConfigDesc,
+ /* .paLanguages = */ g_aUsbHidLanguages,
+ /* .cLanguages = */ RT_ELEMENTS(g_aUsbHidLanguages),
+ /* .fUseCachedDescriptors = */ true,
+ /* .fUseCachedStringsDescriptors = */ true
+};
+
/*******************************************************************************
* Internal Functions *
@@ -565,7 +1040,11 @@ DECLINLINE(bool) usbHidQueueRemove(PUSBHIDURBQUEUE pQueue, PVUSBURB pUrb)
{
PVUSBURB pCur = pQueue->pHead;
if (pCur == pUrb)
+ {
pQueue->pHead = pUrb->Dev.pNext;
+ if (!pUrb->Dev.pNext)
+ pQueue->ppTail = &pQueue->pHead;
+ }
else
{
while (pCur)
@@ -579,9 +1058,10 @@ DECLINLINE(bool) usbHidQueueRemove(PUSBHIDURBQUEUE pQueue, PVUSBURB pUrb)
}
if (!pCur)
return false;
+ if (!pUrb->Dev.pNext)
+ pQueue->ppTail = &pCur->Dev.pNext;
}
- if (!pUrb->Dev.pNext)
- pQueue->ppTail = &pQueue->pHead;
+ pUrb->Dev.pNext = NULL;
return true;
}
@@ -622,7 +1102,8 @@ static void usbHidLinkDone(PUSBHID pThis, PVUSBURB pUrb)
*/
static int usbHidCompleteStall(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb, const char *pszWhy)
{
- Log(("usbHidCompleteStall/#%u: pUrb=%p:%s: %s\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, pszWhy));
+ LogRelFlow(("usbHidCompleteStall/#%u: pUrb=%p:%s: %s\n",
+ pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, pszWhy));
pUrb->enmStatus = VUSBSTATUS_STALL;
@@ -645,7 +1126,8 @@ static int usbHidCompleteStall(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb, cons
*/
static int usbHidCompleteOk(PUSBHID pThis, PVUSBURB pUrb, size_t cbData)
{
- Log(("usbHidCompleteOk/#%u: pUrb=%p:%s cbData=%#zx\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, cbData));
+ LogRelFlow(("usbHidCompleteOk/#%u: pUrb=%p:%s cbData=%#zx\n",
+ pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, cbData));
pUrb->enmStatus = VUSBSTATUS_OK;
pUrb->cbData = (uint32_t)cbData;
@@ -679,6 +1161,7 @@ static int usbHidResetWorker(PUSBHID pThis, PVUSBURB pUrb, bool fSetConfig)
*/
pThis->enmState = USBHIDREQSTATE_READY;
pThis->fHasPendingChanges = false;
+ pThis->fTouchStateUpdated = false;
for (unsigned i = 0; i < RT_ELEMENTS(pThis->aEps); i++)
pThis->aEps[i].fHalted = false;
@@ -714,29 +1197,41 @@ static int8_t clamp_i8(int32_t val)
/**
* Create a USB HID report report based on the currently accumulated data.
*/
-static size_t usbHidFillReport(PUSBHIDTM_REPORT pReport, PUSBHIDM_ACCUM pAccumulated, bool isAbsolute)
+static size_t usbHidFillReport(PUSBHIDTM_REPORT pReport,
+ PUSBHIDM_ACCUM pAccumulated, USBHIDMODE enmMode)
{
size_t cbCopy;
- if (isAbsolute)
+ switch (enmMode)
{
- pReport->t.btn = pAccumulated->btn;
- pReport->t.cx = pAccumulated->dX;
- pReport->t.cy = pAccumulated->dY;
- pReport->t.dz = clamp_i8(pAccumulated->dZ);
+ case USBHIDMODE_ABSOLUTE:
+ pReport->t.fButtons = pAccumulated->u.Absolute.fButtons;
+ pReport->t.dz = clamp_i8(pAccumulated->u.Absolute.dz);
+ pReport->t.dw = clamp_i8(pAccumulated->u.Absolute.dw);
+ pReport->t.padding = 0;
+ pReport->t.x = pAccumulated->u.Absolute.x;
+ pReport->t.y = pAccumulated->u.Absolute.y;
cbCopy = sizeof(pReport->t);
-// LogRel(("Abs movement, X=%d, Y=%d, dZ=%d, btn=%02x, report size %d\n", pReport->t.cx, pReport->t.cy, pReport->t.dz, pReport->t.btn, cbCopy));
- }
- else
- {
- pReport->m.btn = pAccumulated->btn;
- pReport->m.dx = clamp_i8(pAccumulated->dX);
- pReport->m.dy = clamp_i8(pAccumulated->dY);
- pReport->m.dz = clamp_i8(pAccumulated->dZ);
-
+ LogRel3(("Abs event, x=%d, y=%d, fButtons=%02x, report size %d\n",
+ pReport->t.x, pReport->t.y, pReport->t.fButtons,
+ cbCopy));
+ break;
+ case USBHIDMODE_RELATIVE:
+ pReport->m.fButtons = pAccumulated->u.Relative.fButtons;
+ pReport->m.dx = clamp_i8(pAccumulated->u.Relative.dx);
+ pReport->m.dy = clamp_i8(pAccumulated->u.Relative.dy);
+ pReport->m.dz = clamp_i8(pAccumulated->u.Relative.dz);
+
cbCopy = sizeof(pReport->m);
-// LogRel(("Rel movement, dX=%d, dY=%d, dZ=%d, btn=%02x, report size %d\n", pReport->m.dx, pReport->m.dy, pReport->m.dz, pReport->m.btn, cbCopy));
+ LogRel3(("Rel event, dx=%d, dy=%d, dz=%d, fButtons=%02x, report size %d\n",
+ pReport->m.dx, pReport->m.dy, pReport->m.dz,
+ pReport->m.fButtons, cbCopy));
+ break;
+ default:
+ AssertFailed(); /* Unexpected here. */
+ cbCopy = 0;
+ break;
}
/* Clear the accumulated movement. */
@@ -745,6 +1240,155 @@ static size_t usbHidFillReport(PUSBHIDTM_REPORT pReport, PUSBHIDM_ACCUM pAccumul
return cbCopy;
}
+DECLINLINE(MTCONTACT *) usbHidFindMTContact(MTCONTACT *paContacts, size_t cContacts,
+ uint8_t u8Mask, uint8_t u8Value)
+{
+ size_t i;
+ for (i = 0; i < cContacts; i++)
+ {
+ if ((paContacts[i].status & u8Mask) == u8Value)
+ {
+ return &paContacts[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int usbHidSendMultiTouchReport(PUSBHID pThis, PVUSBURB pUrb)
+{
+ uint8_t i;
+ MTCONTACT *pRepContact;
+ MTCONTACT *pCurContact;
+
+ /* Number of contacts to be reported. In hybrid mode the first report contains
+ * total number of contacts and subsequent reports contain 0.
+ */
+ uint8_t cContacts = 0;
+
+ Assert(pThis->fHasPendingChanges);
+
+ if (!pThis->fTouchReporting)
+ {
+ pThis->fTouchReporting = true;
+ pThis->fTouchStateUpdated = false;
+
+ /* Update the reporting state with the new current state.
+ * Also mark all active contacts in reporting state as dirty,
+ * that is they must be reported to the guest.
+ */
+ for (i = 0; i < MT_CONTACT_MAX_COUNT; i++)
+ {
+ pRepContact = &pThis->aReportingContactState[i];
+ pCurContact = &pThis->aCurrentContactState[i];
+
+ if (pCurContact->status & MT_CONTACT_S_ACTIVE)
+ {
+ if (pCurContact->status & MT_CONTACT_S_REUSED)
+ {
+ pCurContact->status &= ~MT_CONTACT_S_REUSED;
+
+ /* Keep x,y. Will report lost contact at this point. */
+ pRepContact->id = pCurContact->oldId;
+ pRepContact->flags = 0;
+ pRepContact->status = MT_CONTACT_S_REUSED;
+ }
+ else if (pThis->aCurrentContactState[i].status & MT_CONTACT_S_CANCELLED)
+ {
+ pCurContact->status &= ~(MT_CONTACT_S_CANCELLED | MT_CONTACT_S_ACTIVE);
+
+ /* Keep x,y. Will report lost contact at this point. */
+ pRepContact->id = pCurContact->id;
+ pRepContact->flags = 0;
+ pRepContact->status = 0;
+ }
+ else
+ {
+ if (pCurContact->flags == 0)
+ {
+ pCurContact->status &= ~MT_CONTACT_S_ACTIVE; /* Contact disapeared. */
+ }
+
+ pRepContact->x = pCurContact->x;
+ pRepContact->y = pCurContact->y;
+ pRepContact->id = pCurContact->id;
+ pRepContact->flags = pCurContact->flags;
+ pRepContact->status = 0;
+ }
+
+ cContacts++;
+
+ pRepContact->status |= MT_CONTACT_S_DIRTY;
+ }
+ else
+ {
+ pRepContact->status = 0;
+ }
+ }
+ }
+
+ /* Report current state. */
+ USBHIDMT_REPORT *p = (USBHIDMT_REPORT *)&pUrb->abData[0];
+ RT_ZERO(*p);
+
+ p->idReport = REPORTID_TOUCH_EVENT;
+ p->cContacts = cContacts;
+
+ uint8_t iReportedContact;
+ for (iReportedContact = 0; iReportedContact < MT_CONTACTS_PER_REPORT; iReportedContact++)
+ {
+ /* Find the next not reported contact. */
+ pRepContact = usbHidFindMTContact(pThis->aReportingContactState, RT_ELEMENTS(pThis->aReportingContactState),
+ MT_CONTACT_S_DIRTY, MT_CONTACT_S_DIRTY);
+
+ if (!pRepContact)
+ {
+ LogRel3(("usbHid: no more touch contacts to report\n"));
+ break;
+ }
+
+ if (pRepContact->status & MT_CONTACT_S_REUSED)
+ {
+ /* Do not clear DIRTY flag for contacts which were reused.
+ * Because two reports must be generated:
+ * one for old contact off, and the second for new contact on.
+ */
+ pRepContact->status &= ~MT_CONTACT_S_REUSED;
+ }
+ else
+ {
+ pRepContact->status &= ~MT_CONTACT_S_DIRTY;
+ }
+
+ p->aContacts[iReportedContact].fContact = pRepContact->flags;
+ p->aContacts[iReportedContact].cContact = pRepContact->id;
+ p->aContacts[iReportedContact].x = pRepContact->x >> pThis->u8CoordShift;
+ p->aContacts[iReportedContact].y = pRepContact->y >> pThis->u8CoordShift;
+ }
+
+ p->u32ScanTime = pThis->u32LastTouchScanTime * 10;
+
+ Assert(iReportedContact > 0);
+
+ /* Reset TouchReporting if all contacts reported. */
+ pRepContact = usbHidFindMTContact(pThis->aReportingContactState, RT_ELEMENTS(pThis->aReportingContactState),
+ MT_CONTACT_S_DIRTY, MT_CONTACT_S_DIRTY);
+
+ if (!pRepContact)
+ {
+ LogRel3(("usbHid: all touch contacts reported\n"));
+ pThis->fTouchReporting = false;
+ pThis->fHasPendingChanges = pThis->fTouchStateUpdated;
+ }
+ else
+ {
+ pThis->fHasPendingChanges = true;
+ }
+
+ LogRel3(("usbHid: reporting touch contact:\n%.*Rhxd\n", sizeof(USBHIDMT_REPORT), p));
+ return usbHidCompleteOk(pThis, pUrb, sizeof(USBHIDMT_REPORT));
+}
+
/**
* Sends a state report to the host if there is a pending URB.
*/
@@ -752,18 +1396,26 @@ static int usbHidSendReport(PUSBHID pThis)
{
PVUSBURB pUrb = usbHidQueueRemoveHead(&pThis->ToHostQueue);
+ if (pThis->enmMode == USBHIDMODE_MULTI_TOUCH)
+ {
+ /* This device uses a different reporting method and fHasPendingChanges maintenance. */
+ if (pUrb)
+ return usbHidSendMultiTouchReport(pThis, pUrb);
+ return VINF_SUCCESS;
+ }
+
if (pUrb)
{
PUSBHIDTM_REPORT pReport = (PUSBHIDTM_REPORT)&pUrb->abData[0];
size_t cbCopy;
- cbCopy = usbHidFillReport(pReport, &pThis->PtrDelta, pThis->isAbsolute);
+ cbCopy = usbHidFillReport(pReport, &pThis->PtrDelta, pThis->enmMode);
pThis->fHasPendingChanges = false;
return usbHidCompleteOk(pThis, pUrb, cbCopy);
}
else
{
- Log2(("No available URB for USB mouse\n"));
+ LogRelFlow(("No available URB for USB mouse\n"));
pThis->fHasPendingChanges = true;
}
return VINF_EOF;
@@ -781,17 +1433,11 @@ static DECLCALLBACK(void *) usbHidMouseQueryInterface(PPDMIBASE pInterface, cons
}
/**
- * Relative mouse event handler.
- *
- * @returns VBox status code.
- * @param pInterface Pointer to the mouse port interface (KBDState::Mouse.iPort).
- * @param i32DeltaX The X delta.
- * @param i32DeltaY The Y delta.
- * @param i32DeltaZ The Z delta.
- * @param i32DeltaW The W delta.
- * @param fButtonStates The button states.
+ * @interface_method_impl{PDMIMOUSEPORT,pfnPutEvent}
*/
-static DECLCALLBACK(int) usbHidMousePutEvent(PPDMIMOUSEPORT pInterface, int32_t i32DeltaX, int32_t i32DeltaY, int32_t i32DeltaZ, int32_t i32DeltaW, uint32_t fButtonStates)
+static DECLCALLBACK(int) usbHidMousePutEvent(PPDMIMOUSEPORT pInterface,
+ int32_t dx, int32_t dy, int32_t dz,
+ int32_t dw, uint32_t fButtons)
{
PUSBHID pThis = RT_FROM_MEMBER(pInterface, USBHID, Lun0.IPort);
RTCritSectEnter(&pThis->CritSect);
@@ -799,10 +1445,10 @@ static DECLCALLBACK(int) usbHidMousePutEvent(PPDMIMOUSEPORT pInterface, int32_t
/* Accumulate movement - the events from the front end may arrive
* at a much higher rate than USB can handle.
*/
- pThis->PtrDelta.btn = fButtonStates;
- pThis->PtrDelta.dX += i32DeltaX;
- pThis->PtrDelta.dY += i32DeltaY;
- pThis->PtrDelta.dZ -= i32DeltaZ; /* Inverted! */
+ pThis->PtrDelta.u.Relative.fButtons = fButtons;
+ pThis->PtrDelta.u.Relative.dx += dx;
+ pThis->PtrDelta.u.Relative.dy += dy;
+ pThis->PtrDelta.u.Relative.dz -= dz; /* Inverted! */
/* Send a report if possible. */
usbHidSendReport(pThis);
@@ -812,37 +1458,206 @@ static DECLCALLBACK(int) usbHidMousePutEvent(PPDMIMOUSEPORT pInterface, int32_t
}
/**
- * Absolute mouse event handler.
- *
- * @returns VBox status code.
- * @param pInterface Pointer to the mouse port interface (KBDState::Mouse.iPort).
- * @param u32X The X coordinate.
- * @param u32Y The Y coordinate.
- * @param i32DeltaZ The Z delta.
- * @param i32DeltaW The W delta.
- * @param fButtonStates The button states.
+ * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventAbs}
*/
-static DECLCALLBACK(int) usbHidMousePutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t u32X, uint32_t u32Y, int32_t i32DeltaZ, int32_t i32DeltaW, uint32_t fButtonStates)
+static DECLCALLBACK(int) usbHidMousePutEventAbs(PPDMIMOUSEPORT pInterface,
+ uint32_t x, uint32_t y,
+ int32_t dz, int32_t dw,
+ uint32_t fButtons)
{
PUSBHID pThis = RT_FROM_MEMBER(pInterface, USBHID, Lun0.IPort);
RTCritSectEnter(&pThis->CritSect);
- Assert(pThis->isAbsolute);
+ Assert(pThis->enmMode == USBHIDMODE_ABSOLUTE);
/* Accumulate movement - the events from the front end may arrive
* at a much higher rate than USB can handle. Probably not a real issue
* when only the Z axis is relative (X/Y movement isn't technically
* accumulated and only the last value is used).
*/
- pThis->PtrDelta.btn = fButtonStates;
- pThis->PtrDelta.dX = u32X >> pThis->u8CoordShift;
- pThis->PtrDelta.dY = u32Y >> pThis->u8CoordShift;
- pThis->PtrDelta.dZ -= i32DeltaZ; /* Inverted! */
+ pThis->PtrDelta.u.Absolute.fButtons = fButtons;
+ pThis->PtrDelta.u.Absolute.x = x >> pThis->u8CoordShift;
+ pThis->PtrDelta.u.Absolute.y = y >> pThis->u8CoordShift;
+ pThis->PtrDelta.u.Absolute.dz -= dz; /* Inverted! */
+ pThis->PtrDelta.u.Absolute.dw -= dw; /* Inverted! */
+
+ /* Send a report if possible. */
+ usbHidSendReport(pThis);
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventMultiTouch}
+ */
+static DECLCALLBACK(int) usbHidMousePutEventMultiTouch(PPDMIMOUSEPORT pInterface,
+ uint8_t cContacts,
+ const uint64_t *pau64Contacts,
+ uint32_t u32ScanTime)
+{
+ uint8_t i;
+ uint8_t j;
+
+ /* Make a copy of new contacts */
+ MTCONTACT *paNewContacts = (MTCONTACT *)RTMemTmpAlloc(sizeof(MTCONTACT) * cContacts);
+ if (!paNewContacts)
+ return VERR_NO_MEMORY;
+
+ for (i = 0; i < cContacts; i++)
+ {
+ uint32_t u32Lo = RT_LO_U32(pau64Contacts[i]);
+ uint32_t u32Hi = RT_HI_U32(pau64Contacts[i]);
+ paNewContacts[i].x = (uint16_t)u32Lo;
+ paNewContacts[i].y = (uint16_t)(u32Lo >> 16);
+ paNewContacts[i].id = RT_BYTE1(u32Hi);
+ paNewContacts[i].flags = RT_BYTE2(u32Hi) & (MT_CONTACT_F_IN_CONTACT | MT_CONTACT_F_IN_RANGE);
+ paNewContacts[i].status = MT_CONTACT_S_DIRTY;
+ paNewContacts[i].oldId = 0; /* Not used. */
+ if (paNewContacts[i].flags & MT_CONTACT_F_IN_CONTACT)
+ {
+ paNewContacts[i].flags |= MT_CONTACT_F_IN_RANGE;
+ }
+ }
+
+ PUSBHID pThis = RT_FROM_MEMBER(pInterface, USBHID, Lun0.IPort);
+ MTCONTACT *pCurContact = NULL;
+ MTCONTACT *pNewContact = NULL;
+
+ RTCritSectEnter(&pThis->CritSect);
+
+ Assert(pThis->enmMode == USBHIDMODE_MULTI_TOUCH);
+
+ /* Maintain a state of all current contacts.
+ * Intr URBs will be completed according to the state.
+ */
+
+ /* Mark all existing contacts as dirty. */
+ for (i = 0; i < RT_ELEMENTS(pThis->aCurrentContactState); i++)
+ pThis->aCurrentContactState[i].status |= MT_CONTACT_S_DIRTY;
+
+ /* Update existing contacts and mark new contacts. */
+ for (i = 0; i < cContacts; i++)
+ {
+ pNewContact = &paNewContacts[i];
+
+ /* Find existing contact with the same id. */
+ pCurContact = NULL;
+ for (j = 0; j < RT_ELEMENTS(pThis->aCurrentContactState); j++)
+ {
+ if ( (pThis->aCurrentContactState[j].status & MT_CONTACT_S_ACTIVE) != 0
+ && pThis->aCurrentContactState[j].id == pNewContact->id)
+ {
+ pCurContact = &pThis->aCurrentContactState[j];
+ break;
+ }
+ }
+
+ if (pCurContact)
+ {
+ pNewContact->status &= ~MT_CONTACT_S_DIRTY;
+
+ pCurContact->x = pNewContact->x;
+ pCurContact->y = pNewContact->y;
+ if (pCurContact->flags == 0) /* Contact disappeared already. */
+ {
+ if ((pCurContact->status & MT_CONTACT_S_REUSED) == 0)
+ {
+ pCurContact->status |= MT_CONTACT_S_REUSED; /* Report to the guest that the contact not in touch. */
+ pCurContact->oldId = pCurContact->id;
+ }
+ }
+ pCurContact->flags = pNewContact->flags;
+ pCurContact->status &= ~MT_CONTACT_S_DIRTY;
+ }
+ }
+
+ /* Append new contacts (the dirty one in the paNewContacts). */
+ for (i = 0; i < cContacts; i++)
+ {
+ pNewContact = &paNewContacts[i];
+
+ if (pNewContact->status & MT_CONTACT_S_DIRTY)
+ {
+ /* It is a new contact, copy is to one of not ACTIVE or not updated existing contacts. */
+ pCurContact = usbHidFindMTContact(pThis->aCurrentContactState, RT_ELEMENTS(pThis->aCurrentContactState),
+ MT_CONTACT_S_ACTIVE, 0);
+
+ if (pCurContact)
+ {
+ *pCurContact = *pNewContact;
+ pCurContact->status = MT_CONTACT_S_ACTIVE; /* Reset status. */
+ }
+ else
+ {
+ /* Dirty existing contacts can be reused. */
+ pCurContact = usbHidFindMTContact(pThis->aCurrentContactState, RT_ELEMENTS(pThis->aCurrentContactState),
+ MT_CONTACT_S_ACTIVE | MT_CONTACT_S_DIRTY,
+ MT_CONTACT_S_ACTIVE | MT_CONTACT_S_DIRTY);
+
+ if (pCurContact)
+ {
+ pCurContact->x = pNewContact->x;
+ pCurContact->y = pNewContact->y;
+ if ((pCurContact->status & MT_CONTACT_S_REUSED) == 0)
+ {
+ pCurContact->status |= MT_CONTACT_S_REUSED; /* Report to the guest that the contact not in touch. */
+ pCurContact->oldId = pCurContact->id;
+ }
+ pCurContact->flags = pNewContact->flags;
+ pCurContact->status &= ~MT_CONTACT_S_DIRTY;
+ }
+ else
+ {
+ LogRel3(("usbHid: dropped new contact: %d,%d id %d flags %RX8 status %RX8 oldId %d\n",
+ pNewContact->x,
+ pNewContact->y,
+ pNewContact->id,
+ pNewContact->flags,
+ pNewContact->status,
+ pNewContact->oldId
+ ));
+ }
+ }
+ }
+ }
+
+ /* Mark still dirty existing contacts as cancelled, because a new set of contacts does not include them. */
+ for (i = 0; i < RT_ELEMENTS(pThis->aCurrentContactState); i++)
+ {
+ pCurContact = &pThis->aCurrentContactState[i];
+ if (pCurContact->status & MT_CONTACT_S_DIRTY)
+ {
+ pCurContact->status |= MT_CONTACT_S_CANCELLED;
+ pCurContact->status &= ~MT_CONTACT_S_DIRTY;
+ }
+ }
+
+ pThis->u32LastTouchScanTime = u32ScanTime;
+
+ LogRel3(("usbHid: scanTime (ms): %d\n", pThis->u32LastTouchScanTime));
+ for (i = 0; i < RT_ELEMENTS(pThis->aCurrentContactState); i++)
+ {
+ LogRel3(("usbHid: contact state[%d]: %d,%d id %d flags %RX8 status %RX8 oldId %d\n",
+ i,
+ pThis->aCurrentContactState[i].x,
+ pThis->aCurrentContactState[i].y,
+ pThis->aCurrentContactState[i].id,
+ pThis->aCurrentContactState[i].flags,
+ pThis->aCurrentContactState[i].status,
+ pThis->aCurrentContactState[i].oldId
+ ));
+ }
+
+ pThis->fTouchStateUpdated = true;
+ pThis->fHasPendingChanges = true;
/* Send a report if possible. */
usbHidSendReport(pThis);
RTCritSectLeave(&pThis->CritSect);
+
+ RTMemTmpFree(paNewContacts);
return VINF_SUCCESS;
}
@@ -852,7 +1667,6 @@ static DECLCALLBACK(int) usbHidMousePutEventAbs(PPDMIMOUSEPORT pInterface, uint3
static DECLCALLBACK(PVUSBURB) usbHidUrbReap(PPDMUSBINS pUsbIns, RTMSINTERVAL cMillies)
{
PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
- LogFlow(("usbHidUrbReap/#%u: cMillies=%u\n", pUsbIns->iInstance, cMillies));
RTCritSectEnter(&pThis->CritSect);
@@ -874,7 +1688,8 @@ static DECLCALLBACK(PVUSBURB) usbHidUrbReap(PPDMUSBINS pUsbIns, RTMSINTERVAL cMi
RTCritSectLeave(&pThis->CritSect);
if (pUrb)
- Log(("usbHidUrbReap/#%u: pUrb=%p:%s\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc));
+ LogRelFlow(("usbHidUrbReap/#%u: pUrb=%p:%s\n", pUsbIns->iInstance, pUrb,
+ pUrb->pszDesc));
return pUrb;
}
@@ -885,7 +1700,8 @@ static DECLCALLBACK(PVUSBURB) usbHidUrbReap(PPDMUSBINS pUsbIns, RTMSINTERVAL cMi
static DECLCALLBACK(int) usbHidUrbCancel(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
{
PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
- LogFlow(("usbHidUrbCancel/#%u: pUrb=%p:%s\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc));
+ LogRelFlow(("usbHidUrbCancel/#%u: pUrb=%p:%s\n", pUsbIns->iInstance, pUrb,
+ pUrb->pszDesc));
RTCritSectEnter(&pThis->CritSect);
/*
@@ -923,7 +1739,7 @@ static int usbHidHandleIntrDevToHost(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb
case USBHIDREQSTATE_DATA_TO_HOST:
{
AssertFailed();
- Log(("usbHidHandleIntrDevToHost: Entering STATUS\n"));
+ LogRelFlow(("usbHidHandleIntrDevToHost: Entering STATUS\n"));
return usbHidCompleteOk(pThis, pUrb, 0);
}
@@ -933,28 +1749,172 @@ static int usbHidHandleIntrDevToHost(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb
case USBHIDREQSTATE_STATUS:
{
AssertFailed();
- Log(("usbHidHandleIntrDevToHost: Entering READY\n"));
+ LogRelFlow(("usbHidHandleIntrDevToHost: Entering READY\n"));
pThis->enmState = USBHIDREQSTATE_READY;
return usbHidCompleteOk(pThis, pUrb, 0);
}
case USBHIDREQSTATE_READY:
usbHidQueueAddTail(&pThis->ToHostQueue, pUrb);
+ LogRelFlow(("usbHidHandleIntrDevToHost: Added %p:%s to the queue\n",
+ pUrb, pUrb->pszDesc));
/* If a report is pending, send it right away. */
if (pThis->fHasPendingChanges)
usbHidSendReport(pThis);
- LogFlow(("usbHidHandleIntrDevToHost: Added %p:%s to the queue\n", pUrb, pUrb->pszDesc));
return VINF_SUCCESS;
/*
* Bad states, stall.
*/
default:
- Log(("usbHidHandleIntrDevToHost: enmState=%d cbData=%#x\n", pThis->enmState, pUrb->cbData));
+ LogRelFlow(("usbHidHandleIntrDevToHost: enmState=%d cbData=%#x\n",
+ pThis->enmState, pUrb->cbData));
return usbHidCompleteStall(pThis, NULL, pUrb, "Really bad state (D2H)!");
}
}
+#define GET_REPORT 0x01
+#define GET_IDLE 0x02
+#define GET_PROTOCOL 0x03
+#define SET_REPORT 0x09
+#define SET_IDLE 0x0A
+#define SET_PROTOCOL 0x0B
+
+static uint8_t sau8QASampleBlob[256] =
+{
+ 0xfc, 0x28, 0xfe, 0x84, 0x40, 0xcb, 0x9a, 0x87,
+ 0x0d, 0xbe, 0x57, 0x3c, 0xb6, 0x70, 0x09, 0x88,
+ 0x07, 0x97, 0x2d, 0x2b, 0xe3, 0x38, 0x34, 0xb6,
+ 0x6c, 0xed, 0xb0, 0xf7, 0xe5, 0x9c, 0xf6, 0xc2,
+ 0x2e, 0x84, 0x1b, 0xe8, 0xb4, 0x51, 0x78, 0x43,
+ 0x1f, 0x28, 0x4b, 0x7c, 0x2d, 0x53, 0xaf, 0xfc,
+ 0x47, 0x70, 0x1b, 0x59, 0x6f, 0x74, 0x43, 0xc4,
+ 0xf3, 0x47, 0x18, 0x53, 0x1a, 0xa2, 0xa1, 0x71,
+ 0xc7, 0x95, 0x0e, 0x31, 0x55, 0x21, 0xd3, 0xb5,
+ 0x1e, 0xe9, 0x0c, 0xba, 0xec, 0xb8, 0x89, 0x19,
+ 0x3e, 0xb3, 0xaf, 0x75, 0x81, 0x9d, 0x53, 0xb9,
+ 0x41, 0x57, 0xf4, 0x6d, 0x39, 0x25, 0x29, 0x7c,
+ 0x87, 0xd9, 0xb4, 0x98, 0x45, 0x7d, 0xa7, 0x26,
+ 0x9c, 0x65, 0x3b, 0x85, 0x68, 0x89, 0xd7, 0x3b,
+ 0xbd, 0xff, 0x14, 0x67, 0xf2, 0x2b, 0xf0, 0x2a,
+ 0x41, 0x54, 0xf0, 0xfd, 0x2c, 0x66, 0x7c, 0xf8,
+ 0xc0, 0x8f, 0x33, 0x13, 0x03, 0xf1, 0xd3, 0xc1,
+ 0x0b, 0x89, 0xd9, 0x1b, 0x62, 0xcd, 0x51, 0xb7,
+ 0x80, 0xb8, 0xaf, 0x3a, 0x10, 0xc1, 0x8a, 0x5b,
+ 0xe8, 0x8a, 0x56, 0xf0, 0x8c, 0xaa, 0xfa, 0x35,
+ 0xe9, 0x42, 0xc4, 0xd8, 0x55, 0xc3, 0x38, 0xcc,
+ 0x2b, 0x53, 0x5c, 0x69, 0x52, 0xd5, 0xc8, 0x73,
+ 0x02, 0x38, 0x7c, 0x73, 0xb6, 0x41, 0xe7, 0xff,
+ 0x05, 0xd8, 0x2b, 0x79, 0x9a, 0xe2, 0x34, 0x60,
+ 0x8f, 0xa3, 0x32, 0x1f, 0x09, 0x78, 0x62, 0xbc,
+ 0x80, 0xe3, 0x0f, 0xbd, 0x65, 0x20, 0x08, 0x13,
+ 0xc1, 0xe2, 0xee, 0x53, 0x2d, 0x86, 0x7e, 0xa7,
+ 0x5a, 0xc5, 0xd3, 0x7d, 0x98, 0xbe, 0x31, 0x48,
+ 0x1f, 0xfb, 0xda, 0xaf, 0xa2, 0xa8, 0x6a, 0x89,
+ 0xd6, 0xbf, 0xf2, 0xd3, 0x32, 0x2a, 0x9a, 0xe4,
+ 0xcf, 0x17, 0xb7, 0xb8, 0xf4, 0xe1, 0x33, 0x08,
+ 0x24, 0x8b, 0xc4, 0x43, 0xa5, 0xe5, 0x24, 0xc2
+};
+
+static int usbHidRequestClass(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
+{
+ PVUSBSETUP pSetup = (PVUSBSETUP)&pUrb->abData[0];
+
+ if (pThis->enmMode != USBHIDMODE_MULTI_TOUCH)
+ {
+ LogRelFlow(("usbHid: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
+ pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue,
+ pSetup->wIndex, pSetup->wLength));
+ return usbHidCompleteStall(pThis, pEp, pUrb, "Unsupported class req");
+ }
+
+ int rc = VINF_SUCCESS;
+
+ switch (pSetup->bRequest)
+ {
+ case SET_REPORT:
+ case GET_REPORT:
+ {
+ uint8_t u8ReportType = RT_HI_U8(pSetup->wValue);
+ uint8_t u8ReportID = RT_LO_U8(pSetup->wValue);
+ LogRelFlow(("usbHid: %s: type %d, ID %d, data\n%.*Rhxd\n",
+ pSetup->bRequest == GET_REPORT? "GET_REPORT": "SET_REPORT",
+ u8ReportType, u8ReportID,
+ pUrb->cbData - sizeof(VUSBSETUP), &pUrb->abData[sizeof(VUSBSETUP)]));
+ if (pSetup->bRequest == GET_REPORT)
+ {
+ uint32_t cbData = 0; /* 0 means that the report is unsupported. */
+
+ if (u8ReportType == 1 && u8ReportID == REPORTID_TOUCH_POINTER)
+ {
+ USBHIDMT_REPORT_POINTER *p = (USBHIDMT_REPORT_POINTER *)&pUrb->abData[sizeof(VUSBSETUP)];
+ /* The actual state should be reported here. */
+ p->idReport = REPORTID_TOUCH_POINTER;
+ p->fButtons = 0;
+ p->x = 0;
+ p->y = 0;
+ cbData = sizeof(USBHIDMT_REPORT_POINTER);
+ }
+ else if (u8ReportType == 1 && u8ReportID == REPORTID_TOUCH_EVENT)
+ {
+ USBHIDMT_REPORT *p = (USBHIDMT_REPORT *)&pUrb->abData[sizeof(VUSBSETUP)];
+ /* The actual state should be reported here. */
+ RT_ZERO(*p);
+ p->idReport = REPORTID_TOUCH_EVENT;
+ cbData = sizeof(USBHIDMT_REPORT);
+ }
+ else if (u8ReportType == 3 && u8ReportID == REPORTID_TOUCH_MAX_COUNT)
+ {
+ pUrb->abData[sizeof(VUSBSETUP) + 0] = REPORTID_TOUCH_MAX_COUNT;
+ pUrb->abData[sizeof(VUSBSETUP) + 1] = MT_CONTACT_MAX_COUNT; /* Contact count maximum. */
+ pUrb->abData[sizeof(VUSBSETUP) + 2] = 0; /* Device identifier */
+ cbData = 3;
+ }
+ else if (u8ReportType == 3 && u8ReportID == REPORTID_TOUCH_QABLOB)
+ {
+ uint32_t cbLeft = pUrb->cbData;
+ pUrb->abData[sizeof(VUSBSETUP) + 0] = REPORTID_TOUCH_QABLOB; /* Report Id. */
+ memcpy(&pUrb->abData[sizeof(VUSBSETUP) + 1],
+ sau8QASampleBlob, sizeof(sau8QASampleBlob));
+ cbData = sizeof(sau8QASampleBlob) + 1;
+ }
+ else if (u8ReportType == 3 && u8ReportID == REPORTID_TOUCH_DEVCONFIG)
+ {
+ pUrb->abData[sizeof(VUSBSETUP) + 0] = REPORTID_TOUCH_DEVCONFIG;
+ pUrb->abData[sizeof(VUSBSETUP) + 1] = 2; /* Device mode:
+ * "HID touch device supporting contact
+ * identifier and contact count maximum."
+ */
+ pUrb->abData[sizeof(VUSBSETUP) + 2] = 0; /* Device identifier */
+ cbData = 3;
+ }
+
+ if (cbData > 0)
+ {
+ rc = usbHidCompleteOk(pThis, pUrb, sizeof(VUSBSETUP) + cbData);
+ }
+ else
+ {
+ rc = usbHidCompleteStall(pThis, pEp, pUrb, "Unsupported GET_REPORT MT");
+ }
+ }
+ else
+ {
+ /* SET_REPORT */
+ rc = usbHidCompleteOk(pThis, pUrb, pUrb->cbData);
+ }
+ } break;
+ default:
+ {
+ LogRelFlow(("usbHid: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
+ pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue,
+ pSetup->wIndex, pSetup->wLength));
+ rc = usbHidCompleteStall(pThis, pEp, pUrb, "Unsupported class req MT");
+ }
+ }
+
+ return rc;
+}
/**
* Handles request sent to the default control pipe.
@@ -977,10 +1937,12 @@ static int usbHidHandleDefaultPipe(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
switch (pSetup->wValue >> 8)
{
case VUSB_DT_STRING:
- Log(("usbHid: GET_DESCRIPTOR DT_STRING wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
+ LogRelFlow(("usbHid: GET_DESCRIPTOR DT_STRING wValue=%#x wIndex=%#x\n",
+ pSetup->wValue, pSetup->wIndex));
break;
default:
- Log(("usbHid: GET_DESCRIPTOR, huh? wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
+ LogRelFlow(("usbHid: GET_DESCRIPTOR, huh? wValue=%#x wIndex=%#x\n",
+ pSetup->wValue, pSetup->wIndex));
break;
}
break;
@@ -996,53 +1958,77 @@ static int usbHidHandleDefaultPipe(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
case DT_IF_HID_DESCRIPTOR:
{
- if (pThis->isAbsolute)
+ switch (pThis->enmMode)
{
+ case USBHIDMODE_ABSOLUTE:
cbDesc = sizeof(g_UsbHidTIfHidDesc);
pDesc = (const uint8_t *)&g_UsbHidTIfHidDesc;
- }
- else
- {
+ break;
+ case USBHIDMODE_RELATIVE:
cbDesc = sizeof(g_UsbHidMIfHidDesc);
pDesc = (const uint8_t *)&g_UsbHidMIfHidDesc;
+ break;
+ case USBHIDMODE_MULTI_TOUCH:
+ cbDesc = sizeof(g_UsbHidMTIfHidDesc);
+ pDesc = (const uint8_t *)&g_UsbHidMTIfHidDesc;
+ break;
+ default:
+ cbDesc = 0;
+ pDesc = 0;
+ break;
}
/* Returned data is written after the setup message. */
cbCopy = pUrb->cbData - sizeof(*pSetup);
cbCopy = RT_MIN(cbCopy, cbDesc);
- Log(("usbHidMouse: GET_DESCRIPTOR DT_IF_HID_DESCRIPTOR wValue=%#x wIndex=%#x cbCopy=%#x\n", pSetup->wValue, pSetup->wIndex, cbCopy));
+ LogRelFlow(("usbHidMouse: GET_DESCRIPTOR DT_IF_HID_DESCRIPTOR wValue=%#x wIndex=%#x cbCopy=%#x\n",
+ pSetup->wValue, pSetup->wIndex,
+ cbCopy));
memcpy(&pUrb->abData[sizeof(*pSetup)], pDesc, cbCopy);
return usbHidCompleteOk(pThis, pUrb, cbCopy + sizeof(*pSetup));
}
case DT_IF_HID_REPORT:
{
- if (pThis->isAbsolute)
+ switch (pThis->enmMode)
{
+ case USBHIDMODE_ABSOLUTE:
cbDesc = sizeof(g_UsbHidTReportDesc);
pDesc = (const uint8_t *)&g_UsbHidTReportDesc;
- }
- else
- {
+ break;
+ case USBHIDMODE_RELATIVE:
cbDesc = sizeof(g_UsbHidMReportDesc);
pDesc = (const uint8_t *)&g_UsbHidMReportDesc;
+ break;
+ case USBHIDMODE_MULTI_TOUCH:
+ cbDesc = sizeof(g_UsbHidMTReportDesc);
+ pDesc = (const uint8_t *)&g_UsbHidMTReportDesc;
+ break;
+ default:
+ cbDesc = 0;
+ pDesc = 0;
+ break;
}
/* Returned data is written after the setup message. */
cbCopy = pUrb->cbData - sizeof(*pSetup);
cbCopy = RT_MIN(cbCopy, cbDesc);
- Log(("usbHid: GET_DESCRIPTOR DT_IF_HID_REPORT wValue=%#x wIndex=%#x cbCopy=%#x\n", pSetup->wValue, pSetup->wIndex, cbCopy));
+ LogRelFlow(("usbHid: GET_DESCRIPTOR DT_IF_HID_REPORT wValue=%#x wIndex=%#x cbCopy=%#x\n",
+ pSetup->wValue, pSetup->wIndex,
+ cbCopy));
memcpy(&pUrb->abData[sizeof(*pSetup)], pDesc, cbCopy);
return usbHidCompleteOk(pThis, pUrb, cbCopy + sizeof(*pSetup));
}
default:
- Log(("usbHid: GET_DESCRIPTOR, huh? wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
+ LogRelFlow(("usbHid: GET_DESCRIPTOR, huh? wValue=%#x wIndex=%#x\n",
+ pSetup->wValue, pSetup->wIndex));
break;
}
break;
}
default:
- Log(("usbHid: Bad GET_DESCRIPTOR req: bmRequestType=%#x\n", pSetup->bmRequestType));
+ LogRelFlow(("usbHid: Bad GET_DESCRIPTOR req: bmRequestType=%#x\n",
+ pSetup->bmRequestType));
return usbHidCompleteStall(pThis, pEp, pUrb, "Bad GET_DESCRIPTOR");
}
break;
@@ -1054,7 +2040,8 @@ static int usbHidHandleDefaultPipe(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
if (pSetup->wLength != 2)
{
- Log(("usbHid: Bad GET_STATUS req: wLength=%#x\n", pSetup->wLength));
+ LogRelFlow(("usbHid: Bad GET_STATUS req: wLength=%#x\n",
+ pSetup->wLength));
break;
}
Assert(pSetup->wValue == 0);
@@ -1063,7 +2050,7 @@ static int usbHidHandleDefaultPipe(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
case VUSB_TO_DEVICE | VUSB_REQ_STANDARD | VUSB_DIR_TO_HOST:
{
Assert(pSetup->wIndex == 0);
- Log(("usbHid: GET_STATUS (device)\n"));
+ LogRelFlow(("usbHid: GET_STATUS (device)\n"));
wRet = 0; /* Not self-powered, no remote wakeup. */
memcpy(&pUrb->abData[sizeof(*pSetup)], &wRet, sizeof(wRet));
return usbHidCompleteOk(pThis, pUrb, sizeof(wRet) + sizeof(*pSetup));
@@ -1078,7 +2065,8 @@ static int usbHidHandleDefaultPipe(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
}
else
{
- Log(("usbHid: GET_STATUS (interface) invalid, wIndex=%#x\n", pSetup->wIndex));
+ LogRelFlow(("usbHid: GET_STATUS (interface) invalid, wIndex=%#x\n",
+ pSetup->wIndex));
}
break;
}
@@ -1093,13 +2081,15 @@ static int usbHidHandleDefaultPipe(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
}
else
{
- Log(("usbHid: GET_STATUS (endpoint) invalid, wIndex=%#x\n", pSetup->wIndex));
+ LogRelFlow(("usbHid: GET_STATUS (endpoint) invalid, wIndex=%#x\n",
+ pSetup->wIndex));
}
break;
}
default:
- Log(("usbHid: Bad GET_STATUS req: bmRequestType=%#x\n", pSetup->bmRequestType));
+ LogRelFlow(("usbHid: Bad GET_STATUS req: bmRequestType=%#x\n",
+ pSetup->bmRequestType));
return usbHidCompleteStall(pThis, pEp, pUrb, "Bad GET_STATUS");
}
break;
@@ -1110,25 +2100,30 @@ static int usbHidHandleDefaultPipe(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
}
/** @todo implement this. */
- Log(("usbHid: Implement standard request: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
- pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
+ LogRelFlow(("usbHid: Implement standard request: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
+ pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue,
+ pSetup->wIndex, pSetup->wLength));
usbHidCompleteStall(pThis, pEp, pUrb, "TODO: standard request stuff");
}
- /* 3.1 Bulk-Only Mass Storage Reset */
- else if ( pSetup->bmRequestType == (VUSB_REQ_CLASS | VUSB_TO_INTERFACE)
- && pSetup->bRequest == 0xff
- && !pSetup->wValue
- && !pSetup->wLength
- && pSetup->wIndex == 0)
+ else if ((pSetup->bmRequestType & VUSB_REQ_MASK) == VUSB_REQ_CLASS)
{
- Log(("usbHidHandleDefaultPipe: Bulk-Only Mass Storage Reset\n"));
- return usbHidResetWorker(pThis, pUrb, false /*fSetConfig*/);
+ /* Only VUSB_TO_INTERFACE is allowed. */
+ if ((pSetup->bmRequestType & VUSB_RECIP_MASK) == VUSB_TO_INTERFACE)
+ {
+ return usbHidRequestClass(pThis, pEp, pUrb);
+ }
+
+ LogRelFlow(("usbHid: invalid recipient of class req: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
+ pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue,
+ pSetup->wIndex, pSetup->wLength));
+ return usbHidCompleteStall(pThis, pEp, pUrb, "Invalid recip");
}
else
{
- Log(("usbHid: Unknown control msg: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
- pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
+ LogRelFlow(("usbHid: Unknown control msg: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
+ pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue,
+ pSetup->wIndex, pSetup->wLength));
return usbHidCompleteStall(pThis, pEp, pUrb, "Unknown control msg");
}
@@ -1142,7 +2137,8 @@ static int usbHidHandleDefaultPipe(PUSBHID pThis, PUSBHIDEP pEp, PVUSBURB pUrb)
static DECLCALLBACK(int) usbHidQueue(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
{
PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
- LogFlow(("usbHidQueue/#%u: pUrb=%p:%s EndPt=%#x\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc, pUrb->EndPt));
+ LogRelFlow(("usbHidQueue/#%u: pUrb=%p:%s EndPt=%#x\n", pUsbIns->iInstance,
+ pUrb, pUrb->pszDesc, pUrb->EndPt));
RTCritSectEnter(&pThis->CritSect);
/*
@@ -1178,7 +2174,8 @@ static DECLCALLBACK(int) usbHidQueue(PPDMUSBINS pUsbIns, PVUSBURB pUrb)
static DECLCALLBACK(int) usbHidUsbClearHaltedEndpoint(PPDMUSBINS pUsbIns, unsigned uEndpoint)
{
PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
- LogFlow(("usbHidUsbClearHaltedEndpoint/#%u: uEndpoint=%#x\n", pUsbIns->iInstance, uEndpoint));
+ LogRelFlow(("usbHidUsbClearHaltedEndpoint/#%u: uEndpoint=%#x\n",
+ pUsbIns->iInstance, uEndpoint));
if ((uEndpoint & ~0x80) < RT_ELEMENTS(pThis->aEps))
{
@@ -1196,7 +2193,8 @@ static DECLCALLBACK(int) usbHidUsbClearHaltedEndpoint(PPDMUSBINS pUsbIns, unsign
*/
static DECLCALLBACK(int) usbHidUsbSetInterface(PPDMUSBINS pUsbIns, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
{
- LogFlow(("usbHidUsbSetInterface/#%u: bInterfaceNumber=%u bAlternateSetting=%u\n", pUsbIns->iInstance, bInterfaceNumber, bAlternateSetting));
+ LogRelFlow(("usbHidUsbSetInterface/#%u: bInterfaceNumber=%u bAlternateSetting=%u\n",
+ pUsbIns->iInstance, bInterfaceNumber, bAlternateSetting));
Assert(bAlternateSetting == 0);
return VINF_SUCCESS;
}
@@ -1209,7 +2207,8 @@ static DECLCALLBACK(int) usbHidUsbSetConfiguration(PPDMUSBINS pUsbIns, uint8_t b
const void *pvOldCfgDesc, const void *pvOldIfState, const void *pvNewCfgDesc)
{
PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
- LogFlow(("usbHidUsbSetConfiguration/#%u: bConfigurationValue=%u\n", pUsbIns->iInstance, bConfigurationValue));
+ LogRelFlow(("usbHidUsbSetConfiguration/#%u: bConfigurationValue=%u\n",
+ pUsbIns->iInstance, bConfigurationValue));
Assert(bConfigurationValue == 1);
RTCritSectEnter(&pThis->CritSect);
@@ -1223,8 +2222,10 @@ static DECLCALLBACK(int) usbHidUsbSetConfiguration(PPDMUSBINS pUsbIns, uint8_t b
/*
* Set received event type to absolute or relative.
*/
- pThis->Lun0.pDrv->pfnReportModes(pThis->Lun0.pDrv, !pThis->isAbsolute,
- pThis->isAbsolute);
+ pThis->Lun0.pDrv->pfnReportModes(pThis->Lun0.pDrv,
+ pThis->enmMode == USBHIDMODE_RELATIVE,
+ pThis->enmMode == USBHIDMODE_ABSOLUTE,
+ pThis->enmMode == USBHIDMODE_MULTI_TOUCH);
RTCritSectLeave(&pThis->CritSect);
return VINF_SUCCESS;
@@ -1237,11 +2238,17 @@ static DECLCALLBACK(int) usbHidUsbSetConfiguration(PPDMUSBINS pUsbIns, uint8_t b
static DECLCALLBACK(PCPDMUSBDESCCACHE) usbHidUsbGetDescriptorCache(PPDMUSBINS pUsbIns)
{
PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
- LogFlow(("usbHidUsbGetDescriptorCache/#%u:\n", pUsbIns->iInstance));
- if (pThis->isAbsolute) {
+ LogRelFlow(("usbHidUsbGetDescriptorCache/#%u:\n", pUsbIns->iInstance));
+ switch (pThis->enmMode)
+ {
+ case USBHIDMODE_ABSOLUTE:
return &g_UsbHidTDescCache;
- } else {
+ case USBHIDMODE_RELATIVE:
return &g_UsbHidMDescCache;
+ case USBHIDMODE_MULTI_TOUCH:
+ return &g_UsbHidMTDescCache;
+ default:
+ return NULL;
}
}
@@ -1252,7 +2259,7 @@ static DECLCALLBACK(PCPDMUSBDESCCACHE) usbHidUsbGetDescriptorCache(PPDMUSBINS pU
static DECLCALLBACK(int) usbHidUsbReset(PPDMUSBINS pUsbIns, bool fResetOnLinux)
{
PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
- LogFlow(("usbHidUsbReset/#%u:\n", pUsbIns->iInstance));
+ LogRelFlow(("usbHidUsbReset/#%u:\n", pUsbIns->iInstance));
RTCritSectEnter(&pThis->CritSect);
int rc = usbHidResetWorker(pThis, NULL, false /*fSetConfig*/);
@@ -1268,7 +2275,7 @@ static DECLCALLBACK(int) usbHidUsbReset(PPDMUSBINS pUsbIns, bool fResetOnLinux)
static void usbHidDestruct(PPDMUSBINS pUsbIns)
{
PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
- LogFlow(("usbHidDestruct/#%u:\n", pUsbIns->iInstance));
+ LogRelFlow(("usbHidDestruct/#%u:\n", pUsbIns->iInstance));
if (RTCritSectIsInitialized(&pThis->CritSect))
{
@@ -1291,7 +2298,8 @@ static void usbHidDestruct(PPDMUSBINS pUsbIns)
static DECLCALLBACK(int) usbHidConstruct(PPDMUSBINS pUsbIns, int iInstance, PCFGMNODE pCfg, PCFGMNODE pCfgGlobal)
{
PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
- Log(("usbHidConstruct/#%u:\n", iInstance));
+ char szMode[64];
+ LogRelFlow(("usbHidConstruct/#%u:\n", iInstance));
/*
* Perform the basic structure initialization first so the destructor
@@ -1311,16 +2319,28 @@ static DECLCALLBACK(int) usbHidConstruct(PPDMUSBINS pUsbIns, int iInstance, PCFG
/*
* Validate and read the configuration.
*/
- rc = CFGMR3ValidateConfig(pCfg, "/", "Absolute|CoordShift", "Config", "UsbHid", iInstance);
+ rc = CFGMR3ValidateConfig(pCfg, "/", "Mode|CoordShift", "Config", "UsbHid", iInstance);
if (RT_FAILURE(rc))
return rc;
- rc = CFGMR3QueryBoolDef(pCfg, "Absolute", &pThis->isAbsolute, false);
+ rc = CFGMR3QueryStringDef(pCfg, "Mode", szMode, sizeof(szMode), "relative");
if (RT_FAILURE(rc))
return PDMUsbHlpVMSetError(pUsbIns, rc, RT_SRC_POS, N_("HID failed to query settings"));
+ if (!RTStrCmp(szMode, "relative"))
+ pThis->enmMode = USBHIDMODE_RELATIVE;
+ else if (!RTStrCmp(szMode, "absolute"))
+ pThis->enmMode = USBHIDMODE_ABSOLUTE;
+ else if (!RTStrCmp(szMode, "multitouch"))
+ pThis->enmMode = USBHIDMODE_MULTI_TOUCH;
+ else
+ return PDMUsbHlpVMSetError(pUsbIns, rc, RT_SRC_POS,
+ N_("Invalid HID device mode"));
+
+ LogRelFlow(("usbHidConstruct/#%u: mode '%s'\n", iInstance, szMode));
pThis->Lun0.IBase.pfnQueryInterface = usbHidMouseQueryInterface;
pThis->Lun0.IPort.pfnPutEvent = usbHidMousePutEvent;
pThis->Lun0.IPort.pfnPutEventAbs = usbHidMousePutEventAbs;
+ pThis->Lun0.IPort.pfnPutEventMultiTouch = usbHidMousePutEventMultiTouch;
/*
* Attach the mouse driver.
diff --git a/src/VBox/Devices/Input/testcase/Makefile.kmk b/src/VBox/Devices/Input/testcase/Makefile.kmk
new file mode 100644
index 00000000..48e59399
--- /dev/null
+++ b/src/VBox/Devices/Input/testcase/Makefile.kmk
@@ -0,0 +1,34 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-makefile for input test cases.
+#
+
+#
+# Copyright (C) 2013 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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+ PROGRAMS += tstUsbMouse
+endif
+tstUsbMouse_TEMPLATE = VBOXR3TSTEXE
+tstUsbMouse_DEFS = VBOX_WITH_VUSB
+tstUsbMouse_INCS = \
+ ../../build
+tstUsbMouse_LIBS = $(LIB_VMM)
+tstUsbMouse_SOURCES = \
+ tstUsbMouse.cpp \
+ ../UsbMouse.cpp
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Devices/Input/testcase/tstUsbMouse.cpp b/src/VBox/Devices/Input/testcase/tstUsbMouse.cpp
new file mode 100644
index 00000000..8b733c4b
--- /dev/null
+++ b/src/VBox/Devices/Input/testcase/tstUsbMouse.cpp
@@ -0,0 +1,329 @@
+/* $Id: tstUsbMouse.cpp $ */
+/** @file
+ * tstUsbMouse.cpp - testcase USB mouse and tablet devices.
+ */
+
+/*
+ * Copyright (C) 2013 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.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "VBoxDD.h"
+#include <VBox/vmm/pdmdrv.h>
+#include <iprt/alloc.h>
+#include <iprt/stream.h>
+#include <iprt/test.h>
+#include <iprt/uuid.h>
+
+/** Test mouse driver structure. */
+typedef struct DRVTSTMOUSE
+{
+ /** The USBHID structure. */
+ struct USBHID *pUsbHid;
+ /** The base interface for the mouse driver. */
+ PDMIBASE IBase;
+ /** Our mouse connector interface. */
+ PDMIMOUSECONNECTOR IConnector;
+ /** The base interface of the attached mouse port. */
+ PPDMIBASE pDrvBase;
+ /** The mouse port interface of the attached mouse port. */
+ PPDMIMOUSEPORT pDrv;
+ /** Is relative mode currently supported? */
+ bool fRel;
+ /** Is absolute mode currently supported? */
+ bool fAbs;
+ /** Is multi-touch mode currently supported? */
+ bool fMT;
+} DRVTSTMOUSE, *PDRVTSTMOUSE;
+
+
+/** Global mouse driver variable.
+ * @todo To be improved some time. */
+static DRVTSTMOUSE s_drvTstMouse;
+
+
+/** @interface_method_impl{PDMUSBHLPR3,pfnVMSetErrorV} */
+static DECLCALLBACK(int) tstVMSetErrorV(PPDMUSBINS pUsbIns, int rc,
+ RT_SRC_POS_DECL, const char *pszFormat,
+ va_list va)
+{
+ NOREF(pUsbIns);
+ RTPrintf("Error: %s:%u:%s:", RT_SRC_POS_ARGS);
+ RTPrintfV(pszFormat, va);
+ return rc;
+}
+
+/** @interface_method_impl{PDMUSBHLPR3,pfnDriverAttach} */
+/** @todo We currently just take the driver interface from the global
+ * variable. This is sufficient for a unit test but still a bit sad. */
+static DECLCALLBACK(int) tstDriverAttach(PPDMUSBINS pUsbIns, RTUINT iLun,
+ PPDMIBASE pBaseInterface,
+ PPDMIBASE *ppBaseInterface,
+ const char *pszDesc)
+{
+ NOREF(iLun);
+ NOREF(pszDesc);
+ s_drvTstMouse.pDrvBase = pBaseInterface;
+ s_drvTstMouse.pDrv = PDMIBASE_QUERY_INTERFACE(pBaseInterface,
+ PDMIMOUSEPORT);
+ *ppBaseInterface = &s_drvTstMouse.IBase;
+ return VINF_SUCCESS;
+}
+
+
+static PDMUSBHLP s_tstUsbHlp;
+
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) tstMouseQueryInterface(PPDMIBASE pInterface,
+ const char *pszIID)
+{
+ PDRVTSTMOUSE pThis = RT_FROM_MEMBER(pInterface, DRVTSTMOUSE, IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSECONNECTOR, &pThis->IConnector);
+ return NULL;
+}
+
+
+/**
+ * @interface_method_impl{PDMIMOUSECONNECTOR,pfnReportModes}
+ */
+static DECLCALLBACK(void) tstMouseReportModes(PPDMIMOUSECONNECTOR pInterface,
+ bool fRel, bool fAbs, bool fMT)
+{
+ PDRVTSTMOUSE pDrv = RT_FROM_MEMBER(pInterface, DRVTSTMOUSE, IConnector);
+ pDrv->fRel = fRel;
+ pDrv->fAbs = fAbs;
+ pDrv->fMT = fMT;
+}
+
+
+static int tstMouseConstruct(int iInstance, const char *pcszMode,
+ uint8_t u8CoordShift, PPDMUSBINS *ppThis)
+{
+ int rc = VERR_NO_MEMORY;
+ PPDMUSBINS pThis = (PPDMUSBINS)RTMemAllocZ( sizeof(*pThis)
+ + g_UsbHidMou.cbInstance);
+ PCFGMNODE pCfg = NULL;
+ if (pThis)
+ pCfg = CFGMR3CreateTree(NULL);
+ if (pCfg)
+ rc = CFGMR3InsertString(pCfg, "Mode", pcszMode);
+ if (RT_SUCCESS(rc))
+ rc = CFGMR3InsertInteger(pCfg, "CoordShift", u8CoordShift);
+ if (RT_SUCCESS(rc))
+ {
+ s_drvTstMouse.pDrv = NULL;
+ s_drvTstMouse.pDrvBase = NULL;
+ pThis->iInstance = iInstance;
+ pThis->pHlpR3 = &s_tstUsbHlp;
+ rc = g_UsbHidMou.pfnConstruct(pThis, iInstance, pCfg, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ *ppThis = pThis;
+ return rc;
+ }
+ }
+ /* Failure */
+ if (pCfg)
+ CFGMR3DestroyTree(pCfg);
+ if (pThis)
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+static void testConstructAndDestruct(RTTEST hTest)
+{
+ PPDMUSBINS pThis;
+ RTTestSub(hTest, "simple construction and destruction");
+ int rc = tstMouseConstruct(0, "relative", 1, &pThis);
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ if (pThis)
+ g_UsbHidMou.pfnDestruct(pThis);
+}
+
+
+static void testSendPositionRel(RTTEST hTest)
+{
+ PPDMUSBINS pThis = NULL;
+ VUSBURB Urb;
+ RTTestSub(hTest, "sending a relative position event");
+ int rc = tstMouseConstruct(0, "relative", 1, &pThis);
+ RT_ZERO(Urb);
+ if (RT_SUCCESS(rc))
+ rc = g_UsbHidMou.pfnUsbReset(pThis, false);
+ if (RT_SUCCESS(rc) && !s_drvTstMouse.pDrv)
+ rc = VERR_PDM_MISSING_INTERFACE;
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ if (RT_SUCCESS(rc))
+ {
+ s_drvTstMouse.pDrv->pfnPutEvent(s_drvTstMouse.pDrv, 123, -16, 1, -1, 3);
+ Urb.EndPt = 0x01;
+ rc = g_UsbHidMou.pfnUrbQueue(pThis, &Urb);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ PVUSBURB pUrb = g_UsbHidMou.pfnUrbReap(pThis, 0);
+ if (pUrb)
+ {
+ if (pUrb == &Urb)
+ {
+ if ( Urb.abData[0] != 3 /* Buttons */
+ || Urb.abData[1] != 123 /* x */
+ || Urb.abData[2] != 240 /* 256 - y */
+ || Urb.abData[3] != 255 /* z */)
+ rc = VERR_GENERAL_FAILURE;
+ }
+ else
+ rc = VERR_GENERAL_FAILURE;
+ }
+ else
+ rc = VERR_GENERAL_FAILURE;
+ }
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ if (pThis)
+ g_UsbHidMou.pfnDestruct(pThis);
+}
+
+
+static void testSendPositionAbs(RTTEST hTest)
+{
+ PPDMUSBINS pThis = NULL;
+ VUSBURB Urb;
+ RTTestSub(hTest, "sending an absolute position event");
+ int rc = tstMouseConstruct(0, "absolute", 1, &pThis);
+ RT_ZERO(Urb);
+ if (RT_SUCCESS(rc))
+ {
+ rc = g_UsbHidMou.pfnUsbReset(pThis, false);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ if (s_drvTstMouse.pDrv)
+ s_drvTstMouse.pDrv->pfnPutEventAbs(s_drvTstMouse.pDrv, 300, 200, 1,
+ 3, 3);
+ else
+ rc = VERR_PDM_MISSING_INTERFACE;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ Urb.EndPt = 0x01;
+ rc = g_UsbHidMou.pfnUrbQueue(pThis, &Urb);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ PVUSBURB pUrb = g_UsbHidMou.pfnUrbReap(pThis, 0);
+ if (pUrb)
+ {
+ if (pUrb == &Urb)
+ {
+ if ( Urb.abData[0] != 3 /* Buttons */
+ || (int8_t)Urb.abData[1] != -1 /* dz */
+ || (int8_t)Urb.abData[2] != -3 /* dw */
+ || *(uint16_t *)&Urb.abData[4] != 150 /* x >> 1 */
+ || *(uint16_t *)&Urb.abData[6] != 100 /* y >> 1 */)
+ rc = VERR_GENERAL_FAILURE;
+ }
+ else
+ rc = VERR_GENERAL_FAILURE;
+ }
+ else
+ rc = VERR_GENERAL_FAILURE;
+ }
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ if (pThis)
+ g_UsbHidMou.pfnDestruct(pThis);
+}
+
+#if 0
+/** @todo PDM interface was updated. This is not working anymore. */
+static void testSendPositionMT(RTTEST hTest)
+{
+ PPDMUSBINS pThis = NULL;
+ VUSBURB Urb;
+ RTTestSub(hTest, "sending a multi-touch position event");
+ int rc = tstMouseConstruct(0, "multitouch", 1, &pThis);
+ RT_ZERO(Urb);
+ if (RT_SUCCESS(rc))
+ {
+ rc = g_UsbHidMou.pfnUsbReset(pThis, false);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ if (s_drvTstMouse.pDrv)
+ s_drvTstMouse.pDrv->pfnPutEventMT(s_drvTstMouse.pDrv, 300, 200, 2,
+ 3);
+ else
+ rc = VERR_PDM_MISSING_INTERFACE;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ Urb.EndPt = 0x01;
+ rc = g_UsbHidMou.pfnUrbQueue(pThis, &Urb);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ PVUSBURB pUrb = g_UsbHidMou.pfnUrbReap(pThis, 0);
+ if (pUrb)
+ {
+ if (pUrb == &Urb)
+ {
+ if ( Urb.abData[0] != 1 /* Report ID */
+ || Urb.abData[1] != 3 /* Contact flags */
+ || *(uint16_t *)&Urb.abData[2] != 150 /* x >> 1 */
+ || *(uint16_t *)&Urb.abData[4] != 100 /* y >> 1 */
+ || Urb.abData[6] != 2 /* Contact number */)
+ rc = VERR_GENERAL_FAILURE;
+ }
+ else
+ rc = VERR_GENERAL_FAILURE;
+ }
+ else
+ rc = VERR_GENERAL_FAILURE;
+ }
+ RTTEST_CHECK_RC_OK(hTest, rc);
+ if (pThis)
+ g_UsbHidMou.pfnDestruct(pThis);
+}
+#endif
+
+int main()
+{
+ /*
+ * Init the runtime, test and say hello.
+ */
+ RTTEST hTest;
+ PDRVTSTMOUSE pThis;
+ int rc = RTTestInitAndCreate("tstUsbMouse", &hTest);
+ if (rc)
+ return rc;
+ RTTestBanner(hTest);
+ /* Set up our faked PDMUSBHLP interface. */
+ s_tstUsbHlp.pfnVMSetErrorV = tstVMSetErrorV;
+ s_tstUsbHlp.pfnDriverAttach = tstDriverAttach;
+ /* Set up our global mouse driver */
+ s_drvTstMouse.IBase.pfnQueryInterface = tstMouseQueryInterface;
+ s_drvTstMouse.IConnector.pfnReportModes = tstMouseReportModes;
+
+ /*
+ * Run the tests.
+ */
+ testConstructAndDestruct(hTest);
+ testSendPositionRel(hTest);
+ testSendPositionAbs(hTest);
+ /* testSendPositionMT(hTest); */
+ return RTTestSummaryAndDestroy(hTest);
+}