VirtualBox

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

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

Cleanup: Removed VBOX_WITH_NEW_PS2M switch.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.1 KB
Line 
1/* $Id: DevPS2.cpp 62573 2016-07-27 09:03:31Z vboxsync $ */
2/** @file
3 * DevPS2 - PS/2 keyboard & mouse controller device.
4 */
5
6/*
7 * Copyright (C) 2006-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 * 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, ~0);
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 NOREF(pvUser);
664 if (cb == 1)
665 {
666 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
667 *pu32 = kbd_read_data(pThis, Port);
668 Log2(("kbdIOPortDataRead: Port=%#x cb=%d *pu32=%#x\n", Port, cb, *pu32));
669 return VINF_SUCCESS;
670 }
671 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
672 return VERR_IOM_IOPORT_UNUSED;
673}
674
675/**
676 * Port I/O Handler for keyboard data OUT operations.
677 *
678 * @returns VBox status code.
679 *
680 * @param pDevIns The device instance.
681 * @param pvUser User argument - ignored.
682 * @param Port Port number used for the IN operation.
683 * @param u32 The value to output.
684 * @param cb The value size in bytes.
685 */
686PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
687{
688 int rc = VINF_SUCCESS;
689 NOREF(pvUser);
690 if (cb == 1 || cb == 2)
691 {
692 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
693 rc = kbd_write_data(pThis, Port, (uint8_t)u32);
694 Log2(("kbdIOPortDataWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
695 }
696 else
697 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
698 return rc;
699}
700
701/**
702 * Port I/O Handler for keyboard status IN operations.
703 *
704 * @returns VBox status code.
705 *
706 * @param pDevIns The device instance.
707 * @param pvUser User argument - ignored.
708 * @param Port Port number used for the IN operation.
709 * @param pu32 Where to store the result.
710 * @param cb Number of bytes read.
711 */
712PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
713{
714 uint16_t fluff = 0;
715 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
716
717 NOREF(pvUser);
718 switch (cb) {
719 case 2:
720 fluff = 0xff00;
721 case 1:
722 *pu32 = fluff | kbd_read_status(pThis, Port);
723 Log2(("kbdIOPortStatusRead: Port=%#x cb=%d -> *pu32=%#x\n", Port, cb, *pu32));
724 return VINF_SUCCESS;
725 default:
726 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
727 return VERR_IOM_IOPORT_UNUSED;
728 }
729}
730
731/**
732 * Port I/O Handler for keyboard command OUT operations.
733 *
734 * @returns VBox status code.
735 *
736 * @param pDevIns The device instance.
737 * @param pvUser User argument - ignored.
738 * @param Port Port number used for the IN operation.
739 * @param u32 The value to output.
740 * @param cb The value size in bytes.
741 */
742PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
743{
744 int rc = VINF_SUCCESS;
745 NOREF(pvUser);
746 if (cb == 1 || cb == 2)
747 {
748 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
749 rc = kbd_write_command(pThis, Port, (uint8_t)u32);
750 Log2(("kbdIOPortCommandWrite: Port=%#x cb=%d u32=%#x rc=%Rrc\n", Port, cb, u32, rc));
751 }
752 else
753 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
754 return rc;
755}
756
757#ifdef IN_RING3
758
759/**
760 * Saves a state of the keyboard device.
761 *
762 * @returns VBox status code.
763 * @param pDevIns The device instance.
764 * @param pSSM The handle to save the state to.
765 */
766static DECLCALLBACK(int) kbdSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
767{
768 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
769 kbd_save(pSSM, pThis);
770 PS2KSaveState(&pThis->Kbd, pSSM);
771 PS2MSaveState(&pThis->Aux, pSSM);
772 return VINF_SUCCESS;
773}
774
775
776/**
777 * Loads a saved keyboard device state.
778 *
779 * @returns VBox status code.
780 * @param pDevIns The device instance.
781 * @param pSSM The handle to the saved state.
782 * @param uVersion The data unit version number.
783 * @param uPass The data pass.
784 */
785static DECLCALLBACK(int) kbdLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
786{
787 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
788 int rc;
789
790 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
791 rc = kbd_load(pSSM, pThis, uVersion);
792 if (uVersion >= 6)
793 rc = PS2KLoadState(&pThis->Kbd, pSSM, uVersion);
794 if (uVersion >= 8)
795 rc = PS2MLoadState(&pThis->Aux, pSSM, uVersion);
796 return rc;
797}
798
799/**
800 * @callback_method_impl{FNSSMDEVLOADDONE, Key state fix-up after loading
801 */
802static DECLCALLBACK(int) kbdLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
803{
804 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
805 int rc;
806
807 rc = PS2KLoadDone(&pThis->Kbd, pSSM);
808 return rc;
809}
810
811/**
812 * Reset notification.
813 *
814 * @returns VBox status code.
815 * @param pDevIns The device instance data.
816 */
817static DECLCALLBACK(void) kbdReset(PPDMDEVINS pDevIns)
818{
819 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
820
821 kbd_reset(pThis);
822 PS2KReset(&pThis->Kbd);
823 PS2MReset(&pThis->Aux);
824}
825
826
827/* -=-=-=-=-=- real code -=-=-=-=-=- */
828
829
830/**
831 * Attach command.
832 *
833 * This is called to let the device attach to a driver for a specified LUN
834 * during runtime. This is not called during VM construction, the device
835 * constructor have to attach to all the available drivers.
836 *
837 * This is like plugging in the keyboard or mouse after turning on the PC.
838 *
839 * @returns VBox status code.
840 * @param pDevIns The device instance.
841 * @param iLUN The logical unit which is being detached.
842 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
843 * @remark The keyboard controller doesn't support this action, this is just
844 * implemented to try out the driver<->device structure.
845 */
846static DECLCALLBACK(int) kbdAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
847{
848 int rc;
849 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
850
851 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
852 ("PS/2 device does not support hotplugging\n"),
853 VERR_INVALID_PARAMETER);
854
855 switch (iLUN)
856 {
857 /* LUN #0: keyboard */
858 case 0:
859 rc = PS2KAttach(&pThis->Kbd, pDevIns, iLUN, fFlags);
860 if (RT_FAILURE(rc))
861 return rc;
862 break;
863
864 /* LUN #1: aux/mouse */
865 case 1:
866 rc = PS2MAttach(&pThis->Aux, pDevIns, iLUN, fFlags);
867 break;
868
869 default:
870 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
871 return VERR_PDM_NO_SUCH_LUN;
872 }
873
874 return rc;
875}
876
877
878/**
879 * Detach notification.
880 *
881 * This is called when a driver is detaching itself from a LUN of the device.
882 * The device should adjust it's state to reflect this.
883 *
884 * This is like unplugging the network cable to use it for the laptop or
885 * something while the PC is still running.
886 *
887 * @param pDevIns The device instance.
888 * @param iLUN The logical unit which is being detached.
889 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
890 * @remark The keyboard controller doesn't support this action, this is just
891 * implemented to try out the driver<->device structure.
892 */
893static DECLCALLBACK(void) kbdDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
894{
895#if 0
896 /*
897 * Reset the interfaces and update the controller state.
898 */
899 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
900 switch (iLUN)
901 {
902 /* LUN #0: keyboard */
903 case 0:
904 pThis->Keyboard.pDrv = NULL;
905 pThis->Keyboard.pDrvBase = NULL;
906 break;
907
908 /* LUN #1: aux/mouse */
909 case 1:
910 pThis->Mouse.pDrv = NULL;
911 pThis->Mouse.pDrvBase = NULL;
912 break;
913
914 default:
915 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
916 break;
917 }
918#else
919 NOREF(pDevIns); NOREF(iLUN); NOREF(fFlags);
920#endif
921}
922
923
924/**
925 * @copydoc FNPDMDEVRELOCATE
926 */
927static DECLCALLBACK(void) kbdRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
928{
929 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
930 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
931 PS2KRelocate(&pThis->Kbd, offDelta, pDevIns);
932 PS2MRelocate(&pThis->Aux, offDelta, pDevIns);
933}
934
935
936/**
937 * @interface_method_impl{PDMDEVREG,pfnConstruct}
938 */
939static DECLCALLBACK(int) kbdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
940{
941 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
942 int rc;
943 bool fGCEnabled;
944 bool fR0Enabled;
945 Assert(iInstance == 0);
946
947 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
948
949 /*
950 * Validate and read the configuration.
951 */
952 if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0R0Enabled\0"))
953 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
954 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
955 if (RT_FAILURE(rc))
956 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"GCEnabled\" from the config"));
957 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
958 if (RT_FAILURE(rc))
959 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"R0Enabled\" from the config"));
960 Log(("pckbd: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
961
962
963 /*
964 * Initialize the interfaces.
965 */
966 pThis->pDevInsR3 = pDevIns;
967 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
968 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
969
970 rc = PS2KConstruct(&pThis->Kbd, pDevIns, pThis, iInstance);
971 if (RT_FAILURE(rc))
972 return rc;
973
974 rc = PS2MConstruct(&pThis->Aux, pDevIns, pThis, iInstance);
975 if (RT_FAILURE(rc))
976 return rc;
977
978 /*
979 * Register I/O ports, save state, keyboard event handler and mouse event handlers.
980 */
981 rc = PDMDevHlpIOPortRegister(pDevIns, 0x60, 1, NULL, kbdIOPortDataWrite, kbdIOPortDataRead, NULL, NULL, "PC Keyboard - Data");
982 if (RT_FAILURE(rc))
983 return rc;
984 rc = PDMDevHlpIOPortRegister(pDevIns, 0x64, 1, NULL, kbdIOPortCommandWrite, kbdIOPortStatusRead, NULL, NULL, "PC Keyboard - Command / Status");
985 if (RT_FAILURE(rc))
986 return rc;
987 if (fGCEnabled)
988 {
989 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
990 if (RT_FAILURE(rc))
991 return rc;
992 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
993 if (RT_FAILURE(rc))
994 return rc;
995 }
996 if (fR0Enabled)
997 {
998 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
999 if (RT_FAILURE(rc))
1000 return rc;
1001 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
1002 if (RT_FAILURE(rc))
1003 return rc;
1004 }
1005 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCKBD_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
1006 NULL, NULL, NULL,
1007 NULL, kbdSaveExec, NULL,
1008 NULL, kbdLoadExec, kbdLoadDone);
1009 if (RT_FAILURE(rc))
1010 return rc;
1011
1012 /*
1013 * Attach to the keyboard and mouse drivers.
1014 */
1015 rc = kbdAttach(pDevIns, 0 /* keyboard LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
1016 if (RT_FAILURE(rc))
1017 return rc;
1018 rc = kbdAttach(pDevIns, 1 /* aux/mouse LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
1019 if (RT_FAILURE(rc))
1020 return rc;
1021
1022 /*
1023 * Initialize the device state.
1024 */
1025 kbdReset(pDevIns);
1026
1027 return VINF_SUCCESS;
1028}
1029
1030
1031/**
1032 * The device registration structure.
1033 */
1034const PDMDEVREG g_DevicePS2KeyboardMouse =
1035{
1036 /* u32Version */
1037 PDM_DEVREG_VERSION,
1038 /* szName */
1039 "pckbd",
1040 /* szRCMod */
1041 "VBoxDDRC.rc",
1042 /* szR0Mod */
1043 "VBoxDDR0.r0",
1044 /* pszDescription */
1045 "PS/2 Keyboard and Mouse device. Emulates both the keyboard, mouse and the keyboard controller. "
1046 "LUN #0 is the keyboard connector. "
1047 "LUN #1 is the aux/mouse connector.",
1048 /* fFlags */
1049 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,
1050 /* fClass */
1051 PDM_DEVREG_CLASS_INPUT,
1052 /* cMaxInstances */
1053 1,
1054 /* cbInstance */
1055 sizeof(KBDState),
1056 /* pfnConstruct */
1057 kbdConstruct,
1058 /* pfnDestruct */
1059 NULL,
1060 /* pfnRelocate */
1061 kbdRelocate,
1062 /* pfnMemSetup */
1063 NULL,
1064 /* pfnPowerOn */
1065 NULL,
1066 /* pfnReset */
1067 kbdReset,
1068 /* pfnSuspend */
1069 NULL,
1070 /* pfnResume */
1071 NULL,
1072 /* pfnAttach */
1073 kbdAttach,
1074 /* pfnDetach */
1075 kbdDetach,
1076 /* pfnQueryInterface. */
1077 NULL,
1078 /* pfnInitComplete */
1079 NULL,
1080 /* pfnPowerOff */
1081 NULL,
1082 /* pfnSoftReset */
1083 NULL,
1084 /* u32VersionEnd */
1085 PDM_DEVREG_VERSION
1086};
1087
1088#endif /* IN_RING3 */
1089#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1090
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use