VirtualBox

source: vbox/trunk/src/VBox/Devices/Input/pckbd.c@ 452

Last change on this file since 452 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
File size: 52.3 KB
Line 
1#ifdef VBOX
2/** @file
3 *
4 * VBox input devices:
5 * PS/2 keyboard & mouse controller device
6 */
7
8/*
9 * Copyright (C) 2006 InnoTek Systemberatung GmbH
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License as published by the Free Software Foundation,
15 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
16 * distribution. VirtualBox OSE is distributed in the hope that it will
17 * be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * If you received this file as part of a commercial VirtualBox
20 * distribution, then only the terms of your commercial VirtualBox
21 * license agreement apply instead of the previous paragraph.
22 *
23 * --------------------------------------------------------------------
24 *
25 * This code is based on:
26 *
27 * QEMU PC keyboard emulation (revision 1.12)
28 *
29 * Copyright (c) 2003 Fabrice Bellard
30 *
31 * Permission is hereby granted, free of charge, to any person obtaining a copy
32 * of this software and associated documentation files (the "Software"), to deal
33 * in the Software without restriction, including without limitation the rights
34 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35 * copies of the Software, and to permit persons to whom the Software is
36 * furnished to do so, subject to the following conditions:
37 *
38 * The above copyright notice and this permission notice shall be included in
39 * all copies or substantial portions of the Software.
40 *
41 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
44 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
47 * THE SOFTWARE.
48 *
49 */
50
51
52/*******************************************************************************
53* Header Files *
54*******************************************************************************/
55#define LOG_GROUP LOG_GROUP_DEV_KBD
56#include "vl_vbox.h"
57#include <VBox/pdm.h>
58#include <VBox/err.h>
59
60#include <VBox/log.h>
61#include <iprt/assert.h>
62
63#include "Builtins.h"
64
65#define PCKBD_SAVED_STATE_VERSION 2
66
67
68/*******************************************************************************
69* Internal Functions *
70*******************************************************************************/
71PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
72PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
73PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
74PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
75
76#endif /* VBOX */
77
78
79#ifndef VBOX
80#include "vl.h"
81#endif
82
83#ifdef VBOX
84/* debug PC keyboard */
85#define DEBUG_KBD
86
87/* debug PC keyboard : only mouse */
88#define DEBUG_MOUSE
89#endif /* VBOX */
90
91/* Keyboard Controller Commands */
92#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
93#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
94#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
95#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
96#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
97#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
98#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
99#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
100#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
101#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
102#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
103#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
104#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
105#define KBD_CCMD_WRITE_OBUF 0xD2
106#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
107 initiated by the auxiliary device */
108#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
109#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
110#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
111#define KBD_CCMD_RESET 0xFE
112
113/* Keyboard Commands */
114#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
115#define KBD_CMD_ECHO 0xEE
116#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
117#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
118#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
119#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */
120#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */
121#define KBD_CMD_RESET 0xFF /* Reset */
122
123/* Keyboard Replies */
124#define KBD_REPLY_POR 0xAA /* Power on reset */
125#define KBD_REPLY_ACK 0xFA /* Command ACK */
126#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
127
128/* Status Register Bits */
129#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
130#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
131#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
132#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
133#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
134#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
135#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
136#define KBD_STAT_PERR 0x80 /* Parity error */
137
138/* Controller Mode Register Bits */
139#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
140#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
141#define KBD_MODE_SYS 0x04 /* The system flag (?) */
142#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
143#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
144#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
145#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
146#define KBD_MODE_RFU 0x80
147
148/* Mouse Commands */
149#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
150#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
151#define AUX_SET_RES 0xE8 /* Set resolution */
152#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
153#define AUX_SET_STREAM 0xEA /* Set stream mode */
154#define AUX_POLL 0xEB /* Poll */
155#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
156#define AUX_SET_WRAP 0xEE /* Set wrap mode */
157#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
158#define AUX_GET_TYPE 0xF2 /* Get type */
159#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
160#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
161#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
162#define AUX_SET_DEFAULT 0xF6
163#define AUX_RESET 0xFF /* Reset aux device */
164#define AUX_ACK 0xFA /* Command byte ACK. */
165
166#define MOUSE_STATUS_REMOTE 0x40
167#define MOUSE_STATUS_ENABLED 0x20
168#define MOUSE_STATUS_SCALE21 0x10
169
170#define KBD_QUEUE_SIZE 256
171
172typedef struct {
173#ifndef VBOX
174 uint8_t aux[KBD_QUEUE_SIZE];
175#endif /* !VBOX */
176 uint8_t data[KBD_QUEUE_SIZE];
177 int rptr, wptr, count;
178} KBDQueue;
179
180#ifdef VBOX
181
182#define MOUSE_CMD_QUEUE_SIZE 8
183
184typedef struct {
185 uint8_t data[MOUSE_CMD_QUEUE_SIZE];
186 int rptr, wptr, count;
187} MouseCmdQueue;
188
189
190#define MOUSE_EVENT_QUEUE_SIZE 256
191
192typedef struct {
193 uint8_t data[MOUSE_EVENT_QUEUE_SIZE];
194 int rptr, wptr, count;
195} MouseEventQueue;
196
197#endif /* VBOX */
198
199typedef struct KBDState {
200 KBDQueue queue;
201#ifdef VBOX
202 MouseCmdQueue mouse_command_queue;
203 MouseEventQueue mouse_event_queue;
204#endif /* VBOX */
205 uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
206 uint8_t status;
207 uint8_t mode;
208 /* keyboard state */
209 int32_t kbd_write_cmd;
210 int32_t scan_enabled;
211 /* mouse state */
212 int32_t mouse_write_cmd;
213 uint8_t mouse_status;
214 uint8_t mouse_resolution;
215 uint8_t mouse_sample_rate;
216 uint8_t mouse_wrap;
217 uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
218 uint8_t mouse_detect_state;
219 int32_t mouse_dx; /* current values, needed for 'poll' mode */
220 int32_t mouse_dy;
221 int32_t mouse_dz;
222 uint8_t mouse_buttons;
223
224#ifdef VBOX
225 /** Pointer to the device instance. */
226 PPDMDEVINSHC pDevInsHC;
227 /** Pointer to the device instance. */
228 PPDMDEVINSGC pDevInsGC;
229 /**
230 * Keyboard port - LUN#0.
231 */
232 struct
233 {
234 /** The base interface for the keyboard port. */
235 PDMIBASE Base;
236 /** The keyboard port base interface. */
237 PDMIKEYBOARDPORT Port;
238
239 /** The base interface of the attached keyboard driver. */
240 HCPTRTYPE(PPDMIBASE) pDrvBase;
241 /** The keyboard interface of the attached keyboard driver. */
242 HCPTRTYPE(PPDMIKEYBOARDCONNECTOR) pDrv;
243 } Keyboard;
244
245 /**
246 * Mouse port - LUN#1.
247 */
248 struct
249 {
250 /** The base interface for the mouse port. */
251 PDMIBASE Base;
252 /** The mouse port base interface. */
253 PDMIMOUSEPORT Port;
254
255 /** The base interface of the attached mouse driver. */
256 HCPTRTYPE(PPDMIBASE) pDrvBase;
257 /** The mouse interface of the attached mouse driver. */
258 HCPTRTYPE(PPDMIMOUSECONNECTOR) pDrv;
259 } Mouse;
260#endif
261} KBDState;
262
263#ifndef VBOX
264KBDState kbd_state;
265#endif
266
267/* update irq and KBD_STAT_[MOUSE_]OBF */
268/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
269 incorrect, but it avoids having to simulate exact delays */
270static void kbd_update_irq(KBDState *s)
271{
272 KBDQueue *q = &s->queue;
273#ifdef VBOX
274 MouseCmdQueue *mcq = &s->mouse_command_queue;
275 MouseEventQueue *meq = &s->mouse_event_queue;
276#endif /* VBOX */
277 int irq12_level, irq1_level;
278
279 irq1_level = 0;
280 irq12_level = 0;
281 s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
282#ifdef VBOX
283 if (q->count != 0)
284 {
285 s->status |= KBD_STAT_OBF;
286 if ((s->mode & KBD_MODE_KBD_INT) && !(s->mode & KBD_MODE_DISABLE_KBD))
287 irq1_level = 1;
288 }
289 else if (mcq->count != 0 || meq->count != 0)
290 {
291 s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
292 if (s->mode & KBD_MODE_MOUSE_INT)
293 irq12_level = 1;
294 }
295#else /* !VBOX */
296 if (q->count != 0) {
297 s->status |= KBD_STAT_OBF;
298 if (q->aux[q->rptr]) {
299 s->status |= KBD_STAT_MOUSE_OBF;
300 if (s->mode & KBD_MODE_MOUSE_INT)
301 irq12_level = 1;
302 } else {
303 if ((s->mode & KBD_MODE_KBD_INT) &&
304 !(s->mode & KBD_MODE_DISABLE_KBD))
305 irq1_level = 1;
306 }
307 }
308#endif /* !VBOX */
309#ifndef VBOX
310 pic_set_irq(1, irq1_level);
311 pic_set_irq(12, irq12_level);
312#else /* VBOX */
313 PDMDevHlpISASetIrq(CTXSUFF(s->pDevIns), 1, irq1_level);
314 PDMDevHlpISASetIrq(CTXSUFF(s->pDevIns), 12, irq12_level);
315#endif /* VBOX */
316}
317
318static void kbd_queue(KBDState *s, int b, int aux)
319{
320 KBDQueue *q = &s->queue;
321#ifdef VBOX
322 MouseCmdQueue *mcq = &s->mouse_command_queue;
323 MouseEventQueue *meq = &s->mouse_event_queue;
324#endif /* VBOX */
325
326#if defined(DEBUG_MOUSE) || defined(DEBUG_KBD)
327 if (aux)
328 Log(("mouse event: 0x%02x\n", b));
329#ifdef DEBUG_KBD
330 else
331 Log(("kbd event: 0x%02x\n", b));
332#endif
333#endif
334#ifdef VBOX
335 switch (aux)
336 {
337 case 0: /* keyboard */
338 if (q->count >= KBD_QUEUE_SIZE)
339 return;
340 q->data[q->wptr] = b;
341 if (++q->wptr == KBD_QUEUE_SIZE)
342 q->wptr = 0;
343 q->count++;
344 break;
345 case 1: /* mouse command response */
346 if (mcq->count >= MOUSE_CMD_QUEUE_SIZE)
347 return;
348 mcq->data[mcq->wptr] = b;
349 if (++mcq->wptr == MOUSE_CMD_QUEUE_SIZE)
350 mcq->wptr = 0;
351 mcq->count++;
352 break;
353 case 2: /* mouse event data */
354 if (meq->count >= MOUSE_EVENT_QUEUE_SIZE)
355 return;
356 meq->data[meq->wptr] = b;
357 if (++meq->wptr == MOUSE_EVENT_QUEUE_SIZE)
358 meq->wptr = 0;
359 meq->count++;
360 break;
361 default:
362 AssertMsgFailed(("aux=%d\n", aux));
363 }
364#else /* !VBOX */
365 if (q->count >= KBD_QUEUE_SIZE)
366 return;
367 q->aux[q->wptr] = aux;
368 q->data[q->wptr] = b;
369 if (++q->wptr == KBD_QUEUE_SIZE)
370 q->wptr = 0;
371 q->count++;
372#endif /* !VBOX */
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 = opaque;
380 kbd_queue(s, keycode, 0);
381}
382#endif /* IN_RING3 */
383
384static uint32_t kbd_read_status(void *opaque, uint32_t addr)
385{
386 KBDState *s = opaque;
387 int val;
388 val = s->status;
389#if defined(DEBUG_KBD)
390 Log(("kbd: read status=0x%02x\n", val));
391#endif
392 return val;
393}
394
395#ifndef VBOX
396static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
397{
398#else /* VBOX (we need VMReset return code passed back) */
399static int kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
400{
401 int rc = VINF_SUCCESS;
402#endif /* VBOX */
403 KBDState *s = opaque;
404
405#ifdef DEBUG_KBD
406 Log(("kbd: write cmd=0x%02x\n", val));
407#endif
408 switch(val) {
409 case KBD_CCMD_READ_MODE:
410 kbd_queue(s, s->mode, 0);
411 break;
412 case KBD_CCMD_WRITE_MODE:
413 case KBD_CCMD_WRITE_OBUF:
414 case KBD_CCMD_WRITE_AUX_OBUF:
415 case KBD_CCMD_WRITE_MOUSE:
416 case KBD_CCMD_WRITE_OUTPORT:
417 s->write_cmd = val;
418 break;
419 case KBD_CCMD_MOUSE_DISABLE:
420 s->mode |= KBD_MODE_DISABLE_MOUSE;
421 break;
422 case KBD_CCMD_MOUSE_ENABLE:
423 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
424 break;
425 case KBD_CCMD_TEST_MOUSE:
426 kbd_queue(s, 0x00, 0);
427 break;
428 case KBD_CCMD_SELF_TEST:
429 s->status |= KBD_STAT_SELFTEST;
430 kbd_queue(s, 0x55, 0);
431 break;
432 case KBD_CCMD_KBD_TEST:
433 kbd_queue(s, 0x00, 0);
434 break;
435 case KBD_CCMD_KBD_DISABLE:
436 s->mode |= KBD_MODE_DISABLE_KBD;
437 kbd_update_irq(s);
438 break;
439 case KBD_CCMD_KBD_ENABLE:
440 s->mode &= ~KBD_MODE_DISABLE_KBD;
441 kbd_update_irq(s);
442 break;
443 case KBD_CCMD_READ_INPORT:
444 kbd_queue(s, 0x00, 0);
445 break;
446 case KBD_CCMD_READ_OUTPORT:
447 /* XXX: check that */
448#ifdef TARGET_I386
449# ifndef VBOX
450 val = 0x01 | (ioport_get_a20() << 1);
451# else /* VBOX */
452 val = 0x01 | (PDMDevHlpA20IsEnabled(CTXSUFF(s->pDevIns)) << 1);
453# endif /* VBOX */
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 VBOX
466 ioport_set_a20(1);
467#else /* VBOX */
468# ifndef IN_RING3
469 if (!PDMDevHlpA20IsEnabled(CTXSUFF(s->pDevIns)))
470 rc = VINF_IOM_HC_IOPORT_WRITE;
471# else /* IN_RING3 */
472 PDMDevHlpA20Set(CTXSUFF(s->pDevIns), true);
473# endif /* IN_RING3 */
474#endif /* VBOX */
475 break;
476 case KBD_CCMD_DISABLE_A20:
477#ifndef VBOX
478 ioport_set_a20(0);
479#else /* VBOX */
480# ifndef IN_RING3
481 if (PDMDevHlpA20IsEnabled(CTXSUFF(s->pDevIns)))
482 rc = VINF_IOM_HC_IOPORT_WRITE;
483# else /* IN_RING3 */
484 PDMDevHlpA20Set(CTXSUFF(s->pDevIns), false);
485# endif /* !IN_RING3 */
486#endif /* VBOX */
487 break;
488#endif
489 case KBD_CCMD_RESET:
490#ifndef VBOX
491 qemu_system_reset_request();
492#else /* VBOX */
493# ifndef IN_RING3
494 rc = VINF_IOM_HC_IOPORT_WRITE;
495# else /* IN_RING3 */
496 rc = PDMDevHlpVMReset(CTXSUFF(s->pDevIns));
497# endif /* !IN_RING3 */
498#endif /* VBOX */
499 break;
500 case 0xff:
501 /* ignore that - I don't know what is its use */
502 break;
503 default:
504 Log(("kbd: unsupported keyboard cmd=0x%02x\n", val));
505 break;
506 }
507#ifdef VBOX
508 return rc;
509#endif
510}
511
512static uint32_t kbd_read_data(void *opaque, uint32_t addr)
513{
514 KBDState *s = opaque;
515 KBDQueue *q;
516#ifdef VBOX
517 MouseCmdQueue *mcq;
518 MouseEventQueue *meq;
519#endif /* VBOX */
520 int val, index, aux;
521
522 q = &s->queue;
523#ifdef VBOX
524 mcq = &s->mouse_command_queue;
525 meq = &s->mouse_event_queue;
526 if (q->count == 0 && mcq->count == 0 && meq->count == 0) {
527#else /* !VBOX */
528 if (q->count == 0) {
529#endif /* !VBOX */
530 /* NOTE: if no data left, we return the last keyboard one
531 (needed for EMM386) */
532 /* XXX: need a timer to do things correctly */
533 index = q->rptr - 1;
534 if (index < 0)
535 index = KBD_QUEUE_SIZE - 1;
536 val = q->data[index];
537 } else {
538#ifdef VBOX
539 aux = (s->status & KBD_STAT_MOUSE_OBF);
540 if (!aux)
541 {
542 val = q->data[q->rptr];
543 if (++q->rptr == KBD_QUEUE_SIZE)
544 q->rptr = 0;
545 q->count--;
546 }
547 else
548 {
549 if (mcq->count)
550 {
551 val = mcq->data[mcq->rptr];
552 if (++mcq->rptr == MOUSE_CMD_QUEUE_SIZE)
553 mcq->rptr = 0;
554 mcq->count--;
555 }
556 else
557 {
558 val = meq->data[meq->rptr];
559 if (++meq->rptr == MOUSE_EVENT_QUEUE_SIZE)
560 meq->rptr = 0;
561 meq->count--;
562 }
563 }
564#else /* !VBOX */
565 aux = q->aux[q->rptr];
566 val = q->data[q->rptr];
567 if (++q->rptr == KBD_QUEUE_SIZE)
568 q->rptr = 0;
569 q->count--;
570#endif /* !VBOX */
571 /* reading deasserts IRQ */
572#ifndef VBOX
573 if (aux)
574 pic_set_irq(12, 0);
575 else
576 pic_set_irq(1, 0);
577#else /* VBOX */
578 if (aux)
579 PDMDevHlpISASetIrq(CTXSUFF(s->pDevIns), 12, 0);
580 else
581 PDMDevHlpISASetIrq(CTXSUFF(s->pDevIns), 1, 0);
582#endif /* VBOX */
583 }
584 /* reassert IRQs if data left */
585 kbd_update_irq(s);
586#ifdef DEBUG_KBD
587 Log(("kbd: read data=0x%02x\n", val));
588#endif
589 return val;
590}
591
592static void kbd_reset_keyboard(KBDState *s)
593{
594 s->scan_enabled = 1;
595}
596
597#ifndef VBOX
598static void kbd_write_keyboard(KBDState *s, int val)
599#else
600static int kbd_write_keyboard(KBDState *s, int val)
601#endif
602{
603 switch(s->kbd_write_cmd) {
604 default:
605 case -1:
606 switch(val) {
607 case 0x00:
608 kbd_queue(s, KBD_REPLY_ACK, 0);
609 break;
610 case 0x05:
611 kbd_queue(s, KBD_REPLY_RESEND, 0);
612 break;
613 case KBD_CMD_GET_ID:
614 kbd_queue(s, KBD_REPLY_ACK, 0);
615 kbd_queue(s, 0xab, 0);
616 kbd_queue(s, 0x83, 0);
617 break;
618 case KBD_CMD_ECHO:
619 kbd_queue(s, KBD_CMD_ECHO, 0);
620 break;
621 case KBD_CMD_ENABLE:
622 s->scan_enabled = 1;
623 kbd_queue(s, KBD_REPLY_ACK, 0);
624 break;
625 case KBD_CMD_SET_LEDS:
626 case KBD_CMD_SET_RATE:
627 s->kbd_write_cmd = val;
628 kbd_queue(s, KBD_REPLY_ACK, 0);
629 break;
630 case KBD_CMD_RESET_DISABLE:
631 kbd_reset_keyboard(s);
632 s->scan_enabled = 0;
633 kbd_queue(s, KBD_REPLY_ACK, 0);
634 break;
635 case KBD_CMD_RESET_ENABLE:
636 kbd_reset_keyboard(s);
637 s->scan_enabled = 1;
638 kbd_queue(s, KBD_REPLY_ACK, 0);
639 break;
640 case KBD_CMD_RESET:
641 kbd_reset_keyboard(s);
642 kbd_queue(s, KBD_REPLY_ACK, 0);
643 kbd_queue(s, KBD_REPLY_POR, 0);
644 break;
645 default:
646 kbd_queue(s, KBD_REPLY_ACK, 0);
647 break;
648 }
649 break;
650 case KBD_CMD_SET_LEDS:
651 {
652#ifdef IN_RING3
653 PDMKEYBLEDS enmLeds = 0
654 | ((val & 0x01) ? PDMKEYBLEDS_SCROLLLOCK : 0)
655 | ((val & 0x02) ? PDMKEYBLEDS_NUMLOCK : 0)
656 | ((val & 0x04) ? PDMKEYBLEDS_CAPSLOCK : 0)
657 ;
658 s->Keyboard.pDrv->pfnLedStatusChange(s->Keyboard.pDrv, enmLeds);
659#else
660 return VINF_IOM_HC_IOPORT_WRITE;
661#endif
662 kbd_queue(s, KBD_REPLY_ACK, 0);
663 s->kbd_write_cmd = -1;
664 }
665 break;
666 case KBD_CMD_SET_RATE:
667 kbd_queue(s, KBD_REPLY_ACK, 0);
668 s->kbd_write_cmd = -1;
669 break;
670 }
671
672 return VINF_SUCCESS;
673}
674
675#ifdef VBOX
676static void kbd_mouse_send_packet(KBDState *s, bool fToCmdQueue)
677#else /* !VBOX */
678static void kbd_mouse_send_packet(KBDState *s)
679#endif /* !VBOX */
680{
681#ifdef VBOX
682 int aux = fToCmdQueue ? 1 : 2;
683#endif /* VBOX */
684 unsigned int b;
685 int dx1, dy1, dz1;
686
687 dx1 = s->mouse_dx;
688 dy1 = s->mouse_dy;
689 dz1 = s->mouse_dz;
690 /* XXX: increase range to 8 bits ? */
691 if (dx1 > 127)
692 dx1 = 127;
693 else if (dx1 < -127)
694 dx1 = -127;
695 if (dy1 > 127)
696 dy1 = 127;
697 else if (dy1 < -127)
698 dy1 = -127;
699 b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
700#ifdef VBOX
701 kbd_queue(s, b, aux);
702 kbd_queue(s, dx1 & 0xff, aux);
703 kbd_queue(s, dy1 & 0xff, aux);
704#else /* !VBOX */
705 kbd_queue(s, b, 1);
706 kbd_queue(s, dx1 & 0xff, 1);
707 kbd_queue(s, dy1 & 0xff, 1);
708#endif /* !VBOX */
709 /* extra byte for IMPS/2 or IMEX */
710 switch(s->mouse_type) {
711 default:
712 break;
713 case 3:
714 if (dz1 > 127)
715 dz1 = 127;
716 else if (dz1 < -127)
717 dz1 = -127;
718#ifdef VBOX
719 kbd_queue(s, dz1 & 0xff, aux);
720#else /* !VBOX */
721 kbd_queue(s, dz1 & 0xff, 1);
722#endif /* !VBOX */
723 break;
724 case 4:
725 if (dz1 > 7)
726 dz1 = 7;
727 else if (dz1 < -7)
728 dz1 = -7;
729 b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
730#ifdef VBOX
731 kbd_queue(s, b, aux);
732#else /* !VBOX */
733 kbd_queue(s, b, 1);
734#endif /* !VBOX */
735 break;
736 }
737
738 /* update deltas */
739 s->mouse_dx -= dx1;
740 s->mouse_dy -= dy1;
741 s->mouse_dz -= dz1;
742}
743
744#ifdef IN_RING3
745static void pc_kbd_mouse_event(void *opaque,
746 int dx, int dy, int dz, int buttons_state)
747{
748 KBDState *s = opaque;
749
750 /* check if deltas are recorded when disabled */
751 if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
752 return;
753
754 s->mouse_dx += dx;
755 s->mouse_dy -= dy;
756 s->mouse_dz += dz;
757 /* XXX: SDL sometimes generates nul events: we delete them */
758 if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
759 s->mouse_buttons == buttons_state)
760 return;
761 s->mouse_buttons = buttons_state;
762
763#ifdef VBOX
764 if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
765 (s->mouse_event_queue.count < (MOUSE_EVENT_QUEUE_SIZE - 4))) {
766 for(;;) {
767 /* if not remote, send event. Multiple events are sent if
768 too big deltas */
769 kbd_mouse_send_packet(s, false);
770 if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
771 break;
772 }
773 }
774#else /* !VBOX */
775 if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
776 (s->queue.count < (KBD_QUEUE_SIZE - 16))) {
777 for(;;) {
778 /* if not remote, send event. Multiple events are sent if
779 too big deltas */
780 kbd_mouse_send_packet(s);
781 if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
782 break;
783 }
784 }
785#endif /* !VBOX */
786}
787#endif /* IN_RING3 */
788
789static void kbd_write_mouse(KBDState *s, int val)
790{
791#ifdef DEBUG_MOUSE
792 Log(("kbd: write mouse 0x%02x\n", val));
793#endif
794#ifdef VBOX
795 /* Flush the mouse command response queue. */
796 s->mouse_command_queue.count = 0;
797 s->mouse_command_queue.rptr = 0;
798 s->mouse_command_queue.wptr = 0;
799#endif /* VBOX */
800 switch(s->mouse_write_cmd) {
801 default:
802 case -1:
803 /* mouse command */
804 if (s->mouse_wrap) {
805 if (val == AUX_RESET_WRAP) {
806 s->mouse_wrap = 0;
807 kbd_queue(s, AUX_ACK, 1);
808 return;
809 } else if (val != AUX_RESET) {
810 kbd_queue(s, val, 1);
811 return;
812 }
813 }
814 switch(val) {
815 case AUX_SET_SCALE11:
816 s->mouse_status &= ~MOUSE_STATUS_SCALE21;
817 kbd_queue(s, AUX_ACK, 1);
818 break;
819 case AUX_SET_SCALE21:
820 s->mouse_status |= MOUSE_STATUS_SCALE21;
821 kbd_queue(s, AUX_ACK, 1);
822 break;
823 case AUX_SET_STREAM:
824 s->mouse_status &= ~MOUSE_STATUS_REMOTE;
825 kbd_queue(s, AUX_ACK, 1);
826 break;
827 case AUX_SET_WRAP:
828 s->mouse_wrap = 1;
829 kbd_queue(s, AUX_ACK, 1);
830 break;
831 case AUX_SET_REMOTE:
832 s->mouse_status |= MOUSE_STATUS_REMOTE;
833 kbd_queue(s, AUX_ACK, 1);
834 break;
835 case AUX_GET_TYPE:
836 kbd_queue(s, AUX_ACK, 1);
837 kbd_queue(s, s->mouse_type, 1);
838 break;
839 case AUX_SET_RES:
840 case AUX_SET_SAMPLE:
841 s->mouse_write_cmd = val;
842 kbd_queue(s, AUX_ACK, 1);
843 break;
844 case AUX_GET_SCALE:
845 kbd_queue(s, AUX_ACK, 1);
846 kbd_queue(s, s->mouse_status, 1);
847 kbd_queue(s, s->mouse_resolution, 1);
848 kbd_queue(s, s->mouse_sample_rate, 1);
849 break;
850 case AUX_POLL:
851 kbd_queue(s, AUX_ACK, 1);
852#ifdef VBOX
853 kbd_mouse_send_packet(s, true);
854#else /* !VBOX */
855 kbd_mouse_send_packet(s);
856#endif /* !VBOX */
857 break;
858 case AUX_ENABLE_DEV:
859 s->mouse_status |= MOUSE_STATUS_ENABLED;
860 kbd_queue(s, AUX_ACK, 1);
861 break;
862 case AUX_DISABLE_DEV:
863 s->mouse_status &= ~MOUSE_STATUS_ENABLED;
864 kbd_queue(s, AUX_ACK, 1);
865#ifdef VBOX
866 /* Flush the mouse events queue. */
867 s->mouse_event_queue.count = 0;
868 s->mouse_event_queue.rptr = 0;
869 s->mouse_event_queue.wptr = 0;
870#endif /* VBOX */
871 break;
872 case AUX_SET_DEFAULT:
873 s->mouse_sample_rate = 100;
874 s->mouse_resolution = 2;
875 s->mouse_status = 0;
876 kbd_queue(s, AUX_ACK, 1);
877 break;
878 case AUX_RESET:
879 s->mouse_sample_rate = 100;
880 s->mouse_resolution = 2;
881 s->mouse_status = 0;
882 s->mouse_type = 0;
883 kbd_queue(s, AUX_ACK, 1);
884 kbd_queue(s, 0xaa, 1);
885 kbd_queue(s, s->mouse_type, 1);
886#ifdef VBOX
887 /* Flush the mouse events queue. */
888 s->mouse_event_queue.count = 0;
889 s->mouse_event_queue.rptr = 0;
890 s->mouse_event_queue.wptr = 0;
891#endif /* VBOX */
892 break;
893 default:
894 break;
895 }
896 break;
897 case AUX_SET_SAMPLE:
898 s->mouse_sample_rate = val;
899 /* detect IMPS/2 or IMEX */
900 switch(s->mouse_detect_state) {
901 default:
902 case 0:
903 if (val == 200)
904 s->mouse_detect_state = 1;
905 break;
906 case 1:
907 if (val == 100)
908 s->mouse_detect_state = 2;
909 else if (val == 200)
910 s->mouse_detect_state = 3;
911 else
912 s->mouse_detect_state = 0;
913 break;
914 case 2:
915 if (val == 80)
916 s->mouse_type = 3; /* IMPS/2 */
917 s->mouse_detect_state = 0;
918 break;
919 case 3:
920 if (val == 80)
921 s->mouse_type = 4; /* IMEX */
922 s->mouse_detect_state = 0;
923 break;
924 }
925 kbd_queue(s, AUX_ACK, 1);
926 s->mouse_write_cmd = -1;
927 break;
928 case AUX_SET_RES:
929 s->mouse_resolution = val;
930 kbd_queue(s, AUX_ACK, 1);
931 s->mouse_write_cmd = -1;
932 break;
933 }
934}
935
936#ifndef VBOX
937static void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
938{
939#else /* VBOX */
940static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
941{
942 int rc = VINF_SUCCESS;
943#endif /* VBOX */
944 KBDState *s = opaque;
945
946#ifdef DEBUG_KBD
947 Log(("kbd: write data=0x%02x\n", val));
948#endif
949
950 switch(s->write_cmd) {
951 case 0:
952 rc = kbd_write_keyboard(s, val);
953 break;
954 case KBD_CCMD_WRITE_MODE:
955 s->mode = val;
956 kbd_update_irq(s);
957 break;
958 case KBD_CCMD_WRITE_OBUF:
959 kbd_queue(s, val, 0);
960 break;
961 case KBD_CCMD_WRITE_AUX_OBUF:
962 kbd_queue(s, val, 1);
963 break;
964 case KBD_CCMD_WRITE_OUTPORT:
965#ifdef TARGET_I386
966# ifndef VBOX
967 ioport_set_a20((val >> 1) & 1);
968# else /* VBOX */
969# ifndef IN_RING3
970 if (PDMDevHlpA20IsEnabled(CTXSUFF(s->pDevIns)) != !!(val & 2))
971 rc = VINF_IOM_HC_IOPORT_WRITE;
972# else /* IN_RING3 */
973 PDMDevHlpA20Set(CTXSUFF(s->pDevIns), !!(val & 2));
974# endif /* !IN_RING3 */
975# endif /* VBOX */
976#endif
977 if (!(val & 1)) {
978#ifndef VBOX
979 qemu_system_reset_request();
980#else /* VBOX */
981# ifndef IN_RING3
982 rc = VINF_IOM_HC_IOPORT_WRITE;
983# else
984 rc = PDMDevHlpVMReset(CTXSUFF(s->pDevIns));
985# endif
986#endif /* VBOX */
987 }
988 break;
989 case KBD_CCMD_WRITE_MOUSE:
990 kbd_write_mouse(s, val);
991 break;
992 default:
993 break;
994 }
995 s->write_cmd = 0;
996
997#ifdef VBOX
998 return rc;
999#endif
1000}
1001
1002#ifdef IN_RING3
1003
1004static void kbd_reset(void *opaque)
1005{
1006 KBDState *s = opaque;
1007 KBDQueue *q;
1008#ifdef VBOX
1009 MouseCmdQueue *mcq;
1010 MouseEventQueue *meq;
1011#endif /* VBOX */
1012
1013 s->kbd_write_cmd = -1;
1014 s->mouse_write_cmd = -1;
1015 s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
1016 s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
1017#ifdef VBOX /* Resetting everything, keyword was not working right on NT4 reboot. */
1018 s->write_cmd = 0;
1019 s->scan_enabled = 0;
1020 s->mouse_status = 0;
1021 s->mouse_resolution = 0;
1022 s->mouse_sample_rate = 0;
1023 s->mouse_wrap = 0;
1024 s->mouse_type = 0;
1025 s->mouse_detect_state = 0;
1026 s->mouse_dx = 0;
1027 s->mouse_dy = 0;
1028 s->mouse_dz = 0;
1029 s->mouse_buttons = 0;
1030#endif
1031 q = &s->queue;
1032 q->rptr = 0;
1033 q->wptr = 0;
1034 q->count = 0;
1035#ifdef VBOX
1036 mcq = &s->mouse_command_queue;
1037 mcq->rptr = 0;
1038 mcq->wptr = 0;
1039 mcq->count = 0;
1040 meq = &s->mouse_event_queue;
1041 meq->rptr = 0;
1042 meq->wptr = 0;
1043 meq->count = 0;
1044#endif /* VBOX */
1045}
1046
1047static void kbd_save(QEMUFile* f, void* opaque)
1048{
1049#ifdef VBOX
1050 uint32_t cItems;
1051 int i;
1052#endif /* VBOX */
1053 KBDState *s = (KBDState*)opaque;
1054
1055 qemu_put_8s(f, &s->write_cmd);
1056 qemu_put_8s(f, &s->status);
1057 qemu_put_8s(f, &s->mode);
1058 qemu_put_be32s(f, &s->kbd_write_cmd);
1059 qemu_put_be32s(f, &s->scan_enabled);
1060 qemu_put_be32s(f, &s->mouse_write_cmd);
1061 qemu_put_8s(f, &s->mouse_status);
1062 qemu_put_8s(f, &s->mouse_resolution);
1063 qemu_put_8s(f, &s->mouse_sample_rate);
1064 qemu_put_8s(f, &s->mouse_wrap);
1065 qemu_put_8s(f, &s->mouse_type);
1066 qemu_put_8s(f, &s->mouse_detect_state);
1067 qemu_put_be32s(f, &s->mouse_dx);
1068 qemu_put_be32s(f, &s->mouse_dy);
1069 qemu_put_be32s(f, &s->mouse_dz);
1070 qemu_put_8s(f, &s->mouse_buttons);
1071
1072#ifdef VBOX
1073 /*
1074 * We have to save the queues too.
1075 */
1076 cItems = s->queue.count;
1077 SSMR3PutU32(f, cItems);
1078 for (i = s->queue.rptr; cItems-- > 0; i = (i + 1) % ELEMENTS(s->queue.data))
1079 SSMR3PutU8(f, s->queue.data[i]);
1080 Log(("kbd_save: %d keyboard queue items stored\n", s->queue.count));
1081
1082 cItems = s->mouse_command_queue.count;
1083 SSMR3PutU32(f, cItems);
1084 for (i = s->mouse_command_queue.rptr; cItems-- > 0; i = (i + 1) % ELEMENTS(s->mouse_command_queue.data))
1085 SSMR3PutU8(f, s->mouse_command_queue.data[i]);
1086 Log(("kbd_save: %d mouse command queue items stored\n", s->mouse_command_queue.count));
1087
1088 cItems = s->mouse_event_queue.count;
1089 SSMR3PutU32(f, cItems);
1090 for (i = s->mouse_event_queue.rptr; cItems-- > 0; i = (i + 1) % ELEMENTS(s->mouse_event_queue.data))
1091 SSMR3PutU8(f, s->mouse_event_queue.data[i]);
1092 Log(("kbd_save: %d mouse event queue items stored\n", s->mouse_event_queue.count));
1093
1094 /* terminator */
1095 SSMR3PutU32(f, ~0);
1096#endif /* VBOX */
1097}
1098
1099static int kbd_load(QEMUFile* f, void* opaque, int version_id)
1100{
1101#ifdef VBOX
1102 uint32_t u32, i;
1103 int rc;
1104#endif
1105 KBDState *s = (KBDState*)opaque;
1106
1107 if (version_id != PCKBD_SAVED_STATE_VERSION)
1108#ifndef VBOX
1109 return -EINVAL;
1110#else
1111 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1112#endif
1113 qemu_get_8s(f, &s->write_cmd);
1114 qemu_get_8s(f, &s->status);
1115 qemu_get_8s(f, &s->mode);
1116 qemu_get_be32s(f, (uint32_t *)&s->kbd_write_cmd);
1117 qemu_get_be32s(f, (uint32_t *)&s->scan_enabled);
1118 qemu_get_be32s(f, (uint32_t *)&s->mouse_write_cmd);
1119 qemu_get_8s(f, &s->mouse_status);
1120 qemu_get_8s(f, &s->mouse_resolution);
1121 qemu_get_8s(f, &s->mouse_sample_rate);
1122 qemu_get_8s(f, &s->mouse_wrap);
1123 qemu_get_8s(f, &s->mouse_type);
1124 qemu_get_8s(f, &s->mouse_detect_state);
1125 qemu_get_be32s(f, (uint32_t *)&s->mouse_dx);
1126 qemu_get_be32s(f, (uint32_t *)&s->mouse_dy);
1127 qemu_get_be32s(f, (uint32_t *)&s->mouse_dz);
1128 qemu_get_8s(f, &s->mouse_buttons);
1129#ifdef VBOX
1130 s->queue.count = 0;
1131 s->queue.rptr = 0;
1132 s->queue.wptr = 0;
1133 s->mouse_command_queue.count = 0;
1134 s->mouse_command_queue.rptr = 0;
1135 s->mouse_command_queue.wptr = 0;
1136 s->mouse_event_queue.count = 0;
1137 s->mouse_event_queue.rptr = 0;
1138 s->mouse_event_queue.wptr = 0;
1139
1140 /*
1141 * Load the queues
1142 */
1143 rc = SSMR3GetU32(f, &u32);
1144 if (VBOX_FAILURE(rc))
1145 return rc;
1146 if (u32 > ELEMENTS(s->queue.data))
1147 {
1148 AssertMsgFailed(("u32=%#x\n", u32));
1149 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1150 }
1151 for (i = 0; i < u32; i++)
1152 {
1153 rc = SSMR3GetU8(f, &s->queue.data[i]);
1154 if (VBOX_FAILURE(rc))
1155 return rc;
1156 }
1157 s->queue.wptr = u32 % ELEMENTS(s->queue.data);
1158 s->queue.count = u32;
1159 Log(("kbd_load: %d keyboard queue items loaded\n", u32));
1160
1161 rc = SSMR3GetU32(f, &u32);
1162 if (VBOX_FAILURE(rc))
1163 return rc;
1164 if (u32 > ELEMENTS(s->mouse_command_queue.data))
1165 {
1166 AssertMsgFailed(("u32=%#x\n", u32));
1167 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1168 }
1169 for (i = 0; i < u32; i++)
1170 {
1171 rc = SSMR3GetU8(f, &s->mouse_command_queue.data[i]);
1172 if (VBOX_FAILURE(rc))
1173 return rc;
1174 }
1175 s->mouse_command_queue.wptr = u32 % ELEMENTS(s->mouse_command_queue.data);
1176 s->mouse_command_queue.count = u32;
1177 Log(("kbd_load: %d mouse command queue items loaded\n", u32));
1178
1179 rc = SSMR3GetU32(f, &u32);
1180 if (VBOX_FAILURE(rc))
1181 return rc;
1182 if (u32 > ELEMENTS(s->mouse_event_queue.data))
1183 {
1184 AssertMsgFailed(("u32=%#x\n", u32));
1185 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1186 }
1187 for (i = 0; i < u32; i++)
1188 {
1189 rc = SSMR3GetU8(f, &s->mouse_event_queue.data[i]);
1190 if (VBOX_FAILURE(rc))
1191 return rc;
1192 }
1193 s->mouse_event_queue.wptr = u32 % ELEMENTS(s->mouse_event_queue.data);
1194 s->mouse_event_queue.count = u32;
1195 Log(("kbd_load: %d mouse event queue items loaded\n", u32));
1196
1197 /* terminator */
1198 rc = SSMR3GetU32(f, &u32);
1199 if (VBOX_FAILURE(rc))
1200 return rc;
1201 if (u32 != ~0)
1202 {
1203 AssertMsgFailed(("u32=%#x\n", u32));
1204 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1205 }
1206#endif /* VBOX */
1207 return 0;
1208}
1209
1210#ifndef VBOX
1211void kbd_init(void)
1212{
1213 KBDState *s = &kbd_state;
1214
1215 kbd_reset(s);
1216 register_savevm("pckbd", 0, 1, kbd_save, kbd_load, s);
1217 register_ioport_read(0x60, 1, 1, kbd_read_data, s);
1218 register_ioport_write(0x60, 1, 1, kbd_write_data, s);
1219 register_ioport_read(0x64, 1, 1, kbd_read_status, s);
1220 register_ioport_write(0x64, 1, 1, kbd_write_command, s);
1221
1222 qemu_add_kbd_event_handler(pc_kbd_put_keycode, s);
1223 qemu_add_mouse_event_handler(pc_kbd_mouse_event, s);
1224 qemu_register_reset(kbd_reset, s);
1225}
1226#endif /* !VBOX */
1227
1228#endif /* IN_RING3 */
1229
1230
1231#ifdef VBOX /* InnoTek code start */
1232
1233/* -=-=-=-=-=- wrappers -=-=-=-=-=- */
1234
1235/**
1236 * Port I/O Handler for keyboard data IN operations.
1237 *
1238 * @returns VBox status code.
1239 *
1240 * @param pDevIns The device instance.
1241 * @param pvUser User argument - ignored.
1242 * @param Port Port number used for the IN operation.
1243 * @param pu32 Where to store the result.
1244 * @param cb Number of bytes read.
1245 */
1246PDMBOTHCBDECL(int) kbdIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1247{
1248 NOREF(pvUser);
1249 if (cb == 1)
1250 {
1251 *pu32 = kbd_read_data(PDMINS2DATA(pDevIns, KBDState *), Port);
1252 Log2(("kbdIOPortDataRead: Port=%#x cb=%d *pu32=%#x\n", Port, cb, *pu32));
1253 return VINF_SUCCESS;
1254 }
1255 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
1256 return VERR_IOM_IOPORT_UNUSED;
1257}
1258
1259/**
1260 * Port I/O Handler for keyboard data OUT operations.
1261 *
1262 * @returns VBox status code.
1263 *
1264 * @param pDevIns The device instance.
1265 * @param pvUser User argument - ignored.
1266 * @param Port Port number used for the IN operation.
1267 * @param u32 The value to output.
1268 * @param cb The value size in bytes.
1269 */
1270PDMBOTHCBDECL(int) kbdIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
1271{
1272 int rc = VINF_SUCCESS;
1273 NOREF(pvUser);
1274 if (cb == 1)
1275 {
1276 rc = kbd_write_data(PDMINS2DATA(pDevIns, KBDState *), Port, u32);
1277 Log2(("kbdIOPortDataWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
1278 }
1279 else
1280 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
1281 return rc;
1282}
1283
1284/**
1285 * Port I/O Handler for keyboard status IN operations.
1286 *
1287 * @returns VBox status code.
1288 *
1289 * @param pDevIns The device instance.
1290 * @param pvUser User argument - ignored.
1291 * @param Port Port number used for the IN operation.
1292 * @param pu32 Where to store the result.
1293 * @param cb Number of bytes read.
1294 */
1295PDMBOTHCBDECL(int) kbdIOPortStatusRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1296{
1297 NOREF(pvUser);
1298 if (cb == 1)
1299 {
1300 *pu32 = kbd_read_status(PDMINS2DATA(pDevIns, KBDState *), Port);
1301 Log2(("kbdIOPortStatusRead: Port=%#x cb=%d -> *pu32=%#x\n", Port, cb, *pu32));
1302 return VINF_SUCCESS;
1303 }
1304 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
1305 return VERR_IOM_IOPORT_UNUSED;
1306}
1307
1308/**
1309 * Port I/O Handler for keyboard command OUT operations.
1310 *
1311 * @returns VBox status code.
1312 *
1313 * @param pDevIns The device instance.
1314 * @param pvUser User argument - ignored.
1315 * @param Port Port number used for the IN operation.
1316 * @param u32 The value to output.
1317 * @param cb The value size in bytes.
1318 */
1319PDMBOTHCBDECL(int) kbdIOPortCommandWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
1320{
1321 NOREF(pvUser);
1322 if (cb == 1)
1323 {
1324 int rc = kbd_write_command(PDMINS2DATA(pDevIns, KBDState *), Port, u32);
1325 Log2(("kbdIOPortCommandWrite: Port=%#x cb=%d u32=%#x rc=%Vrc\n", Port, cb, u32, rc));
1326 return rc;
1327 }
1328 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
1329 return VINF_SUCCESS;
1330}
1331
1332#ifdef IN_RING3
1333
1334/**
1335 * Saves a state of the keyboard device.
1336 *
1337 * @returns VBox status code.
1338 * @param pDevIns The device instance.
1339 * @param pSSMHandle The handle to save the state to.
1340 */
1341static DECLCALLBACK(int) kbdSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
1342{
1343 kbd_save(pSSMHandle, PDMINS2DATA(pDevIns, KBDState *));
1344 return VINF_SUCCESS;
1345}
1346
1347
1348/**
1349 * Loads a saved keyboard device state.
1350 *
1351 * @returns VBox status code.
1352 * @param pDevIns The device instance.
1353 * @param pSSMHandle The handle to the saved state.
1354 * @param u32Version The data unit version number.
1355 */
1356static DECLCALLBACK(int) kbdLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
1357{
1358 return kbd_load(pSSMHandle, PDMINS2DATA(pDevIns, KBDState *), u32Version);
1359}
1360
1361/**
1362 * Reset notification.
1363 *
1364 * @returns VBox status.
1365 * @param pDevIns The device instance data.
1366 */
1367static DECLCALLBACK(void) kbdReset(PPDMDEVINS pDevIns)
1368{
1369 kbd_reset(PDMINS2DATA(pDevIns, KBDState *));
1370}
1371
1372
1373/* -=-=-=-=-=- Keyboard: IBase -=-=-=-=-=- */
1374
1375/**
1376 * Queries an interface to the driver.
1377 *
1378 * @returns Pointer to interface.
1379 * @returns NULL if the interface was not supported by the device.
1380 * @param pInterface Pointer to the keyboard port base interface (KBDState::Keyboard.Base).
1381 * @param enmInterface The requested interface identification.
1382 */
1383static DECLCALLBACK(void *) kbdKeyboardQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
1384{
1385 KBDState *pData = (KBDState *)((uintptr_t)pInterface - RT_OFFSETOF(KBDState, Keyboard.Base));
1386 switch (enmInterface)
1387 {
1388 case PDMINTERFACE_BASE:
1389 return &pData->Keyboard.Base;
1390 case PDMINTERFACE_KEYBOARD_PORT:
1391 return &pData->Keyboard.Port;
1392 default:
1393 return NULL;
1394 }
1395}
1396
1397
1398/* -=-=-=-=-=- Keyboard: IKeyboardPort -=-=-=-=-=- */
1399
1400/** Converts a keyboard port interface pointer to a keyboard state pointer. */
1401#define IKEYBOARDPORT_2_KBDSTATE(pInterface) ( (KBDState *)((uintptr_t)pInterface - RT_OFFSETOF(KBDState, Keyboard.Port)) )
1402
1403/**
1404 * Keyboard event handler.
1405 *
1406 * @returns VBox status code.
1407 * @param pInterface Pointer to the keyboard port interface (KBDState::Keyboard.Port).
1408 * @param u8KeyCode The keycode.
1409 */
1410static DECLCALLBACK(int) kbdKeyboardPutEvent(PPDMIKEYBOARDPORT pInterface, uint8_t u8KeyCode)
1411{
1412 KBDState *pData = IKEYBOARDPORT_2_KBDSTATE(pInterface);
1413 pc_kbd_put_keycode(pData, u8KeyCode);
1414 return VINF_SUCCESS;
1415}
1416
1417
1418/* -=-=-=-=-=- Mouse: IBase -=-=-=-=-=- */
1419
1420/**
1421 * Queries an interface to the driver.
1422 *
1423 * @returns Pointer to interface.
1424 * @returns NULL if the interface was not supported by the device.
1425 * @param pInterface Pointer to the mouse port base interface (KBDState::Mouse.Base).
1426 * @param enmInterface The requested interface identification.
1427 */
1428static DECLCALLBACK(void *) kbdMouseQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
1429{
1430 KBDState *pData = (KBDState *)((uintptr_t)pInterface - RT_OFFSETOF(KBDState, Mouse.Base));
1431 switch (enmInterface)
1432 {
1433 case PDMINTERFACE_BASE:
1434 return &pData->Mouse.Base;
1435 case PDMINTERFACE_MOUSE_PORT:
1436 return &pData->Mouse.Port;
1437 default:
1438 return NULL;
1439 }
1440}
1441
1442
1443/* -=-=-=-=-=- Mouse: IMousePort -=-=-=-=-=- */
1444
1445/** Converts a mouse port interface pointer to a keyboard state pointer. */
1446#define IMOUSEPORT_2_KBDSTATE(pInterface) ( (KBDState *)((uintptr_t)pInterface - RT_OFFSETOF(KBDState, Mouse.Port)) )
1447
1448/**
1449 * Mouse event handler.
1450 *
1451 * @returns VBox status code.
1452 * @param pInterface Pointer to the mouse port interface (KBDState::Mouse.Port).
1453 * @param i32DeltaX The X delta.
1454 * @param i32DeltaY The Y delta.
1455 * @param i32DeltaZ The Z delta.
1456 * @param fButtonStates The button states.
1457 */
1458static DECLCALLBACK(int) kbdMousePutEvent(PPDMIMOUSEPORT pInterface, int32_t i32DeltaX, int32_t i32DeltaY, int32_t i32DeltaZ, uint32_t fButtonStates)
1459{
1460 KBDState *pData = IMOUSEPORT_2_KBDSTATE(pInterface);
1461 pc_kbd_mouse_event(pData, i32DeltaX, i32DeltaY, i32DeltaZ, fButtonStates);
1462 return VINF_SUCCESS;
1463}
1464
1465
1466/* -=-=-=-=-=- real code -=-=-=-=-=- */
1467
1468
1469/**
1470 * Attach command.
1471 *
1472 * This is called to let the device attach to a driver for a specified LUN
1473 * during runtime. This is not called during VM construction, the device
1474 * constructor have to attach to all the available drivers.
1475 *
1476 * This is like plugging in the keyboard or mouse after turning on the PC.
1477 *
1478 * @returns VBox status code.
1479 * @param pDevIns The device instance.
1480 * @param iLUN The logical unit which is being detached.
1481 * @remark The keyboard controller doesn't support this action, this is just
1482 * implemented to try out the driver<->device structure.
1483 */
1484static DECLCALLBACK(int) kbdAttach(PPDMDEVINS pDevIns, unsigned iLUN)
1485{
1486 int rc;
1487 KBDState *pData = PDMINS2DATA(pDevIns, KBDState *);
1488 switch (iLUN)
1489 {
1490 /* LUN #0: keyboard */
1491 case 0:
1492 rc = pDevIns->pDevHlp->pfnDriverAttach(pDevIns, iLUN, &pData->Keyboard.Base, &pData->Keyboard.pDrvBase, "Keyboard Port");
1493 if (VBOX_SUCCESS(rc))
1494 {
1495 pData->Keyboard.pDrv = pData->Keyboard.pDrvBase->pfnQueryInterface(pData->Keyboard.pDrvBase, PDMINTERFACE_KEYBOARD_CONNECTOR);
1496 if (!pData->Keyboard.pDrv)
1497 {
1498 AssertMsgFailed(("LUN #0 doesn't have a keyboard interface! rc=%Vrc\n", rc));
1499 rc = VERR_PDM_MISSING_INTERFACE;
1500 }
1501 }
1502 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1503 {
1504 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pDevReg->szDeviceName, pDevIns->iInstance));
1505 rc = VINF_SUCCESS;
1506 }
1507 else
1508 AssertMsgFailed(("Failed to attach LUN #0! rc=%Vrc\n", rc));
1509 break;
1510
1511 /* LUN #1: aux/mouse */
1512 case 1:
1513 rc = pDevIns->pDevHlp->pfnDriverAttach(pDevIns, iLUN, &pData->Mouse.Base, &pData->Mouse.pDrvBase, "Aux (Mouse) Port");
1514 if (VBOX_SUCCESS(rc))
1515 {
1516 pData->Mouse.pDrv = pData->Mouse.pDrvBase->pfnQueryInterface(pData->Mouse.pDrvBase, PDMINTERFACE_MOUSE_CONNECTOR);
1517 if (!pData->Mouse.pDrv)
1518 {
1519 AssertMsgFailed(("LUN #1 doesn't have a mouse interface! rc=%Vrc\n", rc));
1520 rc = VERR_PDM_MISSING_INTERFACE;
1521 }
1522 }
1523 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
1524 {
1525 Log(("%s/%d: warning: no driver attached to LUN #1!\n", pDevIns->pDevReg->szDeviceName, pDevIns->iInstance));
1526 rc = VINF_SUCCESS;
1527 }
1528 else
1529 AssertMsgFailed(("Failed to attach LUN #1! rc=%Vrc\n", rc));
1530 break;
1531
1532 default:
1533 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1534 return VERR_PDM_NO_SUCH_LUN;
1535 }
1536
1537 return rc;
1538}
1539
1540
1541/**
1542 * Detach notification.
1543 *
1544 * This is called when a driver is detaching itself from a LUN of the device.
1545 * The device should adjust it's state to reflect this.
1546 *
1547 * This is like unplugging the network cable to use it for the laptop or
1548 * something while the PC is still running.
1549 *
1550 * @param pDevIns The device instance.
1551 * @param iLUN The logical unit which is being detached.
1552 * @remark The keyboard controller doesn't support this action, this is just
1553 * implemented to try out the driver<->device structure.
1554 */
1555static DECLCALLBACK(void) kbdDetach(PPDMDEVINS pDevIns, unsigned iLUN)
1556{
1557#if 0
1558 /*
1559 * Reset the interfaces and update the controller state.
1560 */
1561 KBDState *pData = PDMINS2DATA(pDevIns, KBDState *);
1562 switch (iLUN)
1563 {
1564 /* LUN #0: keyboard */
1565 case 0:
1566 pData->Keyboard.pDrv = NULL;
1567 pData->Keyboard.pDrvBase = NULL;
1568 break;
1569
1570 /* LUN #1: aux/mouse */
1571 case 1:
1572 pData->Mouse.pDrv = NULL;
1573 pData->Mouse.pDrvBase = NULL;
1574 break;
1575
1576 default:
1577 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
1578 break;
1579 }
1580#endif
1581}
1582
1583/**
1584 * @copydoc FNPDMDEVRELOCATE
1585 */
1586static DECLCALLBACK(void) kdbRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1587{
1588 KBDState *pData = PDMINS2DATA(pDevIns, KBDState *);
1589 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
1590}
1591
1592
1593/**
1594 * Construct a device instance for a VM.
1595 *
1596 * @returns VBox status.
1597 * @param pDevIns The device instance data.
1598 * If the registration structure is needed, pDevIns->pDevReg points to it.
1599 * @param iInstance Instance number. Use this to figure out which registers and such to use.
1600 * The device number is also found in pDevIns->iInstance, but since it's
1601 * likely to be freqently used PDM passes it as parameter.
1602 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
1603 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
1604 * iInstance it's expected to be used a bit in this function.
1605 */
1606static DECLCALLBACK(int) kbdConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
1607{
1608 KBDState *pData = PDMINS2DATA(pDevIns, KBDState *);
1609 int rc;
1610 bool fGCEnabled;
1611 bool fR0Enabled;
1612 Assert(iInstance == 0);
1613
1614 /*
1615 * Validate and read the configuration.
1616 */
1617 if (!CFGMR3AreValuesValid(pCfgHandle, "GCEnabled\0R0Enabled\0"))
1618 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1619 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &fGCEnabled);
1620 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1621 fGCEnabled = true;
1622 else if (VBOX_FAILURE(rc))
1623 {
1624 AssertMsgFailed(("configuration error: failed to read GCEnabled as boolean. rc=%Vrc\n", rc));
1625 return rc;
1626 }
1627 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &fR0Enabled);
1628 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1629 fR0Enabled = true;
1630 else if (VBOX_FAILURE(rc))
1631 {
1632 AssertMsgFailed(("configuration error: failed to read R0Enabled as boolean. rc=%Vrc\n", rc));
1633 return rc;
1634 }
1635 Log(("pckbd: fGCEnabled=%d fR0Enabled=%d\n", fGCEnabled, fR0Enabled));
1636
1637
1638 /*
1639 * Initialize the interfaces.
1640 */
1641 pData->pDevInsHC = pDevIns;
1642 pData->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
1643 pData->Keyboard.Base.pfnQueryInterface = kbdKeyboardQueryInterface;
1644 pData->Keyboard.Port.pfnPutEvent = kbdKeyboardPutEvent;
1645
1646 pData->Mouse.Base.pfnQueryInterface = kbdMouseQueryInterface;
1647 pData->Mouse.Port.pfnPutEvent = kbdMousePutEvent;
1648
1649 /*
1650 * Register I/O ports, save state, keyboard event handler and mouse event handlers.
1651 */
1652 rc = PDMDevHlpIOPortRegister(pDevIns, 0x60, 1, NULL, kbdIOPortDataWrite, kbdIOPortDataRead, NULL, NULL, "PC Keyboard - Data");
1653 if (VBOX_FAILURE(rc))
1654 return rc;
1655 rc = PDMDevHlpIOPortRegister(pDevIns, 0x64, 1, NULL, kbdIOPortCommandWrite, kbdIOPortStatusRead, NULL, NULL, "PC Keyboard - Command / Status");
1656 if (VBOX_FAILURE(rc))
1657 return rc;
1658 if (fGCEnabled)
1659 {
1660 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
1661 if (VBOX_FAILURE(rc))
1662 return rc;
1663 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
1664 if (VBOX_FAILURE(rc))
1665 return rc;
1666 }
1667 if (fR0Enabled)
1668 {
1669 rc = pDevIns->pDevHlp->pfnIOPortRegisterR0(pDevIns, 0x60, 1, 0, "kbdIOPortDataWrite", "kbdIOPortDataRead", NULL, NULL, "PC Keyboard - Data");
1670 if (VBOX_FAILURE(rc))
1671 return rc;
1672 rc = pDevIns->pDevHlp->pfnIOPortRegisterR0(pDevIns, 0x64, 1, 0, "kbdIOPortCommandWrite", "kbdIOPortStatusRead", NULL, NULL, "PC Keyboard - Command / Status");
1673 if (VBOX_FAILURE(rc))
1674 return rc;
1675 }
1676 rc = PDMDevHlpSSMRegister(pDevIns, g_DevicePS2KeyboardMouse.szDeviceName, iInstance, PCKBD_SAVED_STATE_VERSION, sizeof(*pData),
1677 NULL, kbdSaveExec, NULL,
1678 NULL, kbdLoadExec, NULL);
1679 if (VBOX_FAILURE(rc))
1680 return rc;
1681
1682 /*
1683 * Attach to the keyboard and mouse drivers.
1684 */
1685 rc = kbdAttach(pDevIns, 0 /* keyboard LUN # */);
1686 if (VBOX_FAILURE(rc))
1687 return rc;
1688 rc = kbdAttach(pDevIns, 1 /* aux/mouse LUN # */);
1689 if (VBOX_FAILURE(rc))
1690 return rc;
1691
1692 /*
1693 * Initialize the device state.
1694 */
1695 kbdReset(pDevIns);
1696
1697 return VINF_SUCCESS;
1698}
1699
1700
1701/**
1702 * The device registration structure.
1703 */
1704const PDMDEVREG g_DevicePS2KeyboardMouse =
1705{
1706 /* u32Version */
1707 PDM_DEVREG_VERSION,
1708 /* szDeviceName */
1709 "pckbd",
1710 /* szGCMod */
1711 "VBoxDDGC.gc",
1712 /* szR0Mod */
1713 "VBoxDDR0.r0",
1714 /* pszDescription */
1715 "PS/2 Keyboard and Mouse device. Emulates both the keyboard, mouse and the keyboard controller. "
1716 "LUN #0 is the keyboard connector. "
1717 "LUN #1 is the aux/mouse connector.",
1718 /* fFlags */
1719 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
1720 /* fClass */
1721 PDM_DEVREG_CLASS_INPUT,
1722 /* cMaxInstances */
1723 1,
1724 /* cbInstance */
1725 sizeof(KBDState),
1726 /* pfnConstruct */
1727 kbdConstruct,
1728 /* pfnDestruct */
1729 NULL,
1730 /* pfnRelocate */
1731 kdbRelocate,
1732 /* pfnIOCtl */
1733 NULL,
1734 /* pfnPowerOn */
1735 NULL,
1736 /* pfnReset */
1737 kbdReset,
1738 /* pfnSuspend */
1739 NULL,
1740 /* pfnResume */
1741 NULL,
1742 /* pfnAttach */
1743 kbdAttach,
1744 /* pfnDetach */
1745 kbdDetach,
1746 /* pfnQueryInterface. */
1747 NULL
1748};
1749
1750#endif /* IN_RING3 */
1751#endif /* VBOX */
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