xref: /qemu/hw/input/ps2.c (revision 76968101)
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  */
2471e8a915SMarkus Armbruster 
250430891cSPeter Maydell #include "qemu/osdep.h"
26ec044a80SHervé Poussineau #include "qemu/log.h"
2749ab747fSPaolo Bonzini #include "hw/input/ps2.h"
28d6454270SMarkus Armbruster #include "migration/vmstate.h"
2949ab747fSPaolo Bonzini #include "ui/console.h"
3066e6536eSGerd Hoffmann #include "ui/input.h"
3171e8a915SMarkus Armbruster #include "sysemu/reset.h"
3254d31236SMarkus Armbruster #include "sysemu/runstate.h"
3349ab747fSPaolo Bonzini 
345edab03dSDon Koch #include "trace.h"
355edab03dSDon Koch 
3649ab747fSPaolo Bonzini /* Keyboard Commands */
3749ab747fSPaolo Bonzini #define KBD_CMD_SET_LEDS	0xED	/* Set keyboard leds */
3849ab747fSPaolo Bonzini #define KBD_CMD_ECHO     	0xEE
3949ab747fSPaolo Bonzini #define KBD_CMD_SCANCODE	0xF0	/* Get/set scancode set */
4049ab747fSPaolo Bonzini #define KBD_CMD_GET_ID 	        0xF2	/* get keyboard ID */
4149ab747fSPaolo Bonzini #define KBD_CMD_SET_RATE	0xF3	/* Set typematic rate */
4249ab747fSPaolo Bonzini #define KBD_CMD_ENABLE		0xF4	/* Enable scanning */
4349ab747fSPaolo Bonzini #define KBD_CMD_RESET_DISABLE	0xF5	/* reset and disable scanning */
4449ab747fSPaolo Bonzini #define KBD_CMD_RESET_ENABLE   	0xF6    /* reset and enable scanning */
4549ab747fSPaolo Bonzini #define KBD_CMD_RESET		0xFF	/* Reset */
46c56b6209SSven Schnelle #define KBD_CMD_SET_MAKE_BREAK  0xFC    /* Set Make and Break mode */
47c56b6209SSven Schnelle #define KBD_CMD_SET_TYPEMATIC   0xFA    /* Set Typematic Make and Break mode */
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 
79620775d1SDaniel P. Berrange /* Bits for 'modifiers' field in PS2KbdState */
80620775d1SDaniel P. Berrange #define MOD_CTRL_L  (1 << 0)
81620775d1SDaniel P. Berrange #define MOD_SHIFT_L (1 << 1)
82620775d1SDaniel P. Berrange #define MOD_ALT_L   (1 << 2)
83620775d1SDaniel P. Berrange #define MOD_CTRL_R  (1 << 3)
84620775d1SDaniel P. Berrange #define MOD_SHIFT_R (1 << 4)
85620775d1SDaniel P. Berrange #define MOD_ALT_R   (1 << 5)
86620775d1SDaniel P. Berrange 
8749ab747fSPaolo Bonzini typedef struct {
882858ab09SGonglei     /* Keep the data array 256 bytes long, which compatibility
892858ab09SGonglei      with older qemu versions. */
902858ab09SGonglei     uint8_t data[256];
9149ab747fSPaolo Bonzini     int rptr, wptr, count;
9249ab747fSPaolo Bonzini } PS2Queue;
9349ab747fSPaolo Bonzini 
948498bb8dSGerd Hoffmann struct PS2State {
9549ab747fSPaolo Bonzini     PS2Queue queue;
9649ab747fSPaolo Bonzini     int32_t write_cmd;
9749ab747fSPaolo Bonzini     void (*update_irq)(void *, int);
9849ab747fSPaolo Bonzini     void *update_arg;
998498bb8dSGerd Hoffmann };
10049ab747fSPaolo Bonzini 
10149ab747fSPaolo Bonzini typedef struct {
10249ab747fSPaolo Bonzini     PS2State common;
10349ab747fSPaolo Bonzini     int scan_enabled;
10449ab747fSPaolo Bonzini     int translate;
10549ab747fSPaolo Bonzini     int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
10649ab747fSPaolo Bonzini     int ledstate;
10757d5c005SHervé Poussineau     bool need_high_bit;
108620775d1SDaniel P. Berrange     unsigned int modifiers; /* bitmask of MOD_* constants above */
10949ab747fSPaolo Bonzini } PS2KbdState;
11049ab747fSPaolo Bonzini 
11149ab747fSPaolo Bonzini typedef struct {
11249ab747fSPaolo Bonzini     PS2State common;
11349ab747fSPaolo Bonzini     uint8_t mouse_status;
11449ab747fSPaolo Bonzini     uint8_t mouse_resolution;
11549ab747fSPaolo Bonzini     uint8_t mouse_sample_rate;
11649ab747fSPaolo Bonzini     uint8_t mouse_wrap;
11749ab747fSPaolo Bonzini     uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
11849ab747fSPaolo Bonzini     uint8_t mouse_detect_state;
11949ab747fSPaolo Bonzini     int mouse_dx; /* current values, needed for 'poll' mode */
12049ab747fSPaolo Bonzini     int mouse_dy;
12149ab747fSPaolo Bonzini     int mouse_dz;
12249ab747fSPaolo Bonzini     uint8_t mouse_buttons;
12349ab747fSPaolo Bonzini } PS2MouseState;
12449ab747fSPaolo Bonzini 
12557d5c005SHervé Poussineau static uint8_t translate_table[256] = {
12657d5c005SHervé Poussineau     0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
12757d5c005SHervé Poussineau     0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
12857d5c005SHervé Poussineau     0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
12957d5c005SHervé Poussineau     0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
13057d5c005SHervé Poussineau     0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
13157d5c005SHervé Poussineau     0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
13257d5c005SHervé Poussineau     0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
13357d5c005SHervé Poussineau     0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
13457d5c005SHervé Poussineau     0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
13557d5c005SHervé Poussineau     0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
13657d5c005SHervé Poussineau     0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
13757d5c005SHervé Poussineau     0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
13857d5c005SHervé Poussineau     0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
13957d5c005SHervé Poussineau     0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
14057d5c005SHervé Poussineau     0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
14157d5c005SHervé Poussineau     0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
14257d5c005SHervé Poussineau     0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
14357d5c005SHervé Poussineau     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
14457d5c005SHervé Poussineau     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
14557d5c005SHervé Poussineau     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
14657d5c005SHervé Poussineau     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
14757d5c005SHervé Poussineau     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
14857d5c005SHervé Poussineau     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
14957d5c005SHervé Poussineau     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
15057d5c005SHervé Poussineau     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
15157d5c005SHervé Poussineau     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
15257d5c005SHervé Poussineau     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
15357d5c005SHervé Poussineau     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
15457d5c005SHervé Poussineau     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
15557d5c005SHervé Poussineau     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
15657d5c005SHervé Poussineau     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
15757d5c005SHervé Poussineau     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
15857d5c005SHervé Poussineau };
15957d5c005SHervé Poussineau 
160620775d1SDaniel P. Berrange static unsigned int ps2_modifier_bit(QKeyCode key)
161620775d1SDaniel P. Berrange {
162620775d1SDaniel P. Berrange     switch (key) {
163620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL:
164620775d1SDaniel P. Berrange         return MOD_CTRL_L;
165620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL_R:
166620775d1SDaniel P. Berrange         return MOD_CTRL_R;
167620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT:
168620775d1SDaniel P. Berrange         return MOD_SHIFT_L;
169620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT_R:
170620775d1SDaniel P. Berrange         return MOD_SHIFT_R;
171620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT:
172620775d1SDaniel P. Berrange         return MOD_ALT_L;
173620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT_R:
174620775d1SDaniel P. Berrange         return MOD_ALT_R;
175620775d1SDaniel P. Berrange     default:
176620775d1SDaniel P. Berrange         return 0;
177620775d1SDaniel P. Berrange     }
178620775d1SDaniel P. Berrange }
179620775d1SDaniel P. Berrange 
180954ee55bSGerd Hoffmann static void ps2_reset_queue(PS2State *s)
181954ee55bSGerd Hoffmann {
182954ee55bSGerd Hoffmann     PS2Queue *q = &s->queue;
183954ee55bSGerd Hoffmann 
184954ee55bSGerd Hoffmann     q->rptr = 0;
185954ee55bSGerd Hoffmann     q->wptr = 0;
186954ee55bSGerd Hoffmann     q->count = 0;
187954ee55bSGerd Hoffmann }
188954ee55bSGerd Hoffmann 
1892a6505b0SSven Schnelle int ps2_queue_empty(PS2State *s)
1902a6505b0SSven Schnelle {
1912a6505b0SSven Schnelle     return s->queue.count == 0;
1922a6505b0SSven Schnelle }
1932a6505b0SSven Schnelle 
1947abe7eb2SGeoffrey McRae void ps2_queue_noirq(PS2State *s, int b)
19549ab747fSPaolo Bonzini {
19649ab747fSPaolo Bonzini     PS2Queue *q = &s->queue;
19749ab747fSPaolo Bonzini 
1987abe7eb2SGeoffrey McRae     if (q->count == PS2_QUEUE_SIZE) {
19949ab747fSPaolo Bonzini         return;
2007abe7eb2SGeoffrey McRae     }
2017abe7eb2SGeoffrey McRae 
20249ab747fSPaolo Bonzini     q->data[q->wptr] = b;
20349ab747fSPaolo Bonzini     if (++q->wptr == PS2_QUEUE_SIZE)
20449ab747fSPaolo Bonzini         q->wptr = 0;
20549ab747fSPaolo Bonzini     q->count++;
2067abe7eb2SGeoffrey McRae }
2077abe7eb2SGeoffrey McRae 
2087abe7eb2SGeoffrey McRae void ps2_raise_irq(PS2State *s)
2097abe7eb2SGeoffrey McRae {
2107abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2117abe7eb2SGeoffrey McRae }
2127abe7eb2SGeoffrey McRae 
2137abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
2147abe7eb2SGeoffrey McRae {
2157abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
2167abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2177abe7eb2SGeoffrey McRae }
2187abe7eb2SGeoffrey McRae 
2197abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
2207abe7eb2SGeoffrey McRae {
2217abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
2227abe7eb2SGeoffrey McRae         return;
2237abe7eb2SGeoffrey McRae     }
2247abe7eb2SGeoffrey McRae 
2257abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2267abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2277abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2287abe7eb2SGeoffrey McRae }
2297abe7eb2SGeoffrey McRae 
2307abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2317abe7eb2SGeoffrey McRae {
2327abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2337abe7eb2SGeoffrey McRae         return;
2347abe7eb2SGeoffrey McRae     }
2357abe7eb2SGeoffrey McRae 
2367abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2377abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2387abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2397abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2407abe7eb2SGeoffrey McRae }
2417abe7eb2SGeoffrey McRae 
2427abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2437abe7eb2SGeoffrey McRae {
2447abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2457abe7eb2SGeoffrey McRae         return;
2467abe7eb2SGeoffrey McRae     }
2477abe7eb2SGeoffrey McRae 
2487abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2497abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2507abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2517abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
25249ab747fSPaolo Bonzini     s->update_irq(s->update_arg, 1);
25349ab747fSPaolo Bonzini }
25449ab747fSPaolo Bonzini 
25557d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
25649ab747fSPaolo Bonzini static void ps2_put_keycode(void *opaque, int keycode)
25749ab747fSPaolo Bonzini {
25849ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
25949ab747fSPaolo Bonzini 
2605edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
261fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
26257d5c005SHervé Poussineau 
26357d5c005SHervé Poussineau     if (s->translate) {
26457d5c005SHervé Poussineau         if (keycode == 0xf0) {
26557d5c005SHervé Poussineau             s->need_high_bit = true;
26657d5c005SHervé Poussineau         } else if (s->need_high_bit) {
26757d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode] | 0x80);
26857d5c005SHervé Poussineau             s->need_high_bit = false;
26957d5c005SHervé Poussineau         } else {
27057d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode]);
27149ab747fSPaolo Bonzini         }
27257d5c005SHervé Poussineau     } else {
27349ab747fSPaolo Bonzini         ps2_queue(&s->common, keycode);
27449ab747fSPaolo Bonzini     }
27557d5c005SHervé Poussineau }
27649ab747fSPaolo Bonzini 
27766e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
27866e6536eSGerd Hoffmann                                InputEvent *evt)
27966e6536eSGerd Hoffmann {
28066e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
28132bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
2828c10e0baSHervé Poussineau     int qcode;
283ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
284620775d1SDaniel P. Berrange     int mod;
28566e6536eSGerd Hoffmann 
286143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
287143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
288143c04c7SGeoffrey McRae         return;
289143c04c7SGeoffrey McRae     }
290143c04c7SGeoffrey McRae 
291fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
2928c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
2938c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
29457d5c005SHervé Poussineau 
295620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
296644f66bfSDaniel P. Berrangé     trace_ps2_keyboard_event(s, qcode, key->down, mod,
297644f66bfSDaniel P. Berrangé                              s->modifiers, s->scancode_set, s->translate);
298620775d1SDaniel P. Berrange     if (key->down) {
299620775d1SDaniel P. Berrange         s->modifiers |= mod;
300620775d1SDaniel P. Berrange     } else {
301620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
302620775d1SDaniel P. Berrange     }
303620775d1SDaniel P. Berrange 
3048c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3058c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
30629fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
30729fd23a5SDaniel P. Berrange                 if (key->down) {
30829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
30929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
31029fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
31129fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
31229fd23a5SDaniel P. Berrange                 }
31329fd23a5SDaniel P. Berrange             } else {
3148c10e0baSHervé Poussineau                 if (key->down) {
3158c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3168c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3178c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
318927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3198c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
3208c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
3218c10e0baSHervé Poussineau                 }
32229fd23a5SDaniel P. Berrange             }
3238c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
324620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
325620775d1SDaniel P. Berrange                 if (key->down) {
326620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
327620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
328620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
329620775d1SDaniel P. Berrange                 } else {
330620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
331620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
332620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
333620775d1SDaniel P. Berrange                 }
334620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
335620775d1SDaniel P. Berrange                 if (key->down) {
336620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
337620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
338620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
339620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
340620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
341620775d1SDaniel P. Berrange                 } else {
342620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
343620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
344620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
345620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
346620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
347620775d1SDaniel P. Berrange                 }
3488f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
3498f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
3508f63458fSDaniel P. Berrange                 if (key->down) {
3518f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3528f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
3538f63458fSDaniel P. Berrange                 } else {
3548f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3558f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
3568f63458fSDaniel P. Berrange                 }
357620775d1SDaniel P. Berrange             } else {
3588c10e0baSHervé Poussineau                 if (key->down) {
3598c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3608c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
3618c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3628c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
3638c10e0baSHervé Poussineau                 } else {
3648c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3658c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
3668c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3678c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
3688c10e0baSHervé Poussineau                 }
369620775d1SDaniel P. Berrange             }
3708c10e0baSHervé Poussineau         } else {
371ab8f9d49SDaniel P. Berrange             if (qcode < qemu_input_map_qcode_to_atset1_len)
372ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
3738c10e0baSHervé Poussineau             if (keycode) {
3748c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
3758c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
3768c10e0baSHervé Poussineau                 }
3778c10e0baSHervé Poussineau                 if (!key->down) {
3788c10e0baSHervé Poussineau                     keycode |= 0x80;
3798c10e0baSHervé Poussineau                 }
3808c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
3818c10e0baSHervé Poussineau             } else {
382ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
383ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
3848c10e0baSHervé Poussineau             }
3858c10e0baSHervé Poussineau         }
3868c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
3878c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
38829fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
38929fd23a5SDaniel P. Berrange                 if (key->down) {
39029fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
39129fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
39229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
39329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
39429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
39529fd23a5SDaniel P. Berrange                 }
39629fd23a5SDaniel P. Berrange             } else {
3978c10e0baSHervé Poussineau                 if (key->down) {
3988c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3998c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4008c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4018c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4028c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4038c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4048c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4058c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4068c10e0baSHervé Poussineau                 }
40729fd23a5SDaniel P. Berrange             }
4088c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
409620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
410620775d1SDaniel P. Berrange                 if (key->down) {
411620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
412620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
413620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
414620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
415620775d1SDaniel P. Berrange                 } else {
416620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
417620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
418620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
419620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
420620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
421620775d1SDaniel P. Berrange                 }
422620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
423620775d1SDaniel P. Berrange                 if (key->down) {
424620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
425620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
426620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
427620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
428620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
429620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
430620775d1SDaniel P. Berrange                 } else {
431620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
432620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
433620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
434620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
435620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
436620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
437620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
438620775d1SDaniel P. Berrange                 }
4398f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4408f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4418f63458fSDaniel P. Berrange                 if (key->down) {
4428f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4438f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4448f63458fSDaniel P. Berrange                 } else {
4458f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4468f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
4478f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4488f63458fSDaniel P. Berrange                 }
449620775d1SDaniel P. Berrange             } else {
4508c10e0baSHervé Poussineau                 if (key->down) {
4518c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4528c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4538c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4548c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4558c10e0baSHervé Poussineau                 } else {
4568c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4578c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4588c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4598c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4608c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4618c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4628c10e0baSHervé Poussineau                 }
463620775d1SDaniel P. Berrange             }
4648c10e0baSHervé Poussineau         } else {
465ab8f9d49SDaniel P. Berrange             if (qcode < qemu_input_map_qcode_to_atset2_len)
466ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
4678c10e0baSHervé Poussineau             if (keycode) {
4688c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4698c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4708c10e0baSHervé Poussineau                 }
4718c10e0baSHervé Poussineau                 if (!key->down) {
4728c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4738c10e0baSHervé Poussineau                 }
4748c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
47557d5c005SHervé Poussineau             } else {
476ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
477ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
47857d5c005SHervé Poussineau             }
47957d5c005SHervé Poussineau         }
48057d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
481ab8f9d49SDaniel P. Berrange         if (qcode < qemu_input_map_qcode_to_atset3_len)
482ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
4838c10e0baSHervé Poussineau         if (keycode) {
4848c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
4858c10e0baSHervé Poussineau             if (!key->down) {
4868c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
48757d5c005SHervé Poussineau             }
48857d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
4898c10e0baSHervé Poussineau         } else {
490ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
491ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
4928c10e0baSHervé Poussineau         }
49366e6536eSGerd Hoffmann     }
49466e6536eSGerd Hoffmann }
49566e6536eSGerd Hoffmann 
4968498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
49749ab747fSPaolo Bonzini {
49849ab747fSPaolo Bonzini     PS2Queue *q;
49949ab747fSPaolo Bonzini     int val, index;
50049ab747fSPaolo Bonzini 
5018498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
50249ab747fSPaolo Bonzini     q = &s->queue;
50349ab747fSPaolo Bonzini     if (q->count == 0) {
50449ab747fSPaolo Bonzini         /* NOTE: if no data left, we return the last keyboard one
50549ab747fSPaolo Bonzini            (needed for EMM386) */
50649ab747fSPaolo Bonzini         /* XXX: need a timer to do things correctly */
50749ab747fSPaolo Bonzini         index = q->rptr - 1;
50849ab747fSPaolo Bonzini         if (index < 0)
50949ab747fSPaolo Bonzini             index = PS2_QUEUE_SIZE - 1;
51049ab747fSPaolo Bonzini         val = q->data[index];
51149ab747fSPaolo Bonzini     } else {
51249ab747fSPaolo Bonzini         val = q->data[q->rptr];
51349ab747fSPaolo Bonzini         if (++q->rptr == PS2_QUEUE_SIZE)
51449ab747fSPaolo Bonzini             q->rptr = 0;
51549ab747fSPaolo Bonzini         q->count--;
51649ab747fSPaolo Bonzini         /* reading deasserts IRQ */
51749ab747fSPaolo Bonzini         s->update_irq(s->update_arg, 0);
51849ab747fSPaolo Bonzini         /* reassert IRQs if data left */
51949ab747fSPaolo Bonzini         s->update_irq(s->update_arg, q->count != 0);
52049ab747fSPaolo Bonzini     }
52149ab747fSPaolo Bonzini     return val;
52249ab747fSPaolo Bonzini }
52349ab747fSPaolo Bonzini 
52449ab747fSPaolo Bonzini static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
52549ab747fSPaolo Bonzini {
5265edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
52749ab747fSPaolo Bonzini     s->ledstate = ledstate;
52849ab747fSPaolo Bonzini     kbd_put_ledstate(ledstate);
52949ab747fSPaolo Bonzini }
53049ab747fSPaolo Bonzini 
53149ab747fSPaolo Bonzini static void ps2_reset_keyboard(PS2KbdState *s)
53249ab747fSPaolo Bonzini {
5335edab03dSDon Koch     trace_ps2_reset_keyboard(s);
53449ab747fSPaolo Bonzini     s->scan_enabled = 1;
53549ab747fSPaolo Bonzini     s->scancode_set = 2;
5366e24ee0cSGerd Hoffmann     ps2_reset_queue(&s->common);
53749ab747fSPaolo Bonzini     ps2_set_ledstate(s, 0);
53849ab747fSPaolo Bonzini }
53949ab747fSPaolo Bonzini 
54049ab747fSPaolo Bonzini void ps2_write_keyboard(void *opaque, int val)
54149ab747fSPaolo Bonzini {
54249ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)opaque;
54349ab747fSPaolo Bonzini 
5445edab03dSDon Koch     trace_ps2_write_keyboard(opaque, val);
54549ab747fSPaolo Bonzini     switch(s->common.write_cmd) {
54649ab747fSPaolo Bonzini     default:
54749ab747fSPaolo Bonzini     case -1:
54849ab747fSPaolo Bonzini         switch(val) {
54949ab747fSPaolo Bonzini         case 0x00:
55049ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
55149ab747fSPaolo Bonzini             break;
55249ab747fSPaolo Bonzini         case 0x05:
55349ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_RESEND);
55449ab747fSPaolo Bonzini             break;
55549ab747fSPaolo Bonzini         case KBD_CMD_GET_ID:
55649ab747fSPaolo Bonzini             /* We emulate a MF2 AT keyboard here */
55749ab747fSPaolo Bonzini             if (s->translate)
5587abe7eb2SGeoffrey McRae                 ps2_queue_3(&s->common,
5597abe7eb2SGeoffrey McRae                     KBD_REPLY_ACK,
5607abe7eb2SGeoffrey McRae                     KBD_REPLY_ID,
5617abe7eb2SGeoffrey McRae                     0x41);
56249ab747fSPaolo Bonzini             else
5637abe7eb2SGeoffrey McRae                 ps2_queue_3(&s->common,
5647abe7eb2SGeoffrey McRae                     KBD_REPLY_ACK,
5657abe7eb2SGeoffrey McRae                     KBD_REPLY_ID,
5667abe7eb2SGeoffrey McRae                     0x83);
56749ab747fSPaolo Bonzini             break;
56849ab747fSPaolo Bonzini         case KBD_CMD_ECHO:
56949ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_CMD_ECHO);
57049ab747fSPaolo Bonzini             break;
57149ab747fSPaolo Bonzini         case KBD_CMD_ENABLE:
57249ab747fSPaolo Bonzini             s->scan_enabled = 1;
57349ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
57449ab747fSPaolo Bonzini             break;
57549ab747fSPaolo Bonzini         case KBD_CMD_SCANCODE:
57649ab747fSPaolo Bonzini         case KBD_CMD_SET_LEDS:
57749ab747fSPaolo Bonzini         case KBD_CMD_SET_RATE:
578c56b6209SSven Schnelle         case KBD_CMD_SET_MAKE_BREAK:
57949ab747fSPaolo Bonzini             s->common.write_cmd = val;
58049ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
58149ab747fSPaolo Bonzini             break;
58249ab747fSPaolo Bonzini         case KBD_CMD_RESET_DISABLE:
58349ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
58449ab747fSPaolo Bonzini             s->scan_enabled = 0;
58549ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
58649ab747fSPaolo Bonzini             break;
58749ab747fSPaolo Bonzini         case KBD_CMD_RESET_ENABLE:
58849ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
58949ab747fSPaolo Bonzini             s->scan_enabled = 1;
59049ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
59149ab747fSPaolo Bonzini             break;
59249ab747fSPaolo Bonzini         case KBD_CMD_RESET:
59349ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
5947abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
5957abe7eb2SGeoffrey McRae                 KBD_REPLY_ACK,
5967abe7eb2SGeoffrey McRae                 KBD_REPLY_POR);
59749ab747fSPaolo Bonzini             break;
598c56b6209SSven Schnelle         case KBD_CMD_SET_TYPEMATIC:
599c56b6209SSven Schnelle             ps2_queue(&s->common, KBD_REPLY_ACK);
600c56b6209SSven Schnelle             break;
60149ab747fSPaolo Bonzini         default:
60206b3611fSHervé Poussineau             ps2_queue(&s->common, KBD_REPLY_RESEND);
60349ab747fSPaolo Bonzini             break;
60449ab747fSPaolo Bonzini         }
60549ab747fSPaolo Bonzini         break;
606c56b6209SSven Schnelle     case KBD_CMD_SET_MAKE_BREAK:
607c56b6209SSven Schnelle         ps2_queue(&s->common, KBD_REPLY_ACK);
608c56b6209SSven Schnelle         s->common.write_cmd = -1;
609c56b6209SSven Schnelle         break;
61049ab747fSPaolo Bonzini     case KBD_CMD_SCANCODE:
61149ab747fSPaolo Bonzini         if (val == 0) {
6127abe7eb2SGeoffrey McRae             if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) {
6134df23b64SHervé Poussineau                 ps2_queue(&s->common, KBD_REPLY_ACK);
61457d5c005SHervé Poussineau                 ps2_put_keycode(s, s->scancode_set);
6157abe7eb2SGeoffrey McRae             }
6164df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
61749ab747fSPaolo Bonzini             s->scancode_set = val;
61849ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
6194df23b64SHervé Poussineau         } else {
6204df23b64SHervé Poussineau             ps2_queue(&s->common, KBD_REPLY_RESEND);
62149ab747fSPaolo Bonzini         }
62249ab747fSPaolo Bonzini         s->common.write_cmd = -1;
62349ab747fSPaolo Bonzini         break;
62449ab747fSPaolo Bonzini     case KBD_CMD_SET_LEDS:
62549ab747fSPaolo Bonzini         ps2_set_ledstate(s, val);
62649ab747fSPaolo Bonzini         ps2_queue(&s->common, KBD_REPLY_ACK);
62749ab747fSPaolo Bonzini         s->common.write_cmd = -1;
62849ab747fSPaolo Bonzini         break;
62949ab747fSPaolo Bonzini     case KBD_CMD_SET_RATE:
63049ab747fSPaolo Bonzini         ps2_queue(&s->common, KBD_REPLY_ACK);
63149ab747fSPaolo Bonzini         s->common.write_cmd = -1;
63249ab747fSPaolo Bonzini         break;
63349ab747fSPaolo Bonzini     }
63449ab747fSPaolo Bonzini }
63549ab747fSPaolo Bonzini 
63649ab747fSPaolo Bonzini /* Set the scancode translation mode.
63749ab747fSPaolo Bonzini    0 = raw scancodes.
63849ab747fSPaolo Bonzini    1 = translated scancodes (used by qemu internally).  */
63949ab747fSPaolo Bonzini 
64049ab747fSPaolo Bonzini void ps2_keyboard_set_translation(void *opaque, int mode)
64149ab747fSPaolo Bonzini {
64249ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)opaque;
6435edab03dSDon Koch     trace_ps2_keyboard_set_translation(opaque, mode);
64449ab747fSPaolo Bonzini     s->translate = mode;
64549ab747fSPaolo Bonzini }
64649ab747fSPaolo Bonzini 
6477abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
64849ab747fSPaolo Bonzini {
649*76968101SVolker Rümelin     /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
650*76968101SVolker Rümelin     const int needed = s->mouse_type ? 4 : 3;
65149ab747fSPaolo Bonzini     unsigned int b;
65249ab747fSPaolo Bonzini     int dx1, dy1, dz1;
65349ab747fSPaolo Bonzini 
6547abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->common.queue.count < needed) {
6557abe7eb2SGeoffrey McRae         return 0;
6567abe7eb2SGeoffrey McRae     }
6577abe7eb2SGeoffrey McRae 
65849ab747fSPaolo Bonzini     dx1 = s->mouse_dx;
65949ab747fSPaolo Bonzini     dy1 = s->mouse_dy;
66049ab747fSPaolo Bonzini     dz1 = s->mouse_dz;
66149ab747fSPaolo Bonzini     /* XXX: increase range to 8 bits ? */
66249ab747fSPaolo Bonzini     if (dx1 > 127)
66349ab747fSPaolo Bonzini         dx1 = 127;
66449ab747fSPaolo Bonzini     else if (dx1 < -127)
66549ab747fSPaolo Bonzini         dx1 = -127;
66649ab747fSPaolo Bonzini     if (dy1 > 127)
66749ab747fSPaolo Bonzini         dy1 = 127;
66849ab747fSPaolo Bonzini     else if (dy1 < -127)
66949ab747fSPaolo Bonzini         dy1 = -127;
67049ab747fSPaolo Bonzini     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
6717abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, b);
6727abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dx1 & 0xff);
6737abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dy1 & 0xff);
67449ab747fSPaolo Bonzini     /* extra byte for IMPS/2 or IMEX */
67549ab747fSPaolo Bonzini     switch(s->mouse_type) {
67649ab747fSPaolo Bonzini     default:
67749ab747fSPaolo Bonzini         break;
67849ab747fSPaolo Bonzini     case 3:
67949ab747fSPaolo Bonzini         if (dz1 > 127)
68049ab747fSPaolo Bonzini             dz1 = 127;
68149ab747fSPaolo Bonzini         else if (dz1 < -127)
68249ab747fSPaolo Bonzini                 dz1 = -127;
6837abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, dz1 & 0xff);
68449ab747fSPaolo Bonzini         break;
68549ab747fSPaolo Bonzini     case 4:
68649ab747fSPaolo Bonzini         if (dz1 > 7)
68749ab747fSPaolo Bonzini             dz1 = 7;
68849ab747fSPaolo Bonzini         else if (dz1 < -7)
68949ab747fSPaolo Bonzini             dz1 = -7;
69049ab747fSPaolo Bonzini         b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
6917abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, b);
69249ab747fSPaolo Bonzini         break;
69349ab747fSPaolo Bonzini     }
69449ab747fSPaolo Bonzini 
6957abe7eb2SGeoffrey McRae     ps2_raise_irq(&s->common);
6967abe7eb2SGeoffrey McRae 
6975edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
69849ab747fSPaolo Bonzini     /* update deltas */
69949ab747fSPaolo Bonzini     s->mouse_dx -= dx1;
70049ab747fSPaolo Bonzini     s->mouse_dy -= dy1;
70149ab747fSPaolo Bonzini     s->mouse_dz -= dz1;
7027abe7eb2SGeoffrey McRae 
7037abe7eb2SGeoffrey McRae     return 1;
70449ab747fSPaolo Bonzini }
70549ab747fSPaolo Bonzini 
7062a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
7072a766d29SGerd Hoffmann                             InputEvent *evt)
70849ab747fSPaolo Bonzini {
7097fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
7108b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
7118b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
7128b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
7138b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
7148b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
7152a766d29SGerd Hoffmann     };
7162a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
717b5a1b443SEric Blake     InputMoveEvent *move;
718b5a1b443SEric Blake     InputBtnEvent *btn;
71949ab747fSPaolo Bonzini 
72049ab747fSPaolo Bonzini     /* check if deltas are recorded when disabled */
72149ab747fSPaolo Bonzini     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
72249ab747fSPaolo Bonzini         return;
72349ab747fSPaolo Bonzini 
724568c73a4SEric Blake     switch (evt->type) {
7252a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
72632bafa8fSEric Blake         move = evt->u.rel.data;
727b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
728b5a1b443SEric Blake             s->mouse_dx += move->value;
729b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
730b5a1b443SEric Blake             s->mouse_dy -= move->value;
7312a766d29SGerd Hoffmann         }
7322a766d29SGerd Hoffmann         break;
73349ab747fSPaolo Bonzini 
7342a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
73532bafa8fSEric Blake         btn = evt->u.btn.data;
736b5a1b443SEric Blake         if (btn->down) {
737b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
738b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
7392a766d29SGerd Hoffmann                 s->mouse_dz--;
740b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
7412a766d29SGerd Hoffmann                 s->mouse_dz++;
7422a766d29SGerd Hoffmann             }
7432a766d29SGerd Hoffmann         } else {
744b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
7452a766d29SGerd Hoffmann         }
7462a766d29SGerd Hoffmann         break;
7472a766d29SGerd Hoffmann 
7482a766d29SGerd Hoffmann     default:
7492a766d29SGerd Hoffmann         /* keep gcc happy */
7502a766d29SGerd Hoffmann         break;
7512a766d29SGerd Hoffmann     }
75249ab747fSPaolo Bonzini }
75349ab747fSPaolo Bonzini 
7542a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
7552a766d29SGerd Hoffmann {
7562a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
7572a766d29SGerd Hoffmann 
758143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
759143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
760143c04c7SGeoffrey McRae         return;
761143c04c7SGeoffrey McRae     }
762143c04c7SGeoffrey McRae 
7632a766d29SGerd Hoffmann     if (s->mouse_buttons) {
764fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
7652a766d29SGerd Hoffmann     }
7662858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
76749ab747fSPaolo Bonzini         /* if not remote, send event. Multiple events are sent if
76849ab747fSPaolo Bonzini            too big deltas */
7697abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
77049ab747fSPaolo Bonzini             if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
77149ab747fSPaolo Bonzini                 break;
77249ab747fSPaolo Bonzini         }
77349ab747fSPaolo Bonzini     }
77449ab747fSPaolo Bonzini }
77549ab747fSPaolo Bonzini 
77649ab747fSPaolo Bonzini void ps2_mouse_fake_event(void *opaque)
77749ab747fSPaolo Bonzini {
7782a766d29SGerd Hoffmann     PS2MouseState *s = opaque;
7795edab03dSDon Koch     trace_ps2_mouse_fake_event(opaque);
7802a766d29SGerd Hoffmann     s->mouse_dx++;
7812a766d29SGerd Hoffmann     ps2_mouse_sync(opaque);
78249ab747fSPaolo Bonzini }
78349ab747fSPaolo Bonzini 
78449ab747fSPaolo Bonzini void ps2_write_mouse(void *opaque, int val)
78549ab747fSPaolo Bonzini {
78649ab747fSPaolo Bonzini     PS2MouseState *s = (PS2MouseState *)opaque;
7875edab03dSDon Koch 
7885edab03dSDon Koch     trace_ps2_write_mouse(opaque, val);
78949ab747fSPaolo Bonzini     switch(s->common.write_cmd) {
79049ab747fSPaolo Bonzini     default:
79149ab747fSPaolo Bonzini     case -1:
79249ab747fSPaolo Bonzini         /* mouse command */
79349ab747fSPaolo Bonzini         if (s->mouse_wrap) {
79449ab747fSPaolo Bonzini             if (val == AUX_RESET_WRAP) {
79549ab747fSPaolo Bonzini                 s->mouse_wrap = 0;
79649ab747fSPaolo Bonzini                 ps2_queue(&s->common, AUX_ACK);
79749ab747fSPaolo Bonzini                 return;
79849ab747fSPaolo Bonzini             } else if (val != AUX_RESET) {
79949ab747fSPaolo Bonzini                 ps2_queue(&s->common, val);
80049ab747fSPaolo Bonzini                 return;
80149ab747fSPaolo Bonzini             }
80249ab747fSPaolo Bonzini         }
80349ab747fSPaolo Bonzini         switch(val) {
80449ab747fSPaolo Bonzini         case AUX_SET_SCALE11:
80549ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
80649ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
80749ab747fSPaolo Bonzini             break;
80849ab747fSPaolo Bonzini         case AUX_SET_SCALE21:
80949ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_SCALE21;
81049ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
81149ab747fSPaolo Bonzini             break;
81249ab747fSPaolo Bonzini         case AUX_SET_STREAM:
81349ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
81449ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
81549ab747fSPaolo Bonzini             break;
81649ab747fSPaolo Bonzini         case AUX_SET_WRAP:
81749ab747fSPaolo Bonzini             s->mouse_wrap = 1;
81849ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
81949ab747fSPaolo Bonzini             break;
82049ab747fSPaolo Bonzini         case AUX_SET_REMOTE:
82149ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_REMOTE;
82249ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
82349ab747fSPaolo Bonzini             break;
82449ab747fSPaolo Bonzini         case AUX_GET_TYPE:
8257abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
8267abe7eb2SGeoffrey McRae                 AUX_ACK,
8277abe7eb2SGeoffrey McRae                 s->mouse_type);
82849ab747fSPaolo Bonzini             break;
82949ab747fSPaolo Bonzini         case AUX_SET_RES:
83049ab747fSPaolo Bonzini         case AUX_SET_SAMPLE:
83149ab747fSPaolo Bonzini             s->common.write_cmd = val;
83249ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
83349ab747fSPaolo Bonzini             break;
83449ab747fSPaolo Bonzini         case AUX_GET_SCALE:
8357abe7eb2SGeoffrey McRae             ps2_queue_4(&s->common,
8367abe7eb2SGeoffrey McRae                 AUX_ACK,
8377abe7eb2SGeoffrey McRae                 s->mouse_status,
8387abe7eb2SGeoffrey McRae                 s->mouse_resolution,
8397abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
84049ab747fSPaolo Bonzini             break;
84149ab747fSPaolo Bonzini         case AUX_POLL:
84249ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
84349ab747fSPaolo Bonzini             ps2_mouse_send_packet(s);
84449ab747fSPaolo Bonzini             break;
84549ab747fSPaolo Bonzini         case AUX_ENABLE_DEV:
84649ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_ENABLED;
84749ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
84849ab747fSPaolo Bonzini             break;
84949ab747fSPaolo Bonzini         case AUX_DISABLE_DEV:
85049ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
85149ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
85249ab747fSPaolo Bonzini             break;
85349ab747fSPaolo Bonzini         case AUX_SET_DEFAULT:
85449ab747fSPaolo Bonzini             s->mouse_sample_rate = 100;
85549ab747fSPaolo Bonzini             s->mouse_resolution = 2;
85649ab747fSPaolo Bonzini             s->mouse_status = 0;
85749ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
85849ab747fSPaolo Bonzini             break;
85949ab747fSPaolo Bonzini         case AUX_RESET:
86049ab747fSPaolo Bonzini             s->mouse_sample_rate = 100;
86149ab747fSPaolo Bonzini             s->mouse_resolution = 2;
86249ab747fSPaolo Bonzini             s->mouse_status = 0;
86349ab747fSPaolo Bonzini             s->mouse_type = 0;
864143c04c7SGeoffrey McRae             ps2_reset_queue(&s->common);
8657abe7eb2SGeoffrey McRae             ps2_queue_3(&s->common,
8667abe7eb2SGeoffrey McRae                 AUX_ACK,
8677abe7eb2SGeoffrey McRae                 0xaa,
8687abe7eb2SGeoffrey McRae                 s->mouse_type);
86949ab747fSPaolo Bonzini             break;
87049ab747fSPaolo Bonzini         default:
87149ab747fSPaolo Bonzini             break;
87249ab747fSPaolo Bonzini         }
87349ab747fSPaolo Bonzini         break;
87449ab747fSPaolo Bonzini     case AUX_SET_SAMPLE:
87549ab747fSPaolo Bonzini         s->mouse_sample_rate = val;
87649ab747fSPaolo Bonzini         /* detect IMPS/2 or IMEX */
87749ab747fSPaolo Bonzini         switch(s->mouse_detect_state) {
87849ab747fSPaolo Bonzini         default:
87949ab747fSPaolo Bonzini         case 0:
88049ab747fSPaolo Bonzini             if (val == 200)
88149ab747fSPaolo Bonzini                 s->mouse_detect_state = 1;
88249ab747fSPaolo Bonzini             break;
88349ab747fSPaolo Bonzini         case 1:
88449ab747fSPaolo Bonzini             if (val == 100)
88549ab747fSPaolo Bonzini                 s->mouse_detect_state = 2;
88649ab747fSPaolo Bonzini             else if (val == 200)
88749ab747fSPaolo Bonzini                 s->mouse_detect_state = 3;
88849ab747fSPaolo Bonzini             else
88949ab747fSPaolo Bonzini                 s->mouse_detect_state = 0;
89049ab747fSPaolo Bonzini             break;
89149ab747fSPaolo Bonzini         case 2:
89249ab747fSPaolo Bonzini             if (val == 80)
89349ab747fSPaolo Bonzini                 s->mouse_type = 3; /* IMPS/2 */
89449ab747fSPaolo Bonzini             s->mouse_detect_state = 0;
89549ab747fSPaolo Bonzini             break;
89649ab747fSPaolo Bonzini         case 3:
89749ab747fSPaolo Bonzini             if (val == 80)
89849ab747fSPaolo Bonzini                 s->mouse_type = 4; /* IMEX */
89949ab747fSPaolo Bonzini             s->mouse_detect_state = 0;
90049ab747fSPaolo Bonzini             break;
90149ab747fSPaolo Bonzini         }
90249ab747fSPaolo Bonzini         ps2_queue(&s->common, AUX_ACK);
90349ab747fSPaolo Bonzini         s->common.write_cmd = -1;
90449ab747fSPaolo Bonzini         break;
90549ab747fSPaolo Bonzini     case AUX_SET_RES:
90649ab747fSPaolo Bonzini         s->mouse_resolution = val;
90749ab747fSPaolo Bonzini         ps2_queue(&s->common, AUX_ACK);
90849ab747fSPaolo Bonzini         s->common.write_cmd = -1;
90949ab747fSPaolo Bonzini         break;
91049ab747fSPaolo Bonzini     }
91149ab747fSPaolo Bonzini }
91249ab747fSPaolo Bonzini 
91349ab747fSPaolo Bonzini static void ps2_common_reset(PS2State *s)
91449ab747fSPaolo Bonzini {
91549ab747fSPaolo Bonzini     s->write_cmd = -1;
916954ee55bSGerd Hoffmann     ps2_reset_queue(s);
91749ab747fSPaolo Bonzini     s->update_irq(s->update_arg, 0);
91849ab747fSPaolo Bonzini }
91949ab747fSPaolo Bonzini 
9202858ab09SGonglei static void ps2_common_post_load(PS2State *s)
9212858ab09SGonglei {
9222858ab09SGonglei     PS2Queue *q = &s->queue;
923802cbcb7SPrasad J Pandit     uint8_t i, size;
924802cbcb7SPrasad J Pandit     uint8_t tmp_data[PS2_QUEUE_SIZE];
9252858ab09SGonglei 
9262858ab09SGonglei     /* set the useful data buffer queue size, < PS2_QUEUE_SIZE */
927a1f2ed2aSPavel Dovgalyuk     size = q->count;
928a1f2ed2aSPavel Dovgalyuk     if (q->count < 0) {
929a1f2ed2aSPavel Dovgalyuk         size = 0;
930a1f2ed2aSPavel Dovgalyuk     } else if (q->count > PS2_QUEUE_SIZE) {
931a1f2ed2aSPavel Dovgalyuk         size = PS2_QUEUE_SIZE;
932a1f2ed2aSPavel Dovgalyuk     }
9332858ab09SGonglei 
9342858ab09SGonglei     /* move the queue elements to the start of data array */
9352858ab09SGonglei     for (i = 0; i < size; i++) {
936802cbcb7SPrasad J Pandit         if (q->rptr < 0 || q->rptr >= sizeof(q->data)) {
9372858ab09SGonglei             q->rptr = 0;
9382858ab09SGonglei         }
939802cbcb7SPrasad J Pandit         tmp_data[i] = q->data[q->rptr++];
9402858ab09SGonglei     }
9412858ab09SGonglei     memcpy(q->data, tmp_data, size);
942802cbcb7SPrasad J Pandit 
9432858ab09SGonglei     /* reset rptr/wptr/count */
9442858ab09SGonglei     q->rptr = 0;
945b55a06dfSliujunjie     q->wptr = (size == PS2_QUEUE_SIZE) ? 0 : size;
9462858ab09SGonglei     q->count = size;
9472858ab09SGonglei }
9482858ab09SGonglei 
94949ab747fSPaolo Bonzini static void ps2_kbd_reset(void *opaque)
95049ab747fSPaolo Bonzini {
95149ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *) opaque;
95249ab747fSPaolo Bonzini 
9535edab03dSDon Koch     trace_ps2_kbd_reset(opaque);
95449ab747fSPaolo Bonzini     ps2_common_reset(&s->common);
955d2e550a8SHervé Poussineau     s->scan_enabled = 1;
95649ab747fSPaolo Bonzini     s->translate = 0;
957089adafdSHervé Poussineau     s->scancode_set = 2;
958620775d1SDaniel P. Berrange     s->modifiers = 0;
95949ab747fSPaolo Bonzini }
96049ab747fSPaolo Bonzini 
96149ab747fSPaolo Bonzini static void ps2_mouse_reset(void *opaque)
96249ab747fSPaolo Bonzini {
96349ab747fSPaolo Bonzini     PS2MouseState *s = (PS2MouseState *) opaque;
96449ab747fSPaolo Bonzini 
9655edab03dSDon Koch     trace_ps2_mouse_reset(opaque);
96649ab747fSPaolo Bonzini     ps2_common_reset(&s->common);
96749ab747fSPaolo Bonzini     s->mouse_status = 0;
96849ab747fSPaolo Bonzini     s->mouse_resolution = 0;
96949ab747fSPaolo Bonzini     s->mouse_sample_rate = 0;
97049ab747fSPaolo Bonzini     s->mouse_wrap = 0;
97149ab747fSPaolo Bonzini     s->mouse_type = 0;
97249ab747fSPaolo Bonzini     s->mouse_detect_state = 0;
97349ab747fSPaolo Bonzini     s->mouse_dx = 0;
97449ab747fSPaolo Bonzini     s->mouse_dy = 0;
97549ab747fSPaolo Bonzini     s->mouse_dz = 0;
97649ab747fSPaolo Bonzini     s->mouse_buttons = 0;
97749ab747fSPaolo Bonzini }
97849ab747fSPaolo Bonzini 
97949ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_common = {
98049ab747fSPaolo Bonzini     .name = "PS2 Common State",
98149ab747fSPaolo Bonzini     .version_id = 3,
98249ab747fSPaolo Bonzini     .minimum_version_id = 2,
98349ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
98449ab747fSPaolo Bonzini         VMSTATE_INT32(write_cmd, PS2State),
98549ab747fSPaolo Bonzini         VMSTATE_INT32(queue.rptr, PS2State),
98649ab747fSPaolo Bonzini         VMSTATE_INT32(queue.wptr, PS2State),
98749ab747fSPaolo Bonzini         VMSTATE_INT32(queue.count, PS2State),
98849ab747fSPaolo Bonzini         VMSTATE_BUFFER(queue.data, PS2State),
98949ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
99049ab747fSPaolo Bonzini     }
99149ab747fSPaolo Bonzini };
99249ab747fSPaolo Bonzini 
99349ab747fSPaolo Bonzini static bool ps2_keyboard_ledstate_needed(void *opaque)
99449ab747fSPaolo Bonzini {
99549ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
99649ab747fSPaolo Bonzini 
99749ab747fSPaolo Bonzini     return s->ledstate != 0; /* 0 is default state */
99849ab747fSPaolo Bonzini }
99949ab747fSPaolo Bonzini 
100049ab747fSPaolo Bonzini static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
100149ab747fSPaolo Bonzini {
100249ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
100349ab747fSPaolo Bonzini 
100449ab747fSPaolo Bonzini     kbd_put_ledstate(s->ledstate);
100549ab747fSPaolo Bonzini     return 0;
100649ab747fSPaolo Bonzini }
100749ab747fSPaolo Bonzini 
100849ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
100949ab747fSPaolo Bonzini     .name = "ps2kbd/ledstate",
101049ab747fSPaolo Bonzini     .version_id = 3,
101149ab747fSPaolo Bonzini     .minimum_version_id = 2,
101249ab747fSPaolo Bonzini     .post_load = ps2_kbd_ledstate_post_load,
10135cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
101449ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
101549ab747fSPaolo Bonzini         VMSTATE_INT32(ledstate, PS2KbdState),
101649ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
101749ab747fSPaolo Bonzini     }
101849ab747fSPaolo Bonzini };
101949ab747fSPaolo Bonzini 
102057d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
102157d5c005SHervé Poussineau {
102257d5c005SHervé Poussineau     PS2KbdState *s = opaque;
102357d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
102457d5c005SHervé Poussineau }
102557d5c005SHervé Poussineau 
102657d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
102757d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
102857d5c005SHervé Poussineau     .version_id = 1,
102957d5c005SHervé Poussineau     .minimum_version_id = 1,
103057d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
103157d5c005SHervé Poussineau     .fields = (VMStateField[]) {
103257d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
103357d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
103457d5c005SHervé Poussineau     }
103557d5c005SHervé Poussineau };
103657d5c005SHervé Poussineau 
103749ab747fSPaolo Bonzini static int ps2_kbd_post_load(void* opaque, int version_id)
103849ab747fSPaolo Bonzini {
103949ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState*)opaque;
10402858ab09SGonglei     PS2State *ps2 = &s->common;
104149ab747fSPaolo Bonzini 
104249ab747fSPaolo Bonzini     if (version_id == 2)
104349ab747fSPaolo Bonzini         s->scancode_set=2;
10442858ab09SGonglei 
10452858ab09SGonglei     ps2_common_post_load(ps2);
10462858ab09SGonglei 
104749ab747fSPaolo Bonzini     return 0;
104849ab747fSPaolo Bonzini }
104949ab747fSPaolo Bonzini 
105044b1ff31SDr. David Alan Gilbert static int ps2_kbd_pre_save(void *opaque)
10512858ab09SGonglei {
10522858ab09SGonglei     PS2KbdState *s = (PS2KbdState *)opaque;
10532858ab09SGonglei     PS2State *ps2 = &s->common;
10542858ab09SGonglei 
10552858ab09SGonglei     ps2_common_post_load(ps2);
105644b1ff31SDr. David Alan Gilbert 
105744b1ff31SDr. David Alan Gilbert     return 0;
10582858ab09SGonglei }
10592858ab09SGonglei 
106049ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_keyboard = {
106149ab747fSPaolo Bonzini     .name = "ps2kbd",
106249ab747fSPaolo Bonzini     .version_id = 3,
106349ab747fSPaolo Bonzini     .minimum_version_id = 2,
106449ab747fSPaolo Bonzini     .post_load = ps2_kbd_post_load,
10652858ab09SGonglei     .pre_save = ps2_kbd_pre_save,
106649ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
106749ab747fSPaolo Bonzini         VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
106849ab747fSPaolo Bonzini         VMSTATE_INT32(scan_enabled, PS2KbdState),
106949ab747fSPaolo Bonzini         VMSTATE_INT32(translate, PS2KbdState),
107049ab747fSPaolo Bonzini         VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
107149ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
107249ab747fSPaolo Bonzini     },
10735cd8cadaSJuan Quintela     .subsections = (const VMStateDescription*[]) {
10745cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
107557d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
10765cd8cadaSJuan Quintela         NULL
107749ab747fSPaolo Bonzini     }
107849ab747fSPaolo Bonzini };
107949ab747fSPaolo Bonzini 
10802858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
10812858ab09SGonglei {
10822858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
10832858ab09SGonglei     PS2State *ps2 = &s->common;
10842858ab09SGonglei 
10852858ab09SGonglei     ps2_common_post_load(ps2);
10862858ab09SGonglei 
10872858ab09SGonglei     return 0;
10882858ab09SGonglei }
10892858ab09SGonglei 
109044b1ff31SDr. David Alan Gilbert static int ps2_mouse_pre_save(void *opaque)
10912858ab09SGonglei {
10922858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
10932858ab09SGonglei     PS2State *ps2 = &s->common;
10942858ab09SGonglei 
10952858ab09SGonglei     ps2_common_post_load(ps2);
109644b1ff31SDr. David Alan Gilbert 
109744b1ff31SDr. David Alan Gilbert     return 0;
10982858ab09SGonglei }
10992858ab09SGonglei 
110049ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_mouse = {
110149ab747fSPaolo Bonzini     .name = "ps2mouse",
110249ab747fSPaolo Bonzini     .version_id = 2,
110349ab747fSPaolo Bonzini     .minimum_version_id = 2,
11042858ab09SGonglei     .post_load = ps2_mouse_post_load,
11052858ab09SGonglei     .pre_save = ps2_mouse_pre_save,
110649ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
110749ab747fSPaolo Bonzini         VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
110849ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_status, PS2MouseState),
110949ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
111049ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
111149ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
111249ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_type, PS2MouseState),
111349ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
111449ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dx, PS2MouseState),
111549ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dy, PS2MouseState),
111649ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dz, PS2MouseState),
111749ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
111849ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
111949ab747fSPaolo Bonzini     }
112049ab747fSPaolo Bonzini };
112149ab747fSPaolo Bonzini 
112266e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
112366e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
112466e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
112566e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
112666e6536eSGerd Hoffmann };
112766e6536eSGerd Hoffmann 
112849ab747fSPaolo Bonzini void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
112949ab747fSPaolo Bonzini {
113049ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState));
113149ab747fSPaolo Bonzini 
11325edab03dSDon Koch     trace_ps2_kbd_init(s);
113349ab747fSPaolo Bonzini     s->common.update_irq = update_irq;
113449ab747fSPaolo Bonzini     s->common.update_arg = update_arg;
113549ab747fSPaolo Bonzini     s->scancode_set = 2;
113649ab747fSPaolo Bonzini     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
113766e6536eSGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
113866e6536eSGerd Hoffmann                                 &ps2_keyboard_handler);
113949ab747fSPaolo Bonzini     qemu_register_reset(ps2_kbd_reset, s);
114049ab747fSPaolo Bonzini     return s;
114149ab747fSPaolo Bonzini }
114249ab747fSPaolo Bonzini 
11432a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
11442a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
11452a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
11462a766d29SGerd Hoffmann     .event = ps2_mouse_event,
11472a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
11482a766d29SGerd Hoffmann };
11492a766d29SGerd Hoffmann 
115049ab747fSPaolo Bonzini void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
115149ab747fSPaolo Bonzini {
115249ab747fSPaolo Bonzini     PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState));
115349ab747fSPaolo Bonzini 
11545edab03dSDon Koch     trace_ps2_mouse_init(s);
115549ab747fSPaolo Bonzini     s->common.update_irq = update_irq;
115649ab747fSPaolo Bonzini     s->common.update_arg = update_arg;
115749ab747fSPaolo Bonzini     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
11582a766d29SGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
11592a766d29SGerd Hoffmann                                 &ps2_mouse_handler);
116049ab747fSPaolo Bonzini     qemu_register_reset(ps2_mouse_reset, s);
116149ab747fSPaolo Bonzini     return s;
116249ab747fSPaolo Bonzini }
1163