VirtualBox

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

Last change on this file since 29945 was 28909, checked in by vboxsync, 15 years ago

Route input to PS/2 keyboard unless USB keyboard was explicitly activated.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.0 KB
Line 
1/* $Id: DevPS2.cpp 28909 2010-04-29 16:34:17Z vboxsync $ */
2/** @file
3 * DevPS2 - PS/2 keyboard & mouse controller device.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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* Header Files *
46*******************************************************************************/
47#define LOG_GROUP LOG_GROUP_DEV_KBD
48#include "vl_vbox.h"
49#include <VBox/pdmdev.h>
50#include <iprt/assert.h>
51#include <iprt/uuid.h>
52
53#include "../Builtins.h"
54
55#define PCKBD_SAVED_STATE_VERSION 5
56
57
58#ifndef VBOX_DEVICE_STRUCT_TESTCASE
59/*******************************************************************************
60* Internal Functions *
61*******************************************************************************/
62RT_C_DECLS_BEGIN
63PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
64PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
65PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
66PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
67RT_C_DECLS_END
68#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
69
70/* debug PC keyboard */
71#define DEBUG_KBD
72
73/* debug PC keyboard : only mouse */
74#define DEBUG_MOUSE
75
76/* Keyboard Controller Commands */
77#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
78#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
79#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
80#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
81#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
82#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
83#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
84#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
85#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
86#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
87#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
88#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
89#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
90#define KBD_CCMD_WRITE_OBUF 0xD2
91#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
92 initiated by the auxiliary device */
93#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
94#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
95#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
96#define KBD_CCMD_READ_TSTINP 0xE0 /* Read test inputs T0, T1 */
97#define KBD_CCMD_RESET 0xFE
98
99/* Keyboard Commands */
100#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
101#define KBD_CMD_ECHO 0xEE
102#define KBD_CMD_SCANCODE 0xF0 /* Get/set scancode set */
103#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
104#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
105#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
106#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */
107#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */
108#define KBD_CMD_RESET 0xFF /* Reset */
109
110/* Keyboard Replies */
111#define KBD_REPLY_POR 0xAA /* Power on reset */
112#define KBD_REPLY_ACK 0xFA /* Command ACK */
113#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
114
115/* Status Register Bits */
116#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
117#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
118#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
119#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
120#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
121#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
122#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
123#define KBD_STAT_PERR 0x80 /* Parity error */
124
125/* Controller Mode Register Bits */
126#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
127#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
128#define KBD_MODE_SYS 0x04 /* The system flag (?) */
129#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
130#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
131#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
132#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
133#define KBD_MODE_RFU 0x80
134
135/* Mouse Commands */
136#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
137#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
138#define AUX_SET_RES 0xE8 /* Set resolution */
139#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
140#define AUX_SET_STREAM 0xEA /* Set stream mode */
141#define AUX_POLL 0xEB /* Poll */
142#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
143#define AUX_SET_WRAP 0xEE /* Set wrap mode */
144#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
145#define AUX_GET_TYPE 0xF2 /* Get type */
146#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
147#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
148#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
149#define AUX_SET_DEFAULT 0xF6
150#define AUX_RESET 0xFF /* Reset aux device */
151#define AUX_ACK 0xFA /* Command byte ACK. */
152#define AUX_NACK 0xFE /* Command byte NACK. */
153
154#define MOUSE_STATUS_REMOTE 0x40
155#define MOUSE_STATUS_ENABLED 0x20
156#define MOUSE_STATUS_SCALE21 0x10
157
158#define KBD_QUEUE_SIZE 256
159
160/** Supported mouse protocols */
161enum
162{
163 MOUSE_PROT_PS2 = 0,
164 MOUSE_PROT_IMPS2 = 3,
165 MOUSE_PROT_IMEX = 4
166};
167
168/** @name Mouse flags */
169/** @{ */
170/** IMEX horizontal scroll-wheel mode is active */
171# define MOUSE_REPORT_HORIZONTAL 0x01
172/** @} */
173
174typedef struct {
175 uint8_t data[KBD_QUEUE_SIZE];
176 int rptr, wptr, count;
177} KBDQueue;
178
179#define MOUSE_CMD_QUEUE_SIZE 8
180
181typedef struct {
182 uint8_t data[MOUSE_CMD_QUEUE_SIZE];
183 int rptr, wptr, count;
184} MouseCmdQueue;
185
186
187#define MOUSE_EVENT_QUEUE_SIZE 256
188
189typedef struct {
190 uint8_t data[MOUSE_EVENT_QUEUE_SIZE];
191 int rptr, wptr, count;
192} MouseEventQueue;
193
194typedef struct KBDState {
195 KBDQueue queue;
196 MouseCmdQueue mouse_command_queue;
197 MouseEventQueue mouse_event_queue;
198 uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
199 uint8_t status;
200 uint8_t mode;
201 /* keyboard state */
202 int32_t kbd_write_cmd;
203 int32_t scan_enabled;
204 int32_t translate;
205 int32_t scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
206 /* mouse state */
207 int32_t mouse_write_cmd;
208 uint8_t mouse_status;
209 uint8_t mouse_resolution;
210 uint8_t mouse_sample_rate;
211 uint8_t mouse_wrap;
212 uint8_t mouse_type; /* MOUSE_PROT_PS2, *_IMPS/2, *_IMEX */
213 uint8_t mouse_detect_state;
214 int32_t mouse_dx; /* current values, needed for 'poll' mode */
215 int32_t mouse_dy;
216 int32_t mouse_dz;
217 int32_t mouse_dw;
218 int32_t mouse_flags;
219 uint8_t mouse_buttons;
220 uint8_t mouse_buttons_reported;
221
222 /** Pointer to the device instance - RC. */
223 PPDMDEVINSRC pDevInsRC;
224 /** Pointer to the device instance - R3 . */
225 PPDMDEVINSR3 pDevInsR3;
226 /** Pointer to the device instance. */
227 PPDMDEVINSR0 pDevInsR0;
228 /** Critical section protecting the state. */
229 PDMCRITSECT CritSect;
230 /**
231 * Keyboard port - LUN#0.
232 *
233 * @implements PDMIBASE
234 * @implements PDMIKEYBOARDPORT
235 */
236 struct
237 {
238 /** The base interface for the keyboard port. */
239 PDMIBASE IBase;
240 /** The keyboard port base interface. */
241 PDMIKEYBOARDPORT IPort;
242
243 /** The base interface of the attached keyboard driver. */
244 R3PTRTYPE(PPDMIBASE) pDrvBase;
245 /** The keyboard interface of the attached keyboard driver. */
246 R3PTRTYPE(PPDMIKEYBOARDCONNECTOR) pDrv;
247 } Keyboard;
248
249 /**
250 * Mouse port - LUN#1.
251 *
252 * @implements PDMIBASE
253 * @implements PDMIMOUSEPORT
254 */
255 struct
256 {
257 /** The base interface for the mouse port. */
258 PDMIBASE IBase;
259 /** The mouse port base interface. */
260 PDMIMOUSEPORT IPort;
261
262 /** The base interface of the attached mouse driver. */
263 R3PTRTYPE(PPDMIBASE) pDrvBase;
264 /** The mouse interface of the attached mouse driver. */
265 R3PTRTYPE(PPDMIMOUSECONNECTOR) pDrv;
266 } Mouse;
267} KBDState;
268
269/* Table to convert from PC scancodes to raw scancodes. */
270static const unsigned char ps2_raw_keycode[128] = {
271 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
272 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
273 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
274 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
275 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
276 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
277 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
278 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
279};
280
281#ifndef VBOX_DEVICE_STRUCT_TESTCASE
282
283/* update irq and KBD_STAT_[MOUSE_]OBF */
284/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
285 incorrect, but it avoids having to simulate exact delays */
286static void kbd_update_irq(KBDState *s)
287{
288 KBDQueue *q = &s->queue;
289 MouseCmdQueue *mcq = &s->mouse_command_queue;
290 MouseEventQueue *meq = &s->mouse_event_queue;
291 int irq12_level, irq1_level;
292
293 irq1_level = 0;
294 irq12_level = 0;
295
296 /* Determine new OBF state, but only if OBF is clear. If OBF was already
297 * set, we cannot risk changing the event type after an ISR potentially
298 * started executing! Only kbd_read_data() clears the OBF bits.
299 */
300 if (!(s->status & KBD_STAT_OBF)) {
301 s->status &= ~KBD_STAT_MOUSE_OBF;
302 /* Keyboard data has priority if both kbd and aux data is available. */
303 if (q->count != 0)
304 {
305 s->status |= KBD_STAT_OBF;
306 }
307 else if (mcq->count != 0 || meq->count != 0)
308 {
309 s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
310 }
311 }
312 /* Determine new IRQ state. */
313 if (s->status & KBD_STAT_OBF) {
314 if (s->status & KBD_STAT_MOUSE_OBF)
315 {
316 if (s->mode & KBD_MODE_MOUSE_INT)
317 irq12_level = 1;
318 }
319 else
320 { /* KBD_STAT_OBF set but KBD_STAT_MOUSE_OBF isn't. */
321 if ((s->mode & KBD_MODE_KBD_INT) && !(s->mode & KBD_MODE_DISABLE_KBD))
322 irq1_level = 1;
323 }
324 }
325 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, irq1_level);
326 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, irq12_level);
327}
328
329static void kbd_queue(KBDState *s, int b, int aux)
330{
331 KBDQueue *q = &s->queue;
332 MouseCmdQueue *mcq = &s->mouse_command_queue;
333 MouseEventQueue *meq = &s->mouse_event_queue;
334
335#if defined(DEBUG_MOUSE) || defined(DEBUG_KBD)
336 if (aux == 1)
337 LogRel3(("%s: mouse command response: 0x%02x\n", __PRETTY_FUNCTION__, b));
338 else if (aux == 2)
339 LogRel3(("%s: mouse event data: 0x%02x\n", __PRETTY_FUNCTION__, b));
340#ifdef DEBUG_KBD
341 else
342 LogRel3(("%s: kbd event: 0x%02x\n", __PRETTY_FUNCTION__, b));
343#endif
344#endif
345 switch (aux)
346 {
347 case 0: /* keyboard */
348 if (q->count >= KBD_QUEUE_SIZE)
349 return;
350 q->data[q->wptr] = b;
351 if (++q->wptr == KBD_QUEUE_SIZE)
352 q->wptr = 0;
353 q->count++;
354 break;
355 case 1: /* mouse command response */
356 if (mcq->count >= MOUSE_CMD_QUEUE_SIZE)
357 return;
358 mcq->data[mcq->wptr] = b;
359 if (++mcq->wptr == MOUSE_CMD_QUEUE_SIZE)
360 mcq->wptr = 0;
361 mcq->count++;
362 break;
363 case 2: /* mouse event data */
364 if (meq->count >= MOUSE_EVENT_QUEUE_SIZE)
365 return;
366 meq->data[meq->wptr] = b;
367 if (++meq->wptr == MOUSE_EVENT_QUEUE_SIZE)
368 meq->wptr = 0;
369 meq->count++;
370 break;
371 default:
372 AssertMsgFailed(("aux=%d\n", aux));
373 }
374 kbd_update_irq(s);
375}
376
377#ifdef IN_RING3
378static void pc_kbd_put_keycode(void *opaque, int keycode)
379{
380 KBDState *s = (KBDState*)opaque;
381
382 /* XXX: add support for scancode sets 1 and 3 */
383 if (!s->translate && keycode < 0xe0 && s->scancode_set == 2)
384 {
385 if (keycode & 0x80)
386 kbd_queue(s, 0xf0, 0);
387 keycode = ps2_raw_keycode[keycode & 0x7f];
388 }
389 kbd_queue(s, keycode, 0);
390}
391#endif /* IN_RING3 */
392
393static uint32_t kbd_read_status(void *opaque, uint32_t addr)
394{
395 KBDState *s = (KBDState*)opaque;
396 int val;
397 val = s->status;
398#if defined(DEBUG_KBD)
399 Log(("kbd: read status=0x%02x\n", val));
400#endif
401 return val;
402}
403
404static int kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
405{
406 int rc = VINF_SUCCESS;
407 KBDState *s = (KBDState*)opaque;
408
409#ifdef DEBUG_KBD
410 Log(("kbd: write cmd=0x%02x\n", val));
411#endif
412 switch(val) {
413 case KBD_CCMD_READ_MODE:
414 kbd_queue(s, s->mode, 0);
415 break;
416 case KBD_CCMD_WRITE_MODE:
417 case KBD_CCMD_WRITE_OBUF:
418 case KBD_CCMD_WRITE_AUX_OBUF:
419 case KBD_CCMD_WRITE_MOUSE:
420 case KBD_CCMD_WRITE_OUTPORT:
421 s->write_cmd = val;
422 break;
423 case KBD_CCMD_MOUSE_DISABLE:
424 s->mode |= KBD_MODE_DISABLE_MOUSE;
425 break;
426 case KBD_CCMD_MOUSE_ENABLE:
427 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
428 break;
429 case KBD_CCMD_TEST_MOUSE:
430 kbd_queue(s, 0x00, 0);
431 break;
432 case KBD_CCMD_SELF_TEST:
433 s->status |= KBD_STAT_SELFTEST;
434 kbd_queue(s, 0x55, 0);
435 break;
436 case KBD_CCMD_KBD_TEST:
437 kbd_queue(s, 0x00, 0);
438 break;
439 case KBD_CCMD_KBD_DISABLE:
440 s->mode |= KBD_MODE_DISABLE_KBD;
441 kbd_update_irq(s);
442 break;
443 case KBD_CCMD_KBD_ENABLE:
444 s->mode &= ~KBD_MODE_DISABLE_KBD;
445 kbd_update_irq(s);
446 break;
447 case KBD_CCMD_READ_INPORT:
448 kbd_queue(s, 0x00, 0);
449 break;
450 case KBD_CCMD_READ_OUTPORT:
451 /* XXX: check that */
452#ifdef TARGET_I386
453 val = 0x01 | (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)) << 1);
454#else
455 val = 0x01;
456#endif
457 if (s->status & KBD_STAT_OBF)
458 val |= 0x10;
459 if (s->status & KBD_STAT_MOUSE_OBF)
460 val |= 0x20;
461 kbd_queue(s, val, 0);
462 break;
463#ifdef TARGET_I386
464 case KBD_CCMD_ENABLE_A20:
465# ifndef IN_RING3
466 if (!PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
467 rc = VINF_IOM_HC_IOPORT_WRITE;
468# else /* IN_RING3 */
469 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), true);
470# endif /* IN_RING3 */
471 break;
472 case KBD_CCMD_DISABLE_A20:
473# ifndef IN_RING3
474 if (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)))
475 rc = VINF_IOM_HC_IOPORT_WRITE;
476# else /* IN_RING3 */
477 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), false);
478# endif /* !IN_RING3 */
479 break;
480#endif
481 case KBD_CCMD_READ_TSTINP:
482 /* Keyboard clock line is zero IFF keyboard is disabled */
483 val = (s->mode & KBD_MODE_DISABLE_KBD) ? 0 : 1;
484 kbd_queue(s, val, 0);
485 break;
486 case KBD_CCMD_RESET:
487#ifndef IN_RING3
488 rc = VINF_IOM_HC_IOPORT_WRITE;
489#else /* IN_RING3 */
490 rc = PDMDevHlpVMReset(s->CTX_SUFF(pDevIns));
491#endif /* !IN_RING3 */
492 break;
493 case 0xff:
494 /* ignore that - I don't know what is its use */
495 break;
496 /* Make OS/2 happy. */
497 /* The 8042 RAM is readble using commands 0x20 thru 0x3f, and writable
498 by 0x60 thru 0x7f. Now days only the firs byte, the mode, is used.
499 We'll ignore the writes (0x61..7f) and return 0 for all the reads
500 just to make some OS/2 debug stuff a bit happier. */
501 case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
502 case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
503 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
504 case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
505 kbd_queue(s, 0, 0);
506 Log(("kbd: reading non-standard RAM addr %#x\n", val & 0x1f));
507 break;
508 default:
509 Log(("kbd: unsupported keyboard cmd=0x%02x\n", val));
510 break;
511 }
512 return rc;
513}
514
515static uint32_t kbd_read_data(void *opaque, uint32_t addr)
516{
517 KBDState *s = (KBDState*)opaque;
518 KBDQueue *q;
519 MouseCmdQueue *mcq;
520 MouseEventQueue *meq;
521 int val, index, aux;
522
523 q = &s->queue;
524 mcq = &s->mouse_command_queue;
525 meq = &s->mouse_event_queue;
526 if (q->count == 0 && mcq->count == 0 && meq->count == 0) {
527 /* NOTE: if no data left, we return the last keyboard one
528 (needed for EMM386) */
529 /* XXX: need a timer to do things correctly */
530 index = q->rptr - 1;
531 if (index < 0)
532 index = KBD_QUEUE_SIZE - 1;
533 val = q->data[index];
534 } else {
535 aux = (s->status & KBD_STAT_MOUSE_OBF);
536 if (!aux)
537 {
538 val = q->data[q->rptr];
539 if (++q->rptr == KBD_QUEUE_SIZE)
540 q->rptr = 0;
541 q->count--;
542 }
543 else
544 {
545 if (mcq->count)
546 {
547 val = mcq->data[mcq->rptr];
548 if (++mcq->rptr == MOUSE_CMD_QUEUE_SIZE)
549 mcq->rptr = 0;
550 mcq->count--;
551 }
552 else
553 {
554 val = meq->data[meq->rptr];
555 if (++meq->rptr == MOUSE_EVENT_QUEUE_SIZE)
556 meq->rptr = 0;
557 meq->count--;
558 }
559 }
560 /* reading deasserts IRQ */
561 if (aux)
562 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, 0);
563 else
564 PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, 0);
565 /* Reading data clears the OBF bits, too. */
566 s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
567 }
568 /* reassert IRQs if data left */
569 kbd_update_irq(s);
570#ifdef DEBUG_KBD
571 Log(("kbd: read data=0x%02x\n", val));
572#endif
573 return val;
574}
575
576static void kbd_reset_keyboard(KBDState *s)
577{
578 s->scan_enabled = 1;
579 s->scancode_set = 2;
580}
581
582static int kbd_write_keyboard(KBDState *s, int val)
583{
584 switch(s->kbd_write_cmd) {
585 default:
586 case -1:
587 switch(val) {
588 case 0x00:
589 kbd_queue(s, KBD_REPLY_ACK, 0);
590 break;
591 case 0x05:
592 kbd_queue(s, KBD_REPLY_RESEND, 0);
593 break;
594 case KBD_CMD_GET_ID:
595 kbd_queue(s, KBD_REPLY_ACK, 0);
596 kbd_queue(s, 0xab, 0);
597 kbd_queue(s, 0x83, 0);
598 break;
599 case KBD_CMD_ECHO:
600 kbd_queue(s, KBD_CMD_ECHO, 0);
601 break;
602 case KBD_CMD_ENABLE:
603 s->scan_enabled = 1;
604 kbd_queue(s, KBD_REPLY_ACK, 0);
605 break;
606 case KBD_CMD_SCANCODE:
607 case KBD_CMD_SET_LEDS:
608 case KBD_CMD_SET_RATE:
609 s->kbd_write_cmd = val;
610 kbd_queue(s, KBD_REPLY_ACK, 0);
611 break;
612 case KBD_CMD_RESET_DISABLE:
613 kbd_reset_keyboard(s);
614 s->scan_enabled = 0;
615 kbd_queue(s, KBD_REPLY_ACK, 0);
616 break;
617 case KBD_CMD_RESET_ENABLE:
618 kbd_reset_keyboard(s);
619 s->scan_enabled = 1;
620 kbd_queue(s, KBD_REPLY_ACK, 0);
621 break;
622 case KBD_CMD_RESET:
623 kbd_reset_keyboard(s);
624 kbd_queue(s, KBD_REPLY_ACK, 0);
625 kbd_queue(s, KBD_REPLY_POR, 0);
626 break;
627 default:
628 kbd_queue(s, KBD_REPLY_ACK, 0);
629 break;
630 }
631 break;
632 case KBD_CMD_SCANCODE:
633#ifdef IN_RING3
634 if (val == 0) {
635 if (s->scancode_set == 1)
636 pc_kbd_put_keycode(s, 0x43);
637 else if (s->scancode_set == 2)
638 pc_kbd_put_keycode(s, 0x41);
639 else if (s->scancode_set == 3)
640 pc_kbd_put_keycode(s, 0x3f);
641 } else {
642 if (val >= 1 && val <= 3)
643 s->scancode_set = val;
644 kbd_queue(s, KBD_REPLY_ACK, 0);
645 }
646#else
647 return VINF_IOM_HC_IOPORT_WRITE;
648#endif
649 case KBD_CMD_SET_LEDS:
650 {
651#ifdef IN_RING3
652 PDMKEYBLEDS enmLeds = PDMKEYBLEDS_NONE;
653 if (val & 0x01)
654 enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_SCROLLLOCK);
655 if (val & 0x02)
656 enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_NUMLOCK);
657 if (val & 0x04)
658 enmLeds = (PDMKEYBLEDS)(enmLeds | PDMKEYBLEDS_CAPSLOCK);
659 s->Keyboard.pDrv->pfnLedStatusChange(s->Keyboard.pDrv, enmLeds);
660#else
661 return VINF_IOM_HC_IOPORT_WRITE;
662#endif
663 kbd_queue(s, KBD_REPLY_ACK, 0);
664 s->kbd_write_cmd = -1;
665 }
666 break;
667 case KBD_CMD_SET_RATE:
668 kbd_queue(s, KBD_REPLY_ACK, 0);
669 s->kbd_write_cmd = -1;
670 break;
671 }
672
673 return VINF_SUCCESS;
674}
675
676static void kbd_mouse_set_reported_buttons(KBDState *s, unsigned fButtons, unsigned fButtonMask)
677{
678 s->mouse_buttons_reported |= (fButtons & fButtonMask);
679 s->mouse_buttons_reported &= (fButtons | ~fButtonMask);
680}
681
682/**
683 * Send a single relative packet in 3-byte PS/2 format, optionally with our
684 * packed button protocol extension, to the PS/2 controller.
685 * @param s keyboard state object
686 * @param dx relative X value, must be between -256 and +255
687 * @param dy relative y value, must be between -256 and +255
688 * @param fButtonsLow the state of the two first mouse buttons
689 * @param fButtonsPacked the state of the upper three mouse buttons and
690 * scroll wheel movement, packed as per the
691 * MOUSE_EXT_* defines. For standard PS/2 packets
692 * only pass the value of button 3 here.
693 */
694static void kbd_mouse_send_rel3_packet(KBDState *s, bool fToCmdQueue)
695{
696 int aux = fToCmdQueue ? 1 : 2;
697 int dx1 = s->mouse_dx < 0 ? RT_MAX(s->mouse_dx, -256)
698 : RT_MIN(s->mouse_dx, 255);
699 int dy1 = s->mouse_dy < 0 ? RT_MAX(s->mouse_dy, -256)
700 : RT_MIN(s->mouse_dy, 255);
701 unsigned int b;
702 unsigned fButtonsLow = s->mouse_buttons & 0x07;
703 s->mouse_dx -= dx1;
704 s->mouse_dy -= dy1;
705 kbd_mouse_set_reported_buttons(s, fButtonsLow, 0x07);
706 LogRel3(("%s: dx1=%d, dy1=%d, fButtonsLow=0x%x\n",
707 __PRETTY_FUNCTION__, dx1, dy1, fButtonsLow));
708 b = 0x08 | ((dx1 < 0 ? 1 : 0) << 4) | ((dy1 < 0 ? 1 : 0) << 5)
709 | fButtonsLow;
710 kbd_queue(s, b, aux);
711 kbd_queue(s, dx1 & 0xff, aux);
712 kbd_queue(s, dy1 & 0xff, aux);
713}
714
715static void kbd_mouse_send_imps2_byte4(KBDState *s, bool fToCmdQueue)
716{
717 int aux = fToCmdQueue ? 1 : 2;
718
719 int dz1 = s->mouse_dz < 0 ? RT_MAX(s->mouse_dz, -127)
720 : RT_MIN(s->mouse_dz, 127);
721 LogRel3(("%s: dz1=%d\n", __PRETTY_FUNCTION__, dz1));
722 s->mouse_dz -= dz1;
723 kbd_queue(s, dz1 & 0xff, aux);
724}
725
726static void kbd_mouse_send_imex_byte4(KBDState *s, bool fToCmdQueue)
727{
728 int aux = fToCmdQueue ? 1 : 2;
729
730 if (s->mouse_dw)
731 {
732 int dw1 = s->mouse_dw < 0 ? RT_MAX(s->mouse_dw, -31)
733 : RT_MIN(s->mouse_dw, 32);
734 LogRel3(("%s: dw1=%d\n", __PRETTY_FUNCTION__, dw1));
735 s->mouse_dw -= dw1;
736 kbd_queue(s, 0x40 | (dw1 & 0x3f), aux);
737 }
738 else if (s->mouse_flags & MOUSE_REPORT_HORIZONTAL && s->mouse_dz)
739 {
740 int dz1 = s->mouse_dz < 0 ? RT_MAX(s->mouse_dz, -31)
741 : RT_MIN(s->mouse_dz, 32);
742 LogRel3(("%s: dz1=%d\n", __PRETTY_FUNCTION__, dz1));
743 s->mouse_dz -= dz1;
744 kbd_queue(s, 0x80 | (dz1 & 0x3f), aux);
745 }
746 else
747 {
748 int dz1 = s->mouse_dz < 0 ? RT_MAX(s->mouse_dz, -7)
749 : RT_MIN(s->mouse_dz, 8);
750 unsigned fButtonsHigh = s->mouse_buttons & 0x18;
751 LogRel3(("%s: dz1=%d fButtonsHigh=0x%x\n",
752 __PRETTY_FUNCTION__, dz1, fButtonsHigh));
753 s->mouse_dz -= dz1;
754 kbd_mouse_set_reported_buttons(s, fButtonsHigh, 0x18);
755 kbd_queue(s, (dz1 & 0x0f) | (fButtonsHigh << 1), aux);
756 }
757}
758
759/**
760 * Send a single relative packet in (IM)PS/2 or IMEX format to the PS/2
761 * controller.
762 * @param s keyboard state object
763 * @param fToCmdQueue should this packet go to the command queue (or the
764 * event queue)?
765 */
766static void kbd_mouse_send_packet(KBDState *s, bool fToCmdQueue)
767{
768 kbd_mouse_send_rel3_packet(s, fToCmdQueue);
769 if (s->mouse_type == MOUSE_PROT_IMPS2)
770 kbd_mouse_send_imps2_byte4(s, fToCmdQueue);
771 if (s->mouse_type == MOUSE_PROT_IMEX)
772 kbd_mouse_send_imex_byte4(s, fToCmdQueue);
773}
774
775static bool kbd_mouse_unreported(KBDState *s)
776{
777 return s->mouse_dx
778 || s->mouse_dy
779 || s->mouse_dz
780 || s->mouse_dw
781 || s->mouse_buttons != s->mouse_buttons_reported;
782}
783
784#ifdef IN_RING3
785static size_t kbd_mouse_event_queue_free(KBDState *s)
786{
787 AssertReturn(s->mouse_event_queue.count <= MOUSE_EVENT_QUEUE_SIZE, 0);
788 return MOUSE_EVENT_QUEUE_SIZE - s->mouse_event_queue.count;
789}
790
791static void pc_kbd_mouse_event(void *opaque, int dx, int dy, int dz, int dw,
792 int buttons_state)
793{
794 LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d, buttons_state=0x%x\n",
795 __PRETTY_FUNCTION__, dx, dy, dz, dw, buttons_state));
796 KBDState *s = (KBDState*)opaque;
797
798 /* check if deltas are recorded when disabled */
799 if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
800 return;
801 AssertReturnVoid((buttons_state & ~0x1f) == 0);
802
803 s->mouse_dx += dx;
804 s->mouse_dy -= dy;
805 if ( (s->mouse_type == MOUSE_PROT_IMPS2)
806 || (s->mouse_type == MOUSE_PROT_IMEX))
807 s->mouse_dz += dz;
808 if ( ( (s->mouse_type == MOUSE_PROT_IMEX)
809 && s->mouse_flags & MOUSE_REPORT_HORIZONTAL))
810 s->mouse_dw += dw;
811 s->mouse_buttons = buttons_state;
812 if (!(s->mouse_status & MOUSE_STATUS_REMOTE))
813 /* if not remote, send event. Multiple events are sent if
814 too big deltas */
815 while ( kbd_mouse_unreported(s)
816 && kbd_mouse_event_queue_free(s) > 4)
817 kbd_mouse_send_packet(s, false);
818}
819
820/* Report a change in status down the driver chain */
821static void kbd_mouse_update_downstream_status(KBDState *pThis)
822{
823 PPDMIMOUSECONNECTOR pDrv = pThis->Mouse.pDrv;
824 bool fEnabled = !!(pThis->mouse_status & MOUSE_STATUS_ENABLED);
825 pDrv->pfnReportModes(pDrv, fEnabled, false);
826}
827#endif /* IN_RING3 */
828
829static int kbd_write_mouse(KBDState *s, int val)
830{
831#ifdef DEBUG_MOUSE
832 LogRelFlowFunc(("kbd: write mouse 0x%02x\n", val));
833#endif
834 int rc = VINF_SUCCESS;
835 /* Flush the mouse command response queue. */
836 s->mouse_command_queue.count = 0;
837 s->mouse_command_queue.rptr = 0;
838 s->mouse_command_queue.wptr = 0;
839 switch(s->mouse_write_cmd) {
840 default:
841 case -1:
842 /* mouse command */
843 if (s->mouse_wrap) {
844 if (val == AUX_RESET_WRAP) {
845 s->mouse_wrap = 0;
846 kbd_queue(s, AUX_ACK, 1);
847 return VINF_SUCCESS;
848 } else if (val != AUX_RESET) {
849 kbd_queue(s, val, 1);
850 return VINF_SUCCESS;
851 }
852 }
853 switch(val) {
854 case AUX_SET_SCALE11:
855 s->mouse_status &= ~MOUSE_STATUS_SCALE21;
856 kbd_queue(s, AUX_ACK, 1);
857 break;
858 case AUX_SET_SCALE21:
859 s->mouse_status |= MOUSE_STATUS_SCALE21;
860 kbd_queue(s, AUX_ACK, 1);
861 break;
862 case AUX_SET_STREAM:
863 s->mouse_status &= ~MOUSE_STATUS_REMOTE;
864 kbd_queue(s, AUX_ACK, 1);
865 break;
866 case AUX_SET_WRAP:
867 s->mouse_wrap = 1;
868 kbd_queue(s, AUX_ACK, 1);
869 break;
870 case AUX_SET_REMOTE:
871 s->mouse_status |= MOUSE_STATUS_REMOTE;
872 kbd_queue(s, AUX_ACK, 1);
873 break;
874 case AUX_GET_TYPE:
875 kbd_queue(s, AUX_ACK, 1);
876 kbd_queue(s, s->mouse_type, 1);
877 break;
878 case AUX_SET_RES:
879 case AUX_SET_SAMPLE:
880 s->mouse_write_cmd = val;
881 kbd_queue(s, AUX_ACK, 1);
882 break;
883 case AUX_GET_SCALE:
884 kbd_queue(s, AUX_ACK, 1);
885 kbd_queue(s, s->mouse_status, 1);
886 kbd_queue(s, s->mouse_resolution, 1);
887 kbd_queue(s, s->mouse_sample_rate, 1);
888 break;
889 case AUX_POLL:
890 kbd_queue(s, AUX_ACK, 1);
891 kbd_mouse_send_packet(s, true);
892 break;
893 case AUX_ENABLE_DEV:
894#ifdef IN_RING3
895 LogRelFlowFunc(("Enabling mouse device\n"));
896 s->mouse_status |= MOUSE_STATUS_ENABLED;
897 kbd_queue(s, AUX_ACK, 1);
898 kbd_mouse_update_downstream_status(s);
899#else
900 LogRelFlowFunc(("Enabling mouse device, R0 stub\n"));
901 rc = VINF_IOM_HC_IOPORT_WRITE;
902#endif
903 break;
904 case AUX_DISABLE_DEV:
905#ifdef IN_RING3
906 s->mouse_status &= ~MOUSE_STATUS_ENABLED;
907 kbd_queue(s, AUX_ACK, 1);
908 /* Flush the mouse events queue. */
909 s->mouse_event_queue.count = 0;
910 s->mouse_event_queue.rptr = 0;
911 s->mouse_event_queue.wptr = 0;
912 kbd_mouse_update_downstream_status(s);
913#else
914 rc = VINF_IOM_HC_IOPORT_WRITE;
915#endif
916 break;
917 case AUX_SET_DEFAULT:
918#ifdef IN_RING3
919 s->mouse_sample_rate = 100;
920 s->mouse_resolution = 2;
921 s->mouse_status = 0;
922 kbd_queue(s, AUX_ACK, 1);
923 kbd_mouse_update_downstream_status(s);
924#else
925 rc = VINF_IOM_HC_IOPORT_WRITE;
926#endif
927 break;
928 case AUX_RESET:
929#ifdef IN_RING3
930 s->mouse_sample_rate = 100;
931 s->mouse_resolution = 2;
932 s->mouse_status = 0;
933 s->mouse_type = MOUSE_PROT_PS2;
934 kbd_queue(s, AUX_ACK, 1);
935 kbd_queue(s, 0xaa, 1);
936 kbd_queue(s, s->mouse_type, 1);
937 /* Flush the mouse events queue. */
938 s->mouse_event_queue.count = 0;
939 s->mouse_event_queue.rptr = 0;
940 s->mouse_event_queue.wptr = 0;
941 kbd_mouse_update_downstream_status(s);
942#else
943 rc = VINF_IOM_HC_IOPORT_WRITE;
944#endif
945 break;
946 default:
947 /* NACK all commands we don't know.
948
949 The usecase for this is the OS/2 mouse driver which will try
950 read 0xE2 in order to figure out if it's a trackpoint device
951 or not. If it doesn't get a NACK (or ACK) on the command it'll
952 do several hundred thousand status reads before giving up. This
953 is slows down the OS/2 boot up considerably. (It also seems that
954 the code is somehow vulnerable while polling like this and that
955 mouse or keyboard input at this point might screw things up badly.)
956
957 From http://www.win.tue.nl/~aeb/linux/kbd/scancodes-13.html:
958
959 Every command or data byte sent to the mouse (except for the
960 resend command fe) is ACKed with fa. If the command or data
961 is invalid, it is NACKed with fe. If the next byte is again
962 invalid, the reply is ERROR: fc. */
963 /** @todo send error if we NACKed the previous command? */
964 kbd_queue(s, AUX_NACK, 1);
965 break;
966 }
967 break;
968 case AUX_SET_SAMPLE:
969 s->mouse_sample_rate = val;
970 /* detect IMPS/2 or IMEX */
971 /* And enable horizontal scrolling reporting when requested */
972 switch(s->mouse_detect_state) {
973 default:
974 case 0:
975 if (val == 200)
976 s->mouse_detect_state = 1;
977 break;
978 case 1:
979 if (val == 100)
980 s->mouse_detect_state = 2;
981 else if (val == 200)
982 s->mouse_detect_state = 3;
983 else if ((val == 80) && s->mouse_type == MOUSE_PROT_IMEX)
984 /* enable horizontal scrolling, byte two */
985 s->mouse_detect_state = 4;
986 else
987 s->mouse_detect_state = 0;
988 break;
989 case 2:
990 if (val == 80 && s->mouse_type < MOUSE_PROT_IMEX)
991 {
992 LogRelFlowFunc(("switching mouse device to IMPS/2 mode\n"));
993 s->mouse_type = MOUSE_PROT_IMPS2;
994 }
995 s->mouse_detect_state = 0;
996 break;
997 case 3:
998 if (val == 80)
999 {
1000 LogRelFlowFunc(("switching mouse device to IMEX mode\n"));
1001 s->mouse_type = MOUSE_PROT_IMEX;
1002 }
1003 s->mouse_detect_state = 0;
1004 break;
1005 case 4:
1006 if (val == 40)
1007 {
1008 LogRelFlowFunc(("enabling IMEX horizontal scrolling reporting\n"));
1009 s->mouse_flags |= MOUSE_REPORT_HORIZONTAL;
1010 }
1011 s->mouse_detect_state = 0;
1012 break;
1013 }
1014 kbd_queue(s, AUX_ACK, 1);
1015 s->mouse_write_cmd = -1;
1016 break;
1017 case AUX_SET_RES:
1018 if (0 <= val && val < 4)
1019 {
1020 s->mouse_resolution = val;
1021 kbd_queue(s, AUX_ACK, 1);
1022 }
1023 else
1024 kbd_queue(s, AUX_NACK, 1);
1025 s->mouse_write_cmd = -1;
1026 break;
1027 }
1028 return rc;
1029}
1030
1031static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
1032{
1033 int rc = VINF_SUCCESS;
1034 KBDState *s = (KBDState*)opaque;
1035
1036#ifdef DEBUG_KBD
1037 Log(("kbd: write data=0x%02x\n", val));
1038#endif
1039
1040 switch(s->write_cmd) {
1041 case 0:
1042 rc = kbd_write_keyboard(s, val);
1043 break;
1044 case KBD_CCMD_WRITE_MODE:
1045 s->mode = val;
1046 s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
1047 kbd_update_irq(s);
1048 break;
1049 case KBD_CCMD_WRITE_OBUF:
1050 kbd_queue(s, val, 0);
1051 break;
1052 case KBD_CCMD_WRITE_AUX_OBUF:
1053 kbd_queue(s, val, 1);
1054 break;
1055 case KBD_CCMD_WRITE_OUTPORT:
1056#ifdef TARGET_I386
1057# ifndef IN_RING3
1058 if (PDMDevHlpA20IsEnabled(s->CTX_SUFF(pDevIns)) != !!(val & 2))
1059 rc = VINF_IOM_HC_IOPORT_WRITE;
1060# else /* IN_RING3 */
1061 PDMDevHlpA20Set(s->CTX_SUFF(pDevIns), !!(val & 2));
1062# endif /* !IN_RING3 */
1063#endif
1064 if (!(val & 1)) {
1065# ifndef IN_RING3
1066 rc = VINF_IOM_HC_IOPORT_WRITE;
1067# else
1068 rc = PDMDevHlpVMReset(s->CTX_SUFF(pDevIns));
1069# endif
1070 }
1071 break;
1072 case KBD_CCMD_WRITE_MOUSE:
1073 rc = kbd_write_mouse(s, val);
1074 break;
1075 default:
1076 break;
1077 }
1078 if (rc != VINF_IOM_HC_IOPORT_WRITE)
1079 s->write_cmd = 0;
1080 return rc;
1081}
1082
1083#ifdef IN_RING3
1084
1085static void kbd_reset(void *opaque)
1086{
1087 KBDState *s = (KBDState*)opaque;
1088 KBDQueue *q;
1089 MouseCmdQueue *mcq;
1090 MouseEventQueue *meq;
1091
1092 s->kbd_write_cmd = -1;
1093 s->mouse_write_cmd = -1;
1094 s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
1095 s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
1096 /* Resetting everything, keyword was not working right on NT4 reboot. */
1097 s->write_cmd = 0;
1098 s->scan_enabled = 0;
1099 s->translate = 0;
1100 s->scancode_set = 2;
1101 if (s->mouse_status)
1102 {
1103 s->mouse_status = 0;
1104 kbd_mouse_update_downstream_status(s);
1105 }
1106 s->mouse_resolution = 0;
1107 s->mouse_sample_rate = 0;
1108 s->mouse_wrap = 0;
1109 s->mouse_type = MOUSE_PROT_PS2;
1110 s->mouse_detect_state = 0;
1111 s->mouse_dx = 0;
1112 s->mouse_dy = 0;
1113 s->mouse_dz = 0;
1114 s->mouse_dw = 0;
1115 s->mouse_flags = 0;
1116 s->mouse_buttons = 0;
1117 s->mouse_buttons_reported = 0;
1118 q = &s->queue;
1119 q->rptr = 0;
1120 q->wptr = 0;
1121 q->count = 0;
1122 mcq = &s->mouse_command_queue;
1123 mcq->rptr = 0;
1124 mcq->wptr = 0;
1125 mcq->count = 0;
1126 meq = &s->mouse_event_queue;
1127 meq->rptr = 0;
1128 meq->wptr = 0;
1129 meq->count = 0;
1130}
1131
1132static void kbd_save(QEMUFile* f, void* opaque)
1133{
1134 uint32_t cItems;
1135 int i;
1136 KBDState *s = (KBDState*)opaque;
1137
1138 qemu_put_8s(f, &s->write_cmd);
1139 qemu_put_8s(f, &s->status);
1140 qemu_put_8s(f, &s->mode);
1141 qemu_put_be32s(f, &s->kbd_write_cmd);
1142 qemu_put_be32s(f, &s->scan_enabled);
1143 qemu_put_be32s(f, &s->mouse_write_cmd);
1144 qemu_put_8s(f, &s->mouse_status);
1145 qemu_put_8s(f, &s->mouse_resolution);
1146 qemu_put_8s(f, &s->mouse_sample_rate);
1147 qemu_put_8s(f, &s->mouse_wrap);
1148 qemu_put_8s(f, &s->mouse_type);
1149 qemu_put_8s(f, &s->mouse_detect_state);
1150 qemu_put_be32s(f, &s->mouse_dx);
1151 qemu_put_be32s(f, &s->mouse_dy);
1152 qemu_put_be32s(f, &s->mouse_dz);
1153 qemu_put_be32s(f, &s->mouse_dw);
1154 qemu_put_be32s(f, &s->mouse_flags);
1155 qemu_put_8s(f, &s->mouse_buttons);
1156 qemu_put_8s(f, &s->mouse_buttons_reported);
1157
1158 /* XXX: s->scancode_set isn't being saved, but we only really support set 2,
1159 * so no real harm done.
1160 */
1161
1162 /*
1163 * We have to save the queues too.
1164 */
1165 cItems = s->queue.count;
1166 SSMR3PutU32(f, cItems);
1167 for (i = s->queue.rptr; cItems-- > 0; i = (i + 1) % RT_ELEMENTS(s->queue.data))
1168 SSMR3PutU8(f, s->queue.data[i]);
1169 Log(("kbd_save: %d keyboard queue items stored\n", s->queue.count));
1170
1171 cItems = s->mouse_command_queue.count;
1172 SSMR3PutU32(f, cItems);
1173 for (i = s->mouse_command_queue.rptr; cItems-- > 0; i = (i + 1) % RT_ELEMENTS(s->mouse_command_queue.data))
1174 SSMR3PutU8(f, s->mouse_command_queue.data[i]);
1175 Log(("kbd_save: %d mouse command queue items stored\n", s->mouse_command_queue.count));
1176
1177 cItems = s->mouse_event_queue.count;
1178 SSMR3PutU32(f, cItems);
1179 for (i = s->mouse_event_queue.rptr; cItems-- > 0; i = (i + 1) % RT_ELEMENTS(s->mouse_event_queue.data))
1180 SSMR3PutU8(f, s->mouse_event_queue.data[i]);
1181 Log(("kbd_save: %d mouse event queue items stored\n", s->mouse_event_queue.count));
1182
1183 /* terminator */
1184 SSMR3PutU32(f, ~0);
1185}
1186
1187static int kbd_load(QEMUFile* f, void* opaque, int version_id)
1188{
1189 uint32_t u32, i;
1190 uint8_t u8Dummy;
1191 uint32_t u32Dummy;
1192 int rc;
1193 KBDState *s = (KBDState*)opaque;
1194
1195#if 0
1196 /** @todo enable this and remove the "if (version_id == 4)" code at some
1197 * later time */
1198 /* Version 4 was never created by any publicly released version of VBox */
1199 AssertReturn(version_id != 4, VERR_NOT_SUPPORTED);
1200#endif
1201 if (version_id < 2 || version_id > PCKBD_SAVED_STATE_VERSION)
1202 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1203 qemu_get_8s(f, &s->write_cmd);
1204 qemu_get_8s(f, &s->status);
1205 qemu_get_8s(f, &s->mode);
1206 qemu_get_be32s(f, (uint32_t *)&s->kbd_write_cmd);
1207 qemu_get_be32s(f, (uint32_t *)&s->scan_enabled);
1208 qemu_get_be32s(f, (uint32_t *)&s->mouse_write_cmd);
1209 qemu_get_8s(f, &s->mouse_status);
1210 qemu_get_8s(f, &s->mouse_resolution);
1211 qemu_get_8s(f, &s->mouse_sample_rate);
1212 qemu_get_8s(f, &s->mouse_wrap);
1213 qemu_get_8s(f, &s->mouse_type);
1214 qemu_get_8s(f, &s->mouse_detect_state);
1215 qemu_get_be32s(f, (uint32_t *)&s->mouse_dx);
1216 qemu_get_be32s(f, (uint32_t *)&s->mouse_dy);
1217 qemu_get_be32s(f, (uint32_t *)&s->mouse_dz);
1218 if (version_id > 2)
1219 {
1220 SSMR3GetS32(f, &s->mouse_dw);
1221 SSMR3GetS32(f, &s->mouse_flags);
1222 }
1223 qemu_get_8s(f, &s->mouse_buttons);
1224 if (version_id == 4)
1225 {
1226 SSMR3GetU32(f, &u32Dummy);
1227 SSMR3GetU32(f, &u32Dummy);
1228 }
1229 if (version_id > 3)
1230 SSMR3GetU8(f, &s->mouse_buttons_reported);
1231 if (version_id == 4)
1232 SSMR3GetU8(f, &u8Dummy);
1233 s->queue.count = 0;
1234 s->queue.rptr = 0;
1235 s->queue.wptr = 0;
1236 s->mouse_command_queue.count = 0;
1237 s->mouse_command_queue.rptr = 0;
1238 s->mouse_command_queue.wptr = 0;
1239 s->mouse_event_queue.count = 0;
1240 s->mouse_event_queue.rptr = 0;
1241 s->mouse_event_queue.wptr = 0;
1242
1243 /* Determine the translation state. */
1244 s->translate = (s->mode & KBD_MODE_KCC) == KBD_MODE_KCC;
1245 s->scancode_set = 2; /* XXX: See comment in kbd_save(). */
1246
1247 /*
1248 * Load the queues
1249 */
1250 rc = SSMR3GetU32(f, &u32);
1251 if (RT_FAILURE(rc))
1252 return rc;
1253 if (u32 > RT_ELEMENTS(s->queue.data))
1254 {
1255 AssertMsgFailed(("u32=%#x\n", u32));
1256 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1257 }
1258 for (i = 0; i < u32; i++)
1259 {
1260 rc = SSMR3GetU8(f, &s->queue.data[i]);
1261 if (RT_FAILURE(rc))
1262 return rc;
1263 }
1264 s->queue.wptr = u32 % RT_ELEMENTS(s->queue.data);
1265 s->queue.count = u32;
1266 Log(("kbd_load: %d keyboard queue items loaded\n", u32));
1267
1268 rc = SSMR3GetU32(f, &u32);
1269 if (RT_FAILURE(rc))
1270 return rc;
1271 if (u32 > RT_ELEMENTS(s->mouse_command_queue.data))
1272 {
1273 AssertMsgFailed(("u32=%#x\n", u32));
1274 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1275 }
1276 for (i = 0; i < u32; i++)
1277 {
1278 rc = SSMR3GetU8(f, &s->mouse_command_queue.data[i]);
1279 if (RT_FAILURE(rc))
1280 return rc;
1281 }
1282 s->mouse_command_queue.wptr = u32 % RT_ELEMENTS(s->mouse_command_queue.data);
1283 s->mouse_command_queue.count = u32;
1284 Log(("kbd_load: %d mouse command queue items loaded\n", u32));
1285
1286 rc = SSMR3GetU32(f, &u32);
1287 if (RT_FAILURE(rc))
1288 return rc;
1289 if (u32 > RT_ELEMENTS(s->mouse_event_queue.data))
1290 {
1291 AssertMsgFailed(("u32=%#x\n", u32));
1292 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1293 }
1294 for (i = 0; i < u32; i++)
1295 {
1296 rc = SSMR3GetU8(f, &s->mouse_event_queue.data[i]);
1297 if (RT_FAILURE(rc))
1298 return rc;
1299 }
1300 s->mouse_event_queue.wptr = u32 % RT_ELEMENTS(s->mouse_event_queue.data);
1301 s->mouse_event_queue.count = u32;
1302 Log(("kbd_load: %d mouse event queue items loaded\n", u32));
1303
1304 /* terminator */
1305 rc = SSMR3GetU32(f, &u32);
1306 if (RT_FAILURE(rc))
1307 return rc;
1308 if (u32 != ~0U)
1309 {
1310 AssertMsgFailed(("u32=%#x\n", u32));
1311 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1312 }
1313 /* Resend a notification to Main if the device is active */
1314 kbd_mouse_update_downstream_status(s);
1315 return 0;
1316}
1317#endif /* IN_RING3 */
1318
1319
1320/* VirtualBox code start */
1321
1322/* -=-=-=-=-=- wrappers -=-=-=-=-=- */
1323
1324/**
1325 * Port I/O Handler for keyboard data IN operations.
1326 *
1327 * @returns VBox status code.
1328 *
1329 * @param pDevIns The device instance.
1330 * @param pvUser User argument - ignored.
1331 * @param Port Port number used for the IN operation.
1332 * @param pu32 Where to store the result.
1333 * @param cb Number of bytes read.
1334 */
1335PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1336{
1337 NOREF(pvUser);
1338 if (cb == 1)
1339 {
1340 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1341 int rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_HC_IOPORT_READ);
1342 if (RT_LIKELY(rc == VINF_SUCCESS))
1343 {
1344 *pu32 = kbd_read_data(pThis, Port);
1345 PDMCritSectLeave(&pThis->CritSect);
1346 Log2(("kbdIOPortDataRead: Port=%#x cb=%d *pu32=%#x\n", Port, cb, *pu32));
1347 }
1348 return rc;
1349 }
1350 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
1351 return VERR_IOM_IOPORT_UNUSED;
1352}
1353
1354/**
1355 * Port I/O Handler for keyboard data OUT operations.
1356 *
1357 * @returns VBox status code.
1358 *
1359 * @param pDevIns The device instance.
1360 * @param pvUser User argument - ignored.
1361 * @param Port Port number used for the IN operation.
1362 * @param u32 The value to output.
1363 * @param cb The value size in bytes.
1364 */
1365PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
1366{
1367 int rc = VINF_SUCCESS;
1368 NOREF(pvUser);
1369 if (cb == 1)
1370 {
1371 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1372 rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_HC_IOPORT_WRITE);
1373 if (RT_LIKELY(rc == VINF_SUCCESS))
1374 {
1375 rc = kbd_write_data(pThis, Port, u32);
1376 PDMCritSectLeave(&pThis->CritSect);
1377 Log2(("kbdIOPortDataWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
1378 }
1379 }
1380 else
1381 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
1382 return rc;
1383}
1384
1385/**
1386 * Port I/O Handler for keyboard status IN operations.
1387 *
1388 * @returns VBox status code.
1389 *
1390 * @param pDevIns The device instance.
1391 * @param pvUser User argument - ignored.
1392 * @param Port Port number used for the IN operation.
1393 * @param pu32 Where to store the result.
1394 * @param cb Number of bytes read.
1395 */
1396PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1397{
1398 NOREF(pvUser);
1399 if (cb == 1)
1400 {
1401 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1402 int rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_HC_IOPORT_READ);
1403 if (RT_LIKELY(rc == VINF_SUCCESS))
1404 {
1405 *pu32 = kbd_read_status(pThis, Port);
1406 PDMCritSectLeave(&pThis->CritSect);
1407 Log2(("kbdIOPortStatusRead: Port=%#x cb=%d -> *pu32=%#x\n", Port, cb, *pu32));
1408 }
1409 return rc;
1410 }
1411 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
1412 return VERR_IOM_IOPORT_UNUSED;
1413}
1414
1415/**
1416 * Port I/O Handler for keyboard command OUT operations.
1417 *
1418 * @returns VBox status code.
1419 *
1420 * @param pDevIns The device instance.
1421 * @param pvUser User argument - ignored.
1422 * @param Port Port number used for the IN operation.
1423 * @param u32 The value to output.
1424 * @param cb The value size in bytes.
1425 */
1426PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
1427{
1428 int rc = VINF_SUCCESS;
1429 NOREF(pvUser);
1430 if (cb == 1)
1431 {
1432 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1433 rc = PDMCritSectEnter(&pThis->CritSect, VINF_IOM_HC_IOPORT_WRITE);
1434 if (RT_LIKELY(rc == VINF_SUCCESS))
1435 {
1436 rc = kbd_write_command(pThis, Port, u32);
1437 PDMCritSectLeave(&pThis->CritSect);
1438 Log2(("kbdIOPortCommandWrite: Port=%#x cb=%d u32=%#x rc=%Rrc\n", Port, cb, u32, rc));
1439 }
1440 }
1441 else
1442 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
1443 return rc;
1444}
1445
1446#ifdef IN_RING3
1447
1448/**
1449 * Saves a state of the keyboard device.
1450 *
1451 * @returns VBox status code.
1452 * @param pDevIns The device instance.
1453 * @param pSSMHandle The handle to save the state to.
1454 */
1455static DECLCALLBACK(int) kbdSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
1456{
1457 kbd_save(pSSMHandle, PDMINS_2_DATA(pDevIns, KBDState *));
1458 return VINF_SUCCESS;
1459}
1460
1461
1462/**
1463 * Loads a saved keyboard device state.
1464 *
1465 * @returns VBox status code.
1466 * @param pDevIns The device instance.
1467 * @param pSSMHandle The handle to the saved state.
1468 * @param uVersion The data unit version number.
1469 * @param uPass The data pass.
1470 */
1471static DECLCALLBACK(int) kbdLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t uVersion, uint32_t uPass)
1472{
1473 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
1474 return kbd_load(pSSMHandle, PDMINS_2_DATA(pDevIns, KBDState *), uVersion);
1475}
1476
1477/**
1478 * Reset notification.
1479 *
1480 * @returns VBox status.
1481 * @param pDevIns The device instance data.
1482 */
1483static DECLCALLBACK(void) kbdReset(PPDMDEVINS pDevIns)
1484{
1485 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1486
1487 kbd_reset(pThis);
1488 /* Activate the PS/2 keyboard by default. */
1489 pThis->Keyboard.pDrv->pfnSetActive(pThis->Keyboard.pDrv, true);
1490}
1491
1492
1493/* -=-=-=-=-=- Keyboard: IBase -=-=-=-=-=- */
1494
1495/**
1496 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1497 */
1498static DECLCALLBACK(void *) kbdKeyboardQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1499{
1500 KBDState *pThis = RT_FROM_MEMBER(pInterface, KBDState, Keyboard.IBase);
1501 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Keyboard.IBase);
1502 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDPORT, &pThis->Keyboard.IPort);
1503 return NULL;
1504}
1505
1506
1507/* -=-=-=-=-=- Keyboard: IKeyboardPort -=-=-=-=-=- */
1508
1509/**
1510 * Keyboard event handler.
1511 *
1512 * @returns VBox status code.
1513 * @param pInterface Pointer to the keyboard port interface (KBDState::Keyboard.IPort).
1514 * @param u8KeyCode The keycode.
1515 */
1516static DECLCALLBACK(int) kbdKeyboardPutEvent(PPDMIKEYBOARDPORT pInterface, uint8_t u8KeyCode)
1517{
1518 KBDState *pThis = RT_FROM_MEMBER(pInterface, KBDState, Keyboard.IPort);
1519 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
1520 AssertReleaseRC(rc);
1521
1522 pc_kbd_put_keycode(pThis, u8KeyCode);
1523
1524 PDMCritSectLeave(&pThis->CritSect);
1525 return VINF_SUCCESS;
1526}
1527
1528
1529/* -=-=-=-=-=- Mouse: IBase -=-=-=-=-=- */
1530
1531/**
1532 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1533 */
1534static DECLCALLBACK(void *) kbdMouseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1535{
1536 KBDState *pThis = RT_FROM_MEMBER(pInterface, KBDState, Mouse.IBase);
1537 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Mouse.IBase);
1538 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSEPORT, &pThis->Mouse.IPort);
1539 return NULL;
1540}
1541
1542
1543/* -=-=-=-=-=- Mouse: IMousePort -=-=-=-=-=- */
1544
1545/**
1546 * @interface_method_impl{PDMIMOUSEPORT, pfnPutEvent}
1547 */
1548static DECLCALLBACK(int) kbdMousePutEvent(PPDMIMOUSEPORT pInterface, int32_t iDeltaX, int32_t iDeltaY,
1549 int32_t iDeltaZ, int32_t iDeltaW, uint32_t fButtonStates)
1550{
1551 KBDState *pThis = RT_FROM_MEMBER(pInterface, KBDState, Mouse.IPort);
1552 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
1553 AssertReleaseRC(rc);
1554
1555 pc_kbd_mouse_event(pThis, iDeltaX, iDeltaY, iDeltaZ, iDeltaW, fButtonStates);
1556
1557 PDMCritSectLeave(&pThis->CritSect);
1558 return VINF_SUCCESS;
1559}
1560
1561/**
1562 * @interface_method_impl{PDMIMOUSEPORT, pfnPutEventAbs}
1563 */
1564static DECLCALLBACK(int) kbdMousePutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t uX, uint32_t uY, int32_t iDeltaZ, int32_t iDeltaW, uint32_t fButtons)
1565{
1566 AssertFailedReturn(VERR_NOT_SUPPORTED);
1567}
1568
1569
1570/* -=-=-=-=-=- real code -=-=-=-=-=- */
1571
1572
1573/**
1574 * Attach command.
1575 *
1576 * This is called to let the device attach to a driver for a specified LUN
1577 * during runtime. This is not called during VM construction, the device
1578 * constructor have to attach to all the available drivers.
1579 *
1580 * This is like plugging in the keyboard or mouse after turning on the PC.
1581 *
1582 * @returns VBox status code.
1583 * @param pDevIns The device instance.
1584 * @param iLUN The logical unit which is being detached.
1585 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1586 * @remark The keyboard controller doesn't support this action, this is just
1587 * implemented to try out the driver<->device structure.
1588 */
1589static DECLCALLBACK(int) kbdAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1590{
1591 int rc;
1592 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1593
1594 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1595 ("PS/2 device does not support hotplugging\n"),
1596 VERR_INVALID_PARAMETER);
1597
1598 switch (iLUN)
1599 {
1600 /* LUN #0: keyboard */
1601 case 0:
1602 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Keyboard.IBase, &pThis->Keyboard.pDrvBase, "Keyboard Port");
1603 if (RT_SUCCESS(rc))
1604 {
1605 pThis->Keyboard.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Keyboard.pDrvBase, PDMIKEYBOARDCONNECTOR);
1606 if (!pThis->Keyboard.pDrv)
1607 {
1608 AssertLogRelMsgFailed(("LUN #0 doesn't have a keyboard interface! rc=%Rrc\n", rc));
1609 rc = VERR_PDM_MISSING_INTERFACE;
1610 }
1611 }
1612 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1613 {
1614 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
1615 rc = VINF_SUCCESS;
1616 }
1617 else
1618 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
1619 break;
1620
1621 /* LUN #1: aux/mouse */
1622 case 1:
1623 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Mouse.IBase, &pThis->Mouse.pDrvBase, "Aux (Mouse) Port");
1624 if (RT_SUCCESS(rc))
1625 {
1626 pThis->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
1627 if (!pThis->Mouse.pDrv)
1628 {
1629 AssertLogRelMsgFailed(("LUN #1 doesn't have a mouse interface! rc=%Rrc\n", rc));
1630 rc = VERR_PDM_MISSING_INTERFACE;
1631 }
1632 }
1633 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1634 {
1635 Log(("%s/%d: warning: no driver attached to LUN #1!\n", pDevIns->pReg->szName, pDevIns->iInstance));
1636 rc = VINF_SUCCESS;
1637 }
1638 else
1639 AssertLogRelMsgFailed(("Failed to attach LUN #1! rc=%Rrc\n", rc));
1640 break;
1641
1642 default:
1643 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1644 return VERR_PDM_NO_SUCH_LUN;
1645 }
1646
1647 return rc;
1648}
1649
1650
1651/**
1652 * Detach notification.
1653 *
1654 * This is called when a driver is detaching itself from a LUN of the device.
1655 * The device should adjust it's state to reflect this.
1656 *
1657 * This is like unplugging the network cable to use it for the laptop or
1658 * something while the PC is still running.
1659 *
1660 * @param pDevIns The device instance.
1661 * @param iLUN The logical unit which is being detached.
1662 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1663 * @remark The keyboard controller doesn't support this action, this is just
1664 * implemented to try out the driver<->device structure.
1665 */
1666static DECLCALLBACK(void) kbdDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1667{
1668#if 0
1669 /*
1670 * Reset the interfaces and update the controller state.
1671 */
1672 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1673 switch (iLUN)
1674 {
1675 /* LUN #0: keyboard */
1676 case 0:
1677 pThis->Keyboard.pDrv = NULL;
1678 pThis->Keyboard.pDrvBase = NULL;
1679 break;
1680
1681 /* LUN #1: aux/mouse */
1682 case 1:
1683 pThis->Mouse.pDrv = NULL;
1684 pThis->Mouse.pDrvBase = NULL;
1685 break;
1686
1687 default:
1688 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1689 break;
1690 }
1691#endif
1692}
1693
1694
1695/**
1696 * @copydoc FNPDMDEVRELOCATE
1697 */
1698static DECLCALLBACK(void) kdbRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1699{
1700 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1701 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1702}
1703
1704
1705/**
1706 * Destruct a device instance for a VM.
1707 *
1708 * @returns VBox status.
1709 * @param pDevIns The device instance data.
1710 */
1711static DECLCALLBACK(int) kbdDestruct(PPDMDEVINS pDevIns)
1712{
1713 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1714 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1715
1716 PDMR3CritSectDelete(&pThis->CritSect);
1717
1718 return VINF_SUCCESS;
1719}
1720
1721
1722/**
1723 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1724 */
1725static DECLCALLBACK(int) kbdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1726{
1727 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1728 int rc;
1729 bool fGCEnabled;
1730 bool fR0Enabled;
1731 Assert(iInstance == 0);
1732
1733 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1734
1735 /*
1736 * Validate and read the configuration.
1737 */
1738 if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0R0Enabled\0"))
1739 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1740 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
1741 if (RT_FAILURE(rc))
1742 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"GCEnabled\" from the config"));
1743 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
1744 if (RT_FAILURE(rc))
1745 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"R0Enabled\" from the config"));
1746 Log(("pckbd: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
1747
1748
1749 /*
1750 * Initialize the interfaces.
1751 */
1752 pThis->pDevInsR3 = pDevIns;
1753 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1754 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1755 pThis->Keyboard.IBase.pfnQueryInterface = kbdKeyboardQueryInterface;
1756 pThis->Keyboard.IPort.pfnPutEvent = kbdKeyboardPutEvent;
1757
1758 pThis->Mouse.IBase.pfnQueryInterface = kbdMouseQueryInterface;
1759 pThis->Mouse.IPort.pfnPutEvent = kbdMousePutEvent;
1760 pThis->Mouse.IPort.pfnPutEventAbs = kbdMousePutEventAbs;
1761
1762 /*
1763 * Initialize the critical section.
1764 */
1765 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "PS2KM#%d", iInstance);
1766 if (RT_FAILURE(rc))
1767 return rc;
1768
1769 /*
1770 * Register I/O ports, save state, keyboard event handler and mouse event handlers.
1771 */
1772 rc = PDMDevHlpIOPortRegister(pDevIns, 0x60, 1, NULL, kbdIOPortDataWrite, kbdIOPortDataRead, NULL, NULL, "PC Keyboard - Data");
1773 if (RT_FAILURE(rc))
1774 return rc;
1775 rc = PDMDevHlpIOPortRegister(pDevIns, 0x64, 1, NULL, kbdIOPortCommandWrite, kbdIOPortStatusRead, NULL, NULL, "PC Keyboard - Command / Status");
1776 if (RT_FAILURE(rc))
1777 return rc;
1778 if (fGCEnabled)
1779 {
1780 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
1781 if (RT_FAILURE(rc))
1782 return rc;
1783 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
1784 if (RT_FAILURE(rc))
1785 return rc;
1786 }
1787 if (fR0Enabled)
1788 {
1789 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
1790 if (RT_FAILURE(rc))
1791 return rc;
1792 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
1793 if (RT_FAILURE(rc))
1794 return rc;
1795 }
1796 rc = PDMDevHlpSSMRegister(pDevIns, PCKBD_SAVED_STATE_VERSION, sizeof(*pThis), kbdSaveExec, kbdLoadExec);
1797 if (RT_FAILURE(rc))
1798 return rc;
1799
1800 /*
1801 * Attach to the keyboard and mouse drivers.
1802 */
1803 rc = kbdAttach(pDevIns, 0 /* keyboard LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
1804 if (RT_FAILURE(rc))
1805 return rc;
1806 rc = kbdAttach(pDevIns, 1 /* aux/mouse LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
1807 if (RT_FAILURE(rc))
1808 return rc;
1809
1810 /*
1811 * Initialize the device state.
1812 */
1813 kbdReset(pDevIns);
1814
1815 return VINF_SUCCESS;
1816}
1817
1818
1819/**
1820 * The device registration structure.
1821 */
1822const PDMDEVREG g_DevicePS2KeyboardMouse =
1823{
1824 /* u32Version */
1825 PDM_DEVREG_VERSION,
1826 /* szName */
1827 "pckbd",
1828 /* szRCMod */
1829 "VBoxDDGC.gc",
1830 /* szR0Mod */
1831 "VBoxDDR0.r0",
1832 /* pszDescription */
1833 "PS/2 Keyboard and Mouse device. Emulates both the keyboard, mouse and the keyboard controller. "
1834 "LUN #0 is the keyboard connector. "
1835 "LUN #1 is the aux/mouse connector.",
1836 /* fFlags */
1837 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,
1838 /* fClass */
1839 PDM_DEVREG_CLASS_INPUT,
1840 /* cMaxInstances */
1841 1,
1842 /* cbInstance */
1843 sizeof(KBDState),
1844 /* pfnConstruct */
1845 kbdConstruct,
1846 /* pfnDestruct */
1847 kbdDestruct,
1848 /* pfnRelocate */
1849 kdbRelocate,
1850 /* pfnIOCtl */
1851 NULL,
1852 /* pfnPowerOn */
1853 NULL,
1854 /* pfnReset */
1855 kbdReset,
1856 /* pfnSuspend */
1857 NULL,
1858 /* pfnResume */
1859 NULL,
1860 /* pfnAttach */
1861 kbdAttach,
1862 /* pfnDetach */
1863 kbdDetach,
1864 /* pfnQueryInterface. */
1865 NULL,
1866 /* pfnInitComplete */
1867 NULL,
1868 /* pfnPowerOff */
1869 NULL,
1870 /* pfnSoftReset */
1871 NULL,
1872 /* u32VersionEnd */
1873 PDM_DEVREG_VERSION
1874};
1875
1876#endif /* IN_RING3 */
1877#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1878
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