VirtualBox

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

最後變更 在這個檔案從69496是 69496,由 vboxsync 提交於 7 年 前

*: scm --update-copyright-year

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 34.1 KB
 
1/* $Id: DevPS2.cpp 69496 2017-10-28 14:55:58Z vboxsync $ */
2/** @file
3 * DevPS2 - PS/2 keyboard & mouse controller device.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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 * This code is based on:
18 *
19 * QEMU PC keyboard emulation (revision 1.12)
20 *
21 * Copyright (c) 2003 Fabrice Bellard
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
36 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 *
41 */
42
43
44/*********************************************************************************************************************************
45* Header Files *
46*********************************************************************************************************************************/
47#define LOG_GROUP LOG_GROUP_DEV_KBD
48#include <VBox/vmm/pdmdev.h>
49#include <iprt/assert.h>
50#include <iprt/uuid.h>
51
52#include "VBoxDD.h"
53#include "PS2Dev.h"
54
55/* Do not remove this (unless eliminating the corresponding ifdefs), it will
56 * cause instant triple faults when booting Windows VMs. */
57#define TARGET_I386
58
59#define PCKBD_SAVED_STATE_VERSION 8
60
61#ifndef VBOX_DEVICE_STRUCT_TESTCASE
62
63
64/*********************************************************************************************************************************
65* Internal Functions *
66*********************************************************************************************************************************/
67RT_C_DECLS_BEGIN
68PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
69PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
70PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
71PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
72RT_C_DECLS_END
73#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
74
75/* debug PC keyboard */
76#define DEBUG_KBD
77
78/* debug PC keyboard : only mouse */
79#define DEBUG_MOUSE
80
81/* Keyboard Controller Commands */
82#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
83#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
84#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
85#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
86#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
87#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
88#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
89#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
90#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
91#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
92#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
93#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
94#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
95#define KBD_CCMD_WRITE_OBUF 0xD2
96#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
97 initiated by the auxiliary device */
98#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
99#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
100#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
101#define KBD_CCMD_READ_TSTINP 0xE0 /* Read test inputs T0, T1 */
102#define KBD_CCMD_RESET_ALT 0xF0
103#define KBD_CCMD_RESET 0xFE
104
105/* Status Register Bits */
106#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
107#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
108#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
109#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
110#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
111#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
112#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
113#define KBD_STAT_PERR 0x80 /* Parity error */
114
115/* Controller Mode Register Bits */
116#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
117#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
118#define KBD_MODE_SYS 0x04 /* The system flag (?) */
119#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
120#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
121#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
122#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
123#define KBD_MODE_RFU 0x80
124
125
126/**
127 * The keyboard controller/device state.
128 *
129 * @note We use the default critical section for serialize data access.
130 */
131typedef struct KBDState
132{
133 uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
134 uint8_t status;
135 uint8_t mode;
136 uint8_t dbbout; /* data buffer byte */
137 /* keyboard state */
138 int32_t translate;
139 int32_t xlat_state;
140
141 /** Pointer to the device instance - RC. */
142 PPDMDEVINSRC pDevInsRC;
143 /** Pointer to the device instance - R3 . */
144 PPDMDEVINSR3 pDevInsR3;
145 /** Pointer to the device instance. */
146 PPDMDEVINSR0 pDevInsR0;
147
148 /** Keyboard state (implemented in separate PS2K module). */
149#ifdef VBOX_DEVICE_STRUCT_TESTCASE
150 uint8_t KbdFiller[PS2K_STRUCT_FILLER];
151#else
152 PS2K Kbd;
153#endif
154
155 /** Mouse state (implemented in separate PS2M module). */
156#ifdef VBOX_DEVICE_STRUCT_TESTCASE
157 uint8_t AuxFiller[PS2M_STRUCT_FILLER];
158#else
159 PS2M Aux;
160#endif
161} KBDState;
162
163#ifndef VBOX_DEVICE_STRUCT_TESTCASE
164
165/* update irq and KBD_STAT_[MOUSE_]OBF */
166static void kbd_update_irq(KBDState *s)
167{
168 int irq12_level, irq1_level;
169 uint8_t val;
170
171 irq1_level = 0;
172 irq12_level = 0;
173
174 /* Determine new OBF state, but only if OBF is clear. If OBF was already
175 * set, we cannot risk changing the event type after an ISR potentially
176 * started executing! Only kbd_read_data() clears the OBF bits.
177 */
178 if (!(s->status & KBD_STAT_OBF)) {
179 s->status &= ~KBD_STAT_MOUSE_OBF;
180 /* Keyboard data has priority if both kbd and aux data is available. */
181 if (!(s->mode & KBD_MODE_DISABLE_KBD) && PS2KByteFromKbd(&s->Kbd, &val) == VINF_SUCCESS)
182 {
183 bool fHaveData = true;
184
185 /* If scancode translation is on (it usually is), there's more work to do. */
186 if (s->translate)
187 {
188 uint8_t xlated_val;
189
190 s->xlat_state = XlateAT2PC(s->xlat_state, val, &xlated_val);
191 val = xlated_val;
192
193 /* If the translation state is XS_BREAK, there's nothing to report
194 * and we keep going until the state changes or there's no more data.
195 */
196 while (s->xlat_state == XS_BREAK && PS2KByteFromKbd(&s->Kbd, &val) == VINF_SUCCESS)
197 {
198 s->xlat_state = XlateAT2PC(s->xlat_state, val, &xlated_val);
199 val = xlated_val;
200 }
201 /* This can happen if the last byte in the queue is F0... */
202 if (s->xlat_state == XS_BREAK)
203 fHaveData = false;
204 }
205 if (fHaveData)
206 {
207 s->dbbout = val;
208 s->status |= KBD_STAT_OBF;
209 }
210 }
211 else if (!(s->mode & KBD_MODE_DISABLE_MOUSE) && PS2MByteFromAux(&s->Aux, &val) == VINF_SUCCESS)
212 {
213 s->dbbout = val;
214 s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
215 }
216 }
217 /* Determine new IRQ state. */
218 if (s->status & KBD_STAT_OBF) {
219 if (s->status & KBD_STAT_MOUSE_OBF)
220 {
221 if (s->mode & KBD_MODE_MOUSE_INT)
222 irq12_level = 1;
223 }
224 else
225 { /* KBD_STAT_OBF set but KBD_STAT_MOUSE_OBF isn't. */
226 if (s->mode & KBD_MODE_KBD_INT)
227 irq1_level = 1;
228 }
229 }
230 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, irq1_level);
231 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, irq12_level);
232}
233
234void KBCUpdateInterrupts(void *pKbc)
235{
236 KBDState *s = (KBDState *)pKbc;
237 kbd_update_irq(s);
238}
239
240static void kbc_dbb_out(void *opaque, uint8_t val)
241{
242 KBDState *s = (KBDState*)opaque;
243
244 s->dbbout = val;
245 /* Set the OBF and raise IRQ. */
246 s->status |= KBD_STAT_OBF;
247 if (s->mode & KBD_MODE_KBD_INT)
248 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, 1);
249}
250
251static void kbc_dbb_out_aux(void *opaque, uint8_t val)
252{
253 KBDState *s = (KBDState*)opaque;
254
255 s->dbbout = val;
256 /* Set the aux OBF and raise IRQ. */
257 s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
258 if (s->mode & KBD_MODE_MOUSE_INT)
259 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, PDM_IRQ_LEVEL_HIGH);
260}
261
262static uint32_t kbd_read_status(void *opaque, uint32_t addr)
263{
264 KBDState *s = (KBDState*)opaque;
265 int val = s->status;
266 NOREF(addr);
267
268#if defined(DEBUG_KBD)
269 Log(("kbd: read status=0x%02x\n", val));
270#endif
271 return val;
272}
273
274static int kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
275{
276 int rc = VINF_SUCCESS;
277 KBDState *s = (KBDState*)opaque;
278 NOREF(addr);
279
280#ifdef DEBUG_KBD
281 Log(("kbd: write cmd=0x%02x\n", val));
282#endif
283 switch(val) {
284 case KBD_CCMD_READ_MODE:
285 kbc_dbb_out(s, s->mode);
286 break;
287 case KBD_CCMD_WRITE_MODE:
288 case KBD_CCMD_WRITE_OBUF:
289 case KBD_CCMD_WRITE_AUX_OBUF:
290 case KBD_CCMD_WRITE_MOUSE:
291 case KBD_CCMD_WRITE_OUTPORT:
292 s->write_cmd = val;
293 break;
294 case KBD_CCMD_MOUSE_DISABLE:
295 s->mode |= KBD_MODE_DISABLE_MOUSE;
296 break;
297 case KBD_CCMD_MOUSE_ENABLE:
298 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
299 /* Check for queued input. */
300 kbd_update_irq(s);
301 break;
302 case KBD_CCMD_TEST_MOUSE:
303 kbc_dbb_out(s, 0x00);
304 break;
305 case KBD_CCMD_SELF_TEST:
306 /* Enable the A20 line - that is the power-on state(!). */
307# ifndef IN_RING3
308 if (!PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
309 {
310 rc = VINF_IOM_R3_IOPORT_WRITE;
311 break;
312 }
313# else /* IN_RING3 */
314 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), true);
315# endif /* IN_RING3 */
316 s->status |= KBD_STAT_SELFTEST;
317 s->mode |= KBD_MODE_DISABLE_KBD;
318 kbc_dbb_out(s, 0x55);
319 break;
320 case KBD_CCMD_KBD_TEST:
321 kbc_dbb_out(s, 0x00);
322 break;
323 case KBD_CCMD_KBD_DISABLE:
324 s->mode |= KBD_MODE_DISABLE_KBD;
325 break;
326 case KBD_CCMD_KBD_ENABLE:
327 s->mode &= ~KBD_MODE_DISABLE_KBD;
328 /* Check for queued input. */
329 kbd_update_irq(s);
330 break;
331 case KBD_CCMD_READ_INPORT:
332 kbc_dbb_out(s, 0xBF);
333 break;
334 case KBD_CCMD_READ_OUTPORT:
335 /* XXX: check that */
336#ifdef TARGET_I386
337 val = 0x01 | (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)) << 1);
338#else
339 val = 0x01;
340#endif
341 if (s->status & KBD_STAT_OBF)
342 val |= 0x10;
343 if (s->status & KBD_STAT_MOUSE_OBF)
344 val |= 0x20;
345 kbc_dbb_out(s, val);
346 break;
347#ifdef TARGET_I386
348 case KBD_CCMD_ENABLE_A20:
349# ifndef IN_RING3
350 if (!PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
351 rc = VINF_IOM_R3_IOPORT_WRITE;
352# else /* IN_RING3 */
353 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), true);
354# endif /* IN_RING3 */
355 break;
356 case KBD_CCMD_DISABLE_A20:
357# ifndef IN_RING3
358 if (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
359 rc = VINF_IOM_R3_IOPORT_WRITE;
360# else /* IN_RING3 */
361 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), false);
362# endif /* !IN_RING3 */
363 break;
364#endif
365 case KBD_CCMD_READ_TSTINP:
366 /* Keyboard clock line is zero IFF keyboard is disabled */
367 val = (s->mode & KBD_MODE_DISABLE_KBD) ? 0 : 1;
368 kbc_dbb_out(s, val);
369 break;
370 case KBD_CCMD_RESET:
371 case KBD_CCMD_RESET_ALT:
372#ifndef IN_RING3
373 rc = VINF_IOM_R3_IOPORT_WRITE;
374#else /* IN_RING3 */
375 LogRel(("Reset initiated by keyboard controller\n"));
376 rc = PDMDevHlpVMReset(s->CTX_SUFF(pDevIns), PDMVMRESET_F_KBD);
377#endif /* !IN_RING3 */
378 break;
379 case 0xff:
380 /* ignore that - I don't know what is its use */
381 break;
382 /* Make OS/2 happy. */
383 /* The 8042 RAM is readable using commands 0x20 thru 0x3f, and writable
384 by 0x60 thru 0x7f. Now days only the first byte, the mode, is used.
385 We'll ignore the writes (0x61..7f) and return 0 for all the reads
386 just to make some OS/2 debug stuff a bit happier. */
387 case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
388 case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
389 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
390 case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
391 kbc_dbb_out(s, 0);
392 Log(("kbd: reading non-standard RAM addr %#x\n", val & 0x1f));
393 break;
394 default:
395 Log(("kbd: unsupported keyboard cmd=0x%02x\n", val));
396 break;
397 }
398 return rc;
399}
400
401static uint32_t kbd_read_data(void *opaque, uint32_t addr)
402{
403 KBDState *s = (KBDState*)opaque;
404 uint32_t val;
405 NOREF(addr);
406
407 /* Return the current DBB contents. */
408 val = s->dbbout;
409
410 /* Reading the DBB deasserts IRQs... */
411 if (s->status & KBD_STAT_MOUSE_OBF)
412 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, 0);
413 else
414 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, 0);
415 /* ...and clears the OBF bits. */
416 s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
417
418 /* Check if more data is available. */
419 kbd_update_irq(s);
420#ifdef DEBUG_KBD
421 Log(("kbd: read data=0x%02x\n", val));
422#endif
423 return val;
424}
425
426PS2K *KBDGetPS2KFromDevIns(PPDMDEVINS pDevIns)
427{
428 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
429 return &pThis->Kbd;
430}
431
432PS2M *KBDGetPS2MFromDevIns(PPDMDEVINS pDevIns)
433{
434 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
435 return &pThis->Aux;
436}
437
438static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
439{
440 int rc = VINF_SUCCESS;
441 KBDState *s = (KBDState*)opaque;
442 NOREF(addr);
443
444#ifdef DEBUG_KBD
445 Log(("kbd: write data=0x%02x\n", val));
446#endif
447
448 switch(s->write_cmd) {
449 case 0:
450 /* Automatically enables keyboard interface. */
451 s->mode &= ~KBD_MODE_DISABLE_KBD;
452 rc = PS2KByteToKbd(&s->Kbd, val);
453 if (rc == VINF_SUCCESS)
454 kbd_update_irq(s);
455 break;
456 case KBD_CCMD_WRITE_MODE:
457 s->mode = val;
458 s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
459 kbd_update_irq(s);
460 break;
461 case KBD_CCMD_WRITE_OBUF:
462 kbc_dbb_out(s, val);
463 break;
464 case KBD_CCMD_WRITE_AUX_OBUF:
465 kbc_dbb_out_aux(s, val);
466 break;
467 case KBD_CCMD_WRITE_OUTPORT:
468#ifdef TARGET_I386
469# ifndef IN_RING3
470 if (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)) != !!(val & 2))
471 rc = VINF_IOM_R3_IOPORT_WRITE;
472# else /* IN_RING3 */
473 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), !!(val & 2));
474# endif /* !IN_RING3 */
475#endif
476 if (!(val & 1)) {
477# ifndef IN_RING3
478 rc = VINF_IOM_R3_IOPORT_WRITE;
479# else
480 rc = PDMDevHlpVMReset(s->CTX_SUFF(pDevIns), PDMVMRESET_F_KBD);
481# endif
482 }
483 break;
484 case KBD_CCMD_WRITE_MOUSE:
485 /* Automatically enables aux interface. */
486 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
487 rc = PS2MByteToAux(&s->Aux, val);
488 if (rc == VINF_SUCCESS)
489 kbd_update_irq(s);
490 break;
491 default:
492 break;
493 }
494 if (rc != VINF_IOM_R3_IOPORT_WRITE)
495 s->write_cmd = 0;
496 return rc;
497}
498
499#ifdef IN_RING3
500
501static void kbd_reset(void *opaque)
502{
503 KBDState *s = (KBDState*)opaque;
504 s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
505 s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
506 /* Resetting everything, keyword was not working right on NT4 reboot. */
507 s->write_cmd = 0;
508 s->translate = 0;
509}
510
511static void kbd_save(PSSMHANDLE pSSM, KBDState *s)
512{
513 SSMR3PutU8(pSSM, s->write_cmd);
514 SSMR3PutU8(pSSM, s->status);
515 SSMR3PutU8(pSSM, s->mode);
516 SSMR3PutU8(pSSM, s->dbbout);
517
518 /* terminator */
519 SSMR3PutU32(pSSM, UINT32_MAX);
520}
521
522static int kbd_load(PSSMHANDLE pSSM, KBDState *s, uint32_t version_id)
523{
524 uint32_t u32, i;
525 uint8_t u8Dummy;
526 uint32_t u32Dummy;
527 int rc;
528
529#if 0
530 /** @todo enable this and remove the "if (version_id == 4)" code at some
531 * later time */
532 /* Version 4 was never created by any publicly released version of VBox */
533 AssertReturn(version_id != 4, VERR_NOT_SUPPORTED);
534#endif
535 if (version_id < 2 || version_id > PCKBD_SAVED_STATE_VERSION)
536 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
537 SSMR3GetU8(pSSM, &s->write_cmd);
538 SSMR3GetU8(pSSM, &s->status);
539 SSMR3GetU8(pSSM, &s->mode);
540 if (version_id <= 5)
541 {
542 SSMR3GetU32(pSSM, (uint32_t *)&u32Dummy);
543 SSMR3GetU32(pSSM, (uint32_t *)&u32Dummy);
544 }
545 else
546 {
547 SSMR3GetU8(pSSM, &s->dbbout);
548 }
549 if (version_id <= 7)
550 {
551 int32_t i32Dummy;
552 uint8_t u8State;
553 uint8_t u8Rate;
554 uint8_t u8Proto;
555
556 SSMR3GetU32(pSSM, &u32Dummy);
557 SSMR3GetU8(pSSM, &u8State);
558 SSMR3GetU8(pSSM, &u8Dummy);
559 SSMR3GetU8(pSSM, &u8Rate);
560 SSMR3GetU8(pSSM, &u8Dummy);
561 SSMR3GetU8(pSSM, &u8Proto);
562 SSMR3GetU8(pSSM, &u8Dummy);
563 SSMR3GetS32(pSSM, &i32Dummy);
564 SSMR3GetS32(pSSM, &i32Dummy);
565 SSMR3GetS32(pSSM, &i32Dummy);
566 if (version_id > 2)
567 {
568 SSMR3GetS32(pSSM, &i32Dummy);
569 SSMR3GetS32(pSSM, &i32Dummy);
570 }
571 rc = SSMR3GetU8(pSSM, &u8Dummy);
572 if (version_id == 4)
573 {
574 SSMR3GetU32(pSSM, &u32Dummy);
575 rc = SSMR3GetU32(pSSM, &u32Dummy);
576 }
577 if (version_id > 3)
578 rc = SSMR3GetU8(pSSM, &u8Dummy);
579 if (version_id == 4)
580 rc = SSMR3GetU8(pSSM, &u8Dummy);
581 AssertLogRelRCReturn(rc, rc);
582
583 PS2MFixupState(&s->Aux, u8State, u8Rate, u8Proto);
584 }
585
586 /* Determine the translation state. */
587 s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
588
589 /*
590 * Load the queues
591 */
592 if (version_id <= 5)
593 {
594 rc = SSMR3GetU32(pSSM, &u32);
595 if (RT_FAILURE(rc))
596 return rc;
597 for (i = 0; i < u32; i++)
598 {
599 rc = SSMR3GetU8(pSSM, &u8Dummy);
600 if (RT_FAILURE(rc))
601 return rc;
602 }
603 Log(("kbd_load: %d keyboard queue items discarded from old saved state\n", u32));
604 }
605
606 if (version_id <= 7)
607 {
608 rc = SSMR3GetU32(pSSM, &u32);
609 if (RT_FAILURE(rc))
610 return rc;
611 for (i = 0; i < u32; i++)
612 {
613 rc = SSMR3GetU8(pSSM, &u8Dummy);
614 if (RT_FAILURE(rc))
615 return rc;
616 }
617 Log(("kbd_load: %d mouse event queue items discarded from old saved state\n", u32));
618
619 rc = SSMR3GetU32(pSSM, &u32);
620 if (RT_FAILURE(rc))
621 return rc;
622 for (i = 0; i < u32; i++)
623 {
624 rc = SSMR3GetU8(pSSM, &u8Dummy);
625 if (RT_FAILURE(rc))
626 return rc;
627 }
628 Log(("kbd_load: %d mouse command queue items discarded from old saved state\n", u32));
629 }
630
631 /* terminator */
632 rc = SSMR3GetU32(pSSM, &u32);
633 if (RT_FAILURE(rc))
634 return rc;
635 if (u32 != ~0U)
636 {
637 AssertMsgFailed(("u32=%#x\n", u32));
638 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
639 }
640 return 0;
641}
642#endif /* IN_RING3 */
643
644
645/* VirtualBox code start */
646
647/* -=-=-=-=-=- wrappers -=-=-=-=-=- */
648
649/**
650 * Port I/O Handler for keyboard data IN operations.
651 *
652 * @returns VBox status code.
653 *
654 * @param pDevIns The device instance.
655 * @param pvUser User argument - ignored.
656 * @param Port Port number used for the IN operation.
657 * @param pu32 Where to store the result.
658 * @param cb Number of bytes read.
659 */
660PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
661{
662 NOREF(pvUser);
663 if (cb == 1)
664 {
665 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
666 *pu32 = kbd_read_data(pThis, Port);
667 Log2(("kbdIOPortDataRead: Port=%#x cb=%d *pu32=%#x\n", Port, cb, *pu32));
668 return VINF_SUCCESS;
669 }
670 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
671 return VERR_IOM_IOPORT_UNUSED;
672}
673
674/**
675 * Port I/O Handler for keyboard data OUT operations.
676 *
677 * @returns VBox status code.
678 *
679 * @param pDevIns The device instance.
680 * @param pvUser User argument - ignored.
681 * @param Port Port number used for the IN operation.
682 * @param u32 The value to output.
683 * @param cb The value size in bytes.
684 */
685PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
686{
687 int rc = VINF_SUCCESS;
688 NOREF(pvUser);
689 if (cb == 1 || cb == 2)
690 {
691 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
692 rc = kbd_write_data(pThis, Port, (uint8_t)u32);
693 Log2(("kbdIOPortDataWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
694 }
695 else
696 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
697 return rc;
698}
699
700/**
701 * Port I/O Handler for keyboard status IN operations.
702 *
703 * @returns VBox status code.
704 *
705 * @param pDevIns The device instance.
706 * @param pvUser User argument - ignored.
707 * @param Port Port number used for the IN operation.
708 * @param pu32 Where to store the result.
709 * @param cb Number of bytes read.
710 */
711PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
712{
713 uint16_t fluff = 0;
714 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
715
716 NOREF(pvUser);
717 switch (cb) {
718 case 2:
719 fluff = 0xff00;
720 RT_FALL_THRU();
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
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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