xref: /qemu/hw/input/ps2.c (revision 5cd8cada)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU PS/2 keyboard/mouse emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2003 Fabrice Bellard
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
749ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
849ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
949ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1149ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1449ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249ab747fSPaolo Bonzini  * THE SOFTWARE.
2349ab747fSPaolo Bonzini  */
2449ab747fSPaolo Bonzini #include "hw/hw.h"
2549ab747fSPaolo Bonzini #include "hw/input/ps2.h"
2649ab747fSPaolo Bonzini #include "ui/console.h"
2766e6536eSGerd Hoffmann #include "ui/input.h"
2849ab747fSPaolo Bonzini #include "sysemu/sysemu.h"
2949ab747fSPaolo Bonzini 
305edab03dSDon Koch #include "trace.h"
315edab03dSDon Koch 
3249ab747fSPaolo Bonzini /* debug PC keyboard */
3349ab747fSPaolo Bonzini //#define DEBUG_KBD
3449ab747fSPaolo Bonzini 
3549ab747fSPaolo Bonzini /* debug PC keyboard : only mouse */
3649ab747fSPaolo Bonzini //#define DEBUG_MOUSE
3749ab747fSPaolo Bonzini 
3849ab747fSPaolo Bonzini /* Keyboard Commands */
3949ab747fSPaolo Bonzini #define KBD_CMD_SET_LEDS	0xED	/* Set keyboard leds */
4049ab747fSPaolo Bonzini #define KBD_CMD_ECHO     	0xEE
4149ab747fSPaolo Bonzini #define KBD_CMD_SCANCODE	0xF0	/* Get/set scancode set */
4249ab747fSPaolo Bonzini #define KBD_CMD_GET_ID 	        0xF2	/* get keyboard ID */
4349ab747fSPaolo Bonzini #define KBD_CMD_SET_RATE	0xF3	/* Set typematic rate */
4449ab747fSPaolo Bonzini #define KBD_CMD_ENABLE		0xF4	/* Enable scanning */
4549ab747fSPaolo Bonzini #define KBD_CMD_RESET_DISABLE	0xF5	/* reset and disable scanning */
4649ab747fSPaolo Bonzini #define KBD_CMD_RESET_ENABLE   	0xF6    /* reset and enable scanning */
4749ab747fSPaolo Bonzini #define KBD_CMD_RESET		0xFF	/* Reset */
4849ab747fSPaolo Bonzini 
4949ab747fSPaolo Bonzini /* Keyboard Replies */
5049ab747fSPaolo Bonzini #define KBD_REPLY_POR		0xAA	/* Power on reset */
5149ab747fSPaolo Bonzini #define KBD_REPLY_ID		0xAB	/* Keyboard ID */
5249ab747fSPaolo Bonzini #define KBD_REPLY_ACK		0xFA	/* Command ACK */
5349ab747fSPaolo Bonzini #define KBD_REPLY_RESEND	0xFE	/* Command NACK, send the cmd again */
5449ab747fSPaolo Bonzini 
5549ab747fSPaolo Bonzini /* Mouse Commands */
5649ab747fSPaolo Bonzini #define AUX_SET_SCALE11		0xE6	/* Set 1:1 scaling */
5749ab747fSPaolo Bonzini #define AUX_SET_SCALE21		0xE7	/* Set 2:1 scaling */
5849ab747fSPaolo Bonzini #define AUX_SET_RES		0xE8	/* Set resolution */
5949ab747fSPaolo Bonzini #define AUX_GET_SCALE		0xE9	/* Get scaling factor */
6049ab747fSPaolo Bonzini #define AUX_SET_STREAM		0xEA	/* Set stream mode */
6149ab747fSPaolo Bonzini #define AUX_POLL		0xEB	/* Poll */
6249ab747fSPaolo Bonzini #define AUX_RESET_WRAP		0xEC	/* Reset wrap mode */
6349ab747fSPaolo Bonzini #define AUX_SET_WRAP		0xEE	/* Set wrap mode */
6449ab747fSPaolo Bonzini #define AUX_SET_REMOTE		0xF0	/* Set remote mode */
6549ab747fSPaolo Bonzini #define AUX_GET_TYPE		0xF2	/* Get type */
6649ab747fSPaolo Bonzini #define AUX_SET_SAMPLE		0xF3	/* Set sample rate */
6749ab747fSPaolo Bonzini #define AUX_ENABLE_DEV		0xF4	/* Enable aux device */
6849ab747fSPaolo Bonzini #define AUX_DISABLE_DEV		0xF5	/* Disable aux device */
6949ab747fSPaolo Bonzini #define AUX_SET_DEFAULT		0xF6
7049ab747fSPaolo Bonzini #define AUX_RESET		0xFF	/* Reset aux device */
7149ab747fSPaolo Bonzini #define AUX_ACK			0xFA	/* Command byte ACK. */
7249ab747fSPaolo Bonzini 
7349ab747fSPaolo Bonzini #define MOUSE_STATUS_REMOTE     0x40
7449ab747fSPaolo Bonzini #define MOUSE_STATUS_ENABLED    0x20
7549ab747fSPaolo Bonzini #define MOUSE_STATUS_SCALE21    0x10
7649ab747fSPaolo Bonzini 
772858ab09SGonglei #define PS2_QUEUE_SIZE 16  /* Buffer size required by PS/2 protocol */
7849ab747fSPaolo Bonzini 
7949ab747fSPaolo Bonzini typedef struct {
802858ab09SGonglei     /* Keep the data array 256 bytes long, which compatibility
812858ab09SGonglei      with older qemu versions. */
822858ab09SGonglei     uint8_t data[256];
8349ab747fSPaolo Bonzini     int rptr, wptr, count;
8449ab747fSPaolo Bonzini } PS2Queue;
8549ab747fSPaolo Bonzini 
8649ab747fSPaolo Bonzini typedef struct {
8749ab747fSPaolo Bonzini     PS2Queue queue;
8849ab747fSPaolo Bonzini     int32_t write_cmd;
8949ab747fSPaolo Bonzini     void (*update_irq)(void *, int);
9049ab747fSPaolo Bonzini     void *update_arg;
9149ab747fSPaolo Bonzini } PS2State;
9249ab747fSPaolo Bonzini 
9349ab747fSPaolo Bonzini typedef struct {
9449ab747fSPaolo Bonzini     PS2State common;
9549ab747fSPaolo Bonzini     int scan_enabled;
9649ab747fSPaolo Bonzini     /* QEMU uses translated PC scancodes internally.  To avoid multiple
9749ab747fSPaolo Bonzini        conversions we do the translation (if any) in the PS/2 emulation
9849ab747fSPaolo Bonzini        not the keyboard controller.  */
9949ab747fSPaolo Bonzini     int translate;
10049ab747fSPaolo Bonzini     int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
10149ab747fSPaolo Bonzini     int ledstate;
10249ab747fSPaolo Bonzini } PS2KbdState;
10349ab747fSPaolo Bonzini 
10449ab747fSPaolo Bonzini typedef struct {
10549ab747fSPaolo Bonzini     PS2State common;
10649ab747fSPaolo Bonzini     uint8_t mouse_status;
10749ab747fSPaolo Bonzini     uint8_t mouse_resolution;
10849ab747fSPaolo Bonzini     uint8_t mouse_sample_rate;
10949ab747fSPaolo Bonzini     uint8_t mouse_wrap;
11049ab747fSPaolo Bonzini     uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
11149ab747fSPaolo Bonzini     uint8_t mouse_detect_state;
11249ab747fSPaolo Bonzini     int mouse_dx; /* current values, needed for 'poll' mode */
11349ab747fSPaolo Bonzini     int mouse_dy;
11449ab747fSPaolo Bonzini     int mouse_dz;
11549ab747fSPaolo Bonzini     uint8_t mouse_buttons;
11649ab747fSPaolo Bonzini } PS2MouseState;
11749ab747fSPaolo Bonzini 
11849ab747fSPaolo Bonzini /* Table to convert from PC scancodes to raw scancodes.  */
11949ab747fSPaolo Bonzini static const unsigned char ps2_raw_keycode[128] = {
12049ab747fSPaolo Bonzini   0, 118,  22,  30,  38,  37,  46,  54,  61,  62,  70,  69,  78,  85, 102,  13,
12149ab747fSPaolo Bonzini  21,  29,  36,  45,  44,  53,  60,  67,  68,  77,  84,  91,  90,  20,  28,  27,
12249ab747fSPaolo Bonzini  35,  43,  52,  51,  59,  66,  75,  76,  82,  14,  18,  93,  26,  34,  33,  42,
12349ab747fSPaolo Bonzini  50,  49,  58,  65,  73,  74,  89, 124,  17,  41,  88,   5,   6,   4,  12,   3,
12449ab747fSPaolo Bonzini  11,   2,  10,   1,   9, 119, 126, 108, 117, 125, 123, 107, 115, 116, 121, 105,
12549ab747fSPaolo Bonzini 114, 122, 112, 113, 127,  96,  97, 120,   7,  15,  23,  31,  39,  47,  55,  63,
12649ab747fSPaolo Bonzini  71,  79,  86,  94,   8,  16,  24,  32,  40,  48,  56,  64,  72,  80,  87, 111,
12749ab747fSPaolo Bonzini  19,  25,  57,  81,  83,  92,  95,  98,  99, 100, 101, 103, 104, 106, 109, 110
12849ab747fSPaolo Bonzini };
12949ab747fSPaolo Bonzini static const unsigned char ps2_raw_keycode_set3[128] = {
13049ab747fSPaolo Bonzini   0,   8,  22,  30,  38,  37,  46,  54,  61,  62,  70,  69,  78,  85, 102,  13,
13149ab747fSPaolo Bonzini  21,  29,  36,  45,  44,  53,  60,  67,  68,  77,  84,  91,  90,  17,  28,  27,
13249ab747fSPaolo Bonzini  35,  43,  52,  51,  59,  66,  75,  76,  82,  14,  18,  92,  26,  34,  33,  42,
13349ab747fSPaolo Bonzini  50,  49,  58,  65,  73,  74,  89, 126,  25,  41,  20,   7,  15,  23,  31,  39,
13449ab747fSPaolo Bonzini  47,   2,  63,  71,  79, 118,  95, 108, 117, 125, 132, 107, 115, 116, 124, 105,
13549ab747fSPaolo Bonzini 114, 122, 112, 113, 127,  96,  97,  86,  94,  15,  23,  31,  39,  47,  55,  63,
13649ab747fSPaolo Bonzini  71,  79,  86,  94,   8,  16,  24,  32,  40,  48,  56,  64,  72,  80,  87, 111,
13749ab747fSPaolo Bonzini  19,  25,  57,  81,  83,  92,  95,  98,  99, 100, 101, 103, 104, 106, 109, 110
13849ab747fSPaolo Bonzini };
13949ab747fSPaolo Bonzini 
14049ab747fSPaolo Bonzini void ps2_queue(void *opaque, int b)
14149ab747fSPaolo Bonzini {
14249ab747fSPaolo Bonzini     PS2State *s = (PS2State *)opaque;
14349ab747fSPaolo Bonzini     PS2Queue *q = &s->queue;
14449ab747fSPaolo Bonzini 
1452858ab09SGonglei     if (q->count >= PS2_QUEUE_SIZE - 1)
14649ab747fSPaolo Bonzini         return;
14749ab747fSPaolo Bonzini     q->data[q->wptr] = b;
14849ab747fSPaolo Bonzini     if (++q->wptr == PS2_QUEUE_SIZE)
14949ab747fSPaolo Bonzini         q->wptr = 0;
15049ab747fSPaolo Bonzini     q->count++;
15149ab747fSPaolo Bonzini     s->update_irq(s->update_arg, 1);
15249ab747fSPaolo Bonzini }
15349ab747fSPaolo Bonzini 
15449ab747fSPaolo Bonzini /*
15549ab747fSPaolo Bonzini    keycode is expressed as follow:
15649ab747fSPaolo Bonzini    bit 7    - 0 key pressed, 1 = key released
15749ab747fSPaolo Bonzini    bits 6-0 - translated scancode set 2
15849ab747fSPaolo Bonzini  */
15949ab747fSPaolo Bonzini static void ps2_put_keycode(void *opaque, int keycode)
16049ab747fSPaolo Bonzini {
16149ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
16249ab747fSPaolo Bonzini 
1635edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
16449ab747fSPaolo Bonzini     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
16549ab747fSPaolo Bonzini     /* XXX: add support for scancode set 1 */
16649ab747fSPaolo Bonzini     if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) {
16749ab747fSPaolo Bonzini         if (keycode & 0x80) {
16849ab747fSPaolo Bonzini             ps2_queue(&s->common, 0xf0);
16949ab747fSPaolo Bonzini         }
17049ab747fSPaolo Bonzini         if (s->scancode_set == 2) {
17149ab747fSPaolo Bonzini             keycode = ps2_raw_keycode[keycode & 0x7f];
17249ab747fSPaolo Bonzini         } else if (s->scancode_set == 3) {
17349ab747fSPaolo Bonzini             keycode = ps2_raw_keycode_set3[keycode & 0x7f];
17449ab747fSPaolo Bonzini         }
17549ab747fSPaolo Bonzini       }
17649ab747fSPaolo Bonzini     ps2_queue(&s->common, keycode);
17749ab747fSPaolo Bonzini }
17849ab747fSPaolo Bonzini 
17966e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
18066e6536eSGerd Hoffmann                                InputEvent *evt)
18166e6536eSGerd Hoffmann {
18266e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
18366e6536eSGerd Hoffmann     int scancodes[3], i, count;
18466e6536eSGerd Hoffmann 
18566e6536eSGerd Hoffmann     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
18666e6536eSGerd Hoffmann     count = qemu_input_key_value_to_scancode(evt->key->key,
18766e6536eSGerd Hoffmann                                              evt->key->down,
18866e6536eSGerd Hoffmann                                              scancodes);
18966e6536eSGerd Hoffmann     for (i = 0; i < count; i++) {
19066e6536eSGerd Hoffmann         ps2_put_keycode(s, scancodes[i]);
19166e6536eSGerd Hoffmann     }
19266e6536eSGerd Hoffmann }
19366e6536eSGerd Hoffmann 
19449ab747fSPaolo Bonzini uint32_t ps2_read_data(void *opaque)
19549ab747fSPaolo Bonzini {
19649ab747fSPaolo Bonzini     PS2State *s = (PS2State *)opaque;
19749ab747fSPaolo Bonzini     PS2Queue *q;
19849ab747fSPaolo Bonzini     int val, index;
19949ab747fSPaolo Bonzini 
2005edab03dSDon Koch     trace_ps2_read_data(opaque);
20149ab747fSPaolo Bonzini     q = &s->queue;
20249ab747fSPaolo Bonzini     if (q->count == 0) {
20349ab747fSPaolo Bonzini         /* NOTE: if no data left, we return the last keyboard one
20449ab747fSPaolo Bonzini            (needed for EMM386) */
20549ab747fSPaolo Bonzini         /* XXX: need a timer to do things correctly */
20649ab747fSPaolo Bonzini         index = q->rptr - 1;
20749ab747fSPaolo Bonzini         if (index < 0)
20849ab747fSPaolo Bonzini             index = PS2_QUEUE_SIZE - 1;
20949ab747fSPaolo Bonzini         val = q->data[index];
21049ab747fSPaolo Bonzini     } else {
21149ab747fSPaolo Bonzini         val = q->data[q->rptr];
21249ab747fSPaolo Bonzini         if (++q->rptr == PS2_QUEUE_SIZE)
21349ab747fSPaolo Bonzini             q->rptr = 0;
21449ab747fSPaolo Bonzini         q->count--;
21549ab747fSPaolo Bonzini         /* reading deasserts IRQ */
21649ab747fSPaolo Bonzini         s->update_irq(s->update_arg, 0);
21749ab747fSPaolo Bonzini         /* reassert IRQs if data left */
21849ab747fSPaolo Bonzini         s->update_irq(s->update_arg, q->count != 0);
21949ab747fSPaolo Bonzini     }
22049ab747fSPaolo Bonzini     return val;
22149ab747fSPaolo Bonzini }
22249ab747fSPaolo Bonzini 
22349ab747fSPaolo Bonzini static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
22449ab747fSPaolo Bonzini {
2255edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
22649ab747fSPaolo Bonzini     s->ledstate = ledstate;
22749ab747fSPaolo Bonzini     kbd_put_ledstate(ledstate);
22849ab747fSPaolo Bonzini }
22949ab747fSPaolo Bonzini 
23049ab747fSPaolo Bonzini static void ps2_reset_keyboard(PS2KbdState *s)
23149ab747fSPaolo Bonzini {
2325edab03dSDon Koch     trace_ps2_reset_keyboard(s);
23349ab747fSPaolo Bonzini     s->scan_enabled = 1;
23449ab747fSPaolo Bonzini     s->scancode_set = 2;
23549ab747fSPaolo Bonzini     ps2_set_ledstate(s, 0);
23649ab747fSPaolo Bonzini }
23749ab747fSPaolo Bonzini 
23849ab747fSPaolo Bonzini void ps2_write_keyboard(void *opaque, int val)
23949ab747fSPaolo Bonzini {
24049ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)opaque;
24149ab747fSPaolo Bonzini 
2425edab03dSDon Koch     trace_ps2_write_keyboard(opaque, val);
24349ab747fSPaolo Bonzini     switch(s->common.write_cmd) {
24449ab747fSPaolo Bonzini     default:
24549ab747fSPaolo Bonzini     case -1:
24649ab747fSPaolo Bonzini         switch(val) {
24749ab747fSPaolo Bonzini         case 0x00:
24849ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
24949ab747fSPaolo Bonzini             break;
25049ab747fSPaolo Bonzini         case 0x05:
25149ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_RESEND);
25249ab747fSPaolo Bonzini             break;
25349ab747fSPaolo Bonzini         case KBD_CMD_GET_ID:
25449ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
25549ab747fSPaolo Bonzini             /* We emulate a MF2 AT keyboard here */
25649ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ID);
25749ab747fSPaolo Bonzini             if (s->translate)
25849ab747fSPaolo Bonzini                 ps2_queue(&s->common, 0x41);
25949ab747fSPaolo Bonzini             else
26049ab747fSPaolo Bonzini                 ps2_queue(&s->common, 0x83);
26149ab747fSPaolo Bonzini             break;
26249ab747fSPaolo Bonzini         case KBD_CMD_ECHO:
26349ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_CMD_ECHO);
26449ab747fSPaolo Bonzini             break;
26549ab747fSPaolo Bonzini         case KBD_CMD_ENABLE:
26649ab747fSPaolo Bonzini             s->scan_enabled = 1;
26749ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
26849ab747fSPaolo Bonzini             break;
26949ab747fSPaolo Bonzini         case KBD_CMD_SCANCODE:
27049ab747fSPaolo Bonzini         case KBD_CMD_SET_LEDS:
27149ab747fSPaolo Bonzini         case KBD_CMD_SET_RATE:
27249ab747fSPaolo Bonzini             s->common.write_cmd = val;
27349ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
27449ab747fSPaolo Bonzini             break;
27549ab747fSPaolo Bonzini         case KBD_CMD_RESET_DISABLE:
27649ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
27749ab747fSPaolo Bonzini             s->scan_enabled = 0;
27849ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
27949ab747fSPaolo Bonzini             break;
28049ab747fSPaolo Bonzini         case KBD_CMD_RESET_ENABLE:
28149ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
28249ab747fSPaolo Bonzini             s->scan_enabled = 1;
28349ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
28449ab747fSPaolo Bonzini             break;
28549ab747fSPaolo Bonzini         case KBD_CMD_RESET:
28649ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
28749ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
28849ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_POR);
28949ab747fSPaolo Bonzini             break;
29049ab747fSPaolo Bonzini         default:
29149ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
29249ab747fSPaolo Bonzini             break;
29349ab747fSPaolo Bonzini         }
29449ab747fSPaolo Bonzini         break;
29549ab747fSPaolo Bonzini     case KBD_CMD_SCANCODE:
29649ab747fSPaolo Bonzini         if (val == 0) {
29749ab747fSPaolo Bonzini             if (s->scancode_set == 1)
29849ab747fSPaolo Bonzini                 ps2_put_keycode(s, 0x43);
29949ab747fSPaolo Bonzini             else if (s->scancode_set == 2)
30049ab747fSPaolo Bonzini                 ps2_put_keycode(s, 0x41);
30149ab747fSPaolo Bonzini             else if (s->scancode_set == 3)
30249ab747fSPaolo Bonzini                 ps2_put_keycode(s, 0x3f);
30349ab747fSPaolo Bonzini         } else {
30449ab747fSPaolo Bonzini             if (val >= 1 && val <= 3)
30549ab747fSPaolo Bonzini                 s->scancode_set = val;
30649ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
30749ab747fSPaolo Bonzini         }
30849ab747fSPaolo Bonzini         s->common.write_cmd = -1;
30949ab747fSPaolo Bonzini         break;
31049ab747fSPaolo Bonzini     case KBD_CMD_SET_LEDS:
31149ab747fSPaolo Bonzini         ps2_set_ledstate(s, val);
31249ab747fSPaolo Bonzini         ps2_queue(&s->common, KBD_REPLY_ACK);
31349ab747fSPaolo Bonzini         s->common.write_cmd = -1;
31449ab747fSPaolo Bonzini         break;
31549ab747fSPaolo Bonzini     case KBD_CMD_SET_RATE:
31649ab747fSPaolo Bonzini         ps2_queue(&s->common, KBD_REPLY_ACK);
31749ab747fSPaolo Bonzini         s->common.write_cmd = -1;
31849ab747fSPaolo Bonzini         break;
31949ab747fSPaolo Bonzini     }
32049ab747fSPaolo Bonzini }
32149ab747fSPaolo Bonzini 
32249ab747fSPaolo Bonzini /* Set the scancode translation mode.
32349ab747fSPaolo Bonzini    0 = raw scancodes.
32449ab747fSPaolo Bonzini    1 = translated scancodes (used by qemu internally).  */
32549ab747fSPaolo Bonzini 
32649ab747fSPaolo Bonzini void ps2_keyboard_set_translation(void *opaque, int mode)
32749ab747fSPaolo Bonzini {
32849ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)opaque;
3295edab03dSDon Koch     trace_ps2_keyboard_set_translation(opaque, mode);
33049ab747fSPaolo Bonzini     s->translate = mode;
33149ab747fSPaolo Bonzini }
33249ab747fSPaolo Bonzini 
33349ab747fSPaolo Bonzini static void ps2_mouse_send_packet(PS2MouseState *s)
33449ab747fSPaolo Bonzini {
33549ab747fSPaolo Bonzini     unsigned int b;
33649ab747fSPaolo Bonzini     int dx1, dy1, dz1;
33749ab747fSPaolo Bonzini 
33849ab747fSPaolo Bonzini     dx1 = s->mouse_dx;
33949ab747fSPaolo Bonzini     dy1 = s->mouse_dy;
34049ab747fSPaolo Bonzini     dz1 = s->mouse_dz;
34149ab747fSPaolo Bonzini     /* XXX: increase range to 8 bits ? */
34249ab747fSPaolo Bonzini     if (dx1 > 127)
34349ab747fSPaolo Bonzini         dx1 = 127;
34449ab747fSPaolo Bonzini     else if (dx1 < -127)
34549ab747fSPaolo Bonzini         dx1 = -127;
34649ab747fSPaolo Bonzini     if (dy1 > 127)
34749ab747fSPaolo Bonzini         dy1 = 127;
34849ab747fSPaolo Bonzini     else if (dy1 < -127)
34949ab747fSPaolo Bonzini         dy1 = -127;
35049ab747fSPaolo Bonzini     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
35149ab747fSPaolo Bonzini     ps2_queue(&s->common, b);
35249ab747fSPaolo Bonzini     ps2_queue(&s->common, dx1 & 0xff);
35349ab747fSPaolo Bonzini     ps2_queue(&s->common, dy1 & 0xff);
35449ab747fSPaolo Bonzini     /* extra byte for IMPS/2 or IMEX */
35549ab747fSPaolo Bonzini     switch(s->mouse_type) {
35649ab747fSPaolo Bonzini     default:
35749ab747fSPaolo Bonzini         break;
35849ab747fSPaolo Bonzini     case 3:
35949ab747fSPaolo Bonzini         if (dz1 > 127)
36049ab747fSPaolo Bonzini             dz1 = 127;
36149ab747fSPaolo Bonzini         else if (dz1 < -127)
36249ab747fSPaolo Bonzini                 dz1 = -127;
36349ab747fSPaolo Bonzini         ps2_queue(&s->common, dz1 & 0xff);
36449ab747fSPaolo Bonzini         break;
36549ab747fSPaolo Bonzini     case 4:
36649ab747fSPaolo Bonzini         if (dz1 > 7)
36749ab747fSPaolo Bonzini             dz1 = 7;
36849ab747fSPaolo Bonzini         else if (dz1 < -7)
36949ab747fSPaolo Bonzini             dz1 = -7;
37049ab747fSPaolo Bonzini         b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
37149ab747fSPaolo Bonzini         ps2_queue(&s->common, b);
37249ab747fSPaolo Bonzini         break;
37349ab747fSPaolo Bonzini     }
37449ab747fSPaolo Bonzini 
3755edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
37649ab747fSPaolo Bonzini     /* update deltas */
37749ab747fSPaolo Bonzini     s->mouse_dx -= dx1;
37849ab747fSPaolo Bonzini     s->mouse_dy -= dy1;
37949ab747fSPaolo Bonzini     s->mouse_dz -= dz1;
38049ab747fSPaolo Bonzini }
38149ab747fSPaolo Bonzini 
3822a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
3832a766d29SGerd Hoffmann                             InputEvent *evt)
38449ab747fSPaolo Bonzini {
3852a766d29SGerd Hoffmann     static const int bmap[INPUT_BUTTON_MAX] = {
3862a766d29SGerd Hoffmann         [INPUT_BUTTON_LEFT]   = MOUSE_EVENT_LBUTTON,
3872a766d29SGerd Hoffmann         [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
3882a766d29SGerd Hoffmann         [INPUT_BUTTON_RIGHT]  = MOUSE_EVENT_RBUTTON,
3892a766d29SGerd Hoffmann     };
3902a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
39149ab747fSPaolo Bonzini 
39249ab747fSPaolo Bonzini     /* check if deltas are recorded when disabled */
39349ab747fSPaolo Bonzini     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
39449ab747fSPaolo Bonzini         return;
39549ab747fSPaolo Bonzini 
3962a766d29SGerd Hoffmann     switch (evt->kind) {
3972a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
3982a766d29SGerd Hoffmann         if (evt->rel->axis == INPUT_AXIS_X) {
3992a766d29SGerd Hoffmann             s->mouse_dx += evt->rel->value;
4002a766d29SGerd Hoffmann         } else if (evt->rel->axis == INPUT_AXIS_Y) {
4012a766d29SGerd Hoffmann             s->mouse_dy -= evt->rel->value;
4022a766d29SGerd Hoffmann         }
4032a766d29SGerd Hoffmann         break;
40449ab747fSPaolo Bonzini 
4052a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
4062a766d29SGerd Hoffmann         if (evt->btn->down) {
4072a766d29SGerd Hoffmann             s->mouse_buttons |= bmap[evt->btn->button];
4082a766d29SGerd Hoffmann             if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
4092a766d29SGerd Hoffmann                 s->mouse_dz--;
4102a766d29SGerd Hoffmann             } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
4112a766d29SGerd Hoffmann                 s->mouse_dz++;
4122a766d29SGerd Hoffmann             }
4132a766d29SGerd Hoffmann         } else {
4142a766d29SGerd Hoffmann             s->mouse_buttons &= ~bmap[evt->btn->button];
4152a766d29SGerd Hoffmann         }
4162a766d29SGerd Hoffmann         break;
4172a766d29SGerd Hoffmann 
4182a766d29SGerd Hoffmann     default:
4192a766d29SGerd Hoffmann         /* keep gcc happy */
4202a766d29SGerd Hoffmann         break;
4212a766d29SGerd Hoffmann     }
42249ab747fSPaolo Bonzini }
42349ab747fSPaolo Bonzini 
4242a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
4252a766d29SGerd Hoffmann {
4262a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
4272a766d29SGerd Hoffmann 
4282a766d29SGerd Hoffmann     if (s->mouse_buttons) {
4292a766d29SGerd Hoffmann         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
4302a766d29SGerd Hoffmann     }
4312858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
4322858ab09SGonglei         while (s->common.queue.count < PS2_QUEUE_SIZE - 4) {
43349ab747fSPaolo Bonzini             /* if not remote, send event. Multiple events are sent if
43449ab747fSPaolo Bonzini                too big deltas */
43549ab747fSPaolo Bonzini             ps2_mouse_send_packet(s);
43649ab747fSPaolo Bonzini             if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
43749ab747fSPaolo Bonzini                 break;
43849ab747fSPaolo Bonzini         }
43949ab747fSPaolo Bonzini     }
44049ab747fSPaolo Bonzini }
44149ab747fSPaolo Bonzini 
44249ab747fSPaolo Bonzini void ps2_mouse_fake_event(void *opaque)
44349ab747fSPaolo Bonzini {
4442a766d29SGerd Hoffmann     PS2MouseState *s = opaque;
4455edab03dSDon Koch     trace_ps2_mouse_fake_event(opaque);
4462a766d29SGerd Hoffmann     s->mouse_dx++;
4472a766d29SGerd Hoffmann     ps2_mouse_sync(opaque);
44849ab747fSPaolo Bonzini }
44949ab747fSPaolo Bonzini 
45049ab747fSPaolo Bonzini void ps2_write_mouse(void *opaque, int val)
45149ab747fSPaolo Bonzini {
45249ab747fSPaolo Bonzini     PS2MouseState *s = (PS2MouseState *)opaque;
4535edab03dSDon Koch 
4545edab03dSDon Koch     trace_ps2_write_mouse(opaque, val);
45549ab747fSPaolo Bonzini #ifdef DEBUG_MOUSE
45649ab747fSPaolo Bonzini     printf("kbd: write mouse 0x%02x\n", val);
45749ab747fSPaolo Bonzini #endif
45849ab747fSPaolo Bonzini     switch(s->common.write_cmd) {
45949ab747fSPaolo Bonzini     default:
46049ab747fSPaolo Bonzini     case -1:
46149ab747fSPaolo Bonzini         /* mouse command */
46249ab747fSPaolo Bonzini         if (s->mouse_wrap) {
46349ab747fSPaolo Bonzini             if (val == AUX_RESET_WRAP) {
46449ab747fSPaolo Bonzini                 s->mouse_wrap = 0;
46549ab747fSPaolo Bonzini                 ps2_queue(&s->common, AUX_ACK);
46649ab747fSPaolo Bonzini                 return;
46749ab747fSPaolo Bonzini             } else if (val != AUX_RESET) {
46849ab747fSPaolo Bonzini                 ps2_queue(&s->common, val);
46949ab747fSPaolo Bonzini                 return;
47049ab747fSPaolo Bonzini             }
47149ab747fSPaolo Bonzini         }
47249ab747fSPaolo Bonzini         switch(val) {
47349ab747fSPaolo Bonzini         case AUX_SET_SCALE11:
47449ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
47549ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
47649ab747fSPaolo Bonzini             break;
47749ab747fSPaolo Bonzini         case AUX_SET_SCALE21:
47849ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_SCALE21;
47949ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
48049ab747fSPaolo Bonzini             break;
48149ab747fSPaolo Bonzini         case AUX_SET_STREAM:
48249ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
48349ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
48449ab747fSPaolo Bonzini             break;
48549ab747fSPaolo Bonzini         case AUX_SET_WRAP:
48649ab747fSPaolo Bonzini             s->mouse_wrap = 1;
48749ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
48849ab747fSPaolo Bonzini             break;
48949ab747fSPaolo Bonzini         case AUX_SET_REMOTE:
49049ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_REMOTE;
49149ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
49249ab747fSPaolo Bonzini             break;
49349ab747fSPaolo Bonzini         case AUX_GET_TYPE:
49449ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
49549ab747fSPaolo Bonzini             ps2_queue(&s->common, s->mouse_type);
49649ab747fSPaolo Bonzini             break;
49749ab747fSPaolo Bonzini         case AUX_SET_RES:
49849ab747fSPaolo Bonzini         case AUX_SET_SAMPLE:
49949ab747fSPaolo Bonzini             s->common.write_cmd = val;
50049ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
50149ab747fSPaolo Bonzini             break;
50249ab747fSPaolo Bonzini         case AUX_GET_SCALE:
50349ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
50449ab747fSPaolo Bonzini             ps2_queue(&s->common, s->mouse_status);
50549ab747fSPaolo Bonzini             ps2_queue(&s->common, s->mouse_resolution);
50649ab747fSPaolo Bonzini             ps2_queue(&s->common, s->mouse_sample_rate);
50749ab747fSPaolo Bonzini             break;
50849ab747fSPaolo Bonzini         case AUX_POLL:
50949ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
51049ab747fSPaolo Bonzini             ps2_mouse_send_packet(s);
51149ab747fSPaolo Bonzini             break;
51249ab747fSPaolo Bonzini         case AUX_ENABLE_DEV:
51349ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_ENABLED;
51449ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
51549ab747fSPaolo Bonzini             break;
51649ab747fSPaolo Bonzini         case AUX_DISABLE_DEV:
51749ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
51849ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
51949ab747fSPaolo Bonzini             break;
52049ab747fSPaolo Bonzini         case AUX_SET_DEFAULT:
52149ab747fSPaolo Bonzini             s->mouse_sample_rate = 100;
52249ab747fSPaolo Bonzini             s->mouse_resolution = 2;
52349ab747fSPaolo Bonzini             s->mouse_status = 0;
52449ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
52549ab747fSPaolo Bonzini             break;
52649ab747fSPaolo Bonzini         case AUX_RESET:
52749ab747fSPaolo Bonzini             s->mouse_sample_rate = 100;
52849ab747fSPaolo Bonzini             s->mouse_resolution = 2;
52949ab747fSPaolo Bonzini             s->mouse_status = 0;
53049ab747fSPaolo Bonzini             s->mouse_type = 0;
53149ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
53249ab747fSPaolo Bonzini             ps2_queue(&s->common, 0xaa);
53349ab747fSPaolo Bonzini             ps2_queue(&s->common, s->mouse_type);
53449ab747fSPaolo Bonzini             break;
53549ab747fSPaolo Bonzini         default:
53649ab747fSPaolo Bonzini             break;
53749ab747fSPaolo Bonzini         }
53849ab747fSPaolo Bonzini         break;
53949ab747fSPaolo Bonzini     case AUX_SET_SAMPLE:
54049ab747fSPaolo Bonzini         s->mouse_sample_rate = val;
54149ab747fSPaolo Bonzini         /* detect IMPS/2 or IMEX */
54249ab747fSPaolo Bonzini         switch(s->mouse_detect_state) {
54349ab747fSPaolo Bonzini         default:
54449ab747fSPaolo Bonzini         case 0:
54549ab747fSPaolo Bonzini             if (val == 200)
54649ab747fSPaolo Bonzini                 s->mouse_detect_state = 1;
54749ab747fSPaolo Bonzini             break;
54849ab747fSPaolo Bonzini         case 1:
54949ab747fSPaolo Bonzini             if (val == 100)
55049ab747fSPaolo Bonzini                 s->mouse_detect_state = 2;
55149ab747fSPaolo Bonzini             else if (val == 200)
55249ab747fSPaolo Bonzini                 s->mouse_detect_state = 3;
55349ab747fSPaolo Bonzini             else
55449ab747fSPaolo Bonzini                 s->mouse_detect_state = 0;
55549ab747fSPaolo Bonzini             break;
55649ab747fSPaolo Bonzini         case 2:
55749ab747fSPaolo Bonzini             if (val == 80)
55849ab747fSPaolo Bonzini                 s->mouse_type = 3; /* IMPS/2 */
55949ab747fSPaolo Bonzini             s->mouse_detect_state = 0;
56049ab747fSPaolo Bonzini             break;
56149ab747fSPaolo Bonzini         case 3:
56249ab747fSPaolo Bonzini             if (val == 80)
56349ab747fSPaolo Bonzini                 s->mouse_type = 4; /* IMEX */
56449ab747fSPaolo Bonzini             s->mouse_detect_state = 0;
56549ab747fSPaolo Bonzini             break;
56649ab747fSPaolo Bonzini         }
56749ab747fSPaolo Bonzini         ps2_queue(&s->common, AUX_ACK);
56849ab747fSPaolo Bonzini         s->common.write_cmd = -1;
56949ab747fSPaolo Bonzini         break;
57049ab747fSPaolo Bonzini     case AUX_SET_RES:
57149ab747fSPaolo Bonzini         s->mouse_resolution = val;
57249ab747fSPaolo Bonzini         ps2_queue(&s->common, AUX_ACK);
57349ab747fSPaolo Bonzini         s->common.write_cmd = -1;
57449ab747fSPaolo Bonzini         break;
57549ab747fSPaolo Bonzini     }
57649ab747fSPaolo Bonzini }
57749ab747fSPaolo Bonzini 
57849ab747fSPaolo Bonzini static void ps2_common_reset(PS2State *s)
57949ab747fSPaolo Bonzini {
58049ab747fSPaolo Bonzini     PS2Queue *q;
58149ab747fSPaolo Bonzini     s->write_cmd = -1;
58249ab747fSPaolo Bonzini     q = &s->queue;
58349ab747fSPaolo Bonzini     q->rptr = 0;
58449ab747fSPaolo Bonzini     q->wptr = 0;
58549ab747fSPaolo Bonzini     q->count = 0;
58649ab747fSPaolo Bonzini     s->update_irq(s->update_arg, 0);
58749ab747fSPaolo Bonzini }
58849ab747fSPaolo Bonzini 
5892858ab09SGonglei static void ps2_common_post_load(PS2State *s)
5902858ab09SGonglei {
5912858ab09SGonglei     PS2Queue *q = &s->queue;
5922858ab09SGonglei     int size;
5932858ab09SGonglei     int i;
5942858ab09SGonglei     int tmp_data[PS2_QUEUE_SIZE];
5952858ab09SGonglei 
5962858ab09SGonglei     /* set the useful data buffer queue size, < PS2_QUEUE_SIZE */
5972858ab09SGonglei     size = q->count > PS2_QUEUE_SIZE ? 0 : q->count;
5982858ab09SGonglei 
5992858ab09SGonglei     /* move the queue elements to the start of data array */
6002858ab09SGonglei     if (size > 0) {
6012858ab09SGonglei         for (i = 0; i < size; i++) {
6022858ab09SGonglei             /* move the queue elements to the temporary buffer */
6032858ab09SGonglei             tmp_data[i] = q->data[q->rptr];
6042858ab09SGonglei             if (++q->rptr == 256) {
6052858ab09SGonglei                 q->rptr = 0;
6062858ab09SGonglei             }
6072858ab09SGonglei         }
6082858ab09SGonglei         memcpy(q->data, tmp_data, size);
6092858ab09SGonglei     }
6102858ab09SGonglei     /* reset rptr/wptr/count */
6112858ab09SGonglei     q->rptr = 0;
6122858ab09SGonglei     q->wptr = size;
6132858ab09SGonglei     q->count = size;
6142858ab09SGonglei     s->update_irq(s->update_arg, q->count != 0);
6152858ab09SGonglei }
6162858ab09SGonglei 
61749ab747fSPaolo Bonzini static void ps2_kbd_reset(void *opaque)
61849ab747fSPaolo Bonzini {
61949ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *) opaque;
62049ab747fSPaolo Bonzini 
6215edab03dSDon Koch     trace_ps2_kbd_reset(opaque);
62249ab747fSPaolo Bonzini     ps2_common_reset(&s->common);
62349ab747fSPaolo Bonzini     s->scan_enabled = 0;
62449ab747fSPaolo Bonzini     s->translate = 0;
62549ab747fSPaolo Bonzini     s->scancode_set = 0;
62649ab747fSPaolo Bonzini }
62749ab747fSPaolo Bonzini 
62849ab747fSPaolo Bonzini static void ps2_mouse_reset(void *opaque)
62949ab747fSPaolo Bonzini {
63049ab747fSPaolo Bonzini     PS2MouseState *s = (PS2MouseState *) opaque;
63149ab747fSPaolo Bonzini 
6325edab03dSDon Koch     trace_ps2_mouse_reset(opaque);
63349ab747fSPaolo Bonzini     ps2_common_reset(&s->common);
63449ab747fSPaolo Bonzini     s->mouse_status = 0;
63549ab747fSPaolo Bonzini     s->mouse_resolution = 0;
63649ab747fSPaolo Bonzini     s->mouse_sample_rate = 0;
63749ab747fSPaolo Bonzini     s->mouse_wrap = 0;
63849ab747fSPaolo Bonzini     s->mouse_type = 0;
63949ab747fSPaolo Bonzini     s->mouse_detect_state = 0;
64049ab747fSPaolo Bonzini     s->mouse_dx = 0;
64149ab747fSPaolo Bonzini     s->mouse_dy = 0;
64249ab747fSPaolo Bonzini     s->mouse_dz = 0;
64349ab747fSPaolo Bonzini     s->mouse_buttons = 0;
64449ab747fSPaolo Bonzini }
64549ab747fSPaolo Bonzini 
64649ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_common = {
64749ab747fSPaolo Bonzini     .name = "PS2 Common State",
64849ab747fSPaolo Bonzini     .version_id = 3,
64949ab747fSPaolo Bonzini     .minimum_version_id = 2,
65049ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
65149ab747fSPaolo Bonzini         VMSTATE_INT32(write_cmd, PS2State),
65249ab747fSPaolo Bonzini         VMSTATE_INT32(queue.rptr, PS2State),
65349ab747fSPaolo Bonzini         VMSTATE_INT32(queue.wptr, PS2State),
65449ab747fSPaolo Bonzini         VMSTATE_INT32(queue.count, PS2State),
65549ab747fSPaolo Bonzini         VMSTATE_BUFFER(queue.data, PS2State),
65649ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
65749ab747fSPaolo Bonzini     }
65849ab747fSPaolo Bonzini };
65949ab747fSPaolo Bonzini 
66049ab747fSPaolo Bonzini static bool ps2_keyboard_ledstate_needed(void *opaque)
66149ab747fSPaolo Bonzini {
66249ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
66349ab747fSPaolo Bonzini 
66449ab747fSPaolo Bonzini     return s->ledstate != 0; /* 0 is default state */
66549ab747fSPaolo Bonzini }
66649ab747fSPaolo Bonzini 
66749ab747fSPaolo Bonzini static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
66849ab747fSPaolo Bonzini {
66949ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
67049ab747fSPaolo Bonzini 
67149ab747fSPaolo Bonzini     kbd_put_ledstate(s->ledstate);
67249ab747fSPaolo Bonzini     return 0;
67349ab747fSPaolo Bonzini }
67449ab747fSPaolo Bonzini 
67549ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
67649ab747fSPaolo Bonzini     .name = "ps2kbd/ledstate",
67749ab747fSPaolo Bonzini     .version_id = 3,
67849ab747fSPaolo Bonzini     .minimum_version_id = 2,
67949ab747fSPaolo Bonzini     .post_load = ps2_kbd_ledstate_post_load,
680*5cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
68149ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
68249ab747fSPaolo Bonzini         VMSTATE_INT32(ledstate, PS2KbdState),
68349ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
68449ab747fSPaolo Bonzini     }
68549ab747fSPaolo Bonzini };
68649ab747fSPaolo Bonzini 
68749ab747fSPaolo Bonzini static int ps2_kbd_post_load(void* opaque, int version_id)
68849ab747fSPaolo Bonzini {
68949ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState*)opaque;
6902858ab09SGonglei     PS2State *ps2 = &s->common;
69149ab747fSPaolo Bonzini 
69249ab747fSPaolo Bonzini     if (version_id == 2)
69349ab747fSPaolo Bonzini         s->scancode_set=2;
6942858ab09SGonglei 
6952858ab09SGonglei     ps2_common_post_load(ps2);
6962858ab09SGonglei 
69749ab747fSPaolo Bonzini     return 0;
69849ab747fSPaolo Bonzini }
69949ab747fSPaolo Bonzini 
7002858ab09SGonglei static void ps2_kbd_pre_save(void *opaque)
7012858ab09SGonglei {
7022858ab09SGonglei     PS2KbdState *s = (PS2KbdState *)opaque;
7032858ab09SGonglei     PS2State *ps2 = &s->common;
7042858ab09SGonglei 
7052858ab09SGonglei     ps2_common_post_load(ps2);
7062858ab09SGonglei }
7072858ab09SGonglei 
70849ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_keyboard = {
70949ab747fSPaolo Bonzini     .name = "ps2kbd",
71049ab747fSPaolo Bonzini     .version_id = 3,
71149ab747fSPaolo Bonzini     .minimum_version_id = 2,
71249ab747fSPaolo Bonzini     .post_load = ps2_kbd_post_load,
7132858ab09SGonglei     .pre_save = ps2_kbd_pre_save,
71449ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
71549ab747fSPaolo Bonzini         VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
71649ab747fSPaolo Bonzini         VMSTATE_INT32(scan_enabled, PS2KbdState),
71749ab747fSPaolo Bonzini         VMSTATE_INT32(translate, PS2KbdState),
71849ab747fSPaolo Bonzini         VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
71949ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
72049ab747fSPaolo Bonzini     },
721*5cd8cadaSJuan Quintela     .subsections = (const VMStateDescription*[]) {
722*5cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
723*5cd8cadaSJuan Quintela         NULL
72449ab747fSPaolo Bonzini     }
72549ab747fSPaolo Bonzini };
72649ab747fSPaolo Bonzini 
7272858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
7282858ab09SGonglei {
7292858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
7302858ab09SGonglei     PS2State *ps2 = &s->common;
7312858ab09SGonglei 
7322858ab09SGonglei     ps2_common_post_load(ps2);
7332858ab09SGonglei 
7342858ab09SGonglei     return 0;
7352858ab09SGonglei }
7362858ab09SGonglei 
7372858ab09SGonglei static void ps2_mouse_pre_save(void *opaque)
7382858ab09SGonglei {
7392858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
7402858ab09SGonglei     PS2State *ps2 = &s->common;
7412858ab09SGonglei 
7422858ab09SGonglei     ps2_common_post_load(ps2);
7432858ab09SGonglei }
7442858ab09SGonglei 
74549ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_mouse = {
74649ab747fSPaolo Bonzini     .name = "ps2mouse",
74749ab747fSPaolo Bonzini     .version_id = 2,
74849ab747fSPaolo Bonzini     .minimum_version_id = 2,
7492858ab09SGonglei     .post_load = ps2_mouse_post_load,
7502858ab09SGonglei     .pre_save = ps2_mouse_pre_save,
75149ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
75249ab747fSPaolo Bonzini         VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
75349ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_status, PS2MouseState),
75449ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
75549ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
75649ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
75749ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_type, PS2MouseState),
75849ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
75949ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dx, PS2MouseState),
76049ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dy, PS2MouseState),
76149ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dz, PS2MouseState),
76249ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
76349ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
76449ab747fSPaolo Bonzini     }
76549ab747fSPaolo Bonzini };
76649ab747fSPaolo Bonzini 
76766e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
76866e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
76966e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
77066e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
77166e6536eSGerd Hoffmann };
77266e6536eSGerd Hoffmann 
77349ab747fSPaolo Bonzini void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
77449ab747fSPaolo Bonzini {
77549ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState));
77649ab747fSPaolo Bonzini 
7775edab03dSDon Koch     trace_ps2_kbd_init(s);
77849ab747fSPaolo Bonzini     s->common.update_irq = update_irq;
77949ab747fSPaolo Bonzini     s->common.update_arg = update_arg;
78049ab747fSPaolo Bonzini     s->scancode_set = 2;
78149ab747fSPaolo Bonzini     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
78266e6536eSGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
78366e6536eSGerd Hoffmann                                 &ps2_keyboard_handler);
78449ab747fSPaolo Bonzini     qemu_register_reset(ps2_kbd_reset, s);
78549ab747fSPaolo Bonzini     return s;
78649ab747fSPaolo Bonzini }
78749ab747fSPaolo Bonzini 
7882a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
7892a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
7902a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
7912a766d29SGerd Hoffmann     .event = ps2_mouse_event,
7922a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
7932a766d29SGerd Hoffmann };
7942a766d29SGerd Hoffmann 
79549ab747fSPaolo Bonzini void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
79649ab747fSPaolo Bonzini {
79749ab747fSPaolo Bonzini     PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState));
79849ab747fSPaolo Bonzini 
7995edab03dSDon Koch     trace_ps2_mouse_init(s);
80049ab747fSPaolo Bonzini     s->common.update_irq = update_irq;
80149ab747fSPaolo Bonzini     s->common.update_arg = update_arg;
80249ab747fSPaolo Bonzini     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
8032a766d29SGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
8042a766d29SGerd Hoffmann                                 &ps2_mouse_handler);
80549ab747fSPaolo Bonzini     qemu_register_reset(ps2_mouse_reset, s);
80649ab747fSPaolo Bonzini     return s;
80749ab747fSPaolo Bonzini }
808