/*
* QEMU PS/2 keyboard/mouse emulation
- *
+ *
* Copyright (c) 2003 Fabrice Bellard
- *
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "vl.h"
+#include "hw.h"
+#include "ps2.h"
+#include "console.h"
/* debug PC keyboard */
//#define DEBUG_KBD
/* Keyboard Commands */
#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
#define KBD_CMD_ECHO 0xEE
+#define KBD_CMD_SCANCODE 0xF0 /* Get/set scancode set */
#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
/* Keyboard Replies */
#define KBD_REPLY_POR 0xAA /* Power on reset */
+#define KBD_REPLY_ID 0xAB /* Keyboard ID */
#define KBD_REPLY_ACK 0xFA /* Command ACK */
#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
conversions we do the translation (if any) in the PS/2 emulation
not the keyboard controller. */
int translate;
+ int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
} PS2KbdState;
typedef struct {
/* Table to convert from PC scancodes to raw scancodes. */
static const unsigned char ps2_raw_keycode[128] = {
- 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
- 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
- 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
- 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
- 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
- 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
- 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
- 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+ 0, 118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85, 102, 13,
+ 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+ 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+ 50, 49, 58, 65, 73, 74, 89, 124, 17, 41, 88, 5, 6, 4, 12, 3,
+ 11, 2, 10, 1, 9, 119, 126, 108, 117, 125, 123, 107, 115, 116, 121, 105,
+114, 122, 112, 113, 127, 96, 97, 120, 7, 15, 23, 31, 39, 47, 55, 63,
+ 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87, 111,
+ 19, 25, 57, 81, 83, 92, 95, 98, 99, 100, 101, 103, 104, 106, 109, 110
+};
+static const unsigned char ps2_raw_keycode_set3[128] = {
+ 0, 8, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85, 102, 13,
+ 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 17, 28, 27,
+ 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 92, 26, 34, 33, 42,
+ 50, 49, 58, 65, 73, 74, 89, 126, 25, 41, 20, 7, 15, 23, 31, 39,
+ 47, 2, 63, 71, 79, 118, 95, 108, 117, 125, 132, 107, 115, 116, 124, 105,
+114, 122, 112, 113, 127, 96, 97, 86, 94, 15, 23, 31, 39, 47, 55, 63,
+ 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87, 111,
+ 19, 25, 57, 81, 83, 92, 95, 98, 99, 100, 101, 103, 104, 106, 109, 110
};
void ps2_queue(void *opaque, int b)
s->update_irq(s->update_arg, 1);
}
+/*
+ keycode is expressed as follow:
+ bit 7 - 0 key pressed, 1 = key released
+ bits 6-0 - translated scancode set 2
+ */
static void ps2_put_keycode(void *opaque, int keycode)
{
PS2KbdState *s = opaque;
- if (!s->translate && keycode < 0xe0)
- {
- if (keycode & 0x80)
+
+ /* XXX: add support for scancode set 1 */
+ if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) {
+ if (keycode & 0x80) {
ps2_queue(&s->common, 0xf0);
- keycode = ps2_raw_keycode[keycode & 0x7f];
+ }
+ if (s->scancode_set == 2) {
+ keycode = ps2_raw_keycode[keycode & 0x7f];
+ } else if (s->scancode_set == 3) {
+ keycode = ps2_raw_keycode_set3[keycode & 0x7f];
+ }
}
ps2_queue(&s->common, keycode);
}
PS2State *s = (PS2State *)opaque;
PS2Queue *q;
int val, index;
-
+
q = &s->queue;
if (q->count == 0) {
/* NOTE: if no data left, we return the last keyboard one
static void ps2_reset_keyboard(PS2KbdState *s)
{
s->scan_enabled = 1;
+ s->scancode_set = 2;
+ kbd_put_ledstate(0);
}
void ps2_write_keyboard(void *opaque, int val)
break;
case KBD_CMD_GET_ID:
ps2_queue(&s->common, KBD_REPLY_ACK);
- ps2_queue(&s->common, 0xab);
- ps2_queue(&s->common, 0x83);
+ /* We emulate a MF2 AT keyboard here */
+ ps2_queue(&s->common, KBD_REPLY_ID);
+ if (s->translate)
+ ps2_queue(&s->common, 0x41);
+ else
+ ps2_queue(&s->common, 0x83);
break;
case KBD_CMD_ECHO:
ps2_queue(&s->common, KBD_CMD_ECHO);
s->scan_enabled = 1;
ps2_queue(&s->common, KBD_REPLY_ACK);
break;
+ case KBD_CMD_SCANCODE:
case KBD_CMD_SET_LEDS:
case KBD_CMD_SET_RATE:
s->common.write_cmd = val;
break;
}
break;
+ case KBD_CMD_SCANCODE:
+ if (val == 0) {
+ if (s->scancode_set == 1)
+ ps2_put_keycode(s, 0x43);
+ else if (s->scancode_set == 2)
+ ps2_put_keycode(s, 0x41);
+ else if (s->scancode_set == 3)
+ ps2_put_keycode(s, 0x3f);
+ } else {
+ if (val >= 1 && val <= 3)
+ s->scancode_set = val;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ }
+ s->common.write_cmd = -1;
+ break;
case KBD_CMD_SET_LEDS:
+ kbd_put_ledstate(val);
ps2_queue(&s->common, KBD_REPLY_ACK);
s->common.write_cmd = -1;
break;
s->mouse_dz -= dz1;
}
-static void ps2_mouse_event(void *opaque,
+static void ps2_mouse_event(void *opaque,
int dx, int dy, int dz, int buttons_state)
{
PS2MouseState *s = opaque;
s->mouse_buttons == buttons_state)
return;
s->mouse_buttons = buttons_state;
-
+
if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
(s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
for(;;) {
}
}
+void ps2_mouse_fake_event(void *opaque)
+{
+ ps2_mouse_event(opaque, 1, 0, 0, 0);
+}
+
void ps2_write_mouse(void *opaque, int val)
{
PS2MouseState *s = (PS2MouseState *)opaque;
s->mouse_detect_state = 0;
break;
case 2:
- if (val == 80)
+ if (val == 80)
s->mouse_type = 3; /* IMPS/2 */
s->mouse_detect_state = 0;
break;
case 3:
- if (val == 80)
+ if (val == 80)
s->mouse_type = 4; /* IMEX */
s->mouse_detect_state = 0;
break;
}
}
-static void ps2_reset(void *opaque)
+static void ps2_common_reset(PS2State *s)
{
- PS2State *s = (PS2State *)opaque;
PS2Queue *q;
s->write_cmd = -1;
q = &s->queue;
q->rptr = 0;
q->wptr = 0;
q->count = 0;
+ s->update_irq(s->update_arg, 0);
}
-static void ps2_kbd_save(QEMUFile* f, void* opaque)
+static void ps2_kbd_reset(void *opaque)
{
- PS2KbdState *s = (PS2KbdState*)opaque;
-
- qemu_put_be32s(f, &s->common.write_cmd);
- qemu_put_be32s(f, &s->scan_enabled);
+ PS2KbdState *s = (PS2KbdState *) opaque;
+
+ ps2_common_reset(&s->common);
+ s->scan_enabled = 0;
+ s->translate = 0;
+ s->scancode_set = 0;
}
-static void ps2_mouse_save(QEMUFile* f, void* opaque)
+static void ps2_mouse_reset(void *opaque)
{
- PS2MouseState *s = (PS2MouseState*)opaque;
-
- qemu_put_be32s(f, &s->common.write_cmd);
- qemu_put_8s(f, &s->mouse_status);
- qemu_put_8s(f, &s->mouse_resolution);
- qemu_put_8s(f, &s->mouse_sample_rate);
- qemu_put_8s(f, &s->mouse_wrap);
- qemu_put_8s(f, &s->mouse_type);
- qemu_put_8s(f, &s->mouse_detect_state);
- qemu_put_be32s(f, &s->mouse_dx);
- qemu_put_be32s(f, &s->mouse_dy);
- qemu_put_be32s(f, &s->mouse_dz);
- qemu_put_8s(f, &s->mouse_buttons);
+ PS2MouseState *s = (PS2MouseState *) opaque;
+
+ ps2_common_reset(&s->common);
+ s->mouse_status = 0;
+ s->mouse_resolution = 0;
+ s->mouse_sample_rate = 0;
+ s->mouse_wrap = 0;
+ s->mouse_type = 0;
+ s->mouse_detect_state = 0;
+ s->mouse_dx = 0;
+ s->mouse_dy = 0;
+ s->mouse_dz = 0;
+ s->mouse_buttons = 0;
}
-static int ps2_kbd_load(QEMUFile* f, void* opaque, int version_id)
+static const VMStateDescription vmstate_ps2_common = {
+ .name = "PS2 Common State",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(write_cmd, PS2State),
+ VMSTATE_INT32(queue.rptr, PS2State),
+ VMSTATE_INT32(queue.wptr, PS2State),
+ VMSTATE_INT32(queue.count, PS2State),
+ VMSTATE_BUFFER(queue.data, PS2State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int ps2_kbd_post_load(void* opaque, int version_id)
{
PS2KbdState *s = (PS2KbdState*)opaque;
-
- if (version_id != 1)
- return -EINVAL;
- qemu_get_be32s(f, &s->common.write_cmd);
- qemu_get_be32s(f, &s->scan_enabled);
- return 0;
-}
-static int ps2_mouse_load(QEMUFile* f, void* opaque, int version_id)
-{
- PS2MouseState *s = (PS2MouseState*)opaque;
-
- if (version_id != 1)
- return -EINVAL;
- qemu_get_be32s(f, &s->common.write_cmd);
- qemu_get_8s(f, &s->mouse_status);
- qemu_get_8s(f, &s->mouse_resolution);
- qemu_get_8s(f, &s->mouse_sample_rate);
- qemu_get_8s(f, &s->mouse_wrap);
- qemu_get_8s(f, &s->mouse_type);
- qemu_get_8s(f, &s->mouse_detect_state);
- qemu_get_be32s(f, &s->mouse_dx);
- qemu_get_be32s(f, &s->mouse_dy);
- qemu_get_be32s(f, &s->mouse_dz);
- qemu_get_8s(f, &s->mouse_buttons);
+ if (version_id == 2)
+ s->scancode_set=2;
return 0;
}
+static const VMStateDescription vmstate_ps2_keyboard = {
+ .name = "ps2kbd",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .post_load = ps2_kbd_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
+ VMSTATE_INT32(scan_enabled, PS2KbdState),
+ VMSTATE_INT32(translate, PS2KbdState),
+ VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_ps2_mouse = {
+ .name = "ps2mouse",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
+ VMSTATE_UINT8(mouse_status, PS2MouseState),
+ VMSTATE_UINT8(mouse_resolution, PS2MouseState),
+ VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
+ VMSTATE_UINT8(mouse_wrap, PS2MouseState),
+ VMSTATE_UINT8(mouse_type, PS2MouseState),
+ VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
+ VMSTATE_INT32(mouse_dx, PS2MouseState),
+ VMSTATE_INT32(mouse_dy, PS2MouseState),
+ VMSTATE_INT32(mouse_dz, PS2MouseState),
+ VMSTATE_UINT8(mouse_buttons, PS2MouseState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
{
- PS2KbdState *s = (PS2KbdState *)qemu_mallocz(sizeof(PS2KbdState));
+ PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState));
s->common.update_irq = update_irq;
s->common.update_arg = update_arg;
- ps2_reset(&s->common);
- register_savevm("ps2kbd", 0, 1, ps2_kbd_save, ps2_kbd_load, s);
+ s->scancode_set = 2;
+ vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
qemu_add_kbd_event_handler(ps2_put_keycode, s);
- qemu_register_reset(ps2_reset, &s->common);
+ qemu_register_reset(ps2_kbd_reset, s);
return s;
}
void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
{
- PS2MouseState *s = (PS2MouseState *)qemu_mallocz(sizeof(PS2MouseState));
+ PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState));
s->common.update_irq = update_irq;
s->common.update_arg = update_arg;
- ps2_reset(&s->common);
- register_savevm("ps2mouse", 0, 1, ps2_mouse_save, ps2_mouse_load, s);
- qemu_add_mouse_event_handler(ps2_mouse_event, s);
- qemu_register_reset(ps2_reset, &s->common);
+ vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
+ qemu_add_mouse_event_handler(ps2_mouse_event, s, 0, "QEMU PS/2 Mouse");
+ qemu_register_reset(ps2_mouse_reset, s);
return s;
}