VirtualBox

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

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

DevPS2: Improved logging, gave queues human-readable names.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.3 KB
Line 
1/* $Id: DevPS2.cpp 83443 2020-03-26 16:49:44Z vboxsync $ */
2/** @file
3 * DevPS2 - PS/2 keyboard & mouse controller device.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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 <VBox/AssertGuest.h>
51#include <iprt/assert.h>
52#include <iprt/uuid.h>
53
54#include "VBoxDD.h"
55#include "DevPS2.h"
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61/* Do not remove this (unless eliminating the corresponding ifdefs), it will
62 * cause instant triple faults when booting Windows VMs. */
63#define TARGET_I386
64
65#define PCKBD_SAVED_STATE_VERSION 8
66
67/* debug PC keyboard */
68#define DEBUG_KBD
69
70/* debug PC keyboard : only mouse */
71#define DEBUG_MOUSE
72
73/* Keyboard Controller Commands */
74#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
75#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
76#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
77#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
78#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
79#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
80#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
81#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
82#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
83#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
84#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
85#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
86#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
87#define KBD_CCMD_WRITE_OBUF 0xD2
88#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
89 initiated by the auxiliary device */
90#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
91#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
92#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
93#define KBD_CCMD_READ_TSTINP 0xE0 /* Read test inputs T0, T1 */
94#define KBD_CCMD_RESET_ALT 0xF0
95#define KBD_CCMD_RESET 0xFE
96
97/* Status Register Bits */
98#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
99#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
100#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
101#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
102#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
103#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
104#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
105#define KBD_STAT_PERR 0x80 /* Parity error */
106
107/* Controller Mode Register Bits */
108#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
109#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
110#define KBD_MODE_SYS 0x04 /* The system flag (?) */
111#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
112#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
113#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
114#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
115#define KBD_MODE_RFU 0x80
116
117
118/*********************************************************************************************************************************
119* Structures and Typedefs *
120*********************************************************************************************************************************/
121/** AT to PC scancode translator state. */
122typedef enum
123{
124 XS_IDLE, /**< Starting state. */
125 XS_BREAK, /**< F0 break byte was received. */
126 XS_HIBIT /**< Break code still active. */
127} xlat_state_t;
128
129
130/*********************************************************************************************************************************
131* Global Variables *
132*********************************************************************************************************************************/
133/* Table used by the keyboard controller to optionally translate the incoming
134 * keyboard data. Note that the translation is designed for essentially taking
135 * Scan Set 2 input and producing Scan Set 1 output, but can be turned on and
136 * off regardless of what the keyboard is sending.
137 */
138static uint8_t const g_aAT2PC[128] =
139{
140 0xff,0x43,0x41,0x3f,0x3d,0x3b,0x3c,0x58,0x64,0x44,0x42,0x40,0x3e,0x0f,0x29,0x59,
141 0x65,0x38,0x2a,0x70,0x1d,0x10,0x02,0x5a,0x66,0x71,0x2c,0x1f,0x1e,0x11,0x03,0x5b,
142 0x67,0x2e,0x2d,0x20,0x12,0x05,0x04,0x5c,0x68,0x39,0x2f,0x21,0x14,0x13,0x06,0x5d,
143 0x69,0x31,0x30,0x23,0x22,0x15,0x07,0x5e,0x6a,0x72,0x32,0x24,0x16,0x08,0x09,0x5f,
144 0x6b,0x33,0x25,0x17,0x18,0x0b,0x0a,0x60,0x6c,0x34,0x35,0x26,0x27,0x19,0x0c,0x61,
145 0x6d,0x73,0x28,0x74,0x1a,0x0d,0x62,0x6e,0x3a,0x36,0x1c,0x1b,0x75,0x2b,0x63,0x76,
146 0x55,0x56,0x77,0x78,0x79,0x7a,0x0e,0x7b,0x7c,0x4f,0x7d,0x4b,0x47,0x7e,0x7f,0x6f,
147 0x52,0x53,0x50,0x4c,0x4d,0x48,0x01,0x45,0x57,0x4e,0x51,0x4a,0x37,0x49,0x46,0x54
148};
149
150
151
152/**
153 * Convert an AT (Scan Set 2) scancode to PC (Scan Set 1).
154 *
155 * @param state Current state of the translator
156 * (xlat_state_t).
157 * @param scanIn Incoming scan code.
158 * @param pScanOut Pointer to outgoing scan code. The
159 * contents are only valid if returned
160 * state is not XS_BREAK.
161 *
162 * @return xlat_state_t New state of the translator.
163 */
164static int32_t kbcXlateAT2PC(int32_t state, uint8_t scanIn, uint8_t *pScanOut)
165{
166 uint8_t scan_in;
167 uint8_t scan_out;
168
169 Assert(pScanOut);
170 Assert(state == XS_IDLE || state == XS_BREAK || state == XS_HIBIT);
171
172 /* Preprocess the scan code for a 128-entry translation table. */
173 if (scanIn == 0x83) /* Check for F7 key. */
174 scan_in = 0x02;
175 else if (scanIn == 0x84) /* Check for SysRq key. */
176 scan_in = 0x7f;
177 else
178 scan_in = scanIn;
179
180 /* Values 0x80 and above are passed through, except for 0xF0
181 * which indicates a key release.
182 */
183 if (scan_in < 0x80)
184 {
185 scan_out = g_aAT2PC[scan_in];
186 /* Turn into break code if required. */
187 if (state == XS_BREAK || state == XS_HIBIT)
188 scan_out |= 0x80;
189
190 state = XS_IDLE;
191 }
192 else
193 {
194 /* NB: F0 E0 10 will be translated to E0 E5 (high bit set on last byte)! */
195 if (scan_in == 0xF0) /* Check for break code. */
196 state = XS_BREAK;
197 else if (state == XS_BREAK)
198 state = XS_HIBIT; /* Remember the break bit. */
199 scan_out = scan_in;
200 }
201 LogFlowFunc(("scan code %02X translated to %02X; new state is %d\n",
202 scanIn, scan_out, state));
203
204 *pScanOut = scan_out;
205 return state;
206}
207
208
209/** update irq and KBD_STAT_[MOUSE_]OBF */
210static void kbd_update_irq(PPDMDEVINS pDevIns, PKBDSTATE s)
211{
212 int irq12_level, irq1_level;
213 uint8_t val;
214
215 irq1_level = 0;
216 irq12_level = 0;
217
218 /* Determine new OBF state, but only if OBF is clear. If OBF was already
219 * set, we cannot risk changing the event type after an ISR potentially
220 * started executing! Only kbd_read_data() clears the OBF bits.
221 */
222 if (!(s->status & KBD_STAT_OBF)) {
223 s->status &= ~KBD_STAT_MOUSE_OBF;
224 /* Keyboard data has priority if both kbd and aux data is available. */
225 if (!(s->mode & KBD_MODE_DISABLE_KBD) && PS2KByteFromKbd(pDevIns, &s->Kbd, &val) == VINF_SUCCESS)
226 {
227 bool fHaveData = true;
228
229 /* If scancode translation is on (it usually is), there's more work to do. */
230 if (s->translate)
231 {
232 uint8_t xlated_val;
233
234 s->xlat_state = kbcXlateAT2PC(s->xlat_state, val, &xlated_val);
235 val = xlated_val;
236
237 /* If the translation state is XS_BREAK, there's nothing to report
238 * and we keep going until the state changes or there's no more data.
239 */
240 while (s->xlat_state == XS_BREAK && PS2KByteFromKbd(pDevIns, &s->Kbd, &val) == VINF_SUCCESS)
241 {
242 s->xlat_state = kbcXlateAT2PC(s->xlat_state, val, &xlated_val);
243 val = xlated_val;
244 }
245 /* This can happen if the last byte in the queue is F0... */
246 if (s->xlat_state == XS_BREAK)
247 fHaveData = false;
248 }
249 if (fHaveData)
250 {
251 s->dbbout = val;
252 s->status |= KBD_STAT_OBF;
253 }
254 }
255 else if (!(s->mode & KBD_MODE_DISABLE_MOUSE) && PS2MByteFromAux(&s->Aux, &val) == VINF_SUCCESS)
256 {
257 s->dbbout = val;
258 s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
259 }
260 }
261 /* Determine new IRQ state. */
262 if (s->status & KBD_STAT_OBF) {
263 if (s->status & KBD_STAT_MOUSE_OBF)
264 {
265 if (s->mode & KBD_MODE_MOUSE_INT)
266 irq12_level = 1;
267 }
268 else
269 { /* KBD_STAT_OBF set but KBD_STAT_MOUSE_OBF isn't. */
270 if (s->mode & KBD_MODE_KBD_INT)
271 irq1_level = 1;
272 }
273 }
274 PDMDevHlpISASetIrq(pDevIns, 1, irq1_level);
275 PDMDevHlpISASetIrq(pDevIns, 12, irq12_level);
276}
277
278void KBCUpdateInterrupts(PPDMDEVINS pDevIns)
279{
280 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
281 kbd_update_irq(pDevIns, pThis);
282}
283
284static void kbc_dbb_out(PPDMDEVINS pDevIns, PKBDSTATE s, uint8_t val)
285{
286 s->dbbout = val;
287 /* Set the OBF and raise IRQ. */
288 s->status |= KBD_STAT_OBF;
289 if (s->mode & KBD_MODE_KBD_INT)
290 PDMDevHlpISASetIrq(pDevIns, 1, 1);
291}
292
293static void kbc_dbb_out_aux(PPDMDEVINS pDevIns, PKBDSTATE s, uint8_t val)
294{
295 s->dbbout = val;
296 /* Set the aux OBF and raise IRQ. */
297 s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
298 if (s->mode & KBD_MODE_MOUSE_INT)
299 PDMDevHlpISASetIrq(pDevIns, 12, PDM_IRQ_LEVEL_HIGH);
300}
301
302static VBOXSTRICTRC kbd_write_command(PPDMDEVINS pDevIns, PKBDSTATE s, uint32_t val)
303{
304#ifdef DEBUG_KBD
305 Log(("kbd: write cmd=0x%02x\n", val));
306#endif
307 switch(val) {
308 case KBD_CCMD_READ_MODE:
309 kbc_dbb_out(pDevIns, s, s->mode);
310 break;
311 case KBD_CCMD_WRITE_MODE:
312 case KBD_CCMD_WRITE_OBUF:
313 case KBD_CCMD_WRITE_AUX_OBUF:
314 case KBD_CCMD_WRITE_MOUSE:
315 case KBD_CCMD_WRITE_OUTPORT:
316 s->write_cmd = val;
317 break;
318 case KBD_CCMD_MOUSE_DISABLE:
319 s->mode |= KBD_MODE_DISABLE_MOUSE;
320 PS2MLineDisable(&s->Aux);
321 break;
322 case KBD_CCMD_MOUSE_ENABLE:
323 PS2MLineEnable(&s->Aux);
324 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
325 /* Check for queued input. */
326 /// @todo Can there actually be any?
327 kbd_update_irq(pDevIns, s);
328 break;
329 case KBD_CCMD_TEST_MOUSE:
330 kbc_dbb_out(pDevIns, s, 0x00);
331 break;
332 case KBD_CCMD_SELF_TEST:
333 /* Enable the A20 line - that is the power-on state(!). */
334# ifndef IN_RING3
335 if (!PDMDevHlpA20IsEnabled(pDevIns))
336 return VINF_IOM_R3_IOPORT_WRITE;
337# else /* IN_RING3 */
338 PDMDevHlpA20Set(pDevIns, true);
339# endif /* IN_RING3 */
340 s->status |= KBD_STAT_SELFTEST;
341 s->mode |= KBD_MODE_DISABLE_KBD;
342 kbc_dbb_out(pDevIns, s, 0x55);
343 break;
344 case KBD_CCMD_KBD_TEST:
345 kbc_dbb_out(pDevIns, s, 0x00);
346 break;
347 case KBD_CCMD_KBD_DISABLE:
348 s->mode |= KBD_MODE_DISABLE_KBD;
349 break;
350 case KBD_CCMD_KBD_ENABLE:
351 s->mode &= ~KBD_MODE_DISABLE_KBD;
352 /* Check for queued input. */
353 kbd_update_irq(pDevIns, s);
354 break;
355 case KBD_CCMD_READ_INPORT:
356 kbc_dbb_out(pDevIns, s, 0xBF);
357 break;
358 case KBD_CCMD_READ_OUTPORT:
359 /* XXX: check that */
360#ifdef TARGET_I386
361 val = 0x01 | (PDMDevHlpA20IsEnabled(pDevIns) << 1);
362#else
363 val = 0x01;
364#endif
365 if (s->status & KBD_STAT_OBF)
366 val |= 0x10;
367 if (s->status & KBD_STAT_MOUSE_OBF)
368 val |= 0x20;
369 kbc_dbb_out(pDevIns, s, val);
370 break;
371#ifdef TARGET_I386
372 case KBD_CCMD_ENABLE_A20:
373# ifndef IN_RING3
374 if (!PDMDevHlpA20IsEnabled(pDevIns))
375 return VINF_IOM_R3_IOPORT_WRITE;
376# else /* IN_RING3 */
377 PDMDevHlpA20Set(pDevIns, true);
378# endif /* IN_RING3 */
379 break;
380 case KBD_CCMD_DISABLE_A20:
381# ifndef IN_RING3
382 if (PDMDevHlpA20IsEnabled(pDevIns))
383 return VINF_IOM_R3_IOPORT_WRITE;
384# else /* IN_RING3 */
385 PDMDevHlpA20Set(pDevIns, false);
386# endif /* IN_RING3 */
387 break;
388#endif
389 case KBD_CCMD_READ_TSTINP:
390 /* Keyboard clock line is zero IFF keyboard is disabled */
391 val = (s->mode & KBD_MODE_DISABLE_KBD) ? 0 : 1;
392 kbc_dbb_out(pDevIns, s, val);
393 break;
394 case KBD_CCMD_RESET:
395 case KBD_CCMD_RESET_ALT:
396#ifndef IN_RING3
397 return VINF_IOM_R3_IOPORT_WRITE;
398#else /* IN_RING3 */
399 LogRel(("Reset initiated by keyboard controller\n"));
400 return PDMDevHlpVMReset(pDevIns, PDMVMRESET_F_KBD);
401#endif /* IN_RING3 */
402 case 0xff:
403 /* ignore that - I don't know what is its use */
404 break;
405 /* Make OS/2 happy. */
406 /* The 8042 RAM is readable using commands 0x20 thru 0x3f, and writable
407 by 0x60 thru 0x7f. Now days only the first byte, the mode, is used.
408 We'll ignore the writes (0x61..7f) and return 0 for all the reads
409 just to make some OS/2 debug stuff a bit happier. */
410 case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
411 case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
412 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
413 case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
414 kbc_dbb_out(pDevIns, s, 0);
415 Log(("kbd: reading non-standard RAM addr %#x\n", val & 0x1f));
416 break;
417 default:
418 Log(("kbd: unsupported keyboard cmd=0x%02x\n", val));
419 break;
420 }
421 return VINF_SUCCESS;
422}
423
424static uint32_t kbd_read_data(PPDMDEVINS pDevIns, PKBDSTATE s)
425{
426 uint32_t val;
427
428 /* Return the current DBB contents. */
429 val = s->dbbout;
430
431 /* Reading the DBB deasserts IRQs... */
432 if (s->status & KBD_STAT_MOUSE_OBF)
433 PDMDevHlpISASetIrq(pDevIns, 12, 0);
434 else
435 PDMDevHlpISASetIrq(pDevIns, 1, 0);
436 /* ...and clears the OBF bits. */
437 s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
438
439 /* Check if more data is available. */
440 kbd_update_irq(pDevIns, s);
441#ifdef DEBUG_KBD
442 Log(("kbd: read data=0x%02x\n", val));
443#endif
444 return val;
445}
446
447static VBOXSTRICTRC kbd_write_data(PPDMDEVINS pDevIns, PKBDSTATE s, uint32_t val)
448{
449 VBOXSTRICTRC rc = VINF_SUCCESS;
450
451#ifdef DEBUG_KBD
452 Log(("kbd: write data=0x%02x\n", val));
453#endif
454
455 switch(s->write_cmd) {
456 case 0:
457 /* Automatically enables keyboard interface. */
458 s->mode &= ~KBD_MODE_DISABLE_KBD;
459 rc = PS2KByteToKbd(pDevIns, &s->Kbd, val);
460 if (rc == VINF_SUCCESS)
461 kbd_update_irq(pDevIns, s);
462 break;
463 case KBD_CCMD_WRITE_MODE:
464 s->mode = val;
465 s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
466 kbd_update_irq(pDevIns, s);
467 break;
468 case KBD_CCMD_WRITE_OBUF:
469 kbc_dbb_out(pDevIns, s, val);
470 break;
471 case KBD_CCMD_WRITE_AUX_OBUF:
472 kbc_dbb_out_aux(pDevIns, s, val);
473 break;
474 case KBD_CCMD_WRITE_OUTPORT:
475#ifdef TARGET_I386
476# ifndef IN_RING3
477 if (PDMDevHlpA20IsEnabled(pDevIns) != !!(val & 2))
478 rc = VINF_IOM_R3_IOPORT_WRITE;
479# else /* IN_RING3 */
480 PDMDevHlpA20Set(pDevIns, !!(val & 2));
481# endif /* IN_RING3 */
482#endif
483 if (!(val & 1)) {
484# ifndef IN_RING3
485 rc = VINF_IOM_R3_IOPORT_WRITE;
486# else
487 rc = PDMDevHlpVMReset(pDevIns, PDMVMRESET_F_KBD);
488# endif
489 }
490 break;
491 case KBD_CCMD_WRITE_MOUSE:
492 /* Automatically enables aux interface. */
493 if (s->mode & KBD_MODE_DISABLE_MOUSE)
494 {
495 PS2MLineEnable(&s->Aux);
496 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
497 }
498 rc = PS2MByteToAux(pDevIns, &s->Aux, val);
499 if (rc == VINF_SUCCESS)
500 kbd_update_irq(pDevIns, s);
501 break;
502 default:
503 break;
504 }
505 if (rc != VINF_IOM_R3_IOPORT_WRITE)
506 s->write_cmd = 0;
507 return rc;
508}
509
510#ifdef IN_RING3
511
512static int kbd_load(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PKBDSTATE s, PKBDSTATER3 pThisCC, uint32_t version_id)
513{
514 uint32_t u32, i;
515 uint8_t u8Dummy;
516 uint32_t u32Dummy;
517 int rc;
518
519#if 0
520 /** @todo enable this and remove the "if (version_id == 4)" code at some
521 * later time */
522 /* Version 4 was never created by any publicly released version of VBox */
523 AssertReturn(version_id != 4, VERR_NOT_SUPPORTED);
524#endif
525 if (version_id < 2 || version_id > PCKBD_SAVED_STATE_VERSION)
526 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
527 pHlp->pfnSSMGetU8(pSSM, &s->write_cmd);
528 pHlp->pfnSSMGetU8(pSSM, &s->status);
529 pHlp->pfnSSMGetU8(pSSM, &s->mode);
530 if (version_id <= 5)
531 {
532 pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
533 pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
534 }
535 else
536 {
537 pHlp->pfnSSMGetU8(pSSM, &s->dbbout);
538 }
539 if (version_id <= 7)
540 {
541 int32_t i32Dummy;
542 uint8_t u8State;
543 uint8_t u8Rate;
544 uint8_t u8Proto;
545
546 pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
547 pHlp->pfnSSMGetU8(pSSM, &u8State);
548 pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
549 pHlp->pfnSSMGetU8(pSSM, &u8Rate);
550 pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
551 pHlp->pfnSSMGetU8(pSSM, &u8Proto);
552 pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
553 pHlp->pfnSSMGetS32(pSSM, &i32Dummy);
554 pHlp->pfnSSMGetS32(pSSM, &i32Dummy);
555 pHlp->pfnSSMGetS32(pSSM, &i32Dummy);
556 if (version_id > 2)
557 {
558 pHlp->pfnSSMGetS32(pSSM, &i32Dummy);
559 pHlp->pfnSSMGetS32(pSSM, &i32Dummy);
560 }
561 rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
562 if (version_id == 4)
563 {
564 pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
565 rc = pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
566 }
567 if (version_id > 3)
568 rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
569 if (version_id == 4)
570 rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
571 AssertLogRelRCReturn(rc, rc);
572
573 PS2MR3FixupState(&s->Aux, &pThisCC->Aux, u8State, u8Rate, u8Proto);
574 }
575
576 /* Determine the translation state. */
577 s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
578
579 /*
580 * Load the queues
581 */
582 if (version_id <= 5)
583 {
584 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
585 if (RT_FAILURE(rc))
586 return rc;
587 for (i = 0; i < u32; i++)
588 {
589 rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
590 if (RT_FAILURE(rc))
591 return rc;
592 }
593 Log(("kbd_load: %d keyboard queue items discarded from old saved state\n", u32));
594 }
595
596 if (version_id <= 7)
597 {
598 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
599 if (RT_FAILURE(rc))
600 return rc;
601 for (i = 0; i < u32; i++)
602 {
603 rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
604 if (RT_FAILURE(rc))
605 return rc;
606 }
607 Log(("kbd_load: %d mouse event queue items discarded from old saved state\n", u32));
608
609 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
610 if (RT_FAILURE(rc))
611 return rc;
612 for (i = 0; i < u32; i++)
613 {
614 rc = pHlp->pfnSSMGetU8(pSSM, &u8Dummy);
615 if (RT_FAILURE(rc))
616 return rc;
617 }
618 Log(("kbd_load: %d mouse command queue items discarded from old saved state\n", u32));
619 }
620
621 /* terminator */
622 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
623 if (RT_FAILURE(rc))
624 return rc;
625 if (u32 != ~0U)
626 {
627 AssertMsgFailed(("u32=%#x\n", u32));
628 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
629 }
630 return 0;
631}
632
633#endif /* IN_RING3 */
634
635
636/* VirtualBox code start */
637
638/* -=-=-=-=-=- wrappers -=-=-=-=-=- */
639
640/** Fluff bits indexed by size (1,2,4). */
641static uint32_t const g_afFluff[5] =
642{
643 /* [0] = */ 0,
644 /* [1] = */ 0,
645 /* [2] = */ UINT32_C(0xff00),
646 /* [3] = */ 0,
647 /* [4] = */ UINT32_C(0xffffff00) /* Crazy Apple (Darwin 6.0.2 and earlier). */
648};
649
650/**
651 * @callback_method_impl{FNIOMIOPORTNEWIN,
652 * Port I/O Handler for keyboard data IN operations.}
653 */
654static DECLCALLBACK(VBOXSTRICTRC) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
655{
656 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
657 RT_NOREF(pvUser, offPort);
658 Assert(offPort == 0);
659 Assert(cb == 1 || cb == 2 || cb == 4);
660
661 *pu32 = kbd_read_data(pDevIns, pThis) | g_afFluff[cb];
662 Log2(("kbdIOPortDataRead: cb=%u *pu32=%#x\n", cb, *pu32));
663 return VINF_SUCCESS;
664}
665
666/**
667 * @callback_method_impl{FNIOMIOPORTNEWOUT,
668 * Port I/O Handler for keyboard data OUT operations.}
669 */
670static DECLCALLBACK(VBOXSTRICTRC) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
671{
672 RT_NOREF(offPort, pvUser);
673 Assert(offPort == 0);
674
675 if (cb == 1 || cb == 2)
676 {
677 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
678 VBOXSTRICTRC rc = kbd_write_data(pDevIns, pThis, (uint8_t)u32);
679 Log2(("kbdIOPortDataWrite: Port=0x60+%x cb=%d u32=%#x rc=%Rrc\n", offPort, cb, u32, VBOXSTRICTRC_VAL(rc)));
680 return rc;
681 }
682 Assert(cb == 4);
683 ASSERT_GUEST_MSG_FAILED(("Port=0x60+%x cb=%d\n", offPort, cb));
684 return VINF_SUCCESS;
685}
686
687/**
688 * @callback_method_impl{FNIOMIOPORTNEWIN,
689 * Port I/O Handler for keyboard status IN operations.}
690 */
691static DECLCALLBACK(VBOXSTRICTRC) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
692{
693 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
694 RT_NOREF(offPort, pvUser);
695 Assert(offPort == 0);
696 Assert(cb == 1 || cb == 2 || cb == 4);
697
698 *pu32 = pThis->status | g_afFluff[cb];
699 Log2(("kbdIOPortStatusRead: cb=%u -> *pu32=%#x\n", cb, *pu32));
700 return VINF_SUCCESS;
701}
702
703/**
704 * @callback_method_impl{FNIOMIOPORTNEWIN,
705 * Port I/O Handler for keyboard command OUT operations.}
706 */
707static DECLCALLBACK(VBOXSTRICTRC) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
708{
709 RT_NOREF(offPort, pvUser);
710 Assert(offPort == 0);
711
712 if (cb == 1 || cb == 2)
713 {
714 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
715 VBOXSTRICTRC rc = kbd_write_command(pDevIns, pThis, (uint8_t)u32);
716 Log2(("kbdIOPortCommandWrite: cb=%d u32=%#x rc=%Rrc\n", cb, u32, VBOXSTRICTRC_VAL(rc)));
717 return rc;
718 }
719 Assert(cb == 4);
720 ASSERT_GUEST_MSG_FAILED(("offPort=0x64+%x cb=%d\n", offPort, cb));
721 return VINF_SUCCESS;
722}
723
724/**
725 * Clear a queue.
726 *
727 * @param pQHdr The queue header.
728 * @param cElements The queue size.
729 */
730void PS2CmnClearQueue(PPS2QHDR pQHdr, size_t cElements)
731{
732 Assert(cElements > 0);
733 LogFlowFunc(("Clearing %s queue %p\n", pQHdr->pszDesc, pQHdr));
734 pQHdr->wpos = pQHdr->rpos = pQHdr->rpos % cElements;
735 pQHdr->cUsed = 0;
736}
737
738
739/**
740 * Add a byte to a queue.
741 *
742 * @param pQHdr The queue header.
743 * @param cElements The queue size.
744 * @param pbElements The queue element array.
745 * @param bValue The byte to store.
746 */
747void PS2CmnInsertQueue(PPS2QHDR pQHdr, size_t cElements, uint8_t *pbElements, uint8_t bValue)
748{
749 Assert(cElements > 0);
750
751 /* Check that the queue is not full. */
752 uint32_t cUsed = pQHdr->cUsed;
753 if (cUsed < cElements)
754 {
755 /* Insert data and update circular buffer write position. */
756 uint32_t wpos = pQHdr->wpos % cElements;
757 pbElements[wpos] = bValue;
758
759 wpos += 1;
760 if (wpos < cElements)
761 pQHdr->wpos = wpos;
762 else
763 pQHdr->wpos = 0; /* Roll over. */
764 pQHdr->cUsed = cUsed + 1;
765
766 LogRelFlowFunc(("inserted %#04x into %s queue %p\n", bValue, pQHdr->pszDesc, pQHdr));
767 }
768 else
769 {
770 Assert(cUsed == cElements);
771 LogRelFlowFunc(("%s queue %p full (%zu entries)\n", pQHdr->pszDesc, pQHdr, cElements));
772 }
773}
774
775/**
776 * Retrieve a byte from a queue.
777 *
778 * @param pQHdr The queue header.
779 * @param cElements The queue size.
780 * @param pbElements The queue element array.
781 * @param pbValue Where to return the byte on success.
782 *
783 * @retval VINF_TRY_AGAIN if queue is empty,
784 * @retval VINF_SUCCESS if a byte was read.
785 */
786int PS2CmnRemoveQueue(PPS2QHDR pQHdr, size_t cElements, uint8_t const *pbElements, uint8_t *pbValue)
787{
788 int rc;
789
790 Assert(cElements > 0);
791 Assert(pbValue);
792
793 uint32_t cUsed = (uint32_t)RT_MIN(pQHdr->cUsed, cElements);
794 if (cUsed > 0)
795 {
796 uint32_t rpos = pQHdr->rpos % cElements;
797 *pbValue = pbElements[rpos];
798
799 rpos += 1;
800 if (rpos < cElements)
801 pQHdr->rpos = rpos;
802 else
803 pQHdr->rpos = 0; /* Roll over. */
804 pQHdr->cUsed = cUsed - 1;
805
806 LogFlowFunc(("removed 0x%02X from %s queue %p\n", *pbValue, pQHdr->pszDesc, pQHdr));
807 rc = VINF_SUCCESS;
808 }
809 else
810 {
811 LogFlowFunc(("%s queue %p empty\n", pQHdr->pszDesc, pQHdr));
812 rc = VINF_TRY_AGAIN;
813 }
814 return rc;
815}
816
817#ifdef IN_RING3
818
819/**
820 * Save a queue state.
821 *
822 * @param pHlp The device helpers.
823 * @param pSSM SSM handle to write the state to.
824 * @param pQHdr The queue header.
825 * @param cElements The queue size.
826 * @param pbElements The queue element array.
827 */
828void PS2CmnR3SaveQueue(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PPS2QHDR pQHdr, size_t cElements, uint8_t const *pbElements)
829{
830 uint32_t cItems = (uint32_t)RT_MIN(pQHdr->cUsed, cElements);
831
832 /* Only save the number of items. Note that the read/write
833 * positions aren't saved as they will be rebuilt on load.
834 */
835 pHlp->pfnSSMPutU32(pSSM, cItems);
836
837 LogFlow(("Storing %u items from %s queue %p\n", cItems, pQHdr->pszDesc, pQHdr));
838
839 /* Save queue data - only the bytes actually used (typically zero). */
840 for (uint32_t i = pQHdr->rpos % cElements; cItems-- > 0; i = (i + 1) % cElements)
841 pHlp->pfnSSMPutU8(pSSM, pbElements[i]);
842}
843
844/**
845 * Load a queue state.
846 *
847 * @param pHlp The device helpers.
848 * @param pSSM SSM handle to read the state from.
849 * @param pQHdr The queue header.
850 * @param cElements The queue size.
851 * @param pbElements The queue element array.
852 *
853 * @returns VBox status/error code.
854 */
855int PS2CmnR3LoadQueue(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PPS2QHDR pQHdr, size_t cElements, uint8_t *pbElements)
856{
857 /* On load, always put the read pointer at zero. */
858 uint32_t cUsed;
859 int rc = pHlp->pfnSSMGetU32(pSSM, &cUsed);
860 AssertRCReturn(rc, rc);
861
862 LogFlow(("Loading %u items to %s queue %p\n", cUsed, pQHdr->pszDesc, pQHdr));
863
864 AssertMsgReturn(cUsed <= cElements, ("Saved size=%u, actual=%zu\n", cUsed, cElements),
865 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
866
867 /* Recalculate queue positions and load data in one go. */
868 pQHdr->rpos = 0;
869 pQHdr->wpos = cUsed;
870 pQHdr->cUsed = cUsed;
871 return pHlp->pfnSSMGetMem(pSSM, pbElements, cUsed);
872}
873
874
875/**
876 * @callback_method_impl{FNSSMDEVSAVEEXEC, Saves a state of the keyboard device.}
877 *
878 * @returns VBox status code.
879 * @param pDevIns The device instance.
880 * @param pSSM The handle to save the state to.
881 */
882static DECLCALLBACK(int) kbdR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
883{
884 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
885 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
886
887 pHlp->pfnSSMPutU8(pSSM, pThis->write_cmd);
888 pHlp->pfnSSMPutU8(pSSM, pThis->status);
889 pHlp->pfnSSMPutU8(pSSM, pThis->mode);
890 pHlp->pfnSSMPutU8(pSSM, pThis->dbbout);
891 /* terminator */
892 pHlp->pfnSSMPutU32(pSSM, UINT32_MAX);
893
894 PS2KR3SaveState(pDevIns, &pThis->Kbd, pSSM);
895 PS2MR3SaveState(pDevIns, &pThis->Aux, pSSM);
896 return VINF_SUCCESS;
897}
898
899
900/**
901 * @callback_method_impl{FNSSMDEVLOADEXEC, Loads a saved keyboard device state.}
902 */
903static DECLCALLBACK(int) kbdR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
904{
905 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
906 PKBDSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PKBDSTATER3);
907 int rc;
908
909 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
910 rc = kbd_load(pDevIns->pHlpR3, pSSM, pThis, pThisCC, uVersion);
911 AssertRCReturn(rc, rc);
912
913 if (uVersion >= 6)
914 rc = PS2KR3LoadState(pDevIns, &pThis->Kbd, pSSM, uVersion);
915 AssertRCReturn(rc, rc);
916
917 if (uVersion >= 8)
918 rc = PS2MR3LoadState(pDevIns, &pThis->Aux, &pThisCC->Aux, pSSM, uVersion);
919 AssertRCReturn(rc, rc);
920 return rc;
921}
922
923
924/**
925 * @callback_method_impl{FNSSMDEVLOADDONE, Key state fix-up after loading}
926 */
927static DECLCALLBACK(int) kbdR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
928{
929 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
930 PKBDSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PKBDSTATER3);
931 RT_NOREF(pSSM);
932 if (pThis->mode & KBD_MODE_DISABLE_MOUSE)
933 PS2MLineDisable(&pThis->Aux);
934 if (pThis->mode & KBD_MODE_DISABLE_KBD)
935 PS2KLineDisable(&pThis->Kbd);
936 return PS2KR3LoadDone(pDevIns, &pThis->Kbd, &pThisCC->Kbd);
937}
938
939
940/**
941 * Debug device info handler. Prints basic auxiliary device state.
942 *
943 * @param pDevIns Device instance which registered the info.
944 * @param pHlp Callback functions for doing output.
945 * @param pszArgs Argument string. Optional and specific to the handler.
946 */
947static DECLCALLBACK(void) kbdR3InfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
948{
949 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
950 NOREF(pszArgs);
951
952 pHlp->pfnPrintf(pHlp, "Keyboard controller: Active command %02X, DBB out %02X, translation %s\n",
953 pThis->write_cmd, pThis->dbbout, pThis->translate ? "on" : "off");
954
955 pHlp->pfnPrintf(pHlp, "Mode: %02X ( ", pThis->mode);
956 if (pThis->mode & KBD_MODE_DISABLE_KBD)
957 pHlp->pfnPrintf(pHlp, "DISABLE_KBD ");
958 if (pThis->mode & KBD_MODE_KBD_INT)
959 pHlp->pfnPrintf(pHlp, "KBD_INT ");
960 if (pThis->mode & KBD_MODE_MOUSE_INT)
961 pHlp->pfnPrintf(pHlp, "AUX_INT ");
962 if (pThis->mode & KBD_MODE_SYS)
963 pHlp->pfnPrintf(pHlp, "SYS ");
964 if (pThis->mode & KBD_MODE_NO_KEYLOCK)
965 pHlp->pfnPrintf(pHlp, "NO_KEYLOCK ");
966 if (pThis->mode & KBD_MODE_DISABLE_KBD)
967 pHlp->pfnPrintf(pHlp, "DISABLE_KBD ");
968 if (pThis->mode & KBD_MODE_DISABLE_MOUSE)
969 pHlp->pfnPrintf(pHlp, "DISABLE_AUX ");
970 if (pThis->mode & KBD_MODE_KCC)
971 pHlp->pfnPrintf(pHlp, "KCC ");
972 if (pThis->mode & KBD_MODE_RFU)
973 pHlp->pfnPrintf(pHlp, "RFU ");
974 pHlp->pfnPrintf(pHlp, " )\n");
975
976 pHlp->pfnPrintf(pHlp, "Status: %02X ( ", pThis->status);
977 if (pThis->status & KBD_STAT_OBF)
978 pHlp->pfnPrintf(pHlp, "OBF ");
979 if (pThis->status & KBD_STAT_IBF)
980 pHlp->pfnPrintf(pHlp, "IBF ");
981 if (pThis->status & KBD_STAT_SELFTEST)
982 pHlp->pfnPrintf(pHlp, "SELFTEST ");
983 if (pThis->status & KBD_STAT_CMD)
984 pHlp->pfnPrintf(pHlp, "CMD ");
985 if (pThis->status & KBD_STAT_UNLOCKED)
986 pHlp->pfnPrintf(pHlp, "UNLOCKED ");
987 if (pThis->status & KBD_STAT_MOUSE_OBF)
988 pHlp->pfnPrintf(pHlp, "AUX_OBF ");
989 if (pThis->status & KBD_STAT_GTO)
990 pHlp->pfnPrintf(pHlp, "GTO ");
991 if (pThis->status & KBD_STAT_PERR)
992 pHlp->pfnPrintf(pHlp, "PERR ");
993 pHlp->pfnPrintf(pHlp, " )\n");
994}
995
996
997/* -=-=-=-=-=- real code -=-=-=-=-=- */
998
999/**
1000 * Reset notification.
1001 *
1002 * @returns VBox status code.
1003 * @param pDevIns The device instance data.
1004 */
1005static DECLCALLBACK(void) kbdR3Reset(PPDMDEVINS pDevIns)
1006{
1007 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
1008 PKBDSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PKBDSTATER3);
1009
1010 pThis->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
1011 pThis->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
1012 /* Resetting everything, keyword was not working right on NT4 reboot. */
1013 pThis->write_cmd = 0;
1014 pThis->translate = 0;
1015
1016 PS2KR3Reset(pDevIns, &pThis->Kbd, &pThisCC->Kbd);
1017 PS2MR3Reset(&pThis->Aux);
1018}
1019
1020
1021/**
1022 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
1023 *
1024 * @remark The keyboard controller doesn't support this action, this is just
1025 * implemented to try out the driver<->device structure.
1026 */
1027static DECLCALLBACK(int) kbdR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1028{
1029 PKBDSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PKBDSTATER3);
1030 int rc;
1031
1032 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1033 ("PS/2 device does not support hotplugging\n"),
1034 VERR_INVALID_PARAMETER);
1035
1036 switch (iLUN)
1037 {
1038 /* LUN #0: keyboard */
1039 case 0:
1040 rc = PS2KR3Attach(pDevIns, &pThisCC->Kbd, iLUN, fFlags);
1041 break;
1042
1043 /* LUN #1: aux/mouse */
1044 case 1:
1045 rc = PS2MR3Attach(pDevIns, &pThisCC->Aux, iLUN, fFlags);
1046 break;
1047
1048 default:
1049 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1050 return VERR_PDM_NO_SUCH_LUN;
1051 }
1052
1053 return rc;
1054}
1055
1056
1057/**
1058 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
1059 * @remark The keyboard controller doesn't support this action, this is just
1060 * implemented to try out the driver<->device structure.
1061 */
1062static DECLCALLBACK(void) kbdR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1063{
1064#if 0
1065 /*
1066 * Reset the interfaces and update the controller state.
1067 */
1068 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
1069 switch (iLUN)
1070 {
1071 /* LUN #0: keyboard */
1072 case 0:
1073 pThisCC->Keyboard.pDrv = NULL;
1074 pThisCC->Keyboard.pDrvBase = NULL;
1075 break;
1076
1077 /* LUN #1: aux/mouse */
1078 case 1:
1079 pThisCC->Mouse.pDrv = NULL;
1080 pThisCC->Mouse.pDrvBase = NULL;
1081 break;
1082
1083 default:
1084 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1085 break;
1086 }
1087#else
1088 NOREF(pDevIns); NOREF(iLUN); NOREF(fFlags);
1089#endif
1090}
1091
1092
1093/**
1094 * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
1095 */
1096static DECLCALLBACK(int) kbdR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1097{
1098 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1099 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
1100 PKBDSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PKBDSTATER3);
1101 int rc;
1102 RT_NOREF(iInstance);
1103
1104 Assert(iInstance == 0);
1105
1106 /*
1107 * Validate and read the configuration.
1108 */
1109 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "KbdThrottleEnabled", "");
1110 Log(("pckbd: fRCEnabled=%RTbool fR0Enabled=%RTbool\n", pDevIns->fRCEnabled, pDevIns->fR0Enabled));
1111
1112 /*
1113 * Initialize the sub-components.
1114 */
1115 rc = PS2KR3Construct(pDevIns, &pThis->Kbd, &pThisCC->Kbd, pCfg);
1116 AssertRCReturn(rc, rc);
1117
1118 rc = PS2MR3Construct(pDevIns, &pThis->Aux, &pThisCC->Aux);
1119 AssertRCReturn(rc, rc);
1120
1121 /*
1122 * Register I/O ports.
1123 */
1124 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x60 /*uPort*/, 1 /*cPorts*/, kbdIOPortDataWrite, kbdIOPortDataRead,
1125 "PC Keyboard - Data", NULL /*pExtDescs*/, &pThis->hIoPortData);
1126 AssertRCReturn(rc, rc);
1127 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x64 /*uPort*/, 1 /*cPorts*/, kbdIOPortCommandWrite, kbdIOPortStatusRead,
1128 "PC Keyboard - Command / Status", NULL /*pExtDescs*/, &pThis->hIoPortCmdStatus);
1129 AssertRCReturn(rc, rc);
1130
1131 /*
1132 * Saved state.
1133 */
1134 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCKBD_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
1135 NULL, NULL, NULL,
1136 NULL, kbdR3SaveExec, NULL,
1137 NULL, kbdR3LoadExec, kbdR3LoadDone);
1138 AssertRCReturn(rc, rc);
1139
1140 /*
1141 * Register debugger info callbacks.
1142 */
1143 PDMDevHlpDBGFInfoRegister(pDevIns, "ps2c", "Display keyboard/mouse controller state.", kbdR3InfoState);
1144
1145 /*
1146 * Attach to the keyboard and mouse drivers.
1147 */
1148 rc = kbdR3Attach(pDevIns, 0 /* keyboard LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
1149 AssertRCReturn(rc, rc);
1150 rc = kbdR3Attach(pDevIns, 1 /* aux/mouse LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
1151 AssertRCReturn(rc, rc);
1152
1153 /*
1154 * Initialize the device state.
1155 */
1156 kbdR3Reset(pDevIns);
1157
1158 return VINF_SUCCESS;
1159}
1160
1161#else /* !IN_RING3 */
1162
1163/**
1164 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1165 */
1166static DECLCALLBACK(int) kbdRZConstruct(PPDMDEVINS pDevIns)
1167{
1168 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1169 PKBDSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PKBDSTATE);
1170
1171 int rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortData, kbdIOPortDataWrite, kbdIOPortDataRead, NULL /*pvUser*/);
1172 AssertRCReturn(rc, rc);
1173 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortCmdStatus, kbdIOPortCommandWrite, kbdIOPortStatusRead, NULL /*pvUser*/);
1174 AssertRCReturn(rc, rc);
1175
1176 return VINF_SUCCESS;
1177}
1178
1179#endif /* !IN_RING3 */
1180
1181/**
1182 * The device registration structure.
1183 */
1184const PDMDEVREG g_DevicePS2KeyboardMouse =
1185{
1186 /* .u32Version = */ PDM_DEVREG_VERSION,
1187 /* .uReserved0 = */ 0,
1188 /* .szName = */ "pckbd",
1189 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
1190 /* .fClass = */ PDM_DEVREG_CLASS_INPUT,
1191 /* .cMaxInstances = */ 1,
1192 /* .uSharedVersion = */ 42,
1193 /* .cbInstanceShared = */ sizeof(KBDSTATE),
1194 /* .cbInstanceCC = */ CTX_EXPR(sizeof(KBDSTATER3), 0, 0),
1195 /* .cbInstanceRC = */ 0,
1196 /* .cMaxPciDevices = */ 0,
1197 /* .cMaxMsixVectors = */ 0,
1198 /* .pszDescription = */ "PS/2 Keyboard and Mouse device. Emulates both the keyboard, mouse and the keyboard controller.\n"
1199 "LUN #0 is the keyboard connector.\n"
1200 "LUN #1 is the aux/mouse connector.",
1201#if defined(IN_RING3)
1202 /* .pszRCMod = */ "VBoxDDRC.rc",
1203 /* .pszR0Mod = */ "VBoxDDR0.r0",
1204 /* .pfnConstruct = */ kbdR3Construct,
1205 /* .pfnDestruct = */ NULL,
1206 /* .pfnRelocate = */ NULL,
1207 /* .pfnMemSetup = */ NULL,
1208 /* .pfnPowerOn = */ NULL,
1209 /* .pfnReset = */ kbdR3Reset,
1210 /* .pfnSuspend = */ NULL,
1211 /* .pfnResume = */ NULL,
1212 /* .pfnAttach = */ kbdR3Attach,
1213 /* .pfnDetach = */ kbdR3Detach,
1214 /* .pfnQueryInterface = */ NULL,
1215 /* .pfnInitComplete = */ NULL,
1216 /* .pfnPowerOff = */ NULL,
1217 /* .pfnSoftReset = */ NULL,
1218 /* .pfnReserved0 = */ NULL,
1219 /* .pfnReserved1 = */ NULL,
1220 /* .pfnReserved2 = */ NULL,
1221 /* .pfnReserved3 = */ NULL,
1222 /* .pfnReserved4 = */ NULL,
1223 /* .pfnReserved5 = */ NULL,
1224 /* .pfnReserved6 = */ NULL,
1225 /* .pfnReserved7 = */ NULL,
1226#elif defined(IN_RING0)
1227 /* .pfnEarlyConstruct = */ NULL,
1228 /* .pfnConstruct = */ kbdRZConstruct,
1229 /* .pfnDestruct = */ NULL,
1230 /* .pfnFinalDestruct = */ NULL,
1231 /* .pfnRequest = */ NULL,
1232 /* .pfnReserved0 = */ NULL,
1233 /* .pfnReserved1 = */ NULL,
1234 /* .pfnReserved2 = */ NULL,
1235 /* .pfnReserved3 = */ NULL,
1236 /* .pfnReserved4 = */ NULL,
1237 /* .pfnReserved5 = */ NULL,
1238 /* .pfnReserved6 = */ NULL,
1239 /* .pfnReserved7 = */ NULL,
1240#elif defined(IN_RC)
1241 /* .pfnConstruct = */ kbdRZConstruct,
1242 /* .pfnReserved0 = */ NULL,
1243 /* .pfnReserved1 = */ NULL,
1244 /* .pfnReserved2 = */ NULL,
1245 /* .pfnReserved3 = */ NULL,
1246 /* .pfnReserved4 = */ NULL,
1247 /* .pfnReserved5 = */ NULL,
1248 /* .pfnReserved6 = */ NULL,
1249 /* .pfnReserved7 = */ NULL,
1250#else
1251# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1252#endif
1253 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1254};
1255
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