VirtualBox

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

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

Devices, Main, pdmifs.h: changed the Main-to-Device absolute event protocol to include button and wheel events; some cleanups and fixes

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