VirtualBox

source: vbox/trunk/src/VBox/Devices/Input/DevPS2.cpp@ 71351

Last change on this file since 71351 was 71351, checked in by vboxsync, 7 years ago

PS2K: Throttle keyboard input to satisfy questionable software (see bugref:4118:51).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.5 KB
Line 
1/* $Id: DevPS2.cpp 71351 2018-03-15 13:26:21Z vboxsync $ */
2/** @file
3 * DevPS2 - PS/2 keyboard & mouse controller device.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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 * This code is based on:
19 *
20 * QEMU PC keyboard emulation (revision 1.12)
21 *
22 * Copyright (c) 2003 Fabrice Bellard
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 *
42 */
43
44
45/*********************************************************************************************************************************
46* Header Files *
47*********************************************************************************************************************************/
48#define LOG_GROUP LOG_GROUP_DEV_KBD
49#include <VBox/vmm/pdmdev.h>
50#include <iprt/assert.h>
51#include <iprt/uuid.h>
52
53#include "VBoxDD.h"
54#include "PS2Dev.h"
55
56/* Do not remove this (unless eliminating the corresponding ifdefs), it will
57 * cause instant triple faults when booting Windows VMs. */
58#define TARGET_I386
59
60#define PCKBD_SAVED_STATE_VERSION 8
61
62#ifndef VBOX_DEVICE_STRUCT_TESTCASE
63
64
65/*********************************************************************************************************************************
66* Internal Functions *
67*********************************************************************************************************************************/
68RT_C_DECLS_BEGIN
69PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
70PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
71PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
72PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
73RT_C_DECLS_END
74#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
75
76/* debug PC keyboard */
77#define DEBUG_KBD
78
79/* debug PC keyboard : only mouse */
80#define DEBUG_MOUSE
81
82/* Keyboard Controller Commands */
83#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
84#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
85#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
86#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
87#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
88#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
89#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
90#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
91#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
92#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
93#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
94#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
95#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
96#define KBD_CCMD_WRITE_OBUF 0xD2
97#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
98 initiated by the auxiliary device */
99#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
100#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
101#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
102#define KBD_CCMD_READ_TSTINP 0xE0 /* Read test inputs T0, T1 */
103#define KBD_CCMD_RESET_ALT 0xF0
104#define KBD_CCMD_RESET 0xFE
105
106/* Status Register Bits */
107#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
108#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
109#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
110#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
111#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
112#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
113#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
114#define KBD_STAT_PERR 0x80 /* Parity error */
115
116/* Controller Mode Register Bits */
117#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
118#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
119#define KBD_MODE_SYS 0x04 /* The system flag (?) */
120#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
121#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
122#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
123#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
124#define KBD_MODE_RFU 0x80
125
126
127/**
128 * The keyboard controller/device state.
129 *
130 * @note We use the default critical section for serialize data access.
131 */
132typedef struct KBDState
133{
134 uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
135 uint8_t status;
136 uint8_t mode;
137 uint8_t dbbout; /* data buffer byte */
138 /* keyboard state */
139 int32_t translate;
140 int32_t xlat_state;
141
142 /** Pointer to the device instance - RC. */
143 PPDMDEVINSRC pDevInsRC;
144 /** Pointer to the device instance - R3 . */
145 PPDMDEVINSR3 pDevInsR3;
146 /** Pointer to the device instance. */
147 PPDMDEVINSR0 pDevInsR0;
148
149 /** Keyboard state (implemented in separate PS2K module). */
150#ifdef VBOX_DEVICE_STRUCT_TESTCASE
151 uint8_t KbdFiller[PS2K_STRUCT_FILLER];
152#else
153 PS2K Kbd;
154#endif
155
156 /** Mouse state (implemented in separate PS2M module). */
157#ifdef VBOX_DEVICE_STRUCT_TESTCASE
158 uint8_t AuxFiller[PS2M_STRUCT_FILLER];
159#else
160 PS2M Aux;
161#endif
162} KBDState;
163
164#ifndef VBOX_DEVICE_STRUCT_TESTCASE
165
166/* update irq and KBD_STAT_[MOUSE_]OBF */
167static void kbd_update_irq(KBDState *s)
168{
169 int irq12_level, irq1_level;
170 uint8_t val;
171
172 irq1_level = 0;
173 irq12_level = 0;
174
175 /* Determine new OBF state, but only if OBF is clear. If OBF was already
176 * set, we cannot risk changing the event type after an ISR potentially
177 * started executing! Only kbd_read_data() clears the OBF bits.
178 */
179 if (!(s->status & KBD_STAT_OBF)) {
180 s->status &= ~KBD_STAT_MOUSE_OBF;
181 /* Keyboard data has priority if both kbd and aux data is available. */
182 if (!(s->mode & KBD_MODE_DISABLE_KBD) && PS2KByteFromKbd(&s->Kbd, &val) == VINF_SUCCESS)
183 {
184 bool fHaveData = true;
185
186 /* If scancode translation is on (it usually is), there's more work to do. */
187 if (s->translate)
188 {
189 uint8_t xlated_val;
190
191 s->xlat_state = XlateAT2PC(s->xlat_state, val, &xlated_val);
192 val = xlated_val;
193
194 /* If the translation state is XS_BREAK, there's nothing to report
195 * and we keep going until the state changes or there's no more data.
196 */
197 while (s->xlat_state == XS_BREAK && PS2KByteFromKbd(&s->Kbd, &val) == VINF_SUCCESS)
198 {
199 s->xlat_state = XlateAT2PC(s->xlat_state, val, &xlated_val);
200 val = xlated_val;
201 }
202 /* This can happen if the last byte in the queue is F0... */
203 if (s->xlat_state == XS_BREAK)
204 fHaveData = false;
205 }
206 if (fHaveData)
207 {
208 s->dbbout = val;
209 s->status |= KBD_STAT_OBF;
210 }
211 }
212 else if (!(s->mode & KBD_MODE_DISABLE_MOUSE) && PS2MByteFromAux(&s->Aux, &val) == VINF_SUCCESS)
213 {
214 s->dbbout = val;
215 s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
216 }
217 }
218 /* Determine new IRQ state. */
219 if (s->status & KBD_STAT_OBF) {
220 if (s->status & KBD_STAT_MOUSE_OBF)
221 {
222 if (s->mode & KBD_MODE_MOUSE_INT)
223 irq12_level = 1;
224 }
225 else
226 { /* KBD_STAT_OBF set but KBD_STAT_MOUSE_OBF isn't. */
227 if (s->mode & KBD_MODE_KBD_INT)
228 irq1_level = 1;
229 }
230 }
231 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, irq1_level);
232 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, irq12_level);
233}
234
235void KBCUpdateInterrupts(void *pKbc)
236{
237 KBDState *s = (KBDState *)pKbc;
238 kbd_update_irq(s);
239}
240
241static void kbc_dbb_out(void *opaque, uint8_t val)
242{
243 KBDState *s = (KBDState*)opaque;
244
245 s->dbbout = val;
246 /* Set the OBF and raise IRQ. */
247 s->status |= KBD_STAT_OBF;
248 if (s->mode & KBD_MODE_KBD_INT)
249 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, 1);
250}
251
252static void kbc_dbb_out_aux(void *opaque, uint8_t val)
253{
254 KBDState *s = (KBDState*)opaque;
255
256 s->dbbout = val;
257 /* Set the aux OBF and raise IRQ. */
258 s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
259 if (s->mode & KBD_MODE_MOUSE_INT)
260 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, PDM_IRQ_LEVEL_HIGH);
261}
262
263static uint32_t kbd_read_status(void *opaque, uint32_t addr)
264{
265 KBDState *s = (KBDState*)opaque;
266 int val = s->status;
267 NOREF(addr);
268
269#if defined(DEBUG_KBD)
270 Log(("kbd: read status=0x%02x\n", val));
271#endif
272 return val;
273}
274
275static int kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
276{
277 int rc = VINF_SUCCESS;
278 KBDState *s = (KBDState*)opaque;
279 NOREF(addr);
280
281#ifdef DEBUG_KBD
282 Log(("kbd: write cmd=0x%02x\n", val));
283#endif
284 switch(val) {
285 case KBD_CCMD_READ_MODE:
286 kbc_dbb_out(s, s->mode);
287 break;
288 case KBD_CCMD_WRITE_MODE:
289 case KBD_CCMD_WRITE_OBUF:
290 case KBD_CCMD_WRITE_AUX_OBUF:
291 case KBD_CCMD_WRITE_MOUSE:
292 case KBD_CCMD_WRITE_OUTPORT:
293 s->write_cmd = val;
294 break;
295 case KBD_CCMD_MOUSE_DISABLE:
296 s->mode |= KBD_MODE_DISABLE_MOUSE;
297 break;
298 case KBD_CCMD_MOUSE_ENABLE:
299 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
300 /* Check for queued input. */
301 kbd_update_irq(s);
302 break;
303 case KBD_CCMD_TEST_MOUSE:
304 kbc_dbb_out(s, 0x00);
305 break;
306 case KBD_CCMD_SELF_TEST:
307 /* Enable the A20 line - that is the power-on state(!). */
308# ifndef IN_RING3
309 if (!PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
310 {
311 rc = VINF_IOM_R3_IOPORT_WRITE;
312 break;
313 }
314# else /* IN_RING3 */
315 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), true);
316# endif /* IN_RING3 */
317 s->status |= KBD_STAT_SELFTEST;
318 s->mode |= KBD_MODE_DISABLE_KBD;
319 kbc_dbb_out(s, 0x55);
320 break;
321 case KBD_CCMD_KBD_TEST:
322 kbc_dbb_out(s, 0x00);
323 break;
324 case KBD_CCMD_KBD_DISABLE:
325 s->mode |= KBD_MODE_DISABLE_KBD;
326 break;
327 case KBD_CCMD_KBD_ENABLE:
328 s->mode &= ~KBD_MODE_DISABLE_KBD;
329 /* Check for queued input. */
330 kbd_update_irq(s);
331 break;
332 case KBD_CCMD_READ_INPORT:
333 kbc_dbb_out(s, 0xBF);
334 break;
335 case KBD_CCMD_READ_OUTPORT:
336 /* XXX: check that */
337#ifdef TARGET_I386
338 val = 0x01 | (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)) << 1);
339#else
340 val = 0x01;
341#endif
342 if (s->status & KBD_STAT_OBF)
343 val |= 0x10;
344 if (s->status & KBD_STAT_MOUSE_OBF)
345 val |= 0x20;
346 kbc_dbb_out(s, val);
347 break;
348#ifdef TARGET_I386
349 case KBD_CCMD_ENABLE_A20:
350# ifndef IN_RING3
351 if (!PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
352 rc = VINF_IOM_R3_IOPORT_WRITE;
353# else /* IN_RING3 */
354 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), true);
355# endif /* IN_RING3 */
356 break;
357 case KBD_CCMD_DISABLE_A20:
358# ifndef IN_RING3
359 if (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
360 rc = VINF_IOM_R3_IOPORT_WRITE;
361# else /* IN_RING3 */
362 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), false);
363# endif /* !IN_RING3 */
364 break;
365#endif
366 case KBD_CCMD_READ_TSTINP:
367 /* Keyboard clock line is zero IFF keyboard is disabled */
368 val = (s->mode & KBD_MODE_DISABLE_KBD) ? 0 : 1;
369 kbc_dbb_out(s, val);
370 break;
371 case KBD_CCMD_RESET:
372 case KBD_CCMD_RESET_ALT:
373#ifndef IN_RING3
374 rc = VINF_IOM_R3_IOPORT_WRITE;
375#else /* IN_RING3 */
376 LogRel(("Reset initiated by keyboard controller\n"));
377 rc = PDMDevHlpVMReset(s->CTX_SUFF(pDevIns), PDMVMRESET_F_KBD);
378#endif /* !IN_RING3 */
379 break;
380 case 0xff:
381 /* ignore that - I don't know what is its use */
382 break;
383 /* Make OS/2 happy. */
384 /* The 8042 RAM is readable using commands 0x20 thru 0x3f, and writable
385 by 0x60 thru 0x7f. Now days only the first byte, the mode, is used.
386 We'll ignore the writes (0x61..7f) and return 0 for all the reads
387 just to make some OS/2 debug stuff a bit happier. */
388 case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
389 case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
390 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
391 case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
392 kbc_dbb_out(s, 0);
393 Log(("kbd: reading non-standard RAM addr %#x\n", val & 0x1f));
394 break;
395 default:
396 Log(("kbd: unsupported keyboard cmd=0x%02x\n", val));
397 break;
398 }
399 return rc;
400}
401
402static uint32_t kbd_read_data(void *opaque, uint32_t addr)
403{
404 KBDState *s = (KBDState*)opaque;
405 uint32_t val;
406 NOREF(addr);
407
408 /* Return the current DBB contents. */
409 val = s->dbbout;
410
411 /* Reading the DBB deasserts IRQs... */
412 if (s->status & KBD_STAT_MOUSE_OBF)
413 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, 0);
414 else
415 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, 0);
416 /* ...and clears the OBF bits. */
417 s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
418
419 /* Check if more data is available. */
420 kbd_update_irq(s);
421#ifdef DEBUG_KBD
422 Log(("kbd: read data=0x%02x\n", val));
423#endif
424 return val;
425}
426
427PS2K *KBDGetPS2KFromDevIns(PPDMDEVINS pDevIns)
428{
429 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
430 return &pThis->Kbd;
431}
432
433PS2M *KBDGetPS2MFromDevIns(PPDMDEVINS pDevIns)
434{
435 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
436 return &pThis->Aux;
437}
438
439static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
440{
441 int rc = VINF_SUCCESS;
442 KBDState *s = (KBDState*)opaque;
443 NOREF(addr);
444
445#ifdef DEBUG_KBD
446 Log(("kbd: write data=0x%02x\n", val));
447#endif
448
449 switch(s->write_cmd) {
450 case 0:
451 /* Automatically enables keyboard interface. */
452 s->mode &= ~KBD_MODE_DISABLE_KBD;
453 rc = PS2KByteToKbd(&s->Kbd, val);
454 if (rc == VINF_SUCCESS)
455 kbd_update_irq(s);
456 break;
457 case KBD_CCMD_WRITE_MODE:
458 s->mode = val;
459 s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
460 kbd_update_irq(s);
461 break;
462 case KBD_CCMD_WRITE_OBUF:
463 kbc_dbb_out(s, val);
464 break;
465 case KBD_CCMD_WRITE_AUX_OBUF:
466 kbc_dbb_out_aux(s, val);
467 break;
468 case KBD_CCMD_WRITE_OUTPORT:
469#ifdef TARGET_I386
470# ifndef IN_RING3
471 if (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)) != !!(val & 2))
472 rc = VINF_IOM_R3_IOPORT_WRITE;
473# else /* IN_RING3 */
474 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), !!(val & 2));
475# endif /* !IN_RING3 */
476#endif
477 if (!(val & 1)) {
478# ifndef IN_RING3
479 rc = VINF_IOM_R3_IOPORT_WRITE;
480# else
481 rc = PDMDevHlpVMReset(s->CTX_SUFF(pDevIns), PDMVMRESET_F_KBD);
482# endif
483 }
484 break;
485 case KBD_CCMD_WRITE_MOUSE:
486 /* Automatically enables aux interface. */
487 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
488 rc = PS2MByteToAux(&s->Aux, val);
489 if (rc == VINF_SUCCESS)
490 kbd_update_irq(s);
491 break;
492 default:
493 break;
494 }
495 if (rc != VINF_IOM_R3_IOPORT_WRITE)
496 s->write_cmd = 0;
497 return rc;
498}
499
500#ifdef IN_RING3
501
502static void kbd_reset(void *opaque)
503{
504 KBDState *s = (KBDState*)opaque;
505 s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
506 s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
507 /* Resetting everything, keyword was not working right on NT4 reboot. */
508 s->write_cmd = 0;
509 s->translate = 0;
510}
511
512static void kbd_save(PSSMHANDLE pSSM, KBDState *s)
513{
514 SSMR3PutU8(pSSM, s->write_cmd);
515 SSMR3PutU8(pSSM, s->status);
516 SSMR3PutU8(pSSM, s->mode);
517 SSMR3PutU8(pSSM, s->dbbout);
518
519 /* terminator */
520 SSMR3PutU32(pSSM, UINT32_MAX);
521}
522
523static int kbd_load(PSSMHANDLE pSSM, KBDState *s, uint32_t version_id)
524{
525 uint32_t u32, i;
526 uint8_t u8Dummy;
527 uint32_t u32Dummy;
528 int rc;
529
530#if 0
531 /** @todo enable this and remove the "if (version_id == 4)" code at some
532 * later time */
533 /* Version 4 was never created by any publicly released version of VBox */
534 AssertReturn(version_id != 4, VERR_NOT_SUPPORTED);
535#endif
536 if (version_id < 2 || version_id > PCKBD_SAVED_STATE_VERSION)
537 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
538 SSMR3GetU8(pSSM, &s->write_cmd);
539 SSMR3GetU8(pSSM, &s->status);
540 SSMR3GetU8(pSSM, &s->mode);
541 if (version_id <= 5)
542 {
543 SSMR3GetU32(pSSM, (uint32_t *)&u32Dummy);
544 SSMR3GetU32(pSSM, (uint32_t *)&u32Dummy);
545 }
546 else
547 {
548 SSMR3GetU8(pSSM, &s->dbbout);
549 }
550 if (version_id <= 7)
551 {
552 int32_t i32Dummy;
553 uint8_t u8State;
554 uint8_t u8Rate;
555 uint8_t u8Proto;
556
557 SSMR3GetU32(pSSM, &u32Dummy);
558 SSMR3GetU8(pSSM, &u8State);
559 SSMR3GetU8(pSSM, &u8Dummy);
560 SSMR3GetU8(pSSM, &u8Rate);
561 SSMR3GetU8(pSSM, &u8Dummy);
562 SSMR3GetU8(pSSM, &u8Proto);
563 SSMR3GetU8(pSSM, &u8Dummy);
564 SSMR3GetS32(pSSM, &i32Dummy);
565 SSMR3GetS32(pSSM, &i32Dummy);
566 SSMR3GetS32(pSSM, &i32Dummy);
567 if (version_id > 2)
568 {
569 SSMR3GetS32(pSSM, &i32Dummy);
570 SSMR3GetS32(pSSM, &i32Dummy);
571 }
572 rc = SSMR3GetU8(pSSM, &u8Dummy);
573 if (version_id == 4)
574 {
575 SSMR3GetU32(pSSM, &u32Dummy);
576 rc = SSMR3GetU32(pSSM, &u32Dummy);
577 }
578 if (version_id > 3)
579 rc = SSMR3GetU8(pSSM, &u8Dummy);
580 if (version_id == 4)
581 rc = SSMR3GetU8(pSSM, &u8Dummy);
582 AssertLogRelRCReturn(rc, rc);
583
584 PS2MFixupState(&s->Aux, u8State, u8Rate, u8Proto);
585 }
586
587 /* Determine the translation state. */
588 s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
589
590 /*
591 * Load the queues
592 */
593 if (version_id <= 5)
594 {
595 rc = SSMR3GetU32(pSSM, &u32);
596 if (RT_FAILURE(rc))
597 return rc;
598 for (i = 0; i < u32; i++)
599 {
600 rc = SSMR3GetU8(pSSM, &u8Dummy);
601 if (RT_FAILURE(rc))
602 return rc;
603 }
604 Log(("kbd_load: %d keyboard queue items discarded from old saved state\n", u32));
605 }
606
607 if (version_id <= 7)
608 {
609 rc = SSMR3GetU32(pSSM, &u32);
610 if (RT_FAILURE(rc))
611 return rc;
612 for (i = 0; i < u32; i++)
613 {
614 rc = SSMR3GetU8(pSSM, &u8Dummy);
615 if (RT_FAILURE(rc))
616 return rc;
617 }
618 Log(("kbd_load: %d mouse event queue items discarded from old saved state\n", u32));
619
620 rc = SSMR3GetU32(pSSM, &u32);
621 if (RT_FAILURE(rc))
622 return rc;
623 for (i = 0; i < u32; i++)
624 {
625 rc = SSMR3GetU8(pSSM, &u8Dummy);
626 if (RT_FAILURE(rc))
627 return rc;
628 }
629 Log(("kbd_load: %d mouse command queue items discarded from old saved state\n", u32));
630 }
631
632 /* terminator */
633 rc = SSMR3GetU32(pSSM, &u32);
634 if (RT_FAILURE(rc))
635 return rc;
636 if (u32 != ~0U)
637 {
638 AssertMsgFailed(("u32=%#x\n", u32));
639 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
640 }
641 return 0;
642}
643#endif /* IN_RING3 */
644
645
646/* VirtualBox code start */
647
648/* -=-=-=-=-=- wrappers -=-=-=-=-=- */
649
650/**
651 * Port I/O Handler for keyboard data IN operations.
652 *
653 * @returns VBox status code.
654 *
655 * @param pDevIns The device instance.
656 * @param pvUser User argument - ignored.
657 * @param Port Port number used for the IN operation.
658 * @param pu32 Where to store the result.
659 * @param cb Number of bytes read.
660 */
661PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
662{
663 uint32_t fluff = 0;
664 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
665
666 NOREF(pvUser);
667 switch (cb) {
668 case 4:
669 fluff |= 0xffff0000; /* Crazy Apple (Darwin 6.0.2 and earlier). */
670 RT_FALL_THRU();
671 case 2:
672 fluff |= 0x0000ff00;
673 RT_FALL_THRU();
674 case 1:
675 *pu32 = fluff | kbd_read_data(pThis, Port);
676 Log2(("kbdIOPortDataRead: Port=%#x cb=%d *pu32=%#x\n", Port, cb, *pu32));
677 return VINF_SUCCESS;
678 default:
679 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
680 return VERR_IOM_IOPORT_UNUSED;
681 }
682}
683
684/**
685 * Port I/O Handler for keyboard data OUT operations.
686 *
687 * @returns VBox status code.
688 *
689 * @param pDevIns The device instance.
690 * @param pvUser User argument - ignored.
691 * @param Port Port number used for the IN operation.
692 * @param u32 The value to output.
693 * @param cb The value size in bytes.
694 */
695PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
696{
697 int rc = VINF_SUCCESS;
698 NOREF(pvUser);
699 if (cb == 1 || cb == 2)
700 {
701 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
702 rc = kbd_write_data(pThis, Port, (uint8_t)u32);
703 Log2(("kbdIOPortDataWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
704 }
705 else
706 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
707 return rc;
708}
709
710/**
711 * Port I/O Handler for keyboard status IN operations.
712 *
713 * @returns VBox status code.
714 *
715 * @param pDevIns The device instance.
716 * @param pvUser User argument - ignored.
717 * @param Port Port number used for the IN operation.
718 * @param pu32 Where to store the result.
719 * @param cb Number of bytes read.
720 */
721PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
722{
723 uint32_t fluff = 0;
724 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
725
726 NOREF(pvUser);
727 switch (cb) {
728 case 4:
729 fluff |= 0xffff0000; /* Crazy Apple (Darwin 6.0.2 and earlier). */
730 RT_FALL_THRU();
731 case 2:
732 fluff |= 0x0000ff00;
733 RT_FALL_THRU();
734 case 1:
735 *pu32 = fluff | kbd_read_status(pThis, Port);
736 Log2(("kbdIOPortStatusRead: Port=%#x cb=%d -> *pu32=%#x\n", Port, cb, *pu32));
737 return VINF_SUCCESS;
738 default:
739 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
740 return VERR_IOM_IOPORT_UNUSED;
741 }
742}
743
744/**
745 * Port I/O Handler for keyboard command OUT operations.
746 *
747 * @returns VBox status code.
748 *
749 * @param pDevIns The device instance.
750 * @param pvUser User argument - ignored.
751 * @param Port Port number used for the IN operation.
752 * @param u32 The value to output.
753 * @param cb The value size in bytes.
754 */
755PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
756{
757 int rc = VINF_SUCCESS;
758 NOREF(pvUser);
759 if (cb == 1 || cb == 2)
760 {
761 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
762 rc = kbd_write_command(pThis, Port, (uint8_t)u32);
763 Log2(("kbdIOPortCommandWrite: Port=%#x cb=%d u32=%#x rc=%Rrc\n", Port, cb, u32, rc));
764 }
765 else
766 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
767 return rc;
768}
769
770#ifdef IN_RING3
771
772/**
773 * Saves a state of the keyboard device.
774 *
775 * @returns VBox status code.
776 * @param pDevIns The device instance.
777 * @param pSSM The handle to save the state to.
778 */
779static DECLCALLBACK(int) kbdSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
780{
781 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
782 kbd_save(pSSM, pThis);
783 PS2KSaveState(&pThis->Kbd, pSSM);
784 PS2MSaveState(&pThis->Aux, pSSM);
785 return VINF_SUCCESS;
786}
787
788
789/**
790 * Loads a saved keyboard device state.
791 *
792 * @returns VBox status code.
793 * @param pDevIns The device instance.
794 * @param pSSM The handle to the saved state.
795 * @param uVersion The data unit version number.
796 * @param uPass The data pass.
797 */
798static DECLCALLBACK(int) kbdLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
799{
800 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
801 int rc;
802
803 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
804 rc = kbd_load(pSSM, pThis, uVersion);
805 if (uVersion >= 6)
806 rc = PS2KLoadState(&pThis->Kbd, pSSM, uVersion);
807 if (uVersion >= 8)
808 rc = PS2MLoadState(&pThis->Aux, pSSM, uVersion);
809 return rc;
810}
811
812/**
813 * @callback_method_impl{FNSSMDEVLOADDONE, Key state fix-up after loading}
814 */
815static DECLCALLBACK(int) kbdLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
816{
817 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
818 int rc;
819
820 rc = PS2KLoadDone(&pThis->Kbd, pSSM);
821 return rc;
822}
823
824/**
825 * Reset notification.
826 *
827 * @returns VBox status code.
828 * @param pDevIns The device instance data.
829 */
830static DECLCALLBACK(void) kbdReset(PPDMDEVINS pDevIns)
831{
832 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
833
834 kbd_reset(pThis);
835 PS2KReset(&pThis->Kbd);
836 PS2MReset(&pThis->Aux);
837}
838
839
840/* -=-=-=-=-=- real code -=-=-=-=-=- */
841
842
843/**
844 * Attach command.
845 *
846 * This is called to let the device attach to a driver for a specified LUN
847 * during runtime. This is not called during VM construction, the device
848 * constructor have to attach to all the available drivers.
849 *
850 * This is like plugging in the keyboard or mouse after turning on the PC.
851 *
852 * @returns VBox status code.
853 * @param pDevIns The device instance.
854 * @param iLUN The logical unit which is being detached.
855 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
856 * @remark The keyboard controller doesn't support this action, this is just
857 * implemented to try out the driver<->device structure.
858 */
859static DECLCALLBACK(int) kbdAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
860{
861 int rc;
862 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
863
864 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
865 ("PS/2 device does not support hotplugging\n"),
866 VERR_INVALID_PARAMETER);
867
868 switch (iLUN)
869 {
870 /* LUN #0: keyboard */
871 case 0:
872 rc = PS2KAttach(&pThis->Kbd, pDevIns, iLUN, fFlags);
873 if (RT_FAILURE(rc))
874 return rc;
875 break;
876
877 /* LUN #1: aux/mouse */
878 case 1:
879 rc = PS2MAttach(&pThis->Aux, pDevIns, iLUN, fFlags);
880 break;
881
882 default:
883 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
884 return VERR_PDM_NO_SUCH_LUN;
885 }
886
887 return rc;
888}
889
890
891/**
892 * Detach notification.
893 *
894 * This is called when a driver is detaching itself from a LUN of the device.
895 * The device should adjust it's state to reflect this.
896 *
897 * This is like unplugging the network cable to use it for the laptop or
898 * something while the PC is still running.
899 *
900 * @param pDevIns The device instance.
901 * @param iLUN The logical unit which is being detached.
902 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
903 * @remark The keyboard controller doesn't support this action, this is just
904 * implemented to try out the driver<->device structure.
905 */
906static DECLCALLBACK(void) kbdDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
907{
908#if 0
909 /*
910 * Reset the interfaces and update the controller state.
911 */
912 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
913 switch (iLUN)
914 {
915 /* LUN #0: keyboard */
916 case 0:
917 pThis->Keyboard.pDrv = NULL;
918 pThis->Keyboard.pDrvBase = NULL;
919 break;
920
921 /* LUN #1: aux/mouse */
922 case 1:
923 pThis->Mouse.pDrv = NULL;
924 pThis->Mouse.pDrvBase = NULL;
925 break;
926
927 default:
928 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
929 break;
930 }
931#else
932 NOREF(pDevIns); NOREF(iLUN); NOREF(fFlags);
933#endif
934}
935
936
937/**
938 * @copydoc FNPDMDEVRELOCATE
939 */
940static DECLCALLBACK(void) kbdRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
941{
942 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
943 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
944 PS2KRelocate(&pThis->Kbd, offDelta, pDevIns);
945 PS2MRelocate(&pThis->Aux, offDelta, pDevIns);
946}
947
948
949/**
950 * @interface_method_impl{PDMDEVREG,pfnConstruct}
951 */
952static DECLCALLBACK(int) kbdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
953{
954 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
955 int rc;
956 bool fGCEnabled;
957 bool fR0Enabled;
958 Assert(iInstance == 0);
959
960 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
961
962 /*
963 * Validate and read the configuration.
964 */
965 if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0R0Enabled\0KbdThrottleEnabled\0"))
966 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
967 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
968 if (RT_FAILURE(rc))
969 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"GCEnabled\" from the config"));
970 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
971 if (RT_FAILURE(rc))
972 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"R0Enabled\" from the config"));
973 Log(("pckbd: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
974
975
976 /*
977 * Initialize the interfaces.
978 */
979 pThis->pDevInsR3 = pDevIns;
980 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
981 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
982
983 rc = PS2KConstruct(&pThis->Kbd, pDevIns, pThis, iInstance, pCfg);
984 if (RT_FAILURE(rc))
985 return rc;
986
987 rc = PS2MConstruct(&pThis->Aux, pDevIns, pThis, iInstance);
988 if (RT_FAILURE(rc))
989 return rc;
990
991 /*
992 * Register I/O ports, save state, keyboard event handler and mouse event handlers.
993 */
994 rc = PDMDevHlpIOPortRegister(pDevIns, 0x60, 1, NULL, kbdIOPortDataWrite, kbdIOPortDataRead, NULL, NULL, "PC Keyboard - Data");
995 if (RT_FAILURE(rc))
996 return rc;
997 rc = PDMDevHlpIOPortRegister(pDevIns, 0x64, 1, NULL, kbdIOPortCommandWrite, kbdIOPortStatusRead, NULL, NULL, "PC Keyboard - Command / Status");
998 if (RT_FAILURE(rc))
999 return rc;
1000 if (fGCEnabled)
1001 {
1002 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
1003 if (RT_FAILURE(rc))
1004 return rc;
1005 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
1006 if (RT_FAILURE(rc))
1007 return rc;
1008 }
1009 if (fR0Enabled)
1010 {
1011 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
1012 if (RT_FAILURE(rc))
1013 return rc;
1014 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
1015 if (RT_FAILURE(rc))
1016 return rc;
1017 }
1018 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCKBD_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
1019 NULL, NULL, NULL,
1020 NULL, kbdSaveExec, NULL,
1021 NULL, kbdLoadExec, kbdLoadDone);
1022 if (RT_FAILURE(rc))
1023 return rc;
1024
1025 /*
1026 * Attach to the keyboard and mouse drivers.
1027 */
1028 rc = kbdAttach(pDevIns, 0 /* keyboard LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
1029 if (RT_FAILURE(rc))
1030 return rc;
1031 rc = kbdAttach(pDevIns, 1 /* aux/mouse LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
1032 if (RT_FAILURE(rc))
1033 return rc;
1034
1035 /*
1036 * Initialize the device state.
1037 */
1038 kbdReset(pDevIns);
1039
1040 return VINF_SUCCESS;
1041}
1042
1043
1044/**
1045 * The device registration structure.
1046 */
1047const PDMDEVREG g_DevicePS2KeyboardMouse =
1048{
1049 /* u32Version */
1050 PDM_DEVREG_VERSION,
1051 /* szName */
1052 "pckbd",
1053 /* szRCMod */
1054 "VBoxDDRC.rc",
1055 /* szR0Mod */
1056 "VBoxDDR0.r0",
1057 /* pszDescription */
1058 "PS/2 Keyboard and Mouse device. Emulates both the keyboard, mouse and the keyboard controller. "
1059 "LUN #0 is the keyboard connector. "
1060 "LUN #1 is the aux/mouse connector.",
1061 /* fFlags */
1062 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1063 /* fClass */
1064 PDM_DEVREG_CLASS_INPUT,
1065 /* cMaxInstances */
1066 1,
1067 /* cbInstance */
1068 sizeof(KBDState),
1069 /* pfnConstruct */
1070 kbdConstruct,
1071 /* pfnDestruct */
1072 NULL,
1073 /* pfnRelocate */
1074 kbdRelocate,
1075 /* pfnMemSetup */
1076 NULL,
1077 /* pfnPowerOn */
1078 NULL,
1079 /* pfnReset */
1080 kbdReset,
1081 /* pfnSuspend */
1082 NULL,
1083 /* pfnResume */
1084 NULL,
1085 /* pfnAttach */
1086 kbdAttach,
1087 /* pfnDetach */
1088 kbdDetach,
1089 /* pfnQueryInterface. */
1090 NULL,
1091 /* pfnInitComplete */
1092 NULL,
1093 /* pfnPowerOff */
1094 NULL,
1095 /* pfnSoftReset */
1096 NULL,
1097 /* u32VersionEnd */
1098 PDM_DEVREG_VERSION
1099};
1100
1101#endif /* IN_RING3 */
1102#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1103
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