VirtualBox

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

Last change on this file since 33540 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.8 KB
Line 
1/* $Id: DevPS2.cpp 33540 2010-10-28 09:27:05Z 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 readable 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 to the PS/2 controller.
684 * @param s keyboard state object
685 * @param dx relative X value, must be between -256 and +255
686 * @param dy relative y value, must be between -256 and +255
687 * @param fButtonsLow the state of the two first mouse buttons
688 * @param fButtonsPacked the state of the upper three mouse buttons and
689 * scroll wheel movement, packed as per the
690 * MOUSE_EXT_* defines. For standard PS/2 packets
691 * only pass the value of button 3 here.
692 */
693static void kbd_mouse_send_rel3_packet(KBDState *s, bool fToCmdQueue)
694{
695 int aux = fToCmdQueue ? 1 : 2;
696 int dx1 = s->mouse_dx < 0 ? RT_MAX(s->mouse_dx, -256)
697 : RT_MIN(s->mouse_dx, 255);
698 int dy1 = s->mouse_dy < 0 ? RT_MAX(s->mouse_dy, -256)
699 : RT_MIN(s->mouse_dy, 255);
700 unsigned int b;
701 unsigned fButtonsLow = s->mouse_buttons & 0x07;
702 s->mouse_dx -= dx1;
703 s->mouse_dy -= dy1;
704 kbd_mouse_set_reported_buttons(s, fButtonsLow, 0x07);
705 LogRel3(("%s: dx1=%d, dy1=%d, fButtonsLow=0x%x\n",
706 __PRETTY_FUNCTION__, dx1, dy1, fButtonsLow));
707 b = 0x08 | ((dx1 < 0 ? 1 : 0) << 4) | ((dy1 < 0 ? 1 : 0) << 5)
708 | fButtonsLow;
709 kbd_queue(s, b, aux);
710 kbd_queue(s, dx1 & 0xff, aux);
711 kbd_queue(s, dy1 & 0xff, aux);
712}
713
714static void kbd_mouse_send_imps2_byte4(KBDState *s, bool fToCmdQueue)
715{
716 int aux = fToCmdQueue ? 1 : 2;
717
718 int dz1 = s->mouse_dz < 0 ? RT_MAX(s->mouse_dz, -127)
719 : RT_MIN(s->mouse_dz, 127);
720 LogRel3(("%s: dz1=%d\n", __PRETTY_FUNCTION__, dz1));
721 s->mouse_dz -= dz1;
722 kbd_queue(s, dz1 & 0xff, aux);
723}
724
725static void kbd_mouse_send_imex_byte4(KBDState *s, bool fToCmdQueue)
726{
727 int aux = fToCmdQueue ? 1 : 2;
728 int dz1 = 0, dw1 = 0;
729 unsigned fButtonsHigh = s->mouse_buttons & 0x18;
730
731 if (s->mouse_dw > 0)
732 dw1 = 1;
733 else if (s->mouse_dw < 0)
734 dw1 = -1;
735 else if (s->mouse_dz > 0)
736 dz1 = 1;
737 else if (s->mouse_dz < 0)
738 dz1 = -1;
739 if (s->mouse_dw && s->mouse_flags & MOUSE_REPORT_HORIZONTAL)
740 {
741 LogRel3(("%s: dw1=%d\n", __PRETTY_FUNCTION__, dw1));
742 kbd_queue(s, 0x40 | (dw1 & 0x3f), aux);
743 }
744 else
745 {
746 LogRel3(("%s: dz1=%d, dw1=%d, fButtonsHigh=0x%x\n",
747 __PRETTY_FUNCTION__, dz1, dw1, fButtonsHigh));
748 unsigned u4Low = dw1 > 0 ? 9 /* -7 & 0xf */
749 : dw1 < 0 ? 7
750 : dz1 > 0 ? 1
751 : dz1 < 0 ? 0xf /* -1 & 0xf */
752 : 0;
753 kbd_mouse_set_reported_buttons(s, fButtonsHigh, 0x18);
754 kbd_queue(s, (fButtonsHigh << 1) | u4Low, aux);
755 }
756 s->mouse_dz -= dz1;
757 s->mouse_dw -= dw1;
758}
759
760/**
761 * Send a single relative packet in (IM)PS/2 or IMEX format to the PS/2
762 * controller.
763 * @param s keyboard state object
764 * @param fToCmdQueue should this packet go to the command queue (or the
765 * event queue)?
766 */
767static void kbd_mouse_send_packet(KBDState *s, bool fToCmdQueue)
768{
769 kbd_mouse_send_rel3_packet(s, fToCmdQueue);
770 if (s->mouse_type == MOUSE_PROT_IMPS2)
771 kbd_mouse_send_imps2_byte4(s, fToCmdQueue);
772 if (s->mouse_type == MOUSE_PROT_IMEX)
773 kbd_mouse_send_imex_byte4(s, fToCmdQueue);
774}
775
776static bool kbd_mouse_unreported(KBDState *s)
777{
778 return s->mouse_dx
779 || s->mouse_dy
780 || s->mouse_dz
781 || s->mouse_dw
782 || s->mouse_buttons != s->mouse_buttons_reported;
783}
784
785#ifdef IN_RING3
786static size_t kbd_mouse_event_queue_free(KBDState *s)
787{
788 AssertReturn(s->mouse_event_queue.count <= MOUSE_EVENT_QUEUE_SIZE, 0);
789 return MOUSE_EVENT_QUEUE_SIZE - s->mouse_event_queue.count;
790}
791
792static void pc_kbd_mouse_event(void *opaque, int dx, int dy, int dz, int dw,
793 int buttons_state)
794{
795 LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d, buttons_state=0x%x\n",
796 __PRETTY_FUNCTION__, dx, dy, dz, dw, buttons_state));
797 KBDState *s = (KBDState*)opaque;
798
799 /* check if deltas are recorded when disabled */
800 if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
801 return;
802 AssertReturnVoid((buttons_state & ~0x1f) == 0);
803
804 s->mouse_dx += dx;
805 s->mouse_dy -= dy;
806 if ( (s->mouse_type == MOUSE_PROT_IMPS2)
807 || (s->mouse_type == MOUSE_PROT_IMEX))
808 s->mouse_dz += dz;
809 if (s->mouse_type == MOUSE_PROT_IMEX)
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 if (pThis->Keyboard.pDrv)
1490 pThis->Keyboard.pDrv->pfnSetActive(pThis->Keyboard.pDrv, true);
1491}
1492
1493
1494/* -=-=-=-=-=- Keyboard: IBase -=-=-=-=-=- */
1495
1496/**
1497 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1498 */
1499static DECLCALLBACK(void *) kbdKeyboardQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1500{
1501 KBDState *pThis = RT_FROM_MEMBER(pInterface, KBDState, Keyboard.IBase);
1502 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Keyboard.IBase);
1503 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIKEYBOARDPORT, &pThis->Keyboard.IPort);
1504 return NULL;
1505}
1506
1507
1508/* -=-=-=-=-=- Keyboard: IKeyboardPort -=-=-=-=-=- */
1509
1510/**
1511 * Keyboard event handler.
1512 *
1513 * @returns VBox status code.
1514 * @param pInterface Pointer to the keyboard port interface (KBDState::Keyboard.IPort).
1515 * @param u8KeyCode The keycode.
1516 */
1517static DECLCALLBACK(int) kbdKeyboardPutEvent(PPDMIKEYBOARDPORT pInterface, uint8_t u8KeyCode)
1518{
1519 KBDState *pThis = RT_FROM_MEMBER(pInterface, KBDState, Keyboard.IPort);
1520 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
1521 AssertReleaseRC(rc);
1522
1523 pc_kbd_put_keycode(pThis, u8KeyCode);
1524
1525 PDMCritSectLeave(&pThis->CritSect);
1526 return VINF_SUCCESS;
1527}
1528
1529
1530/* -=-=-=-=-=- Mouse: IBase -=-=-=-=-=- */
1531
1532/**
1533 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1534 */
1535static DECLCALLBACK(void *) kbdMouseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1536{
1537 KBDState *pThis = RT_FROM_MEMBER(pInterface, KBDState, Mouse.IBase);
1538 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Mouse.IBase);
1539 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSEPORT, &pThis->Mouse.IPort);
1540 return NULL;
1541}
1542
1543
1544/* -=-=-=-=-=- Mouse: IMousePort -=-=-=-=-=- */
1545
1546/**
1547 * @interface_method_impl{PDMIMOUSEPORT, pfnPutEvent}
1548 */
1549static DECLCALLBACK(int) kbdMousePutEvent(PPDMIMOUSEPORT pInterface, int32_t iDeltaX, int32_t iDeltaY,
1550 int32_t iDeltaZ, int32_t iDeltaW, uint32_t fButtonStates)
1551{
1552 KBDState *pThis = RT_FROM_MEMBER(pInterface, KBDState, Mouse.IPort);
1553 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
1554 AssertReleaseRC(rc);
1555
1556 pc_kbd_mouse_event(pThis, iDeltaX, iDeltaY, iDeltaZ, iDeltaW, fButtonStates);
1557
1558 PDMCritSectLeave(&pThis->CritSect);
1559 return VINF_SUCCESS;
1560}
1561
1562/**
1563 * @interface_method_impl{PDMIMOUSEPORT, pfnPutEventAbs}
1564 */
1565static DECLCALLBACK(int) kbdMousePutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t uX, uint32_t uY, int32_t iDeltaZ, int32_t iDeltaW, uint32_t fButtons)
1566{
1567 AssertFailedReturn(VERR_NOT_SUPPORTED);
1568}
1569
1570
1571/* -=-=-=-=-=- real code -=-=-=-=-=- */
1572
1573
1574/**
1575 * Attach command.
1576 *
1577 * This is called to let the device attach to a driver for a specified LUN
1578 * during runtime. This is not called during VM construction, the device
1579 * constructor have to attach to all the available drivers.
1580 *
1581 * This is like plugging in the keyboard or mouse after turning on the PC.
1582 *
1583 * @returns VBox status code.
1584 * @param pDevIns The device instance.
1585 * @param iLUN The logical unit which is being detached.
1586 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1587 * @remark The keyboard controller doesn't support this action, this is just
1588 * implemented to try out the driver<->device structure.
1589 */
1590static DECLCALLBACK(int) kbdAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1591{
1592 int rc;
1593 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1594
1595 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
1596 ("PS/2 device does not support hotplugging\n"),
1597 VERR_INVALID_PARAMETER);
1598
1599 switch (iLUN)
1600 {
1601 /* LUN #0: keyboard */
1602 case 0:
1603 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Keyboard.IBase, &pThis->Keyboard.pDrvBase, "Keyboard Port");
1604 if (RT_SUCCESS(rc))
1605 {
1606 pThis->Keyboard.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Keyboard.pDrvBase, PDMIKEYBOARDCONNECTOR);
1607 if (!pThis->Keyboard.pDrv)
1608 {
1609 AssertLogRelMsgFailed(("LUN #0 doesn't have a keyboard interface! rc=%Rrc\n", rc));
1610 rc = VERR_PDM_MISSING_INTERFACE;
1611 }
1612 }
1613 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1614 {
1615 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
1616 rc = VINF_SUCCESS;
1617 }
1618 else
1619 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
1620 break;
1621
1622 /* LUN #1: aux/mouse */
1623 case 1:
1624 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Mouse.IBase, &pThis->Mouse.pDrvBase, "Aux (Mouse) Port");
1625 if (RT_SUCCESS(rc))
1626 {
1627 pThis->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
1628 if (!pThis->Mouse.pDrv)
1629 {
1630 AssertLogRelMsgFailed(("LUN #1 doesn't have a mouse interface! rc=%Rrc\n", rc));
1631 rc = VERR_PDM_MISSING_INTERFACE;
1632 }
1633 }
1634 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1635 {
1636 Log(("%s/%d: warning: no driver attached to LUN #1!\n", pDevIns->pReg->szName, pDevIns->iInstance));
1637 rc = VINF_SUCCESS;
1638 }
1639 else
1640 AssertLogRelMsgFailed(("Failed to attach LUN #1! rc=%Rrc\n", rc));
1641 break;
1642
1643 default:
1644 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1645 return VERR_PDM_NO_SUCH_LUN;
1646 }
1647
1648 return rc;
1649}
1650
1651
1652/**
1653 * Detach notification.
1654 *
1655 * This is called when a driver is detaching itself from a LUN of the device.
1656 * The device should adjust it's state to reflect this.
1657 *
1658 * This is like unplugging the network cable to use it for the laptop or
1659 * something while the PC is still running.
1660 *
1661 * @param pDevIns The device instance.
1662 * @param iLUN The logical unit which is being detached.
1663 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
1664 * @remark The keyboard controller doesn't support this action, this is just
1665 * implemented to try out the driver<->device structure.
1666 */
1667static DECLCALLBACK(void) kbdDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
1668{
1669#if 0
1670 /*
1671 * Reset the interfaces and update the controller state.
1672 */
1673 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1674 switch (iLUN)
1675 {
1676 /* LUN #0: keyboard */
1677 case 0:
1678 pThis->Keyboard.pDrv = NULL;
1679 pThis->Keyboard.pDrvBase = NULL;
1680 break;
1681
1682 /* LUN #1: aux/mouse */
1683 case 1:
1684 pThis->Mouse.pDrv = NULL;
1685 pThis->Mouse.pDrvBase = NULL;
1686 break;
1687
1688 default:
1689 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1690 break;
1691 }
1692#endif
1693}
1694
1695
1696/**
1697 * @copydoc FNPDMDEVRELOCATE
1698 */
1699static DECLCALLBACK(void) kdbRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1700{
1701 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1702 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1703}
1704
1705
1706/**
1707 * Destruct a device instance for a VM.
1708 *
1709 * @returns VBox status.
1710 * @param pDevIns The device instance data.
1711 */
1712static DECLCALLBACK(int) kbdDestruct(PPDMDEVINS pDevIns)
1713{
1714 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1715 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1716
1717 PDMR3CritSectDelete(&pThis->CritSect);
1718
1719 return VINF_SUCCESS;
1720}
1721
1722
1723/**
1724 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1725 */
1726static DECLCALLBACK(int) kbdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1727{
1728 KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
1729 int rc;
1730 bool fGCEnabled;
1731 bool fR0Enabled;
1732 Assert(iInstance == 0);
1733
1734 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1735
1736 /*
1737 * Validate and read the configuration.
1738 */
1739 if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0R0Enabled\0"))
1740 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1741 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
1742 if (RT_FAILURE(rc))
1743 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"GCEnabled\" from the config"));
1744 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
1745 if (RT_FAILURE(rc))
1746 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"R0Enabled\" from the config"));
1747 Log(("pckbd: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
1748
1749
1750 /*
1751 * Initialize the interfaces.
1752 */
1753 pThis->pDevInsR3 = pDevIns;
1754 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1755 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1756 pThis->Keyboard.IBase.pfnQueryInterface = kbdKeyboardQueryInterface;
1757 pThis->Keyboard.IPort.pfnPutEvent = kbdKeyboardPutEvent;
1758
1759 pThis->Mouse.IBase.pfnQueryInterface = kbdMouseQueryInterface;
1760 pThis->Mouse.IPort.pfnPutEvent = kbdMousePutEvent;
1761 pThis->Mouse.IPort.pfnPutEventAbs = kbdMousePutEventAbs;
1762
1763 /*
1764 * Initialize the critical section.
1765 */
1766 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "PS2KM#%d", iInstance);
1767 if (RT_FAILURE(rc))
1768 return rc;
1769
1770 /*
1771 * Register I/O ports, save state, keyboard event handler and mouse event handlers.
1772 */
1773 rc = PDMDevHlpIOPortRegister(pDevIns, 0x60, 1, NULL, kbdIOPortDataWrite, kbdIOPortDataRead, NULL, NULL, "PC Keyboard - Data");
1774 if (RT_FAILURE(rc))
1775 return rc;
1776 rc = PDMDevHlpIOPortRegister(pDevIns, 0x64, 1, NULL, kbdIOPortCommandWrite, kbdIOPortStatusRead, NULL, NULL, "PC Keyboard - Command / Status");
1777 if (RT_FAILURE(rc))
1778 return rc;
1779 if (fGCEnabled)
1780 {
1781 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
1782 if (RT_FAILURE(rc))
1783 return rc;
1784 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
1785 if (RT_FAILURE(rc))
1786 return rc;
1787 }
1788 if (fR0Enabled)
1789 {
1790 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
1791 if (RT_FAILURE(rc))
1792 return rc;
1793 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
1794 if (RT_FAILURE(rc))
1795 return rc;
1796 }
1797 rc = PDMDevHlpSSMRegister(pDevIns, PCKBD_SAVED_STATE_VERSION, sizeof(*pThis), kbdSaveExec, kbdLoadExec);
1798 if (RT_FAILURE(rc))
1799 return rc;
1800
1801 /*
1802 * Attach to the keyboard and mouse drivers.
1803 */
1804 rc = kbdAttach(pDevIns, 0 /* keyboard LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
1805 if (RT_FAILURE(rc))
1806 return rc;
1807 rc = kbdAttach(pDevIns, 1 /* aux/mouse LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
1808 if (RT_FAILURE(rc))
1809 return rc;
1810
1811 /*
1812 * Initialize the device state.
1813 */
1814 kbdReset(pDevIns);
1815
1816 return VINF_SUCCESS;
1817}
1818
1819
1820/**
1821 * The device registration structure.
1822 */
1823const PDMDEVREG g_DevicePS2KeyboardMouse =
1824{
1825 /* u32Version */
1826 PDM_DEVREG_VERSION,
1827 /* szName */
1828 "pckbd",
1829 /* szRCMod */
1830 "VBoxDDGC.gc",
1831 /* szR0Mod */
1832 "VBoxDDR0.r0",
1833 /* pszDescription */
1834 "PS/2 Keyboard and Mouse device. Emulates both the keyboard, mouse and the keyboard controller. "
1835 "LUN #0 is the keyboard connector. "
1836 "LUN #1 is the aux/mouse connector.",
1837 /* fFlags */
1838 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,
1839 /* fClass */
1840 PDM_DEVREG_CLASS_INPUT,
1841 /* cMaxInstances */
1842 1,
1843 /* cbInstance */
1844 sizeof(KBDState),
1845 /* pfnConstruct */
1846 kbdConstruct,
1847 /* pfnDestruct */
1848 kbdDestruct,
1849 /* pfnRelocate */
1850 kdbRelocate,
1851 /* pfnIOCtl */
1852 NULL,
1853 /* pfnPowerOn */
1854 NULL,
1855 /* pfnReset */
1856 kbdReset,
1857 /* pfnSuspend */
1858 NULL,
1859 /* pfnResume */
1860 NULL,
1861 /* pfnAttach */
1862 kbdAttach,
1863 /* pfnDetach */
1864 kbdDetach,
1865 /* pfnQueryInterface. */
1866 NULL,
1867 /* pfnInitComplete */
1868 NULL,
1869 /* pfnPowerOff */
1870 NULL,
1871 /* pfnSoftReset */
1872 NULL,
1873 /* u32VersionEnd */
1874 PDM_DEVREG_VERSION
1875};
1876
1877#endif /* IN_RING3 */
1878#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1879
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