VirtualBox

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

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

gcc 7: Devices: fall thru

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