VirtualBox

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

Last change on this file since 82173 was 82173, checked in by vboxsync, 5 years ago

DevPS2: Less opaque and structure duplicatication. (The opaque stuff would become very tedious later when splitting up the state structures.) bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.2 KB
Line 
1/* $Id: DevPS2.cpp 82173 2019-11-25 13:00:55Z vboxsync $ */
2/** @file
3 * DevPS2 - PS/2 keyboard & mouse controller device.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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 "DevPS2.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/* update irq and KBD_STAT_[MOUSE_]OBF */
129static void kbd_update_irq(KBDState *s)
130{
131 int irq12_level, irq1_level;
132 uint8_t val;
133
134 irq1_level = 0;
135 irq12_level = 0;
136
137 /* Determine new OBF state, but only if OBF is clear. If OBF was already
138 * set, we cannot risk changing the event type after an ISR potentially
139 * started executing! Only kbd_read_data() clears the OBF bits.
140 */
141 if (!(s->status & KBD_STAT_OBF)) {
142 s->status &= ~KBD_STAT_MOUSE_OBF;
143 /* Keyboard data has priority if both kbd and aux data is available. */
144 if (!(s->mode & KBD_MODE_DISABLE_KBD) && PS2KByteFromKbd(&s->Kbd, &val) == VINF_SUCCESS)
145 {
146 bool fHaveData = true;
147
148 /* If scancode translation is on (it usually is), there's more work to do. */
149 if (s->translate)
150 {
151 uint8_t xlated_val;
152
153 s->xlat_state = XlateAT2PC(s->xlat_state, val, &xlated_val);
154 val = xlated_val;
155
156 /* If the translation state is XS_BREAK, there's nothing to report
157 * and we keep going until the state changes or there's no more data.
158 */
159 while (s->xlat_state == XS_BREAK && PS2KByteFromKbd(&s->Kbd, &val) == VINF_SUCCESS)
160 {
161 s->xlat_state = XlateAT2PC(s->xlat_state, val, &xlated_val);
162 val = xlated_val;
163 }
164 /* This can happen if the last byte in the queue is F0... */
165 if (s->xlat_state == XS_BREAK)
166 fHaveData = false;
167 }
168 if (fHaveData)
169 {
170 s->dbbout = val;
171 s->status |= KBD_STAT_OBF;
172 }
173 }
174 else if (!(s->mode & KBD_MODE_DISABLE_MOUSE) && PS2MByteFromAux(&s->Aux, &val) == VINF_SUCCESS)
175 {
176 s->dbbout = val;
177 s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
178 }
179 }
180 /* Determine new IRQ state. */
181 if (s->status & KBD_STAT_OBF) {
182 if (s->status & KBD_STAT_MOUSE_OBF)
183 {
184 if (s->mode & KBD_MODE_MOUSE_INT)
185 irq12_level = 1;
186 }
187 else
188 { /* KBD_STAT_OBF set but KBD_STAT_MOUSE_OBF isn't. */
189 if (s->mode & KBD_MODE_KBD_INT)
190 irq1_level = 1;
191 }
192 }
193 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, irq1_level);
194 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, irq12_level);
195}
196
197void KBCUpdateInterrupts(void *pKbc)
198{
199 KBDState *s = (KBDState *)pKbc;
200 kbd_update_irq(s);
201}
202
203static void kbc_dbb_out(void *opaque, uint8_t val)
204{
205 KBDState *s = (KBDState*)opaque;
206
207 s->dbbout = val;
208 /* Set the OBF and raise IRQ. */
209 s->status |= KBD_STAT_OBF;
210 if (s->mode & KBD_MODE_KBD_INT)
211 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, 1);
212}
213
214static void kbc_dbb_out_aux(void *opaque, uint8_t val)
215{
216 KBDState *s = (KBDState*)opaque;
217
218 s->dbbout = val;
219 /* Set the aux OBF and raise IRQ. */
220 s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
221 if (s->mode & KBD_MODE_MOUSE_INT)
222 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, PDM_IRQ_LEVEL_HIGH);
223}
224
225static uint32_t kbd_read_status(void *opaque, uint32_t addr)
226{
227 KBDState *s = (KBDState*)opaque;
228 int val = s->status;
229 NOREF(addr);
230
231#if defined(DEBUG_KBD)
232 Log(("kbd: read status=0x%02x\n", val));
233#endif
234 return val;
235}
236
237static int kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
238{
239 int rc = VINF_SUCCESS;
240 KBDState *s = (KBDState*)opaque;
241 NOREF(addr);
242
243#ifdef DEBUG_KBD
244 Log(("kbd: write cmd=0x%02x\n", val));
245#endif
246 switch(val) {
247 case KBD_CCMD_READ_MODE:
248 kbc_dbb_out(s, s->mode);
249 break;
250 case KBD_CCMD_WRITE_MODE:
251 case KBD_CCMD_WRITE_OBUF:
252 case KBD_CCMD_WRITE_AUX_OBUF:
253 case KBD_CCMD_WRITE_MOUSE:
254 case KBD_CCMD_WRITE_OUTPORT:
255 s->write_cmd = val;
256 break;
257 case KBD_CCMD_MOUSE_DISABLE:
258 s->mode |= KBD_MODE_DISABLE_MOUSE;
259 break;
260 case KBD_CCMD_MOUSE_ENABLE:
261 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
262 /* Check for queued input. */
263 kbd_update_irq(s);
264 break;
265 case KBD_CCMD_TEST_MOUSE:
266 kbc_dbb_out(s, 0x00);
267 break;
268 case KBD_CCMD_SELF_TEST:
269 /* Enable the A20 line - that is the power-on state(!). */
270# ifndef IN_RING3
271 if (!PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
272 {
273 rc = VINF_IOM_R3_IOPORT_WRITE;
274 break;
275 }
276# else /* IN_RING3 */
277 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), true);
278# endif /* IN_RING3 */
279 s->status |= KBD_STAT_SELFTEST;
280 s->mode |= KBD_MODE_DISABLE_KBD;
281 kbc_dbb_out(s, 0x55);
282 break;
283 case KBD_CCMD_KBD_TEST:
284 kbc_dbb_out(s, 0x00);
285 break;
286 case KBD_CCMD_KBD_DISABLE:
287 s->mode |= KBD_MODE_DISABLE_KBD;
288 break;
289 case KBD_CCMD_KBD_ENABLE:
290 s->mode &= ~KBD_MODE_DISABLE_KBD;
291 /* Check for queued input. */
292 kbd_update_irq(s);
293 break;
294 case KBD_CCMD_READ_INPORT:
295 kbc_dbb_out(s, 0xBF);
296 break;
297 case KBD_CCMD_READ_OUTPORT:
298 /* XXX: check that */
299#ifdef TARGET_I386
300 val = 0x01 | (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)) << 1);
301#else
302 val = 0x01;
303#endif
304 if (s->status & KBD_STAT_OBF)
305 val |= 0x10;
306 if (s->status & KBD_STAT_MOUSE_OBF)
307 val |= 0x20;
308 kbc_dbb_out(s, val);
309 break;
310#ifdef TARGET_I386
311 case KBD_CCMD_ENABLE_A20:
312# ifndef IN_RING3
313 if (!PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
314 rc = VINF_IOM_R3_IOPORT_WRITE;
315# else /* IN_RING3 */
316 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), true);
317# endif /* IN_RING3 */
318 break;
319 case KBD_CCMD_DISABLE_A20:
320# ifndef IN_RING3
321 if (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
322 rc = VINF_IOM_R3_IOPORT_WRITE;
323# else /* IN_RING3 */
324 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), false);
325# endif /* !IN_RING3 */
326 break;
327#endif
328 case KBD_CCMD_READ_TSTINP:
329 /* Keyboard clock line is zero IFF keyboard is disabled */
330 val = (s->mode & KBD_MODE_DISABLE_KBD) ? 0 : 1;
331 kbc_dbb_out(s, val);
332 break;
333 case KBD_CCMD_RESET:
334 case KBD_CCMD_RESET_ALT:
335#ifndef IN_RING3
336 rc = VINF_IOM_R3_IOPORT_WRITE;
337#else /* IN_RING3 */
338 LogRel(("Reset initiated by keyboard controller\n"));
339 rc = PDMDevHlpVMReset(s->CTX_SUFF(pDevIns), PDMVMRESET_F_KBD);
340#endif /* !IN_RING3 */
341 break;
342 case 0xff:
343 /* ignore that - I don't know what is its use */
344 break;
345 /* Make OS/2 happy. */
346 /* The 8042 RAM is readable using commands 0x20 thru 0x3f, and writable
347 by 0x60 thru 0x7f. Now days only the first byte, the mode, is used.
348 We'll ignore the writes (0x61..7f) and return 0 for all the reads
349 just to make some OS/2 debug stuff a bit happier. */
350 case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
351 case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
352 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
353 case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
354 kbc_dbb_out(s, 0);
355 Log(("kbd: reading non-standard RAM addr %#x\n", val & 0x1f));
356 break;
357 default:
358 Log(("kbd: unsupported keyboard cmd=0x%02x\n", val));
359 break;
360 }
361 return rc;
362}
363
364static uint32_t kbd_read_data(void *opaque, uint32_t addr)
365{
366 KBDState *s = (KBDState*)opaque;
367 uint32_t val;
368 NOREF(addr);
369
370 /* Return the current DBB contents. */
371 val = s->dbbout;
372
373 /* Reading the DBB deasserts IRQs... */
374 if (s->status & KBD_STAT_MOUSE_OBF)
375 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, 0);
376 else
377 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, 0);
378 /* ...and clears the OBF bits. */
379 s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
380
381 /* Check if more data is available. */
382 kbd_update_irq(s);
383#ifdef DEBUG_KBD
384 Log(("kbd: read data=0x%02x\n", val));
385#endif
386 return val;
387}
388
389PS2K *KBDGetPS2KFromDevIns(PPDMDEVINS pDevIns)
390{
391 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
392 return &pThis->Kbd;
393}
394
395PS2M *KBDGetPS2MFromDevIns(PPDMDEVINS pDevIns)
396{
397 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
398 return &pThis->Aux;
399}
400
401static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
402{
403 int rc = VINF_SUCCESS;
404 KBDState *s = (KBDState*)opaque;
405 NOREF(addr);
406
407#ifdef DEBUG_KBD
408 Log(("kbd: write data=0x%02x\n", val));
409#endif
410
411 switch(s->write_cmd) {
412 case 0:
413 /* Automatically enables keyboard interface. */
414 s->mode &= ~KBD_MODE_DISABLE_KBD;
415 rc = PS2KByteToKbd(&s->Kbd, val);
416 if (rc == VINF_SUCCESS)
417 kbd_update_irq(s);
418 break;
419 case KBD_CCMD_WRITE_MODE:
420 s->mode = val;
421 s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
422 kbd_update_irq(s);
423 break;
424 case KBD_CCMD_WRITE_OBUF:
425 kbc_dbb_out(s, val);
426 break;
427 case KBD_CCMD_WRITE_AUX_OBUF:
428 kbc_dbb_out_aux(s, val);
429 break;
430 case KBD_CCMD_WRITE_OUTPORT:
431#ifdef TARGET_I386
432# ifndef IN_RING3
433 if (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)) != !!(val & 2))
434 rc = VINF_IOM_R3_IOPORT_WRITE;
435# else /* IN_RING3 */
436 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), !!(val & 2));
437# endif /* !IN_RING3 */
438#endif
439 if (!(val & 1)) {
440# ifndef IN_RING3
441 rc = VINF_IOM_R3_IOPORT_WRITE;
442# else
443 rc = PDMDevHlpVMReset(s->CTX_SUFF(pDevIns), PDMVMRESET_F_KBD);
444# endif
445 }
446 break;
447 case KBD_CCMD_WRITE_MOUSE:
448 /* Automatically enables aux interface. */
449 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
450 rc = PS2MByteToAux(&s->Aux, val);
451 if (rc == VINF_SUCCESS)
452 kbd_update_irq(s);
453 break;
454 default:
455 break;
456 }
457 if (rc != VINF_IOM_R3_IOPORT_WRITE)
458 s->write_cmd = 0;
459 return rc;
460}
461
462#ifdef IN_RING3
463
464static void kbd_reset(void *opaque)
465{
466 KBDState *s = (KBDState*)opaque;
467 s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
468 s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
469 /* Resetting everything, keyword was not working right on NT4 reboot. */
470 s->write_cmd = 0;
471 s->translate = 0;
472}
473
474static void kbd_save(PSSMHANDLE pSSM, KBDState *s)
475{
476 SSMR3PutU8(pSSM, s->write_cmd);
477 SSMR3PutU8(pSSM, s->status);
478 SSMR3PutU8(pSSM, s->mode);
479 SSMR3PutU8(pSSM, s->dbbout);
480
481 /* terminator */
482 SSMR3PutU32(pSSM, UINT32_MAX);
483}
484
485static int kbd_load(PSSMHANDLE pSSM, KBDState *s, uint32_t version_id)
486{
487 uint32_t u32, i;
488 uint8_t u8Dummy;
489 uint32_t u32Dummy;
490 int rc;
491
492#if 0
493 /** @todo enable this and remove the "if (version_id == 4)" code at some
494 * later time */
495 /* Version 4 was never created by any publicly released version of VBox */
496 AssertReturn(version_id != 4, VERR_NOT_SUPPORTED);
497#endif
498 if (version_id < 2 || version_id > PCKBD_SAVED_STATE_VERSION)
499 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
500 SSMR3GetU8(pSSM, &s->write_cmd);
501 SSMR3GetU8(pSSM, &s->status);
502 SSMR3GetU8(pSSM, &s->mode);
503 if (version_id <= 5)
504 {
505 SSMR3GetU32(pSSM, &u32Dummy);
506 SSMR3GetU32(pSSM, &u32Dummy);
507 }
508 else
509 {
510 SSMR3GetU8(pSSM, &s->dbbout);
511 }
512 if (version_id <= 7)
513 {
514 int32_t i32Dummy;
515 uint8_t u8State;
516 uint8_t u8Rate;
517 uint8_t u8Proto;
518
519 SSMR3GetU32(pSSM, &u32Dummy);
520 SSMR3GetU8(pSSM, &u8State);
521 SSMR3GetU8(pSSM, &u8Dummy);
522 SSMR3GetU8(pSSM, &u8Rate);
523 SSMR3GetU8(pSSM, &u8Dummy);
524 SSMR3GetU8(pSSM, &u8Proto);
525 SSMR3GetU8(pSSM, &u8Dummy);
526 SSMR3GetS32(pSSM, &i32Dummy);
527 SSMR3GetS32(pSSM, &i32Dummy);
528 SSMR3GetS32(pSSM, &i32Dummy);
529 if (version_id > 2)
530 {
531 SSMR3GetS32(pSSM, &i32Dummy);
532 SSMR3GetS32(pSSM, &i32Dummy);
533 }
534 rc = SSMR3GetU8(pSSM, &u8Dummy);
535 if (version_id == 4)
536 {
537 SSMR3GetU32(pSSM, &u32Dummy);
538 rc = SSMR3GetU32(pSSM, &u32Dummy);
539 }
540 if (version_id > 3)
541 rc = SSMR3GetU8(pSSM, &u8Dummy);
542 if (version_id == 4)
543 rc = SSMR3GetU8(pSSM, &u8Dummy);
544 AssertLogRelRCReturn(rc, rc);
545
546 PS2MFixupState(&s->Aux, u8State, u8Rate, u8Proto);
547 }
548
549 /* Determine the translation state. */
550 s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
551
552 /*
553 * Load the queues
554 */
555 if (version_id <= 5)
556 {
557 rc = SSMR3GetU32(pSSM, &u32);
558 if (RT_FAILURE(rc))
559 return rc;
560 for (i = 0; i < u32; i++)
561 {
562 rc = SSMR3GetU8(pSSM, &u8Dummy);
563 if (RT_FAILURE(rc))
564 return rc;
565 }
566 Log(("kbd_load: %d keyboard queue items discarded from old saved state\n", u32));
567 }
568
569 if (version_id <= 7)
570 {
571 rc = SSMR3GetU32(pSSM, &u32);
572 if (RT_FAILURE(rc))
573 return rc;
574 for (i = 0; i < u32; i++)
575 {
576 rc = SSMR3GetU8(pSSM, &u8Dummy);
577 if (RT_FAILURE(rc))
578 return rc;
579 }
580 Log(("kbd_load: %d mouse event queue items discarded from old saved state\n", u32));
581
582 rc = SSMR3GetU32(pSSM, &u32);
583 if (RT_FAILURE(rc))
584 return rc;
585 for (i = 0; i < u32; i++)
586 {
587 rc = SSMR3GetU8(pSSM, &u8Dummy);
588 if (RT_FAILURE(rc))
589 return rc;
590 }
591 Log(("kbd_load: %d mouse command queue items discarded from old saved state\n", u32));
592 }
593
594 /* terminator */
595 rc = SSMR3GetU32(pSSM, &u32);
596 if (RT_FAILURE(rc))
597 return rc;
598 if (u32 != ~0U)
599 {
600 AssertMsgFailed(("u32=%#x\n", u32));
601 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
602 }
603 return 0;
604}
605#endif /* IN_RING3 */
606
607
608/* VirtualBox code start */
609
610/* -=-=-=-=-=- wrappers -=-=-=-=-=- */
611
612/**
613 * Port I/O Handler for keyboard data IN operations.
614 *
615 * @returns VBox status code.
616 *
617 * @param pDevIns The device instance.
618 * @param pvUser User argument - ignored.
619 * @param Port Port number used for the IN operation.
620 * @param pu32 Where to store the result.
621 * @param cb Number of bytes read.
622 */
623PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
624{
625 uint32_t fluff = 0;
626 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
627
628 NOREF(pvUser);
629 switch (cb) {
630 case 4:
631 fluff |= 0xffff0000; /* Crazy Apple (Darwin 6.0.2 and earlier). */
632 RT_FALL_THRU();
633 case 2:
634 fluff |= 0x0000ff00;
635 RT_FALL_THRU();
636 case 1:
637 *pu32 = fluff | kbd_read_data(pThis, Port);
638 Log2(("kbdIOPortDataRead: Port=%#x cb=%d *pu32=%#x\n", Port, cb, *pu32));
639 return VINF_SUCCESS;
640 default:
641 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
642 return VERR_IOM_IOPORT_UNUSED;
643 }
644}
645
646/**
647 * Port I/O Handler for keyboard data OUT operations.
648 *
649 * @returns VBox status code.
650 *
651 * @param pDevIns The device instance.
652 * @param pvUser User argument - ignored.
653 * @param Port Port number used for the IN operation.
654 * @param u32 The value to output.
655 * @param cb The value size in bytes.
656 */
657PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
658{
659 int rc = VINF_SUCCESS;
660 NOREF(pvUser);
661 if (cb == 1 || cb == 2)
662 {
663 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
664 rc = kbd_write_data(pThis, Port, (uint8_t)u32);
665 Log2(("kbdIOPortDataWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
666 }
667 else
668 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
669 return rc;
670}
671
672/**
673 * Port I/O Handler for keyboard status IN operations.
674 *
675 * @returns VBox status code.
676 *
677 * @param pDevIns The device instance.
678 * @param pvUser User argument - ignored.
679 * @param Port Port number used for the IN operation.
680 * @param pu32 Where to store the result.
681 * @param cb Number of bytes read.
682 */
683PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
684{
685 uint32_t fluff = 0;
686 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
687
688 NOREF(pvUser);
689 switch (cb) {
690 case 4:
691 fluff |= 0xffff0000; /* Crazy Apple (Darwin 6.0.2 and earlier). */
692 RT_FALL_THRU();
693 case 2:
694 fluff |= 0x0000ff00;
695 RT_FALL_THRU();
696 case 1:
697 *pu32 = fluff | kbd_read_status(pThis, Port);
698 Log2(("kbdIOPortStatusRead: Port=%#x cb=%d -> *pu32=%#x\n", Port, cb, *pu32));
699 return VINF_SUCCESS;
700 default:
701 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
702 return VERR_IOM_IOPORT_UNUSED;
703 }
704}
705
706/**
707 * Port I/O Handler for keyboard command OUT operations.
708 *
709 * @returns VBox status code.
710 *
711 * @param pDevIns The device instance.
712 * @param pvUser User argument - ignored.
713 * @param Port Port number used for the IN operation.
714 * @param u32 The value to output.
715 * @param cb The value size in bytes.
716 */
717PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
718{
719 int rc = VINF_SUCCESS;
720 NOREF(pvUser);
721 if (cb == 1 || cb == 2)
722 {
723 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
724 rc = kbd_write_command(pThis, Port, (uint8_t)u32);
725 Log2(("kbdIOPortCommandWrite: Port=%#x cb=%d u32=%#x rc=%Rrc\n", Port, cb, u32, rc));
726 }
727 else
728 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
729 return rc;
730}
731
732#ifdef IN_RING3
733
734/**
735 * Saves a state of the keyboard device.
736 *
737 * @returns VBox status code.
738 * @param pDevIns The device instance.
739 * @param pSSM The handle to save the state to.
740 */
741static DECLCALLBACK(int) kbdSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
742{
743 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
744 kbd_save(pSSM, pThis);
745 PS2KSaveState(&pThis->Kbd, pSSM);
746 PS2MSaveState(&pThis->Aux, pSSM);
747 return VINF_SUCCESS;
748}
749
750
751/**
752 * Loads a saved keyboard device state.
753 *
754 * @returns VBox status code.
755 * @param pDevIns The device instance.
756 * @param pSSM The handle to the saved state.
757 * @param uVersion The data unit version number.
758 * @param uPass The data pass.
759 */
760static DECLCALLBACK(int) kbdLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
761{
762 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
763 int rc;
764
765 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
766 rc = kbd_load(pSSM, pThis, uVersion);
767 if (uVersion >= 6)
768 rc = PS2KLoadState(&pThis->Kbd, pSSM, uVersion);
769 if (uVersion >= 8)
770 rc = PS2MLoadState(&pThis->Aux, pSSM, uVersion);
771 return rc;
772}
773
774/**
775 * @callback_method_impl{FNSSMDEVLOADDONE, Key state fix-up after loading}
776 */
777static DECLCALLBACK(int) kbdLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
778{
779 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
780 int rc;
781
782 rc = PS2KLoadDone(&pThis->Kbd, pSSM);
783 return rc;
784}
785
786/**
787 * Reset notification.
788 *
789 * @returns VBox status code.
790 * @param pDevIns The device instance data.
791 */
792static DECLCALLBACK(void) kbdReset(PPDMDEVINS pDevIns)
793{
794 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
795
796 kbd_reset(pThis);
797 PS2KReset(&pThis->Kbd);
798 PS2MReset(&pThis->Aux);
799}
800
801
802/* -=-=-=-=-=- real code -=-=-=-=-=- */
803
804
805/**
806 * Attach command.
807 *
808 * This is called to let the device attach to a driver for a specified LUN
809 * during runtime. This is not called during VM construction, the device
810 * constructor have to attach to all the available drivers.
811 *
812 * This is like plugging in the keyboard or mouse after turning on the PC.
813 *
814 * @returns VBox status code.
815 * @param pDevIns The device instance.
816 * @param iLUN The logical unit which is being detached.
817 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
818 * @remark The keyboard controller doesn't support this action, this is just
819 * implemented to try out the driver<->device structure.
820 */
821static DECLCALLBACK(int) kbdAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
822{
823 int rc;
824 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
825
826 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
827 ("PS/2 device does not support hotplugging\n"),
828 VERR_INVALID_PARAMETER);
829
830 switch (iLUN)
831 {
832 /* LUN #0: keyboard */
833 case 0:
834 rc = PS2KAttach(&pThis->Kbd, pDevIns, iLUN, fFlags);
835 if (RT_FAILURE(rc))
836 return rc;
837 break;
838
839 /* LUN #1: aux/mouse */
840 case 1:
841 rc = PS2MAttach(&pThis->Aux, pDevIns, iLUN, fFlags);
842 break;
843
844 default:
845 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
846 return VERR_PDM_NO_SUCH_LUN;
847 }
848
849 return rc;
850}
851
852
853/**
854 * Detach notification.
855 *
856 * This is called when a driver is detaching itself from a LUN of the device.
857 * The device should adjust it's state to reflect this.
858 *
859 * This is like unplugging the network cable to use it for the laptop or
860 * something while the PC is still running.
861 *
862 * @param pDevIns The device instance.
863 * @param iLUN The logical unit which is being detached.
864 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
865 * @remark The keyboard controller doesn't support this action, this is just
866 * implemented to try out the driver<->device structure.
867 */
868static DECLCALLBACK(void) kbdDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
869{
870#if 0
871 /*
872 * Reset the interfaces and update the controller state.
873 */
874 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
875 switch (iLUN)
876 {
877 /* LUN #0: keyboard */
878 case 0:
879 pThis->Keyboard.pDrv = NULL;
880 pThis->Keyboard.pDrvBase = NULL;
881 break;
882
883 /* LUN #1: aux/mouse */
884 case 1:
885 pThis->Mouse.pDrv = NULL;
886 pThis->Mouse.pDrvBase = NULL;
887 break;
888
889 default:
890 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
891 break;
892 }
893#else
894 NOREF(pDevIns); NOREF(iLUN); NOREF(fFlags);
895#endif
896}
897
898
899/**
900 * @copydoc FNPDMDEVRELOCATE
901 */
902static DECLCALLBACK(void) kbdRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
903{
904 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
905 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
906 PS2KRelocate(&pThis->Kbd, offDelta, pDevIns);
907 PS2MRelocate(&pThis->Aux, offDelta, pDevIns);
908}
909
910
911/**
912 * @interface_method_impl{PDMDEVREG,pfnConstruct}
913 */
914static DECLCALLBACK(int) kbdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
915{
916 KBDState *pThis = PDMDEVINS_2_DATA(pDevIns, KBDState *);
917 int rc;
918 bool fGCEnabled;
919 bool fR0Enabled;
920 Assert(iInstance == 0);
921
922 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
923
924 /*
925 * Validate and read the configuration.
926 */
927 if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0R0Enabled\0KbdThrottleEnabled\0"))
928 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
929 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
930 if (RT_FAILURE(rc))
931 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"GCEnabled\" from the config"));
932 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
933 if (RT_FAILURE(rc))
934 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"R0Enabled\" from the config"));
935 Log(("pckbd: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
936
937
938 /*
939 * Initialize the interfaces.
940 */
941 pThis->pDevInsR3 = pDevIns;
942 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
943 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
944
945 rc = PS2KConstruct(&pThis->Kbd, pDevIns, pThis, iInstance, pCfg);
946 if (RT_FAILURE(rc))
947 return rc;
948
949 rc = PS2MConstruct(&pThis->Aux, pDevIns, pThis, iInstance);
950 if (RT_FAILURE(rc))
951 return rc;
952
953 /*
954 * Register I/O ports, save state, keyboard event handler and mouse event handlers.
955 */
956 rc = PDMDevHlpIOPortRegister(pDevIns, 0x60, 1, NULL, kbdIOPortDataWrite, kbdIOPortDataRead, NULL, NULL, "PC Keyboard - Data");
957 if (RT_FAILURE(rc))
958 return rc;
959 rc = PDMDevHlpIOPortRegister(pDevIns, 0x64, 1, NULL, kbdIOPortCommandWrite, kbdIOPortStatusRead, NULL, NULL, "PC Keyboard - Command / Status");
960 if (RT_FAILURE(rc))
961 return rc;
962 if (fGCEnabled)
963 {
964 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
965 if (RT_FAILURE(rc))
966 return rc;
967 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
968 if (RT_FAILURE(rc))
969 return rc;
970 }
971 if (fR0Enabled)
972 {
973 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
974 if (RT_FAILURE(rc))
975 return rc;
976 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
977 if (RT_FAILURE(rc))
978 return rc;
979 }
980 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCKBD_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
981 NULL, NULL, NULL,
982 NULL, kbdSaveExec, NULL,
983 NULL, kbdLoadExec, kbdLoadDone);
984 if (RT_FAILURE(rc))
985 return rc;
986
987 /*
988 * Attach to the keyboard and mouse drivers.
989 */
990 rc = kbdAttach(pDevIns, 0 /* keyboard LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
991 if (RT_FAILURE(rc))
992 return rc;
993 rc = kbdAttach(pDevIns, 1 /* aux/mouse LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
994 if (RT_FAILURE(rc))
995 return rc;
996
997 /*
998 * Initialize the device state.
999 */
1000 kbdReset(pDevIns);
1001
1002 return VINF_SUCCESS;
1003}
1004
1005#endif /* IN_RING3 */
1006
1007/**
1008 * The device registration structure.
1009 */
1010const PDMDEVREG g_DevicePS2KeyboardMouse =
1011{
1012 /* .u32Version = */ PDM_DEVREG_VERSION,
1013 /* .uReserved0 = */ 0,
1014 /* .szName = */ "pckbd",
1015 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ,
1016 /* .fClass = */ PDM_DEVREG_CLASS_INPUT,
1017 /* .cMaxInstances = */ 1,
1018 /* .uSharedVersion = */ 42,
1019 /* .cbInstanceShared = */ sizeof(KBDState),
1020 /* .cbInstanceCC = */ 0,
1021 /* .cbInstanceRC = */ 0,
1022 /* .cMaxPciDevices = */ 0,
1023 /* .cMaxMsixVectors = */ 0,
1024 /* .pszDescription = */ "PS/2 Keyboard and Mouse device. Emulates both the keyboard, mouse and the keyboard controller.\n"
1025 "LUN #0 is the keyboard connector.\n"
1026 "LUN #1 is the aux/mouse connector.",
1027#if defined(IN_RING3)
1028 /* .pszRCMod = */ "VBoxDDRC.rc",
1029 /* .pszR0Mod = */ "VBoxDDR0.r0",
1030 /* .pfnConstruct = */ kbdConstruct,
1031 /* .pfnDestruct = */ NULL,
1032 /* .pfnRelocate = */ kbdRelocate,
1033 /* .pfnMemSetup = */ NULL,
1034 /* .pfnPowerOn = */ NULL,
1035 /* .pfnReset = */ kbdReset,
1036 /* .pfnSuspend = */ NULL,
1037 /* .pfnResume = */ NULL,
1038 /* .pfnAttach = */ kbdAttach,
1039 /* .pfnDetach = */ kbdDetach,
1040 /* .pfnQueryInterface = */ NULL,
1041 /* .pfnInitComplete = */ NULL,
1042 /* .pfnPowerOff = */ NULL,
1043 /* .pfnSoftReset = */ NULL,
1044 /* .pfnReserved0 = */ NULL,
1045 /* .pfnReserved1 = */ NULL,
1046 /* .pfnReserved2 = */ NULL,
1047 /* .pfnReserved3 = */ NULL,
1048 /* .pfnReserved4 = */ NULL,
1049 /* .pfnReserved5 = */ NULL,
1050 /* .pfnReserved6 = */ NULL,
1051 /* .pfnReserved7 = */ NULL,
1052#elif defined(IN_RING0)
1053 /* .pfnEarlyConstruct = */ NULL,
1054 /* .pfnConstruct = */ NULL,
1055 /* .pfnDestruct = */ NULL,
1056 /* .pfnFinalDestruct = */ NULL,
1057 /* .pfnRequest = */ NULL,
1058 /* .pfnReserved0 = */ NULL,
1059 /* .pfnReserved1 = */ NULL,
1060 /* .pfnReserved2 = */ NULL,
1061 /* .pfnReserved3 = */ NULL,
1062 /* .pfnReserved4 = */ NULL,
1063 /* .pfnReserved5 = */ NULL,
1064 /* .pfnReserved6 = */ NULL,
1065 /* .pfnReserved7 = */ NULL,
1066#elif defined(IN_RC)
1067 /* .pfnConstruct = */ NULL,
1068 /* .pfnReserved0 = */ NULL,
1069 /* .pfnReserved1 = */ NULL,
1070 /* .pfnReserved2 = */ NULL,
1071 /* .pfnReserved3 = */ NULL,
1072 /* .pfnReserved4 = */ NULL,
1073 /* .pfnReserved5 = */ NULL,
1074 /* .pfnReserved6 = */ NULL,
1075 /* .pfnReserved7 = */ NULL,
1076#else
1077# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1078#endif
1079 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1080};
1081
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