VirtualBox

source: vbox/trunk/src/VBox/Devices/Input/PS2M.cpp@ 68071

Last change on this file since 68071 was 68052, checked in by vboxsync, 8 years ago

bugref:8778: Double click not working with precision touchpad (user report)
Try to fix sending delayed button clicks to the PS/2 mouse device emulation.

We try to restrict input sent through the PS/2 mouse device to no more than
one packet every 50ms. To do this, we accumulate events. However we were
missing button clicks where both the press and the release occurred within
the timer interval. Additionally we were resetting the button to released
in some circumstances.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.1 KB
Line 
1/* $Id: PS2M.cpp 68052 2017-07-19 15:51:09Z vboxsync $ */
2/** @file
3 * PS2M - PS/2 auxiliary device (mouse) emulation.
4 */
5
6/*
7 * Copyright (C) 2007-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*
19 * References:
20 *
21 * The Undocumented PC (2nd Ed.), Frank van Gilluwe, Addison-Wesley, 1996.
22 * IBM TrackPoint System Version 4.0 Engineering Specification, 1999.
23 * ELAN Microelectronics eKM8025 USB & PS/2 Mouse Controller, 2006.
24 *
25 *
26 * Notes:
27 *
28 * - The auxiliary device commands are very similar to keyboard commands.
29 * Most keyboard commands which do not specifically deal with the keyboard
30 * (enable, disable, reset) have identical counterparts.
31 * - The code refers to 'auxiliary device' and 'mouse'; these terms are not
32 * quite interchangeable. 'Auxiliary device' is used when referring to the
33 * generic PS/2 auxiliary device interface and 'mouse' when referring to
34 * a mouse attached to the auxiliary port.
35 * - The basic modes of operation are reset, stream, and remote. Those are
36 * mutually exclusive. Stream and remote modes can additionally have wrap
37 * mode enabled.
38 * - The auxiliary device sends unsolicited data to the host only when it is
39 * both in stream mode and enabled. Otherwise it only responds to commands.
40 *
41 *
42 * There are three report packet formats supported by the emulated device. The
43 * standard three-byte PS/2 format (with middle button support), IntelliMouse
44 * four-byte format with added scroll wheel, and IntelliMouse Explorer four-byte
45 * format with reduced scroll wheel range but two additional buttons. Note that
46 * the first three bytes of the report are always the same.
47 *
48 * Upon reset, the mouse is always in the standard PS/2 mode. A special 'knock'
49 * sequence can be used to switch to ImPS/2 or ImEx mode. Three consecutive
50 * Set Sampling Rate (0F3h) commands with arguments 200, 100, 80 switch to ImPS/2
51 * mode. While in ImPS/2 or PS/2 mode, three consecutive Set Sampling Rate
52 * commands with arguments 200, 200, 80 switch to ImEx mode. The Read ID (0F2h)
53 * command will report the currently selected protocol.
54 *
55 *
56 * Standard PS/2 pointing device three-byte report packet format:
57 *
58 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
59 * |Bit/byte| bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
60 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
61 * | Byte 1 | Y ovfl | X ovfl | Y sign | X sign | Sync | M btn | R btn | L btn |
62 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
63 * | Byte 2 | X movement delta (two's complement) |
64 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
65 * | Byte 3 | Y movement delta (two's complement) |
66 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
67 *
68 * - The sync bit is always set. It allows software to synchronize data packets
69 * as the X/Y position data typically does not have bit 4 set.
70 * - The overflow bits are set if motion exceeds accumulator range. We use the
71 * maximum range (effectively 9 bits) and do not set the overflow bits.
72 * - Movement in the up/right direction is defined as having positive sign.
73 *
74 *
75 * IntelliMouse PS/2 (ImPS/2) fourth report packet byte:
76 *
77 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
78 * |Bit/byte| bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
79 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
80 * | Byte 4 | Z movement delta (two's complement) |
81 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
82 *
83 * - The valid range for Z delta values is only -8/+7, i.e. 4 bits.
84 *
85 * IntelliMouse Explorer (ImEx) fourth report packet byte:
86 *
87 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
88 * |Bit/byte| bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
89 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
90 * | Byte 4 | 0 | 0 | Btn 5 | Btn 4 | Z mov't delta (two's complement) |
91 * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
92 *
93 */
94
95
96/*********************************************************************************************************************************
97* Header Files *
98*********************************************************************************************************************************/
99#define LOG_GROUP LOG_GROUP_DEV_KBD
100#include <VBox/vmm/pdmdev.h>
101#include <VBox/err.h>
102#include <iprt/assert.h>
103#include <iprt/uuid.h>
104#include "VBoxDD.h"
105#define IN_PS2M
106#include "PS2Dev.h"
107
108
109/*********************************************************************************************************************************
110* Defined Constants And Macros *
111*********************************************************************************************************************************/
112/** @name Auxiliary device commands sent by the system.
113 * @{ */
114#define ACMD_SET_SCALE_11 0xE6 /* Set 1:1 scaling. */
115#define ACMD_SET_SCALE_21 0xE7 /* Set 2:1 scaling. */
116#define ACMD_SET_RES 0xE8 /* Set resolution. */
117#define ACMD_REQ_STATUS 0xE9 /* Get device status. */
118#define ACMD_SET_STREAM 0xEA /* Set stream mode. */
119#define ACMD_READ_REMOTE 0xEB /* Read remote data. */
120#define ACMD_RESET_WRAP 0xEC /* Exit wrap mode. */
121#define ACMD_INVALID_1 0xED
122#define ACMD_SET_WRAP 0xEE /* Set wrap (echo) mode. */
123#define ACMD_INVALID_2 0xEF
124#define ACMD_SET_REMOTE 0xF0 /* Set remote mode. */
125#define ACMD_INVALID_3 0xF1
126#define ACMD_READ_ID 0xF2 /* Read device ID. */
127#define ACMD_SET_SAMP_RATE 0xF3 /* Set sampling rate. */
128#define ACMD_ENABLE 0xF4 /* Enable (streaming mode). */
129#define ACMD_DISABLE 0xF5 /* Disable (streaming mode). */
130#define ACMD_SET_DEFAULT 0xF6 /* Set defaults. */
131#define ACMD_INVALID_4 0xF7
132#define ACMD_INVALID_5 0xF8
133#define ACMD_INVALID_6 0xF9
134#define ACMD_INVALID_7 0xFA
135#define ACMD_INVALID_8 0xFB
136#define ACMD_INVALID_9 0xFC
137#define ACMD_INVALID_10 0xFD
138#define ACMD_RESEND 0xFE /* Resend response. */
139#define ACMD_RESET 0xFF /* Reset device. */
140/** @} */
141
142/** @name Auxiliary device responses sent to the system.
143 * @{ */
144#define ARSP_ID 0x00
145#define ARSP_BAT_OK 0xAA /* Self-test passed. */
146#define ARSP_ACK 0xFA /* Command acknowledged. */
147#define ARSP_ERROR 0xFC /* Bad command. */
148#define ARSP_RESEND 0xFE /* Requesting resend. */
149/** @} */
150
151/** Define a simple PS/2 input device queue. */
152#define DEF_PS2Q_TYPE(name, size) \
153 typedef struct { \
154 uint32_t rpos; \
155 uint32_t wpos; \
156 uint32_t cUsed; \
157 uint32_t cSize; \
158 uint8_t abQueue[size]; \
159 } name
160
161/* Internal mouse queue sizes. The input queue is relatively large,
162 * but the command queue only needs to handle a few bytes.
163 */
164#define AUX_EVT_QUEUE_SIZE 256
165#define AUX_CMD_QUEUE_SIZE 8
166
167
168/*********************************************************************************************************************************
169* Structures and Typedefs *
170*********************************************************************************************************************************/
171
172DEF_PS2Q_TYPE(AuxEvtQ, AUX_EVT_QUEUE_SIZE);
173DEF_PS2Q_TYPE(AuxCmdQ, AUX_CMD_QUEUE_SIZE);
174#ifndef VBOX_DEVICE_STRUCT_TESTCASE /// @todo hack
175DEF_PS2Q_TYPE(GeneriQ, 1);
176#endif
177
178/* Auxiliary device special modes of operation. */
179typedef enum {
180 AUX_MODE_STD, /* Standard operation. */
181 AUX_MODE_RESET, /* Currently in reset. */
182 AUX_MODE_WRAP /* Wrap mode (echoing input). */
183} PS2M_MODE;
184
185/* Auxiliary device operational state. */
186typedef enum {
187 AUX_STATE_RATE_ERR = RT_BIT(0), /* Invalid rate received. */
188 AUX_STATE_RES_ERR = RT_BIT(1), /* Invalid resolution received. */
189 AUX_STATE_SCALING = RT_BIT(4), /* 2:1 scaling in effect. */
190 AUX_STATE_ENABLED = RT_BIT(5), /* Reporting enabled in stream mode. */
191 AUX_STATE_REMOTE = RT_BIT(6) /* Remote mode (reports on request). */
192} PS2M_STATE;
193
194/* Externally visible state bits. */
195#define AUX_STATE_EXTERNAL (AUX_STATE_SCALING | AUX_STATE_ENABLED | AUX_STATE_REMOTE)
196
197/* Protocols supported by the PS/2 mouse. */
198typedef enum {
199 PS2M_PROTO_PS2STD = 0, /* Standard PS/2 mouse protocol. */
200 PS2M_PROTO_IMPS2 = 3, /* IntelliMouse PS/2 protocol. */
201 PS2M_PROTO_IMEX = 4 /* IntelliMouse Explorer protocol. */
202} PS2M_PROTO;
203
204/* Protocol selection 'knock' states. */
205typedef enum {
206 PS2M_KNOCK_INITIAL,
207 PS2M_KNOCK_1ST,
208 PS2M_KNOCK_IMPS2_2ND,
209 PS2M_KNOCK_IMEX_2ND
210} PS2M_KNOCK_STATE;
211
212/**
213 * The PS/2 auxiliary device instance data.
214 */
215typedef struct PS2M
216{
217 /** Pointer to parent device (keyboard controller). */
218 R3PTRTYPE(void *) pParent;
219 /** Operational state. */
220 uint8_t u8State;
221 /** Configured sampling rate. */
222 uint8_t u8SampleRate;
223 /** Configured resolution. */
224 uint8_t u8Resolution;
225 /** Currently processed command (if any). */
226 uint8_t u8CurrCmd;
227 /** Set if the throttle delay is active. */
228 bool fThrottleActive;
229 /** Set if the throttle delay is active. */
230 bool fDelayReset;
231 /** Operational mode. */
232 PS2M_MODE enmMode;
233 /** Currently used protocol. */
234 PS2M_PROTO enmProtocol;
235 /** Currently used protocol. */
236 PS2M_KNOCK_STATE enmKnockState;
237 /** Buffer holding mouse events to be sent to the host. */
238 AuxEvtQ evtQ;
239 /** Command response queue (priority). */
240 AuxCmdQ cmdQ;
241 /** Accumulated horizontal movement. */
242 int32_t iAccumX;
243 /** Accumulated vertical movement. */
244 int32_t iAccumY;
245 /** Accumulated Z axis movement. */
246 int32_t iAccumZ;
247 /** Accumulated button presses. */
248 uint32_t fAccumB;
249 /** Instantaneous button data. */
250 uint32_t fCurrB;
251 /** Button state last sent to the guest. */
252 uint32_t fReportedB;
253 /** Throttling delay in milliseconds. */
254 uint32_t uThrottleDelay;
255
256 /** The device critical section protecting everything - R3 Ptr */
257 R3PTRTYPE(PPDMCRITSECT) pCritSectR3;
258 /** Command delay timer - R3 Ptr. */
259 PTMTIMERR3 pDelayTimerR3;
260 /** Interrupt throttling timer - R3 Ptr. */
261 PTMTIMERR3 pThrottleTimerR3;
262 RTR3PTR Alignment1;
263
264 /** Command delay timer - RC Ptr. */
265 PTMTIMERRC pDelayTimerRC;
266 /** Interrupt throttling timer - RC Ptr. */
267 PTMTIMERRC pThrottleTimerRC;
268
269 /** Command delay timer - R0 Ptr. */
270 PTMTIMERR0 pDelayTimerR0;
271 /** Interrupt throttling timer - R0 Ptr. */
272 PTMTIMERR0 pThrottleTimerR0;
273
274 /**
275 * Mouse port - LUN#1.
276 *
277 * @implements PDMIBASE
278 * @implements PDMIMOUSEPORT
279 */
280 struct
281 {
282 /** The base interface for the mouse port. */
283 PDMIBASE IBase;
284 /** The keyboard port base interface. */
285 PDMIMOUSEPORT IPort;
286
287 /** The base interface of the attached mouse driver. */
288 R3PTRTYPE(PPDMIBASE) pDrvBase;
289 /** The keyboard interface of the attached mouse driver. */
290 R3PTRTYPE(PPDMIMOUSECONNECTOR) pDrv;
291 } Mouse;
292} PS2M, *PPS2M;
293
294AssertCompile(PS2M_STRUCT_FILLER >= sizeof(PS2M));
295
296#ifndef VBOX_DEVICE_STRUCT_TESTCASE
297
298
299/*********************************************************************************************************************************
300* Global Variables *
301*********************************************************************************************************************************/
302
303
304/*********************************************************************************************************************************
305* Internal Functions *
306*********************************************************************************************************************************/
307
308
309/**
310 * Clear a queue.
311 *
312 * @param pQ Pointer to the queue.
313 */
314static void ps2kClearQueue(GeneriQ *pQ)
315{
316 LogFlowFunc(("Clearing queue %p\n", pQ));
317 pQ->wpos = pQ->rpos;
318 pQ->cUsed = 0;
319}
320
321
322/**
323 * Add a byte to a queue.
324 *
325 * @param pQ Pointer to the queue.
326 * @param val The byte to store.
327 */
328static void ps2kInsertQueue(GeneriQ *pQ, uint8_t val)
329{
330 /* Check if queue is full. */
331 if (pQ->cUsed >= pQ->cSize)
332 {
333 LogRelFlowFunc(("queue %p full (%d entries)\n", pQ, pQ->cUsed));
334 return;
335 }
336 /* Insert data and update circular buffer write position. */
337 pQ->abQueue[pQ->wpos] = val;
338 if (++pQ->wpos == pQ->cSize)
339 pQ->wpos = 0; /* Roll over. */
340 ++pQ->cUsed;
341 LogRelFlowFunc(("inserted 0x%02X into queue %p\n", val, pQ));
342}
343
344#ifdef IN_RING3
345
346/**
347 * Save a queue state.
348 *
349 * @param pSSM SSM handle to write the state to.
350 * @param pQ Pointer to the queue.
351 */
352static void ps2kSaveQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
353{
354 uint32_t cItems = pQ->cUsed;
355 int i;
356
357 /* Only save the number of items. Note that the read/write
358 * positions aren't saved as they will be rebuilt on load.
359 */
360 SSMR3PutU32(pSSM, cItems);
361
362 LogFlow(("Storing %d items from queue %p\n", cItems, pQ));
363
364 /* Save queue data - only the bytes actually used (typically zero). */
365 for (i = pQ->rpos; cItems-- > 0; i = (i + 1) % pQ->cSize)
366 SSMR3PutU8(pSSM, pQ->abQueue[i]);
367}
368
369/**
370 * Load a queue state.
371 *
372 * @param pSSM SSM handle to read the state from.
373 * @param pQ Pointer to the queue.
374 *
375 * @return int VBox status/error code.
376 */
377static int ps2kLoadQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
378{
379 int rc;
380
381 /* On load, always put the read pointer at zero. */
382 SSMR3GetU32(pSSM, &pQ->cUsed);
383
384 LogFlow(("Loading %d items to queue %p\n", pQ->cUsed, pQ));
385
386 if (pQ->cUsed > pQ->cSize)
387 {
388 AssertMsgFailed(("Saved size=%u, actual=%u\n", pQ->cUsed, pQ->cSize));
389 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
390 }
391
392 /* Recalculate queue positions and load data in one go. */
393 pQ->rpos = 0;
394 pQ->wpos = pQ->cUsed;
395 rc = SSMR3GetMem(pSSM, pQ->abQueue, pQ->cUsed);
396
397 return rc;
398}
399
400/* Report a change in status down (or is it up?) the driver chain. */
401static void ps2mSetDriverState(PPS2M pThis, bool fEnabled)
402{
403 PPDMIMOUSECONNECTOR pDrv = pThis->Mouse.pDrv;
404 if (pDrv)
405 pDrv->pfnReportModes(pDrv, fEnabled, false, false);
406}
407
408/* Reset the pointing device. */
409static void ps2mReset(PPS2M pThis)
410{
411 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_BAT_OK);
412 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, 0);
413 pThis->enmMode = AUX_MODE_STD;
414 pThis->u8CurrCmd = 0;
415
416 /// @todo move to its proper home!
417 ps2mSetDriverState(pThis, true);
418}
419
420#endif /* IN_RING3 */
421
422/**
423 * Retrieve a byte from a queue.
424 *
425 * @param pQ Pointer to the queue.
426 * @param pVal Pointer to storage for the byte.
427 *
428 * @return int VINF_TRY_AGAIN if queue is empty,
429 * VINF_SUCCESS if a byte was read.
430 */
431static int ps2kRemoveQueue(GeneriQ *pQ, uint8_t *pVal)
432{
433 int rc = VINF_TRY_AGAIN;
434
435 Assert(pVal);
436 if (pQ->cUsed)
437 {
438 *pVal = pQ->abQueue[pQ->rpos];
439 if (++pQ->rpos == pQ->cSize)
440 pQ->rpos = 0; /* Roll over. */
441 --pQ->cUsed;
442 rc = VINF_SUCCESS;
443 LogFlowFunc(("removed 0x%02X from queue %p\n", *pVal, pQ));
444 } else
445 LogFlowFunc(("queue %p empty\n", pQ));
446 return rc;
447}
448
449static void ps2mSetRate(PPS2M pThis, uint8_t rate)
450{
451 Assert(rate);
452 pThis->uThrottleDelay = rate ? 1000 / rate : 0;
453 pThis->u8SampleRate = rate;
454 LogFlowFunc(("Sampling rate %u, throttle delay %u ms\n", pThis->u8SampleRate, pThis->uThrottleDelay));
455}
456
457static void ps2mSetDefaults(PPS2M pThis)
458{
459 LogFlowFunc(("Set mouse defaults\n"));
460 /* Standard protocol, reporting disabled, resolution 2, 1:1 scaling. */
461 pThis->enmProtocol = PS2M_PROTO_PS2STD;
462 pThis->u8State = 0;
463 pThis->u8Resolution = 2;
464
465 /* Sample rate 100 reports per second. */
466 ps2mSetRate(pThis, 100);
467
468 /* Event queue, eccumulators, and button status bits are cleared. */
469 ps2kClearQueue((GeneriQ *)&pThis->evtQ);
470 pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = pThis->fAccumB;
471}
472
473/* Handle the sampling rate 'knock' sequence which selects protocol. */
474static void ps2mRateProtocolKnock(PPS2M pThis, uint8_t rate)
475{
476 switch (pThis->enmKnockState)
477 {
478 case PS2M_KNOCK_INITIAL:
479 if (rate == 200)
480 pThis->enmKnockState = PS2M_KNOCK_1ST;
481 break;
482 case PS2M_KNOCK_1ST:
483 if (rate == 100)
484 pThis->enmKnockState = PS2M_KNOCK_IMPS2_2ND;
485 else if (rate == 200)
486 pThis->enmKnockState = PS2M_KNOCK_IMEX_2ND;
487 else
488 pThis->enmKnockState = PS2M_KNOCK_INITIAL;
489 break;
490 case PS2M_KNOCK_IMPS2_2ND:
491 if (rate == 80)
492 {
493 pThis->enmProtocol = PS2M_PROTO_IMPS2;
494 LogRelFlow(("PS2M: Switching mouse to ImPS/2 protocol.\n"));
495 }
496 pThis->enmKnockState = PS2M_KNOCK_INITIAL;
497 break;
498 case PS2M_KNOCK_IMEX_2ND:
499 if (rate == 80)
500 {
501 pThis->enmProtocol = PS2M_PROTO_IMEX;
502 LogRelFlow(("PS2M: Switching mouse to ImEx protocol.\n"));
503 }
504 /* Fall through! */
505 default:
506 pThis->enmKnockState = PS2M_KNOCK_INITIAL;
507 }
508}
509
510/* Three-button event mask. */
511#define PS2M_STD_BTN_MASK (RT_BIT(0) | RT_BIT(1) | RT_BIT(2))
512
513/* Report accumulated movement and button presses, then clear the accumulators. */
514static void ps2mReportAccumulatedEvents(PPS2M pThis, GeneriQ *pQueue, bool fAccumBtns)
515{
516 uint32_t fBtnState = fAccumBtns ? pThis->fAccumB : pThis->fCurrB;
517 uint8_t val;
518 int dX, dY, dZ;
519
520 /* Clamp the accumulated delta values to the allowed range. */
521 dX = RT_MIN(RT_MAX(pThis->iAccumX, -255), 255);
522 dY = RT_MIN(RT_MAX(pThis->iAccumY, -255), 255);
523 dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -8), 7);
524
525 /* Start with the sync bit and buttons 1-3. */
526 val = RT_BIT(3) | (fBtnState & PS2M_STD_BTN_MASK);
527 /* Set the X/Y sign bits. */
528 if (dX < 0)
529 val |= RT_BIT(4);
530 if (dY < 0)
531 val |= RT_BIT(5);
532
533 /* Send the standard 3-byte packet (always the same). */
534 ps2kInsertQueue(pQueue, val);
535 ps2kInsertQueue(pQueue, dX);
536 ps2kInsertQueue(pQueue, dY);
537
538 /* Add fourth byte if extended protocol is in use. */
539 if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
540 {
541 if (pThis->enmProtocol == PS2M_PROTO_IMPS2)
542 ps2kInsertQueue(pQueue, dZ);
543 else
544 {
545 Assert(pThis->enmProtocol == PS2M_PROTO_IMEX);
546 /* Z value uses 4 bits; buttons 4/5 in bits 4 and 5. */
547 val = dZ & 0x0f;
548 val |= (fBtnState << 1) & (RT_BIT(4) | RT_BIT(5));
549 ps2kInsertQueue(pQueue, val);
550 }
551 }
552
553 /* Clear the movement accumulators, but not necessarily button state. */
554 pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = 0;
555 /* Clear accumulated button state only when it's being used. */
556 if (fAccumBtns)
557 {
558 pThis->fReportedB = pThis->fAccumB;
559 pThis->fAccumB = pThis->fCurrB;
560 }
561}
562
563
564/* Determine whether a reporting rate is one of the valid ones. */
565bool ps2mIsRateSupported(uint8_t rate)
566{
567 static uint8_t aValidRates[] = { 10, 20, 40, 60, 80, 100, 200 };
568 size_t i;
569 bool fValid = false;
570
571 for (i = 0; i < RT_ELEMENTS(aValidRates); ++i)
572 if (aValidRates[i] == rate)
573 {
574 fValid = true;
575 break;
576 }
577
578 return fValid;
579}
580
581/**
582 * Receive and process a byte sent by the keyboard controller.
583 *
584 * @param pThis The PS/2 auxiliary device instance data.
585 * @param cmd The command (or data) byte.
586 */
587int PS2MByteToAux(PPS2M pThis, uint8_t cmd)
588{
589 uint8_t u8Val;
590 bool fHandled = true;
591
592 LogFlowFunc(("cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
593//LogRel(("aux: cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
594
595 if (pThis->enmMode == AUX_MODE_RESET)
596 /* In reset mode, do not respond at all. */
597 return VINF_SUCCESS;
598
599 /* If there's anything left in the command response queue, trash it. */
600 ps2kClearQueue((GeneriQ *)&pThis->cmdQ);
601
602 if (pThis->enmMode == AUX_MODE_WRAP)
603 {
604 /* In wrap mode, bounce most data right back.*/
605 if (cmd == ACMD_RESET || cmd == ACMD_RESET_WRAP)
606 ; /* Handle as regular commands. */
607 else
608 {
609 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, cmd);
610 return VINF_SUCCESS;
611 }
612 }
613
614#ifndef IN_RING3
615 /* Reset, Enable, and Set Default commands must be run in R3. */
616 if (cmd == ACMD_RESET || cmd == ACMD_ENABLE || cmd == ACMD_SET_DEFAULT)
617 return VINF_IOM_R3_IOPORT_WRITE;
618#endif
619
620 switch (cmd)
621 {
622 case ACMD_SET_SCALE_11:
623 pThis->u8State &= ~AUX_STATE_SCALING;
624 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
625 pThis->u8CurrCmd = 0;
626 break;
627 case ACMD_SET_SCALE_21:
628 pThis->u8State |= AUX_STATE_SCALING;
629 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
630 pThis->u8CurrCmd = 0;
631 break;
632 case ACMD_REQ_STATUS:
633 /* Report current status, sample rate, and resolution. */
634 u8Val = (pThis->u8State & AUX_STATE_EXTERNAL) | (pThis->fCurrB & PS2M_STD_BTN_MASK);
635 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
636 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, u8Val);
637 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8Resolution);
638 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8SampleRate);
639 pThis->u8CurrCmd = 0;
640 break;
641 case ACMD_SET_STREAM:
642 pThis->u8State &= ~AUX_STATE_REMOTE;
643 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
644 pThis->u8CurrCmd = 0;
645 break;
646 case ACMD_READ_REMOTE:
647 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
648 ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->cmdQ, false);
649 pThis->u8CurrCmd = 0;
650 break;
651 case ACMD_RESET_WRAP:
652 pThis->enmMode = AUX_MODE_STD;
653 /* NB: Stream mode reporting remains disabled! */
654 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
655 pThis->u8CurrCmd = 0;
656 break;
657 case ACMD_SET_WRAP:
658 pThis->enmMode = AUX_MODE_WRAP;
659 pThis->u8State &= ~AUX_STATE_ENABLED;
660 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
661 pThis->u8CurrCmd = 0;
662 break;
663 case ACMD_SET_REMOTE:
664 pThis->u8State |= AUX_STATE_REMOTE;
665 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
666 pThis->u8CurrCmd = 0;
667 break;
668 case ACMD_READ_ID:
669 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
670 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->enmProtocol);
671 pThis->u8CurrCmd = 0;
672 break;
673 case ACMD_ENABLE:
674 pThis->u8State |= AUX_STATE_ENABLED;
675#ifdef IN_RING3
676 ps2mSetDriverState(pThis, true);
677#else
678 AssertLogRelMsgFailed(("Invalid ACMD_ENABLE outside R3!\n"));
679#endif
680 ps2kClearQueue((GeneriQ *)&pThis->evtQ);
681 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
682 pThis->u8CurrCmd = 0;
683 break;
684 case ACMD_DISABLE:
685 pThis->u8State &= ~AUX_STATE_ENABLED;
686 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
687 pThis->u8CurrCmd = 0;
688 break;
689 case ACMD_SET_DEFAULT:
690 ps2mSetDefaults(pThis);
691 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
692 pThis->u8CurrCmd = 0;
693 break;
694 case ACMD_RESEND:
695 pThis->u8CurrCmd = 0;
696 break;
697 case ACMD_RESET:
698 ps2mSetDefaults(pThis);
699 /// @todo reset more?
700 pThis->u8CurrCmd = cmd;
701 pThis->enmMode = AUX_MODE_RESET;
702 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
703 if (pThis->fDelayReset)
704 /* Slightly delay reset completion; it might take hundreds of ms. */
705 TMTimerSetMillies(pThis->CTX_SUFF(pDelayTimer), 1);
706 else
707#ifdef IN_RING3
708 ps2mReset(pThis);
709#else
710 AssertLogRelMsgFailed(("Invalid ACMD_RESET outside R3!\n"));
711#endif
712 break;
713 /* The following commands need a parameter. */
714 case ACMD_SET_RES:
715 case ACMD_SET_SAMP_RATE:
716 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
717 pThis->u8CurrCmd = cmd;
718 break;
719 default:
720 /* Sending a command instead of a parameter starts the new command. */
721 switch (pThis->u8CurrCmd)
722 {
723 case ACMD_SET_RES:
724 if (cmd < 4) /* Valid resolutions are 0-3. */
725 {
726 pThis->u8Resolution = cmd;
727 pThis->u8State &= ~AUX_STATE_RES_ERR;
728 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
729 pThis->u8CurrCmd = 0;
730 }
731 else
732 {
733 /* Bad resolution. Reply with Resend or Error. */
734 if (pThis->u8State & AUX_STATE_RES_ERR)
735 {
736 pThis->u8State &= ~AUX_STATE_RES_ERR;
737 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ERROR);
738 pThis->u8CurrCmd = 0;
739 }
740 else
741 {
742 pThis->u8State |= AUX_STATE_RES_ERR;
743 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
744 /* NB: Current command remains unchanged. */
745 }
746 }
747 break;
748 case ACMD_SET_SAMP_RATE:
749 if (ps2mIsRateSupported(cmd))
750 {
751 pThis->u8State &= ~AUX_STATE_RATE_ERR;
752 ps2mSetRate(pThis, cmd);
753 ps2mRateProtocolKnock(pThis, cmd);
754 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
755 pThis->u8CurrCmd = 0;
756 }
757 else
758 {
759 /* Bad rate. Reply with Resend or Error. */
760 if (pThis->u8State & AUX_STATE_RATE_ERR)
761 {
762 pThis->u8State &= ~AUX_STATE_RATE_ERR;
763 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ERROR);
764 pThis->u8CurrCmd = 0;
765 }
766 else
767 {
768 pThis->u8State |= AUX_STATE_RATE_ERR;
769 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
770 /* NB: Current command remains unchanged. */
771 }
772 }
773 break;
774 default:
775 fHandled = false;
776 }
777 /* Fall through only to handle unrecognized commands. */
778 if (fHandled)
779 break;
780 /* fall thru */
781
782 case ACMD_INVALID_1:
783 case ACMD_INVALID_2:
784 case ACMD_INVALID_3:
785 case ACMD_INVALID_4:
786 case ACMD_INVALID_5:
787 case ACMD_INVALID_6:
788 case ACMD_INVALID_7:
789 case ACMD_INVALID_8:
790 case ACMD_INVALID_9:
791 case ACMD_INVALID_10:
792 Log(("Unsupported command 0x%02X!\n", cmd));
793 ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
794 pThis->u8CurrCmd = 0;
795 break;
796 }
797 LogFlowFunc(("Active cmd now 0x%02X; updating interrupts\n", pThis->u8CurrCmd));
798// KBCUpdateInterrupts(pThis->pParent);
799 return VINF_SUCCESS;
800}
801
802/**
803 * Send a byte (keystroke or command response) to the keyboard controller.
804 *
805 * @returns VINF_SUCCESS or VINF_TRY_AGAIN.
806 * @param pThis The PS/2 auxiliary device instance data.
807 * @param pb Where to return the byte we've read.
808 * @remarks Caller must have entered the device critical section.
809 */
810int PS2MByteFromAux(PPS2M pThis, uint8_t *pb)
811{
812 int rc;
813
814 AssertPtr(pb);
815
816 /* Anything in the command queue has priority over data
817 * in the event queue. Additionally, keystrokes are /// @todo true?
818 * blocked if a command is currently in progress, even if
819 * the command queue is empty.
820 */
821 /// @todo Probably should flush/not fill queue if stream mode reporting disabled?!
822 rc = ps2kRemoveQueue((GeneriQ *)&pThis->cmdQ, pb);
823 if (rc != VINF_SUCCESS && !pThis->u8CurrCmd && (pThis->u8State & AUX_STATE_ENABLED))
824 rc = ps2kRemoveQueue((GeneriQ *)&pThis->evtQ, pb);
825
826 LogFlowFunc(("mouse sends 0x%02x (%svalid data)\n", *pb, rc == VINF_SUCCESS ? "" : "not "));
827//if (rc == VINF_SUCCESS) LogRel(("aux: sends 0x%02X\n", *pb));
828
829 return rc;
830}
831
832#ifdef IN_RING3
833
834/* Event rate throttling timer to emulate the auxiliary device sampling rate.
835 */
836static DECLCALLBACK(void) ps2mThrottleTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
837{
838 RT_NOREF2(pDevIns, pTimer);
839 PPS2M pThis = (PS2M *)pvUser;
840 uint32_t uHaveEvents;
841
842 /* Grab the lock to avoid races with PutEvent(). */
843 int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
844 AssertReleaseRC(rc);
845
846#if 0
847 /* If the input queue is not empty, restart the timer. */
848#else
849 /* If more movement is accumulated, report it and restart the timer. */
850 uHaveEvents = pThis->iAccumX | pThis->iAccumY | pThis->iAccumZ | (pThis->fAccumB != pThis->fReportedB);
851 LogFlowFunc(("Have%s events\n", uHaveEvents ? "" : " no"));
852
853 if (uHaveEvents)
854#endif
855 {
856 /* Report accumulated data, poke the KBC, and start the timer. */
857 ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->evtQ, true);
858 KBCUpdateInterrupts(pThis->pParent);
859 TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
860 }
861 else
862 pThis->fThrottleActive = false;
863
864 PDMCritSectLeave(pThis->pCritSectR3);
865}
866
867/* The auxiliary device is specified to take up to about 500 milliseconds. We need
868 * to delay sending the result to the host for at least a tiny little while.
869 */
870static DECLCALLBACK(void) ps2mDelayTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
871{
872 RT_NOREF2(pDevIns, pTimer);
873 PPS2M pThis = (PS2M *)pvUser;
874
875 LogFlowFunc(("Delay timer: cmd %02X\n", pThis->u8CurrCmd));
876
877 Assert(pThis->u8CurrCmd == ACMD_RESET);
878 ps2mReset(pThis);
879
880 /// @todo Might want a PS2MCompleteCommand() to push last response, clear command, and kick the KBC...
881 /* Give the KBC a kick. */
882 KBCUpdateInterrupts(pThis->pParent);
883}
884
885
886/**
887 * Debug device info handler. Prints basic auxiliary device state.
888 *
889 * @param pDevIns Device instance which registered the info.
890 * @param pHlp Callback functions for doing output.
891 * @param pszArgs Argument string. Optional and specific to the handler.
892 */
893static DECLCALLBACK(void) ps2mInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
894{
895 static const char *pcszModes[] = { "normal", "reset", "wrap" };
896 static const char *pcszProtocols[] = { "PS/2", NULL, NULL, "ImPS/2", "ImEx" };
897 PPS2M pThis = KBDGetPS2MFromDevIns(pDevIns);
898 NOREF(pszArgs);
899
900 Assert(pThis->enmMode <= RT_ELEMENTS(pcszModes));
901 Assert(pThis->enmProtocol <= RT_ELEMENTS(pcszProtocols));
902 pHlp->pfnPrintf(pHlp, "PS/2 mouse state: %s, %s mode, reporting %s\n",
903 pcszModes[pThis->enmMode],
904 pThis->u8State & AUX_STATE_REMOTE ? "remote" : "stream",
905 pThis->u8State & AUX_STATE_ENABLED ? "enabled" : "disabled");
906 pHlp->pfnPrintf(pHlp, "Protocol: %s, scaling %u:1\n",
907 pcszProtocols[pThis->enmProtocol], pThis->u8State & AUX_STATE_SCALING ? 2 : 1);
908 pHlp->pfnPrintf(pHlp, "Active command %02X\n", pThis->u8CurrCmd);
909 pHlp->pfnPrintf(pHlp, "Sampling rate %u reports/sec, resolution %u counts/mm\n",
910 pThis->u8SampleRate, 1 << pThis->u8Resolution);
911 pHlp->pfnPrintf(pHlp, "Command queue: %d items (%d max)\n",
912 pThis->cmdQ.cUsed, pThis->cmdQ.cSize);
913 pHlp->pfnPrintf(pHlp, "Event queue : %d items (%d max)\n",
914 pThis->evtQ.cUsed, pThis->evtQ.cSize);
915}
916
917/* -=-=-=-=-=- Mouse: IBase -=-=-=-=-=- */
918
919/**
920 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
921 */
922static DECLCALLBACK(void *) ps2mQueryInterface(PPDMIBASE pInterface, const char *pszIID)
923{
924 PPS2M pThis = RT_FROM_MEMBER(pInterface, PS2M, Mouse.IBase);
925 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Mouse.IBase);
926 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSEPORT, &pThis->Mouse.IPort);
927 return NULL;
928}
929
930
931/* -=-=-=-=-=- Mouse: IMousePort -=-=-=-=-=- */
932
933/**
934 * Mouse event handler.
935 *
936 * @returns VBox status code.
937 * @param pThis The PS/2 auxiliary device instance data.
938 * @param dx X direction movement delta.
939 * @param dy Y direction movement delta.
940 * @param dz Z (vertical scroll) movement delta.
941 * @param dw W (horizontal scroll) movement delta.
942 * @param fButtons Depressed button mask.
943 */
944static int ps2mPutEventWorker(PPS2M pThis, int32_t dx, int32_t dy,
945 int32_t dz, int32_t dw, uint32_t fButtons)
946{
947 RT_NOREF1(dw);
948 int rc = VINF_SUCCESS;
949
950 /* Update internal accumulators and button state. */
951 pThis->iAccumX += dx;
952 pThis->iAccumY += dy;
953 pThis->iAccumZ += dz;
954 pThis->fAccumB |= fButtons; /// @todo accumulate based on current protocol?
955 pThis->fCurrB = fButtons;
956
957#if 1
958 /* Report the event and start the throttle timer unless it's already running. */
959 if (!pThis->fThrottleActive)
960 {
961 ps2mReportAccumulatedEvents(pThis, (GeneriQ *)&pThis->evtQ, true);
962 KBCUpdateInterrupts(pThis->pParent);
963 pThis->fThrottleActive = true;
964 TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
965 }
966#else
967 /* Clamp the delta values to the allowed range. */
968 dx = RT_MIN(RT_MAX(dx, -256), 255);
969 dy = RT_MIN(RT_MAX(dy, -256), 255);
970
971 /* Start with the sync bit. */
972 val = RT_BIT(3);
973 /* Add buttons 1-3. */
974 val |= fButtons & PS2M_STD_BTN_MASK;
975 /* Set the X/Y sign bits. */
976 if (dx < 0)
977 val |= RT_BIT(4);
978 if (dy < 0)
979 val |= RT_BIT(5);
980
981 ps2kInsertQueue((GeneriQ *)&pThis->evtQ, val);
982 ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dx);
983 ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dy);
984 if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
985 {
986 ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dz);
987 }
988#endif
989
990 return rc;
991}
992
993/* -=-=-=-=-=- Mouse: IMousePort -=-=-=-=-=- */
994
995/**
996 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEvent}
997 */
998static DECLCALLBACK(int) ps2mPutEvent(PPDMIMOUSEPORT pInterface, int32_t dx, int32_t dy,
999 int32_t dz, int32_t dw, uint32_t fButtons)
1000{
1001 PPS2M pThis = RT_FROM_MEMBER(pInterface, PS2M, Mouse.IPort);
1002 int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
1003 AssertReleaseRC(rc);
1004
1005 LogRelFlowFunc(("dX=%d dY=%d dZ=%d dW=%d buttons=%02X\n", dx, dy, dz, dw, fButtons));
1006 /* NB: The PS/2 Y axis direction is inverted relative to ours. */
1007 ps2mPutEventWorker(pThis, dx, -dy, dz, dw, fButtons);
1008
1009 PDMCritSectLeave(pThis->pCritSectR3);
1010 return VINF_SUCCESS;
1011}
1012
1013/**
1014 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventAbs}
1015 */
1016static DECLCALLBACK(int) ps2mPutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t x, uint32_t y,
1017 int32_t dz, int32_t dw, uint32_t fButtons)
1018{
1019 AssertFailedReturn(VERR_NOT_SUPPORTED);
1020 NOREF(pInterface); NOREF(x); NOREF(y); NOREF(dz); NOREF(dw); NOREF(fButtons);
1021}
1022
1023/**
1024 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventMultiTouch}
1025 */
1026static DECLCALLBACK(int) ps2mPutEventMT(PPDMIMOUSEPORT pInterface, uint8_t cContacts,
1027 const uint64_t *pau64Contacts, uint32_t u32ScanTime)
1028{
1029 AssertFailedReturn(VERR_NOT_SUPPORTED);
1030 NOREF(pInterface); NOREF(cContacts); NOREF(pau64Contacts); NOREF(u32ScanTime);
1031}
1032
1033
1034
1035/**
1036 * Attach command.
1037 *
1038 * This is called to let the device attach to a driver for a
1039 * specified LUN.
1040 *
1041 * This is like plugging in the mouse after turning on the
1042 * system.
1043 *
1044 * @returns VBox status code.
1045 * @param pThis The PS/2 auxiliary device instance data.
1046 * @param pDevIns The device instance.
1047 * @param iLUN The logical unit which is being detached.
1048 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1049 */
1050int PS2MAttach(PPS2M pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1051{
1052 int rc;
1053
1054 /* The LUN must be 1, i.e. mouse. */
1055 Assert(iLUN == 1);
1056 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1057 ("PS/2 mouse does not support hotplugging\n"),
1058 VERR_INVALID_PARAMETER);
1059
1060 LogFlowFunc(("iLUN=%d\n", iLUN));
1061
1062 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Mouse.IBase, &pThis->Mouse.pDrvBase, "Mouse Port");
1063 if (RT_SUCCESS(rc))
1064 {
1065 pThis->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
1066 if (!pThis->Mouse.pDrv)
1067 {
1068 AssertLogRelMsgFailed(("LUN #1 doesn't have a mouse interface! rc=%Rrc\n", rc));
1069 rc = VERR_PDM_MISSING_INTERFACE;
1070 }
1071 }
1072 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1073 {
1074 Log(("%s/%d: warning: no driver attached to LUN #1!\n", pDevIns->pReg->szName, pDevIns->iInstance));
1075 rc = VINF_SUCCESS;
1076 }
1077 else
1078 AssertLogRelMsgFailed(("Failed to attach LUN #1! rc=%Rrc\n", rc));
1079
1080 return rc;
1081}
1082
1083void PS2MSaveState(PPS2M pThis, PSSMHANDLE pSSM)
1084{
1085 LogFlowFunc(("Saving PS2M state\n"));
1086
1087 /* Save the core auxiliary device state. */
1088 SSMR3PutU8(pSSM, pThis->u8State);
1089 SSMR3PutU8(pSSM, pThis->u8SampleRate);
1090 SSMR3PutU8(pSSM, pThis->u8Resolution);
1091 SSMR3PutU8(pSSM, pThis->u8CurrCmd);
1092 SSMR3PutU8(pSSM, pThis->enmMode);
1093 SSMR3PutU8(pSSM, pThis->enmProtocol);
1094 SSMR3PutU8(pSSM, pThis->enmKnockState);
1095
1096 /* Save the command and event queues. */
1097 ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
1098 ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->evtQ);
1099
1100 /* Save the command delay timer. Note that the rate throttling
1101 * timer is *not* saved.
1102 */
1103 TMR3TimerSave(pThis->CTX_SUFF(pDelayTimer), pSSM);
1104}
1105
1106int PS2MLoadState(PPS2M pThis, PSSMHANDLE pSSM, uint32_t uVersion)
1107{
1108 uint8_t u8;
1109 int rc;
1110
1111 NOREF(uVersion);
1112 LogFlowFunc(("Loading PS2M state version %u\n", uVersion));
1113
1114 /* Load the basic auxiliary device state. */
1115 SSMR3GetU8(pSSM, &pThis->u8State);
1116 SSMR3GetU8(pSSM, &pThis->u8SampleRate);
1117 SSMR3GetU8(pSSM, &pThis->u8Resolution);
1118 SSMR3GetU8(pSSM, &pThis->u8CurrCmd);
1119 SSMR3GetU8(pSSM, &u8);
1120 pThis->enmMode = (PS2M_MODE)u8;
1121 SSMR3GetU8(pSSM, &u8);
1122 pThis->enmProtocol = (PS2M_PROTO)u8;
1123 SSMR3GetU8(pSSM, &u8);
1124 pThis->enmKnockState = (PS2M_KNOCK_STATE)u8;
1125
1126 /* Load the command and event queues. */
1127 rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
1128 AssertRCReturn(rc, rc);
1129 rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->evtQ);
1130 AssertRCReturn(rc, rc);
1131
1132 /* Load the command delay timer, just in case. */
1133 rc = TMR3TimerLoad(pThis->CTX_SUFF(pDelayTimer), pSSM);
1134 AssertRCReturn(rc, rc);
1135
1136 /* Recalculate the throttling delay. */
1137 ps2mSetRate(pThis, pThis->u8SampleRate);
1138
1139 ps2mSetDriverState(pThis, !!(pThis->u8State & AUX_STATE_ENABLED));
1140
1141 return rc;
1142}
1143
1144void PS2MFixupState(PPS2M pThis, uint8_t u8State, uint8_t u8Rate, uint8_t u8Proto)
1145{
1146 LogFlowFunc(("Fixing up old PS2M state version\n"));
1147
1148 /* Load the basic auxiliary device state. */
1149 pThis->u8State = u8State;
1150 pThis->u8SampleRate = u8Rate ? u8Rate : 40; /* In case it wasn't saved right. */
1151 pThis->enmProtocol = (PS2M_PROTO)u8Proto;
1152
1153 /* Recalculate the throttling delay. */
1154 ps2mSetRate(pThis, pThis->u8SampleRate);
1155
1156 ps2mSetDriverState(pThis, !!(pThis->u8State & AUX_STATE_ENABLED));
1157}
1158
1159void PS2MReset(PPS2M pThis)
1160{
1161 LogFlowFunc(("Resetting PS2M\n"));
1162
1163 pThis->u8CurrCmd = 0;
1164
1165 /* Clear the queues. */
1166 ps2kClearQueue((GeneriQ *)&pThis->cmdQ);
1167 ps2mSetDefaults(pThis); /* Also clears event queue. */
1168
1169 /* Activate the PS/2 mouse by default. */
1170// if (pThis->Mouse.pDrv)
1171// pThis->Mouse.pDrv->pfnSetActive(pThis->Mouse.pDrv, true);
1172}
1173
1174void PS2MRelocate(PPS2M pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns)
1175{
1176 RT_NOREF2(pDevIns, offDelta);
1177 LogFlowFunc(("Relocating PS2M\n"));
1178 pThis->pDelayTimerRC = TMTimerRCPtr(pThis->pDelayTimerR3);
1179 pThis->pThrottleTimerRC = TMTimerRCPtr(pThis->pThrottleTimerR3);
1180}
1181
1182int PS2MConstruct(PPS2M pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance)
1183{
1184 RT_NOREF1(iInstance);
1185
1186 LogFlowFunc(("iInstance=%d\n", iInstance));
1187
1188 pThis->pParent = pParent;
1189
1190 /* Initialize the queues. */
1191 pThis->evtQ.cSize = AUX_EVT_QUEUE_SIZE;
1192 pThis->cmdQ.cSize = AUX_CMD_QUEUE_SIZE;
1193
1194 pThis->Mouse.IBase.pfnQueryInterface = ps2mQueryInterface;
1195 pThis->Mouse.IPort.pfnPutEvent = ps2mPutEvent;
1196 pThis->Mouse.IPort.pfnPutEventAbs = ps2mPutEventAbs;
1197 pThis->Mouse.IPort.pfnPutEventMultiTouch = ps2mPutEventMT;
1198
1199 /*
1200 * Initialize the critical section pointer(s).
1201 */
1202 pThis->pCritSectR3 = pDevIns->pCritSectRoR3;
1203
1204 /*
1205 * Create the input rate throttling timer. Does not use virtual time!
1206 */
1207 PTMTIMER pTimer;
1208 int rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, ps2mThrottleTimer, pThis,
1209 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2M Throttle Timer", &pTimer);
1210 if (RT_FAILURE(rc))
1211 return rc;
1212
1213 pThis->pThrottleTimerR3 = pTimer;
1214 pThis->pThrottleTimerR0 = TMTimerR0Ptr(pTimer);
1215 pThis->pThrottleTimerRC = TMTimerRCPtr(pTimer);
1216
1217 /*
1218 * Create the command delay timer.
1219 */
1220 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ps2mDelayTimer, pThis,
1221 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "PS2M Delay Timer", &pTimer);
1222 if (RT_FAILURE(rc))
1223 return rc;
1224
1225 pThis->pDelayTimerR3 = pTimer;
1226 pThis->pDelayTimerR0 = TMTimerR0Ptr(pTimer);
1227 pThis->pDelayTimerRC = TMTimerRCPtr(pTimer);
1228
1229 /*
1230 * Register debugger info callbacks.
1231 */
1232 PDMDevHlpDBGFInfoRegister(pDevIns, "ps2m", "Display PS/2 mouse state.", ps2mInfoState);
1233
1234 /// @todo Where should we do this?
1235 ps2mSetDriverState(pThis, true);
1236 pThis->u8State = 0;
1237 pThis->enmMode = AUX_MODE_STD;
1238
1239 return rc;
1240}
1241
1242#endif
1243
1244#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette