xref: /qemu/hw/input/ps2.c (revision 7abe7eb2)
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  */
240430891cSPeter Maydell #include "qemu/osdep.h"
25ec044a80SHervé Poussineau #include "qemu/log.h"
2649ab747fSPaolo Bonzini #include "hw/hw.h"
2749ab747fSPaolo Bonzini #include "hw/input/ps2.h"
2849ab747fSPaolo Bonzini #include "ui/console.h"
2966e6536eSGerd Hoffmann #include "ui/input.h"
3049ab747fSPaolo Bonzini #include "sysemu/sysemu.h"
3149ab747fSPaolo Bonzini 
325edab03dSDon Koch #include "trace.h"
335edab03dSDon Koch 
3449ab747fSPaolo Bonzini /* debug PC keyboard */
3549ab747fSPaolo Bonzini //#define DEBUG_KBD
3649ab747fSPaolo Bonzini 
3749ab747fSPaolo Bonzini /* debug PC keyboard : only mouse */
3849ab747fSPaolo Bonzini //#define DEBUG_MOUSE
3949ab747fSPaolo Bonzini 
4049ab747fSPaolo Bonzini /* Keyboard Commands */
4149ab747fSPaolo Bonzini #define KBD_CMD_SET_LEDS	0xED	/* Set keyboard leds */
4249ab747fSPaolo Bonzini #define KBD_CMD_ECHO     	0xEE
4349ab747fSPaolo Bonzini #define KBD_CMD_SCANCODE	0xF0	/* Get/set scancode set */
4449ab747fSPaolo Bonzini #define KBD_CMD_GET_ID 	        0xF2	/* get keyboard ID */
4549ab747fSPaolo Bonzini #define KBD_CMD_SET_RATE	0xF3	/* Set typematic rate */
4649ab747fSPaolo Bonzini #define KBD_CMD_ENABLE		0xF4	/* Enable scanning */
4749ab747fSPaolo Bonzini #define KBD_CMD_RESET_DISABLE	0xF5	/* reset and disable scanning */
4849ab747fSPaolo Bonzini #define KBD_CMD_RESET_ENABLE   	0xF6    /* reset and enable scanning */
4949ab747fSPaolo Bonzini #define KBD_CMD_RESET		0xFF	/* Reset */
5049ab747fSPaolo Bonzini 
5149ab747fSPaolo Bonzini /* Keyboard Replies */
5249ab747fSPaolo Bonzini #define KBD_REPLY_POR		0xAA	/* Power on reset */
5349ab747fSPaolo Bonzini #define KBD_REPLY_ID		0xAB	/* Keyboard ID */
5449ab747fSPaolo Bonzini #define KBD_REPLY_ACK		0xFA	/* Command ACK */
5549ab747fSPaolo Bonzini #define KBD_REPLY_RESEND	0xFE	/* Command NACK, send the cmd again */
5649ab747fSPaolo Bonzini 
5749ab747fSPaolo Bonzini /* Mouse Commands */
5849ab747fSPaolo Bonzini #define AUX_SET_SCALE11		0xE6	/* Set 1:1 scaling */
5949ab747fSPaolo Bonzini #define AUX_SET_SCALE21		0xE7	/* Set 2:1 scaling */
6049ab747fSPaolo Bonzini #define AUX_SET_RES		0xE8	/* Set resolution */
6149ab747fSPaolo Bonzini #define AUX_GET_SCALE		0xE9	/* Get scaling factor */
6249ab747fSPaolo Bonzini #define AUX_SET_STREAM		0xEA	/* Set stream mode */
6349ab747fSPaolo Bonzini #define AUX_POLL		0xEB	/* Poll */
6449ab747fSPaolo Bonzini #define AUX_RESET_WRAP		0xEC	/* Reset wrap mode */
6549ab747fSPaolo Bonzini #define AUX_SET_WRAP		0xEE	/* Set wrap mode */
6649ab747fSPaolo Bonzini #define AUX_SET_REMOTE		0xF0	/* Set remote mode */
6749ab747fSPaolo Bonzini #define AUX_GET_TYPE		0xF2	/* Get type */
6849ab747fSPaolo Bonzini #define AUX_SET_SAMPLE		0xF3	/* Set sample rate */
6949ab747fSPaolo Bonzini #define AUX_ENABLE_DEV		0xF4	/* Enable aux device */
7049ab747fSPaolo Bonzini #define AUX_DISABLE_DEV		0xF5	/* Disable aux device */
7149ab747fSPaolo Bonzini #define AUX_SET_DEFAULT		0xF6
7249ab747fSPaolo Bonzini #define AUX_RESET		0xFF	/* Reset aux device */
7349ab747fSPaolo Bonzini #define AUX_ACK			0xFA	/* Command byte ACK. */
7449ab747fSPaolo Bonzini 
7549ab747fSPaolo Bonzini #define MOUSE_STATUS_REMOTE     0x40
7649ab747fSPaolo Bonzini #define MOUSE_STATUS_ENABLED    0x20
7749ab747fSPaolo Bonzini #define MOUSE_STATUS_SCALE21    0x10
7849ab747fSPaolo Bonzini 
792858ab09SGonglei #define PS2_QUEUE_SIZE 16  /* Buffer size required by PS/2 protocol */
8049ab747fSPaolo Bonzini 
81620775d1SDaniel P. Berrange /* Bits for 'modifiers' field in PS2KbdState */
82620775d1SDaniel P. Berrange #define MOD_CTRL_L  (1 << 0)
83620775d1SDaniel P. Berrange #define MOD_SHIFT_L (1 << 1)
84620775d1SDaniel P. Berrange #define MOD_ALT_L   (1 << 2)
85620775d1SDaniel P. Berrange #define MOD_CTRL_R  (1 << 3)
86620775d1SDaniel P. Berrange #define MOD_SHIFT_R (1 << 4)
87620775d1SDaniel P. Berrange #define MOD_ALT_R   (1 << 5)
88620775d1SDaniel P. Berrange 
8949ab747fSPaolo Bonzini typedef struct {
902858ab09SGonglei     /* Keep the data array 256 bytes long, which compatibility
912858ab09SGonglei      with older qemu versions. */
922858ab09SGonglei     uint8_t data[256];
9349ab747fSPaolo Bonzini     int rptr, wptr, count;
9449ab747fSPaolo Bonzini } PS2Queue;
9549ab747fSPaolo Bonzini 
968498bb8dSGerd Hoffmann struct PS2State {
9749ab747fSPaolo Bonzini     PS2Queue queue;
9849ab747fSPaolo Bonzini     int32_t write_cmd;
9949ab747fSPaolo Bonzini     void (*update_irq)(void *, int);
10049ab747fSPaolo Bonzini     void *update_arg;
1018498bb8dSGerd Hoffmann };
10249ab747fSPaolo Bonzini 
10349ab747fSPaolo Bonzini typedef struct {
10449ab747fSPaolo Bonzini     PS2State common;
10549ab747fSPaolo Bonzini     int scan_enabled;
10649ab747fSPaolo Bonzini     int translate;
10749ab747fSPaolo Bonzini     int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
10849ab747fSPaolo Bonzini     int ledstate;
10957d5c005SHervé Poussineau     bool need_high_bit;
110620775d1SDaniel P. Berrange     unsigned int modifiers; /* bitmask of MOD_* constants above */
11149ab747fSPaolo Bonzini } PS2KbdState;
11249ab747fSPaolo Bonzini 
11349ab747fSPaolo Bonzini typedef struct {
11449ab747fSPaolo Bonzini     PS2State common;
11549ab747fSPaolo Bonzini     uint8_t mouse_status;
11649ab747fSPaolo Bonzini     uint8_t mouse_resolution;
11749ab747fSPaolo Bonzini     uint8_t mouse_sample_rate;
11849ab747fSPaolo Bonzini     uint8_t mouse_wrap;
11949ab747fSPaolo Bonzini     uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
12049ab747fSPaolo Bonzini     uint8_t mouse_detect_state;
12149ab747fSPaolo Bonzini     int mouse_dx; /* current values, needed for 'poll' mode */
12249ab747fSPaolo Bonzini     int mouse_dy;
12349ab747fSPaolo Bonzini     int mouse_dz;
12449ab747fSPaolo Bonzini     uint8_t mouse_buttons;
12549ab747fSPaolo Bonzini } PS2MouseState;
12649ab747fSPaolo Bonzini 
12757d5c005SHervé Poussineau static uint8_t translate_table[256] = {
12857d5c005SHervé Poussineau     0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
12957d5c005SHervé Poussineau     0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
13057d5c005SHervé Poussineau     0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
13157d5c005SHervé Poussineau     0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
13257d5c005SHervé Poussineau     0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
13357d5c005SHervé Poussineau     0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
13457d5c005SHervé Poussineau     0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
13557d5c005SHervé Poussineau     0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
13657d5c005SHervé Poussineau     0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
13757d5c005SHervé Poussineau     0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
13857d5c005SHervé Poussineau     0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
13957d5c005SHervé Poussineau     0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
14057d5c005SHervé Poussineau     0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
14157d5c005SHervé Poussineau     0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
14257d5c005SHervé Poussineau     0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
14357d5c005SHervé Poussineau     0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
14457d5c005SHervé Poussineau     0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
14557d5c005SHervé Poussineau     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
14657d5c005SHervé Poussineau     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
14757d5c005SHervé Poussineau     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
14857d5c005SHervé Poussineau     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
14957d5c005SHervé Poussineau     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
15057d5c005SHervé Poussineau     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
15157d5c005SHervé Poussineau     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
15257d5c005SHervé Poussineau     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
15357d5c005SHervé Poussineau     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
15457d5c005SHervé Poussineau     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
15557d5c005SHervé Poussineau     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
15657d5c005SHervé Poussineau     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
15757d5c005SHervé Poussineau     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
15857d5c005SHervé Poussineau     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
15957d5c005SHervé Poussineau     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
16057d5c005SHervé Poussineau };
16157d5c005SHervé Poussineau 
162620775d1SDaniel P. Berrange static unsigned int ps2_modifier_bit(QKeyCode key)
163620775d1SDaniel P. Berrange {
164620775d1SDaniel P. Berrange     switch (key) {
165620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL:
166620775d1SDaniel P. Berrange         return MOD_CTRL_L;
167620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL_R:
168620775d1SDaniel P. Berrange         return MOD_CTRL_R;
169620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT:
170620775d1SDaniel P. Berrange         return MOD_SHIFT_L;
171620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT_R:
172620775d1SDaniel P. Berrange         return MOD_SHIFT_R;
173620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT:
174620775d1SDaniel P. Berrange         return MOD_ALT_L;
175620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT_R:
176620775d1SDaniel P. Berrange         return MOD_ALT_R;
177620775d1SDaniel P. Berrange     default:
178620775d1SDaniel P. Berrange         return 0;
179620775d1SDaniel P. Berrange     }
180620775d1SDaniel P. Berrange }
181620775d1SDaniel P. Berrange 
182954ee55bSGerd Hoffmann static void ps2_reset_queue(PS2State *s)
183954ee55bSGerd Hoffmann {
184954ee55bSGerd Hoffmann     PS2Queue *q = &s->queue;
185954ee55bSGerd Hoffmann 
186954ee55bSGerd Hoffmann     q->rptr = 0;
187954ee55bSGerd Hoffmann     q->wptr = 0;
188954ee55bSGerd Hoffmann     q->count = 0;
189954ee55bSGerd Hoffmann }
190954ee55bSGerd Hoffmann 
191*7abe7eb2SGeoffrey McRae void ps2_queue_noirq(PS2State *s, int b)
19249ab747fSPaolo Bonzini {
19349ab747fSPaolo Bonzini     PS2Queue *q = &s->queue;
19449ab747fSPaolo Bonzini 
195*7abe7eb2SGeoffrey McRae     if (q->count == PS2_QUEUE_SIZE) {
19649ab747fSPaolo Bonzini         return;
197*7abe7eb2SGeoffrey McRae     }
198*7abe7eb2SGeoffrey McRae 
19949ab747fSPaolo Bonzini     q->data[q->wptr] = b;
20049ab747fSPaolo Bonzini     if (++q->wptr == PS2_QUEUE_SIZE)
20149ab747fSPaolo Bonzini         q->wptr = 0;
20249ab747fSPaolo Bonzini     q->count++;
203*7abe7eb2SGeoffrey McRae }
204*7abe7eb2SGeoffrey McRae 
205*7abe7eb2SGeoffrey McRae void ps2_raise_irq(PS2State *s)
206*7abe7eb2SGeoffrey McRae {
207*7abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
208*7abe7eb2SGeoffrey McRae }
209*7abe7eb2SGeoffrey McRae 
210*7abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
211*7abe7eb2SGeoffrey McRae {
212*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
213*7abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
214*7abe7eb2SGeoffrey McRae }
215*7abe7eb2SGeoffrey McRae 
216*7abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
217*7abe7eb2SGeoffrey McRae {
218*7abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
219*7abe7eb2SGeoffrey McRae         return;
220*7abe7eb2SGeoffrey McRae     }
221*7abe7eb2SGeoffrey McRae 
222*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
223*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
224*7abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
225*7abe7eb2SGeoffrey McRae }
226*7abe7eb2SGeoffrey McRae 
227*7abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
228*7abe7eb2SGeoffrey McRae {
229*7abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
230*7abe7eb2SGeoffrey McRae         return;
231*7abe7eb2SGeoffrey McRae     }
232*7abe7eb2SGeoffrey McRae 
233*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
234*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
235*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
236*7abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
237*7abe7eb2SGeoffrey McRae }
238*7abe7eb2SGeoffrey McRae 
239*7abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
240*7abe7eb2SGeoffrey McRae {
241*7abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
242*7abe7eb2SGeoffrey McRae         return;
243*7abe7eb2SGeoffrey McRae     }
244*7abe7eb2SGeoffrey McRae 
245*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
246*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
247*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
248*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
24949ab747fSPaolo Bonzini     s->update_irq(s->update_arg, 1);
25049ab747fSPaolo Bonzini }
25149ab747fSPaolo Bonzini 
25257d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
25349ab747fSPaolo Bonzini static void ps2_put_keycode(void *opaque, int keycode)
25449ab747fSPaolo Bonzini {
25549ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
25649ab747fSPaolo Bonzini 
2575edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
25849ab747fSPaolo Bonzini     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
25957d5c005SHervé Poussineau 
26057d5c005SHervé Poussineau     if (s->translate) {
26157d5c005SHervé Poussineau         if (keycode == 0xf0) {
26257d5c005SHervé Poussineau             s->need_high_bit = true;
26357d5c005SHervé Poussineau         } else if (s->need_high_bit) {
26457d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode] | 0x80);
26557d5c005SHervé Poussineau             s->need_high_bit = false;
26657d5c005SHervé Poussineau         } else {
26757d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode]);
26849ab747fSPaolo Bonzini         }
26957d5c005SHervé Poussineau     } else {
27049ab747fSPaolo Bonzini         ps2_queue(&s->common, keycode);
27149ab747fSPaolo Bonzini     }
27257d5c005SHervé Poussineau }
27349ab747fSPaolo Bonzini 
27466e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
27566e6536eSGerd Hoffmann                                InputEvent *evt)
27666e6536eSGerd Hoffmann {
27766e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
27832bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
2798c10e0baSHervé Poussineau     int qcode;
280ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
281620775d1SDaniel P. Berrange     int mod;
28266e6536eSGerd Hoffmann 
283143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
284143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
285143c04c7SGeoffrey McRae         return;
286143c04c7SGeoffrey McRae     }
287143c04c7SGeoffrey McRae 
28866e6536eSGerd Hoffmann     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
2898c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
2908c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
29157d5c005SHervé Poussineau 
292620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
293620775d1SDaniel P. Berrange     trace_ps2_keyboard_event(s, qcode, key->down, mod, s->modifiers);
294620775d1SDaniel P. Berrange     if (key->down) {
295620775d1SDaniel P. Berrange         s->modifiers |= mod;
296620775d1SDaniel P. Berrange     } else {
297620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
298620775d1SDaniel P. Berrange     }
299620775d1SDaniel P. Berrange 
3008c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3018c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
30229fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
30329fd23a5SDaniel P. Berrange                 if (key->down) {
30429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
30529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
30629fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
30729fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
30829fd23a5SDaniel P. Berrange                 }
30929fd23a5SDaniel P. Berrange             } else {
3108c10e0baSHervé Poussineau                 if (key->down) {
3118c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3128c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3138c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
314927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3158c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
3168c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
3178c10e0baSHervé Poussineau                 }
31829fd23a5SDaniel P. Berrange             }
3198c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
320620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
321620775d1SDaniel P. Berrange                 if (key->down) {
322620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
323620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
324620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
325620775d1SDaniel P. Berrange                 } else {
326620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
327620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
328620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
329620775d1SDaniel P. Berrange                 }
330620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
331620775d1SDaniel P. Berrange                 if (key->down) {
332620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
333620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
334620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
335620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
336620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
337620775d1SDaniel P. Berrange                 } else {
338620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
339620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
340620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
341620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
342620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
343620775d1SDaniel P. Berrange                 }
3448f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
3458f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
3468f63458fSDaniel P. Berrange                 if (key->down) {
3478f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3488f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
3498f63458fSDaniel P. Berrange                 } else {
3508f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3518f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
3528f63458fSDaniel P. Berrange                 }
353620775d1SDaniel P. Berrange             } else {
3548c10e0baSHervé Poussineau                 if (key->down) {
3558c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3568c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
3578c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3588c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
3598c10e0baSHervé Poussineau                 } else {
3608c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3618c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
3628c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3638c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
3648c10e0baSHervé Poussineau                 }
365620775d1SDaniel P. Berrange             }
3668c10e0baSHervé Poussineau         } else {
367ab8f9d49SDaniel P. Berrange             if (qcode < qemu_input_map_qcode_to_atset1_len)
368ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
3698c10e0baSHervé Poussineau             if (keycode) {
3708c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
3718c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
3728c10e0baSHervé Poussineau                 }
3738c10e0baSHervé Poussineau                 if (!key->down) {
3748c10e0baSHervé Poussineau                     keycode |= 0x80;
3758c10e0baSHervé Poussineau                 }
3768c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
3778c10e0baSHervé Poussineau             } else {
378ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
379ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
3808c10e0baSHervé Poussineau             }
3818c10e0baSHervé Poussineau         }
3828c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
3838c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
38429fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
38529fd23a5SDaniel P. Berrange                 if (key->down) {
38629fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
38729fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
38829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
38929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
39029fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
39129fd23a5SDaniel P. Berrange                 }
39229fd23a5SDaniel P. Berrange             } else {
3938c10e0baSHervé Poussineau                 if (key->down) {
3948c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3958c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
3968c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
3978c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3988c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
3998c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4008c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4018c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4028c10e0baSHervé Poussineau                 }
40329fd23a5SDaniel P. Berrange             }
4048c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
405620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
406620775d1SDaniel P. Berrange                 if (key->down) {
407620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
408620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
409620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
410620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
411620775d1SDaniel P. Berrange                 } else {
412620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
413620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
414620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
415620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
416620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
417620775d1SDaniel P. Berrange                 }
418620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
419620775d1SDaniel P. Berrange                 if (key->down) {
420620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
421620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
422620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
423620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
424620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
425620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
426620775d1SDaniel P. Berrange                 } else {
427620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
428620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
429620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
430620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
431620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
432620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
433620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
434620775d1SDaniel P. Berrange                 }
4358f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4368f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4378f63458fSDaniel P. Berrange                 if (key->down) {
4388f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4398f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4408f63458fSDaniel P. Berrange                 } else {
4418f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4428f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
4438f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4448f63458fSDaniel P. Berrange                 }
445620775d1SDaniel P. Berrange             } else {
4468c10e0baSHervé Poussineau                 if (key->down) {
4478c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4488c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4498c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4508c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4518c10e0baSHervé Poussineau                 } else {
4528c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4538c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4548c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4558c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4568c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4578c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4588c10e0baSHervé Poussineau                 }
459620775d1SDaniel P. Berrange             }
4608c10e0baSHervé Poussineau         } else {
461ab8f9d49SDaniel P. Berrange             if (qcode < qemu_input_map_qcode_to_atset2_len)
462ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
4638c10e0baSHervé Poussineau             if (keycode) {
4648c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4658c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4668c10e0baSHervé Poussineau                 }
4678c10e0baSHervé Poussineau                 if (!key->down) {
4688c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4698c10e0baSHervé Poussineau                 }
4708c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
47157d5c005SHervé Poussineau             } else {
472ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
473ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
47457d5c005SHervé Poussineau             }
47557d5c005SHervé Poussineau         }
47657d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
477ab8f9d49SDaniel P. Berrange         if (qcode < qemu_input_map_qcode_to_atset3_len)
478ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
4798c10e0baSHervé Poussineau         if (keycode) {
4808c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
4818c10e0baSHervé Poussineau             if (!key->down) {
4828c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
48357d5c005SHervé Poussineau             }
48457d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
4858c10e0baSHervé Poussineau         } else {
486ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
487ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
4888c10e0baSHervé Poussineau         }
48966e6536eSGerd Hoffmann     }
49066e6536eSGerd Hoffmann }
49166e6536eSGerd Hoffmann 
4928498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
49349ab747fSPaolo Bonzini {
49449ab747fSPaolo Bonzini     PS2Queue *q;
49549ab747fSPaolo Bonzini     int val, index;
49649ab747fSPaolo Bonzini 
4978498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
49849ab747fSPaolo Bonzini     q = &s->queue;
49949ab747fSPaolo Bonzini     if (q->count == 0) {
50049ab747fSPaolo Bonzini         /* NOTE: if no data left, we return the last keyboard one
50149ab747fSPaolo Bonzini            (needed for EMM386) */
50249ab747fSPaolo Bonzini         /* XXX: need a timer to do things correctly */
50349ab747fSPaolo Bonzini         index = q->rptr - 1;
50449ab747fSPaolo Bonzini         if (index < 0)
50549ab747fSPaolo Bonzini             index = PS2_QUEUE_SIZE - 1;
50649ab747fSPaolo Bonzini         val = q->data[index];
50749ab747fSPaolo Bonzini     } else {
50849ab747fSPaolo Bonzini         val = q->data[q->rptr];
50949ab747fSPaolo Bonzini         if (++q->rptr == PS2_QUEUE_SIZE)
51049ab747fSPaolo Bonzini             q->rptr = 0;
51149ab747fSPaolo Bonzini         q->count--;
51249ab747fSPaolo Bonzini         /* reading deasserts IRQ */
51349ab747fSPaolo Bonzini         s->update_irq(s->update_arg, 0);
51449ab747fSPaolo Bonzini         /* reassert IRQs if data left */
51549ab747fSPaolo Bonzini         s->update_irq(s->update_arg, q->count != 0);
51649ab747fSPaolo Bonzini     }
51749ab747fSPaolo Bonzini     return val;
51849ab747fSPaolo Bonzini }
51949ab747fSPaolo Bonzini 
52049ab747fSPaolo Bonzini static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
52149ab747fSPaolo Bonzini {
5225edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
52349ab747fSPaolo Bonzini     s->ledstate = ledstate;
52449ab747fSPaolo Bonzini     kbd_put_ledstate(ledstate);
52549ab747fSPaolo Bonzini }
52649ab747fSPaolo Bonzini 
52749ab747fSPaolo Bonzini static void ps2_reset_keyboard(PS2KbdState *s)
52849ab747fSPaolo Bonzini {
5295edab03dSDon Koch     trace_ps2_reset_keyboard(s);
53049ab747fSPaolo Bonzini     s->scan_enabled = 1;
53149ab747fSPaolo Bonzini     s->scancode_set = 2;
5326e24ee0cSGerd Hoffmann     ps2_reset_queue(&s->common);
53349ab747fSPaolo Bonzini     ps2_set_ledstate(s, 0);
53449ab747fSPaolo Bonzini }
53549ab747fSPaolo Bonzini 
53649ab747fSPaolo Bonzini void ps2_write_keyboard(void *opaque, int val)
53749ab747fSPaolo Bonzini {
53849ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)opaque;
53949ab747fSPaolo Bonzini 
5405edab03dSDon Koch     trace_ps2_write_keyboard(opaque, val);
54149ab747fSPaolo Bonzini     switch(s->common.write_cmd) {
54249ab747fSPaolo Bonzini     default:
54349ab747fSPaolo Bonzini     case -1:
54449ab747fSPaolo Bonzini         switch(val) {
54549ab747fSPaolo Bonzini         case 0x00:
54649ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
54749ab747fSPaolo Bonzini             break;
54849ab747fSPaolo Bonzini         case 0x05:
54949ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_RESEND);
55049ab747fSPaolo Bonzini             break;
55149ab747fSPaolo Bonzini         case KBD_CMD_GET_ID:
55249ab747fSPaolo Bonzini             /* We emulate a MF2 AT keyboard here */
55349ab747fSPaolo Bonzini             if (s->translate)
554*7abe7eb2SGeoffrey McRae                 ps2_queue_3(&s->common,
555*7abe7eb2SGeoffrey McRae                     KBD_REPLY_ACK,
556*7abe7eb2SGeoffrey McRae                     KBD_REPLY_ID,
557*7abe7eb2SGeoffrey McRae                     0x41);
55849ab747fSPaolo Bonzini             else
559*7abe7eb2SGeoffrey McRae                 ps2_queue_3(&s->common,
560*7abe7eb2SGeoffrey McRae                     KBD_REPLY_ACK,
561*7abe7eb2SGeoffrey McRae                     KBD_REPLY_ID,
562*7abe7eb2SGeoffrey McRae                     0x83);
56349ab747fSPaolo Bonzini             break;
56449ab747fSPaolo Bonzini         case KBD_CMD_ECHO:
56549ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_CMD_ECHO);
56649ab747fSPaolo Bonzini             break;
56749ab747fSPaolo Bonzini         case KBD_CMD_ENABLE:
56849ab747fSPaolo Bonzini             s->scan_enabled = 1;
56949ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
57049ab747fSPaolo Bonzini             break;
57149ab747fSPaolo Bonzini         case KBD_CMD_SCANCODE:
57249ab747fSPaolo Bonzini         case KBD_CMD_SET_LEDS:
57349ab747fSPaolo Bonzini         case KBD_CMD_SET_RATE:
57449ab747fSPaolo Bonzini             s->common.write_cmd = val;
57549ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
57649ab747fSPaolo Bonzini             break;
57749ab747fSPaolo Bonzini         case KBD_CMD_RESET_DISABLE:
57849ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
57949ab747fSPaolo Bonzini             s->scan_enabled = 0;
58049ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
58149ab747fSPaolo Bonzini             break;
58249ab747fSPaolo Bonzini         case KBD_CMD_RESET_ENABLE:
58349ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
58449ab747fSPaolo Bonzini             s->scan_enabled = 1;
58549ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
58649ab747fSPaolo Bonzini             break;
58749ab747fSPaolo Bonzini         case KBD_CMD_RESET:
58849ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
589*7abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
590*7abe7eb2SGeoffrey McRae                 KBD_REPLY_ACK,
591*7abe7eb2SGeoffrey McRae                 KBD_REPLY_POR);
59249ab747fSPaolo Bonzini             break;
59349ab747fSPaolo Bonzini         default:
59406b3611fSHervé Poussineau             ps2_queue(&s->common, KBD_REPLY_RESEND);
59549ab747fSPaolo Bonzini             break;
59649ab747fSPaolo Bonzini         }
59749ab747fSPaolo Bonzini         break;
59849ab747fSPaolo Bonzini     case KBD_CMD_SCANCODE:
59949ab747fSPaolo Bonzini         if (val == 0) {
600*7abe7eb2SGeoffrey McRae             if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) {
6014df23b64SHervé Poussineau                 ps2_queue(&s->common, KBD_REPLY_ACK);
60257d5c005SHervé Poussineau                 ps2_put_keycode(s, s->scancode_set);
603*7abe7eb2SGeoffrey McRae             }
6044df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
60549ab747fSPaolo Bonzini             s->scancode_set = val;
60649ab747fSPaolo Bonzini             ps2_queue(&s->common, KBD_REPLY_ACK);
6074df23b64SHervé Poussineau         } else {
6084df23b64SHervé Poussineau             ps2_queue(&s->common, KBD_REPLY_RESEND);
60949ab747fSPaolo Bonzini         }
61049ab747fSPaolo Bonzini         s->common.write_cmd = -1;
61149ab747fSPaolo Bonzini         break;
61249ab747fSPaolo Bonzini     case KBD_CMD_SET_LEDS:
61349ab747fSPaolo Bonzini         ps2_set_ledstate(s, val);
61449ab747fSPaolo Bonzini         ps2_queue(&s->common, KBD_REPLY_ACK);
61549ab747fSPaolo Bonzini         s->common.write_cmd = -1;
61649ab747fSPaolo Bonzini         break;
61749ab747fSPaolo Bonzini     case KBD_CMD_SET_RATE:
61849ab747fSPaolo Bonzini         ps2_queue(&s->common, KBD_REPLY_ACK);
61949ab747fSPaolo Bonzini         s->common.write_cmd = -1;
62049ab747fSPaolo Bonzini         break;
62149ab747fSPaolo Bonzini     }
62249ab747fSPaolo Bonzini }
62349ab747fSPaolo Bonzini 
62449ab747fSPaolo Bonzini /* Set the scancode translation mode.
62549ab747fSPaolo Bonzini    0 = raw scancodes.
62649ab747fSPaolo Bonzini    1 = translated scancodes (used by qemu internally).  */
62749ab747fSPaolo Bonzini 
62849ab747fSPaolo Bonzini void ps2_keyboard_set_translation(void *opaque, int mode)
62949ab747fSPaolo Bonzini {
63049ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)opaque;
6315edab03dSDon Koch     trace_ps2_keyboard_set_translation(opaque, mode);
63249ab747fSPaolo Bonzini     s->translate = mode;
63349ab747fSPaolo Bonzini }
63449ab747fSPaolo Bonzini 
635*7abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
63649ab747fSPaolo Bonzini {
637*7abe7eb2SGeoffrey McRae     const int needed = 3 + (s->mouse_type - 2);
63849ab747fSPaolo Bonzini     unsigned int b;
63949ab747fSPaolo Bonzini     int dx1, dy1, dz1;
64049ab747fSPaolo Bonzini 
641*7abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->common.queue.count < needed) {
642*7abe7eb2SGeoffrey McRae         return 0;
643*7abe7eb2SGeoffrey McRae     }
644*7abe7eb2SGeoffrey McRae 
64549ab747fSPaolo Bonzini     dx1 = s->mouse_dx;
64649ab747fSPaolo Bonzini     dy1 = s->mouse_dy;
64749ab747fSPaolo Bonzini     dz1 = s->mouse_dz;
64849ab747fSPaolo Bonzini     /* XXX: increase range to 8 bits ? */
64949ab747fSPaolo Bonzini     if (dx1 > 127)
65049ab747fSPaolo Bonzini         dx1 = 127;
65149ab747fSPaolo Bonzini     else if (dx1 < -127)
65249ab747fSPaolo Bonzini         dx1 = -127;
65349ab747fSPaolo Bonzini     if (dy1 > 127)
65449ab747fSPaolo Bonzini         dy1 = 127;
65549ab747fSPaolo Bonzini     else if (dy1 < -127)
65649ab747fSPaolo Bonzini         dy1 = -127;
65749ab747fSPaolo Bonzini     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
658*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, b);
659*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dx1 & 0xff);
660*7abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dy1 & 0xff);
66149ab747fSPaolo Bonzini     /* extra byte for IMPS/2 or IMEX */
66249ab747fSPaolo Bonzini     switch(s->mouse_type) {
66349ab747fSPaolo Bonzini     default:
66449ab747fSPaolo Bonzini         break;
66549ab747fSPaolo Bonzini     case 3:
66649ab747fSPaolo Bonzini         if (dz1 > 127)
66749ab747fSPaolo Bonzini             dz1 = 127;
66849ab747fSPaolo Bonzini         else if (dz1 < -127)
66949ab747fSPaolo Bonzini                 dz1 = -127;
670*7abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, dz1 & 0xff);
67149ab747fSPaolo Bonzini         break;
67249ab747fSPaolo Bonzini     case 4:
67349ab747fSPaolo Bonzini         if (dz1 > 7)
67449ab747fSPaolo Bonzini             dz1 = 7;
67549ab747fSPaolo Bonzini         else if (dz1 < -7)
67649ab747fSPaolo Bonzini             dz1 = -7;
67749ab747fSPaolo Bonzini         b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
678*7abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, b);
67949ab747fSPaolo Bonzini         break;
68049ab747fSPaolo Bonzini     }
68149ab747fSPaolo Bonzini 
682*7abe7eb2SGeoffrey McRae     ps2_raise_irq(&s->common);
683*7abe7eb2SGeoffrey McRae 
6845edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
68549ab747fSPaolo Bonzini     /* update deltas */
68649ab747fSPaolo Bonzini     s->mouse_dx -= dx1;
68749ab747fSPaolo Bonzini     s->mouse_dy -= dy1;
68849ab747fSPaolo Bonzini     s->mouse_dz -= dz1;
689*7abe7eb2SGeoffrey McRae 
690*7abe7eb2SGeoffrey McRae     return 1;
69149ab747fSPaolo Bonzini }
69249ab747fSPaolo Bonzini 
6932a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
6942a766d29SGerd Hoffmann                             InputEvent *evt)
69549ab747fSPaolo Bonzini {
6967fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
6978b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
6988b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
6998b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
7008b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
7018b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
7022a766d29SGerd Hoffmann     };
7032a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
704b5a1b443SEric Blake     InputMoveEvent *move;
705b5a1b443SEric Blake     InputBtnEvent *btn;
70649ab747fSPaolo Bonzini 
70749ab747fSPaolo Bonzini     /* check if deltas are recorded when disabled */
70849ab747fSPaolo Bonzini     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
70949ab747fSPaolo Bonzini         return;
71049ab747fSPaolo Bonzini 
711568c73a4SEric Blake     switch (evt->type) {
7122a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
71332bafa8fSEric Blake         move = evt->u.rel.data;
714b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
715b5a1b443SEric Blake             s->mouse_dx += move->value;
716b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
717b5a1b443SEric Blake             s->mouse_dy -= move->value;
7182a766d29SGerd Hoffmann         }
7192a766d29SGerd Hoffmann         break;
72049ab747fSPaolo Bonzini 
7212a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
72232bafa8fSEric Blake         btn = evt->u.btn.data;
723b5a1b443SEric Blake         if (btn->down) {
724b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
725b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
7262a766d29SGerd Hoffmann                 s->mouse_dz--;
727b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
7282a766d29SGerd Hoffmann                 s->mouse_dz++;
7292a766d29SGerd Hoffmann             }
7302a766d29SGerd Hoffmann         } else {
731b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
7322a766d29SGerd Hoffmann         }
7332a766d29SGerd Hoffmann         break;
7342a766d29SGerd Hoffmann 
7352a766d29SGerd Hoffmann     default:
7362a766d29SGerd Hoffmann         /* keep gcc happy */
7372a766d29SGerd Hoffmann         break;
7382a766d29SGerd Hoffmann     }
73949ab747fSPaolo Bonzini }
74049ab747fSPaolo Bonzini 
7412a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
7422a766d29SGerd Hoffmann {
7432a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
7442a766d29SGerd Hoffmann 
745143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
746143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
747143c04c7SGeoffrey McRae         return;
748143c04c7SGeoffrey McRae     }
749143c04c7SGeoffrey McRae 
7502a766d29SGerd Hoffmann     if (s->mouse_buttons) {
7512a766d29SGerd Hoffmann         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
7522a766d29SGerd Hoffmann     }
7532858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
75449ab747fSPaolo Bonzini         /* if not remote, send event. Multiple events are sent if
75549ab747fSPaolo Bonzini            too big deltas */
756*7abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
75749ab747fSPaolo Bonzini             if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
75849ab747fSPaolo Bonzini                 break;
75949ab747fSPaolo Bonzini         }
76049ab747fSPaolo Bonzini     }
76149ab747fSPaolo Bonzini }
76249ab747fSPaolo Bonzini 
76349ab747fSPaolo Bonzini void ps2_mouse_fake_event(void *opaque)
76449ab747fSPaolo Bonzini {
7652a766d29SGerd Hoffmann     PS2MouseState *s = opaque;
7665edab03dSDon Koch     trace_ps2_mouse_fake_event(opaque);
7672a766d29SGerd Hoffmann     s->mouse_dx++;
7682a766d29SGerd Hoffmann     ps2_mouse_sync(opaque);
76949ab747fSPaolo Bonzini }
77049ab747fSPaolo Bonzini 
77149ab747fSPaolo Bonzini void ps2_write_mouse(void *opaque, int val)
77249ab747fSPaolo Bonzini {
77349ab747fSPaolo Bonzini     PS2MouseState *s = (PS2MouseState *)opaque;
7745edab03dSDon Koch 
7755edab03dSDon Koch     trace_ps2_write_mouse(opaque, val);
77649ab747fSPaolo Bonzini #ifdef DEBUG_MOUSE
77749ab747fSPaolo Bonzini     printf("kbd: write mouse 0x%02x\n", val);
77849ab747fSPaolo Bonzini #endif
77949ab747fSPaolo Bonzini     switch(s->common.write_cmd) {
78049ab747fSPaolo Bonzini     default:
78149ab747fSPaolo Bonzini     case -1:
78249ab747fSPaolo Bonzini         /* mouse command */
78349ab747fSPaolo Bonzini         if (s->mouse_wrap) {
78449ab747fSPaolo Bonzini             if (val == AUX_RESET_WRAP) {
78549ab747fSPaolo Bonzini                 s->mouse_wrap = 0;
78649ab747fSPaolo Bonzini                 ps2_queue(&s->common, AUX_ACK);
78749ab747fSPaolo Bonzini                 return;
78849ab747fSPaolo Bonzini             } else if (val != AUX_RESET) {
78949ab747fSPaolo Bonzini                 ps2_queue(&s->common, val);
79049ab747fSPaolo Bonzini                 return;
79149ab747fSPaolo Bonzini             }
79249ab747fSPaolo Bonzini         }
79349ab747fSPaolo Bonzini         switch(val) {
79449ab747fSPaolo Bonzini         case AUX_SET_SCALE11:
79549ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
79649ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
79749ab747fSPaolo Bonzini             break;
79849ab747fSPaolo Bonzini         case AUX_SET_SCALE21:
79949ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_SCALE21;
80049ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
80149ab747fSPaolo Bonzini             break;
80249ab747fSPaolo Bonzini         case AUX_SET_STREAM:
80349ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
80449ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
80549ab747fSPaolo Bonzini             break;
80649ab747fSPaolo Bonzini         case AUX_SET_WRAP:
80749ab747fSPaolo Bonzini             s->mouse_wrap = 1;
80849ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
80949ab747fSPaolo Bonzini             break;
81049ab747fSPaolo Bonzini         case AUX_SET_REMOTE:
81149ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_REMOTE;
81249ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
81349ab747fSPaolo Bonzini             break;
81449ab747fSPaolo Bonzini         case AUX_GET_TYPE:
815*7abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
816*7abe7eb2SGeoffrey McRae                 AUX_ACK,
817*7abe7eb2SGeoffrey McRae                 s->mouse_type);
81849ab747fSPaolo Bonzini             break;
81949ab747fSPaolo Bonzini         case AUX_SET_RES:
82049ab747fSPaolo Bonzini         case AUX_SET_SAMPLE:
82149ab747fSPaolo Bonzini             s->common.write_cmd = val;
82249ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
82349ab747fSPaolo Bonzini             break;
82449ab747fSPaolo Bonzini         case AUX_GET_SCALE:
825*7abe7eb2SGeoffrey McRae             ps2_queue_4(&s->common,
826*7abe7eb2SGeoffrey McRae                 AUX_ACK,
827*7abe7eb2SGeoffrey McRae                 s->mouse_status,
828*7abe7eb2SGeoffrey McRae                 s->mouse_resolution,
829*7abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
83049ab747fSPaolo Bonzini             break;
83149ab747fSPaolo Bonzini         case AUX_POLL:
83249ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
83349ab747fSPaolo Bonzini             ps2_mouse_send_packet(s);
83449ab747fSPaolo Bonzini             break;
83549ab747fSPaolo Bonzini         case AUX_ENABLE_DEV:
83649ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_ENABLED;
83749ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
83849ab747fSPaolo Bonzini             break;
83949ab747fSPaolo Bonzini         case AUX_DISABLE_DEV:
84049ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
84149ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
84249ab747fSPaolo Bonzini             break;
84349ab747fSPaolo Bonzini         case AUX_SET_DEFAULT:
84449ab747fSPaolo Bonzini             s->mouse_sample_rate = 100;
84549ab747fSPaolo Bonzini             s->mouse_resolution = 2;
84649ab747fSPaolo Bonzini             s->mouse_status = 0;
84749ab747fSPaolo Bonzini             ps2_queue(&s->common, AUX_ACK);
84849ab747fSPaolo Bonzini             break;
84949ab747fSPaolo Bonzini         case AUX_RESET:
85049ab747fSPaolo Bonzini             s->mouse_sample_rate = 100;
85149ab747fSPaolo Bonzini             s->mouse_resolution = 2;
85249ab747fSPaolo Bonzini             s->mouse_status = 0;
85349ab747fSPaolo Bonzini             s->mouse_type = 0;
854143c04c7SGeoffrey McRae             ps2_reset_queue(&s->common);
855*7abe7eb2SGeoffrey McRae             ps2_queue_3(&s->common,
856*7abe7eb2SGeoffrey McRae                 AUX_ACK,
857*7abe7eb2SGeoffrey McRae                 0xaa,
858*7abe7eb2SGeoffrey McRae                 s->mouse_type);
85949ab747fSPaolo Bonzini             break;
86049ab747fSPaolo Bonzini         default:
86149ab747fSPaolo Bonzini             break;
86249ab747fSPaolo Bonzini         }
86349ab747fSPaolo Bonzini         break;
86449ab747fSPaolo Bonzini     case AUX_SET_SAMPLE:
86549ab747fSPaolo Bonzini         s->mouse_sample_rate = val;
86649ab747fSPaolo Bonzini         /* detect IMPS/2 or IMEX */
86749ab747fSPaolo Bonzini         switch(s->mouse_detect_state) {
86849ab747fSPaolo Bonzini         default:
86949ab747fSPaolo Bonzini         case 0:
87049ab747fSPaolo Bonzini             if (val == 200)
87149ab747fSPaolo Bonzini                 s->mouse_detect_state = 1;
87249ab747fSPaolo Bonzini             break;
87349ab747fSPaolo Bonzini         case 1:
87449ab747fSPaolo Bonzini             if (val == 100)
87549ab747fSPaolo Bonzini                 s->mouse_detect_state = 2;
87649ab747fSPaolo Bonzini             else if (val == 200)
87749ab747fSPaolo Bonzini                 s->mouse_detect_state = 3;
87849ab747fSPaolo Bonzini             else
87949ab747fSPaolo Bonzini                 s->mouse_detect_state = 0;
88049ab747fSPaolo Bonzini             break;
88149ab747fSPaolo Bonzini         case 2:
88249ab747fSPaolo Bonzini             if (val == 80)
88349ab747fSPaolo Bonzini                 s->mouse_type = 3; /* IMPS/2 */
88449ab747fSPaolo Bonzini             s->mouse_detect_state = 0;
88549ab747fSPaolo Bonzini             break;
88649ab747fSPaolo Bonzini         case 3:
88749ab747fSPaolo Bonzini             if (val == 80)
88849ab747fSPaolo Bonzini                 s->mouse_type = 4; /* IMEX */
88949ab747fSPaolo Bonzini             s->mouse_detect_state = 0;
89049ab747fSPaolo Bonzini             break;
89149ab747fSPaolo Bonzini         }
89249ab747fSPaolo Bonzini         ps2_queue(&s->common, AUX_ACK);
89349ab747fSPaolo Bonzini         s->common.write_cmd = -1;
89449ab747fSPaolo Bonzini         break;
89549ab747fSPaolo Bonzini     case AUX_SET_RES:
89649ab747fSPaolo Bonzini         s->mouse_resolution = val;
89749ab747fSPaolo Bonzini         ps2_queue(&s->common, AUX_ACK);
89849ab747fSPaolo Bonzini         s->common.write_cmd = -1;
89949ab747fSPaolo Bonzini         break;
90049ab747fSPaolo Bonzini     }
90149ab747fSPaolo Bonzini }
90249ab747fSPaolo Bonzini 
90349ab747fSPaolo Bonzini static void ps2_common_reset(PS2State *s)
90449ab747fSPaolo Bonzini {
90549ab747fSPaolo Bonzini     s->write_cmd = -1;
906954ee55bSGerd Hoffmann     ps2_reset_queue(s);
90749ab747fSPaolo Bonzini     s->update_irq(s->update_arg, 0);
90849ab747fSPaolo Bonzini }
90949ab747fSPaolo Bonzini 
9102858ab09SGonglei static void ps2_common_post_load(PS2State *s)
9112858ab09SGonglei {
9122858ab09SGonglei     PS2Queue *q = &s->queue;
913802cbcb7SPrasad J Pandit     uint8_t i, size;
914802cbcb7SPrasad J Pandit     uint8_t tmp_data[PS2_QUEUE_SIZE];
9152858ab09SGonglei 
9162858ab09SGonglei     /* set the useful data buffer queue size, < PS2_QUEUE_SIZE */
917802cbcb7SPrasad J Pandit     size = (q->count < 0 || q->count > PS2_QUEUE_SIZE) ? 0 : q->count;
9182858ab09SGonglei 
9192858ab09SGonglei     /* move the queue elements to the start of data array */
9202858ab09SGonglei     for (i = 0; i < size; i++) {
921802cbcb7SPrasad J Pandit         if (q->rptr < 0 || q->rptr >= sizeof(q->data)) {
9222858ab09SGonglei             q->rptr = 0;
9232858ab09SGonglei         }
924802cbcb7SPrasad J Pandit         tmp_data[i] = q->data[q->rptr++];
9252858ab09SGonglei     }
9262858ab09SGonglei     memcpy(q->data, tmp_data, size);
927802cbcb7SPrasad J Pandit 
9282858ab09SGonglei     /* reset rptr/wptr/count */
9292858ab09SGonglei     q->rptr = 0;
9302858ab09SGonglei     q->wptr = size;
9312858ab09SGonglei     q->count = size;
9322858ab09SGonglei     s->update_irq(s->update_arg, q->count != 0);
9332858ab09SGonglei }
9342858ab09SGonglei 
93549ab747fSPaolo Bonzini static void ps2_kbd_reset(void *opaque)
93649ab747fSPaolo Bonzini {
93749ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *) opaque;
93849ab747fSPaolo Bonzini 
9395edab03dSDon Koch     trace_ps2_kbd_reset(opaque);
94049ab747fSPaolo Bonzini     ps2_common_reset(&s->common);
94149ab747fSPaolo Bonzini     s->scan_enabled = 0;
94249ab747fSPaolo Bonzini     s->translate = 0;
943089adafdSHervé Poussineau     s->scancode_set = 2;
944620775d1SDaniel P. Berrange     s->modifiers = 0;
94549ab747fSPaolo Bonzini }
94649ab747fSPaolo Bonzini 
94749ab747fSPaolo Bonzini static void ps2_mouse_reset(void *opaque)
94849ab747fSPaolo Bonzini {
94949ab747fSPaolo Bonzini     PS2MouseState *s = (PS2MouseState *) opaque;
95049ab747fSPaolo Bonzini 
9515edab03dSDon Koch     trace_ps2_mouse_reset(opaque);
95249ab747fSPaolo Bonzini     ps2_common_reset(&s->common);
95349ab747fSPaolo Bonzini     s->mouse_status = 0;
95449ab747fSPaolo Bonzini     s->mouse_resolution = 0;
95549ab747fSPaolo Bonzini     s->mouse_sample_rate = 0;
95649ab747fSPaolo Bonzini     s->mouse_wrap = 0;
95749ab747fSPaolo Bonzini     s->mouse_type = 0;
95849ab747fSPaolo Bonzini     s->mouse_detect_state = 0;
95949ab747fSPaolo Bonzini     s->mouse_dx = 0;
96049ab747fSPaolo Bonzini     s->mouse_dy = 0;
96149ab747fSPaolo Bonzini     s->mouse_dz = 0;
96249ab747fSPaolo Bonzini     s->mouse_buttons = 0;
96349ab747fSPaolo Bonzini }
96449ab747fSPaolo Bonzini 
96549ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_common = {
96649ab747fSPaolo Bonzini     .name = "PS2 Common State",
96749ab747fSPaolo Bonzini     .version_id = 3,
96849ab747fSPaolo Bonzini     .minimum_version_id = 2,
96949ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
97049ab747fSPaolo Bonzini         VMSTATE_INT32(write_cmd, PS2State),
97149ab747fSPaolo Bonzini         VMSTATE_INT32(queue.rptr, PS2State),
97249ab747fSPaolo Bonzini         VMSTATE_INT32(queue.wptr, PS2State),
97349ab747fSPaolo Bonzini         VMSTATE_INT32(queue.count, PS2State),
97449ab747fSPaolo Bonzini         VMSTATE_BUFFER(queue.data, PS2State),
97549ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
97649ab747fSPaolo Bonzini     }
97749ab747fSPaolo Bonzini };
97849ab747fSPaolo Bonzini 
97949ab747fSPaolo Bonzini static bool ps2_keyboard_ledstate_needed(void *opaque)
98049ab747fSPaolo Bonzini {
98149ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
98249ab747fSPaolo Bonzini 
98349ab747fSPaolo Bonzini     return s->ledstate != 0; /* 0 is default state */
98449ab747fSPaolo Bonzini }
98549ab747fSPaolo Bonzini 
98649ab747fSPaolo Bonzini static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
98749ab747fSPaolo Bonzini {
98849ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
98949ab747fSPaolo Bonzini 
99049ab747fSPaolo Bonzini     kbd_put_ledstate(s->ledstate);
99149ab747fSPaolo Bonzini     return 0;
99249ab747fSPaolo Bonzini }
99349ab747fSPaolo Bonzini 
99449ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
99549ab747fSPaolo Bonzini     .name = "ps2kbd/ledstate",
99649ab747fSPaolo Bonzini     .version_id = 3,
99749ab747fSPaolo Bonzini     .minimum_version_id = 2,
99849ab747fSPaolo Bonzini     .post_load = ps2_kbd_ledstate_post_load,
9995cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
100049ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
100149ab747fSPaolo Bonzini         VMSTATE_INT32(ledstate, PS2KbdState),
100249ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
100349ab747fSPaolo Bonzini     }
100449ab747fSPaolo Bonzini };
100549ab747fSPaolo Bonzini 
100657d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
100757d5c005SHervé Poussineau {
100857d5c005SHervé Poussineau     PS2KbdState *s = opaque;
100957d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
101057d5c005SHervé Poussineau }
101157d5c005SHervé Poussineau 
101257d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
101357d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
101457d5c005SHervé Poussineau     .version_id = 1,
101557d5c005SHervé Poussineau     .minimum_version_id = 1,
101657d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
101757d5c005SHervé Poussineau     .fields = (VMStateField[]) {
101857d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
101957d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
102057d5c005SHervé Poussineau     }
102157d5c005SHervé Poussineau };
102257d5c005SHervé Poussineau 
102349ab747fSPaolo Bonzini static int ps2_kbd_post_load(void* opaque, int version_id)
102449ab747fSPaolo Bonzini {
102549ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState*)opaque;
10262858ab09SGonglei     PS2State *ps2 = &s->common;
102749ab747fSPaolo Bonzini 
102849ab747fSPaolo Bonzini     if (version_id == 2)
102949ab747fSPaolo Bonzini         s->scancode_set=2;
10302858ab09SGonglei 
10312858ab09SGonglei     ps2_common_post_load(ps2);
10322858ab09SGonglei 
103349ab747fSPaolo Bonzini     return 0;
103449ab747fSPaolo Bonzini }
103549ab747fSPaolo Bonzini 
103644b1ff31SDr. David Alan Gilbert static int ps2_kbd_pre_save(void *opaque)
10372858ab09SGonglei {
10382858ab09SGonglei     PS2KbdState *s = (PS2KbdState *)opaque;
10392858ab09SGonglei     PS2State *ps2 = &s->common;
10402858ab09SGonglei 
10412858ab09SGonglei     ps2_common_post_load(ps2);
104244b1ff31SDr. David Alan Gilbert 
104344b1ff31SDr. David Alan Gilbert     return 0;
10442858ab09SGonglei }
10452858ab09SGonglei 
104649ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_keyboard = {
104749ab747fSPaolo Bonzini     .name = "ps2kbd",
104849ab747fSPaolo Bonzini     .version_id = 3,
104949ab747fSPaolo Bonzini     .minimum_version_id = 2,
105049ab747fSPaolo Bonzini     .post_load = ps2_kbd_post_load,
10512858ab09SGonglei     .pre_save = ps2_kbd_pre_save,
105249ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
105349ab747fSPaolo Bonzini         VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
105449ab747fSPaolo Bonzini         VMSTATE_INT32(scan_enabled, PS2KbdState),
105549ab747fSPaolo Bonzini         VMSTATE_INT32(translate, PS2KbdState),
105649ab747fSPaolo Bonzini         VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
105749ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
105849ab747fSPaolo Bonzini     },
10595cd8cadaSJuan Quintela     .subsections = (const VMStateDescription*[]) {
10605cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
106157d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
10625cd8cadaSJuan Quintela         NULL
106349ab747fSPaolo Bonzini     }
106449ab747fSPaolo Bonzini };
106549ab747fSPaolo Bonzini 
10662858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
10672858ab09SGonglei {
10682858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
10692858ab09SGonglei     PS2State *ps2 = &s->common;
10702858ab09SGonglei 
10712858ab09SGonglei     ps2_common_post_load(ps2);
10722858ab09SGonglei 
10732858ab09SGonglei     return 0;
10742858ab09SGonglei }
10752858ab09SGonglei 
107644b1ff31SDr. David Alan Gilbert static int ps2_mouse_pre_save(void *opaque)
10772858ab09SGonglei {
10782858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
10792858ab09SGonglei     PS2State *ps2 = &s->common;
10802858ab09SGonglei 
10812858ab09SGonglei     ps2_common_post_load(ps2);
108244b1ff31SDr. David Alan Gilbert 
108344b1ff31SDr. David Alan Gilbert     return 0;
10842858ab09SGonglei }
10852858ab09SGonglei 
108649ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_mouse = {
108749ab747fSPaolo Bonzini     .name = "ps2mouse",
108849ab747fSPaolo Bonzini     .version_id = 2,
108949ab747fSPaolo Bonzini     .minimum_version_id = 2,
10902858ab09SGonglei     .post_load = ps2_mouse_post_load,
10912858ab09SGonglei     .pre_save = ps2_mouse_pre_save,
109249ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
109349ab747fSPaolo Bonzini         VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
109449ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_status, PS2MouseState),
109549ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
109649ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
109749ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
109849ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_type, PS2MouseState),
109949ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
110049ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dx, PS2MouseState),
110149ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dy, PS2MouseState),
110249ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dz, PS2MouseState),
110349ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
110449ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
110549ab747fSPaolo Bonzini     }
110649ab747fSPaolo Bonzini };
110749ab747fSPaolo Bonzini 
110866e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
110966e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
111066e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
111166e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
111266e6536eSGerd Hoffmann };
111366e6536eSGerd Hoffmann 
111449ab747fSPaolo Bonzini void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
111549ab747fSPaolo Bonzini {
111649ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState));
111749ab747fSPaolo Bonzini 
11185edab03dSDon Koch     trace_ps2_kbd_init(s);
111949ab747fSPaolo Bonzini     s->common.update_irq = update_irq;
112049ab747fSPaolo Bonzini     s->common.update_arg = update_arg;
112149ab747fSPaolo Bonzini     s->scancode_set = 2;
112249ab747fSPaolo Bonzini     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
112366e6536eSGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
112466e6536eSGerd Hoffmann                                 &ps2_keyboard_handler);
112549ab747fSPaolo Bonzini     qemu_register_reset(ps2_kbd_reset, s);
112649ab747fSPaolo Bonzini     return s;
112749ab747fSPaolo Bonzini }
112849ab747fSPaolo Bonzini 
11292a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
11302a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
11312a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
11322a766d29SGerd Hoffmann     .event = ps2_mouse_event,
11332a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
11342a766d29SGerd Hoffmann };
11352a766d29SGerd Hoffmann 
113649ab747fSPaolo Bonzini void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
113749ab747fSPaolo Bonzini {
113849ab747fSPaolo Bonzini     PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState));
113949ab747fSPaolo Bonzini 
11405edab03dSDon Koch     trace_ps2_mouse_init(s);
114149ab747fSPaolo Bonzini     s->common.update_irq = update_irq;
114249ab747fSPaolo Bonzini     s->common.update_arg = update_arg;
114349ab747fSPaolo Bonzini     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
11442a766d29SGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
11452a766d29SGerd Hoffmann                                 &ps2_mouse_handler);
114649ab747fSPaolo Bonzini     qemu_register_reset(ps2_mouse_reset, s);
114749ab747fSPaolo Bonzini     return s;
114849ab747fSPaolo Bonzini }
1149