VirtualBox

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

Last change on this file was 99739, checked in by vboxsync, 12 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use