xref: /qemu/hw/input/ps2.c (revision 54d31236b906c8f03eb011717de7bc47000720c3)
10e43e99cSbellard /*
20e43e99cSbellard  * QEMU PS/2 keyboard/mouse emulation
30e43e99cSbellard  *
40e43e99cSbellard  * Copyright (c) 2003 Fabrice Bellard
50e43e99cSbellard  *
60e43e99cSbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
70e43e99cSbellard  * of this software and associated documentation files (the "Software"), to deal
80e43e99cSbellard  * in the Software without restriction, including without limitation the rights
90e43e99cSbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
100e43e99cSbellard  * copies of the Software, and to permit persons to whom the Software is
110e43e99cSbellard  * furnished to do so, subject to the following conditions:
120e43e99cSbellard  *
130e43e99cSbellard  * The above copyright notice and this permission notice shall be included in
140e43e99cSbellard  * all copies or substantial portions of the Software.
150e43e99cSbellard  *
160e43e99cSbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
170e43e99cSbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
180e43e99cSbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
190e43e99cSbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
200e43e99cSbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
210e43e99cSbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
220e43e99cSbellard  * THE SOFTWARE.
230e43e99cSbellard  */
2471e8a915SMarkus Armbruster 
250430891cSPeter Maydell #include "qemu/osdep.h"
26ec044a80SHervé Poussineau #include "qemu/log.h"
270d09e41aSPaolo Bonzini #include "hw/input/ps2.h"
28d6454270SMarkus Armbruster #include "migration/vmstate.h"
2928ecbaeeSPaolo Bonzini #include "ui/console.h"
3066e6536eSGerd Hoffmann #include "ui/input.h"
3171e8a915SMarkus Armbruster #include "sysemu/reset.h"
32*54d31236SMarkus Armbruster #include "sysemu/runstate.h"
330e43e99cSbellard 
345edab03dSDon Koch #include "trace.h"
355edab03dSDon Koch 
360e43e99cSbellard /* debug PC keyboard */
370e43e99cSbellard //#define DEBUG_KBD
380e43e99cSbellard 
390e43e99cSbellard /* debug PC keyboard : only mouse */
400e43e99cSbellard //#define DEBUG_MOUSE
410e43e99cSbellard 
420e43e99cSbellard /* Keyboard Commands */
430e43e99cSbellard #define KBD_CMD_SET_LEDS	0xED	/* Set keyboard leds */
440e43e99cSbellard #define KBD_CMD_ECHO     	0xEE
45e7d93956Saurel32 #define KBD_CMD_SCANCODE	0xF0	/* Get/set scancode set */
460e43e99cSbellard #define KBD_CMD_GET_ID 	        0xF2	/* get keyboard ID */
470e43e99cSbellard #define KBD_CMD_SET_RATE	0xF3	/* Set typematic rate */
480e43e99cSbellard #define KBD_CMD_ENABLE		0xF4	/* Enable scanning */
490e43e99cSbellard #define KBD_CMD_RESET_DISABLE	0xF5	/* reset and disable scanning */
500e43e99cSbellard #define KBD_CMD_RESET_ENABLE   	0xF6    /* reset and enable scanning */
510e43e99cSbellard #define KBD_CMD_RESET		0xFF	/* Reset */
520e43e99cSbellard 
530e43e99cSbellard /* Keyboard Replies */
540e43e99cSbellard #define KBD_REPLY_POR		0xAA	/* Power on reset */
5535c4d671Saurel32 #define KBD_REPLY_ID		0xAB	/* Keyboard ID */
560e43e99cSbellard #define KBD_REPLY_ACK		0xFA	/* Command ACK */
570e43e99cSbellard #define KBD_REPLY_RESEND	0xFE	/* Command NACK, send the cmd again */
580e43e99cSbellard 
590e43e99cSbellard /* Mouse Commands */
600e43e99cSbellard #define AUX_SET_SCALE11		0xE6	/* Set 1:1 scaling */
610e43e99cSbellard #define AUX_SET_SCALE21		0xE7	/* Set 2:1 scaling */
620e43e99cSbellard #define AUX_SET_RES		0xE8	/* Set resolution */
630e43e99cSbellard #define AUX_GET_SCALE		0xE9	/* Get scaling factor */
640e43e99cSbellard #define AUX_SET_STREAM		0xEA	/* Set stream mode */
650e43e99cSbellard #define AUX_POLL		0xEB	/* Poll */
660e43e99cSbellard #define AUX_RESET_WRAP		0xEC	/* Reset wrap mode */
670e43e99cSbellard #define AUX_SET_WRAP		0xEE	/* Set wrap mode */
680e43e99cSbellard #define AUX_SET_REMOTE		0xF0	/* Set remote mode */
690e43e99cSbellard #define AUX_GET_TYPE		0xF2	/* Get type */
700e43e99cSbellard #define AUX_SET_SAMPLE		0xF3	/* Set sample rate */
710e43e99cSbellard #define AUX_ENABLE_DEV		0xF4	/* Enable aux device */
720e43e99cSbellard #define AUX_DISABLE_DEV		0xF5	/* Disable aux device */
730e43e99cSbellard #define AUX_SET_DEFAULT		0xF6
740e43e99cSbellard #define AUX_RESET		0xFF	/* Reset aux device */
750e43e99cSbellard #define AUX_ACK			0xFA	/* Command byte ACK. */
760e43e99cSbellard 
770e43e99cSbellard #define MOUSE_STATUS_REMOTE     0x40
780e43e99cSbellard #define MOUSE_STATUS_ENABLED    0x20
790e43e99cSbellard #define MOUSE_STATUS_SCALE21    0x10
800e43e99cSbellard 
812858ab09SGonglei #define PS2_QUEUE_SIZE 16  /* Buffer size required by PS/2 protocol */
820e43e99cSbellard 
83620775d1SDaniel P. Berrange /* Bits for 'modifiers' field in PS2KbdState */
84620775d1SDaniel P. Berrange #define MOD_CTRL_L  (1 << 0)
85620775d1SDaniel P. Berrange #define MOD_SHIFT_L (1 << 1)
86620775d1SDaniel P. Berrange #define MOD_ALT_L   (1 << 2)
87620775d1SDaniel P. Berrange #define MOD_CTRL_R  (1 << 3)
88620775d1SDaniel P. Berrange #define MOD_SHIFT_R (1 << 4)
89620775d1SDaniel P. Berrange #define MOD_ALT_R   (1 << 5)
90620775d1SDaniel P. Berrange 
910e43e99cSbellard typedef struct {
922858ab09SGonglei     /* Keep the data array 256 bytes long, which compatibility
932858ab09SGonglei      with older qemu versions. */
942858ab09SGonglei     uint8_t data[256];
950e43e99cSbellard     int rptr, wptr, count;
960e43e99cSbellard } PS2Queue;
970e43e99cSbellard 
988498bb8dSGerd Hoffmann struct PS2State {
990e43e99cSbellard     PS2Queue queue;
1000e43e99cSbellard     int32_t write_cmd;
1010e43e99cSbellard     void (*update_irq)(void *, int);
1020e43e99cSbellard     void *update_arg;
1038498bb8dSGerd Hoffmann };
1040e43e99cSbellard 
1050e43e99cSbellard typedef struct {
1060e43e99cSbellard     PS2State common;
1070e43e99cSbellard     int scan_enabled;
108f94f5d71Spbrook     int translate;
109e7d93956Saurel32     int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
1107f540ab5SChristophe Fergeau     int ledstate;
11157d5c005SHervé Poussineau     bool need_high_bit;
112620775d1SDaniel P. Berrange     unsigned int modifiers; /* bitmask of MOD_* constants above */
1130e43e99cSbellard } PS2KbdState;
1140e43e99cSbellard 
1150e43e99cSbellard typedef struct {
1160e43e99cSbellard     PS2State common;
1170e43e99cSbellard     uint8_t mouse_status;
1180e43e99cSbellard     uint8_t mouse_resolution;
1190e43e99cSbellard     uint8_t mouse_sample_rate;
1200e43e99cSbellard     uint8_t mouse_wrap;
1210e43e99cSbellard     uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
1220e43e99cSbellard     uint8_t mouse_detect_state;
1230e43e99cSbellard     int mouse_dx; /* current values, needed for 'poll' mode */
1240e43e99cSbellard     int mouse_dy;
1250e43e99cSbellard     int mouse_dz;
1260e43e99cSbellard     uint8_t mouse_buttons;
1270e43e99cSbellard } PS2MouseState;
1280e43e99cSbellard 
12957d5c005SHervé Poussineau static uint8_t translate_table[256] = {
13057d5c005SHervé Poussineau     0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
13157d5c005SHervé Poussineau     0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
13257d5c005SHervé Poussineau     0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
13357d5c005SHervé Poussineau     0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
13457d5c005SHervé Poussineau     0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
13557d5c005SHervé Poussineau     0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
13657d5c005SHervé Poussineau     0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
13757d5c005SHervé Poussineau     0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
13857d5c005SHervé Poussineau     0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
13957d5c005SHervé Poussineau     0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
14057d5c005SHervé Poussineau     0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
14157d5c005SHervé Poussineau     0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
14257d5c005SHervé Poussineau     0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
14357d5c005SHervé Poussineau     0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
14457d5c005SHervé Poussineau     0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
14557d5c005SHervé Poussineau     0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
14657d5c005SHervé Poussineau     0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
14757d5c005SHervé Poussineau     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
14857d5c005SHervé Poussineau     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
14957d5c005SHervé Poussineau     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
15057d5c005SHervé Poussineau     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
15157d5c005SHervé Poussineau     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
15257d5c005SHervé Poussineau     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
15357d5c005SHervé Poussineau     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
15457d5c005SHervé Poussineau     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
15557d5c005SHervé Poussineau     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
15657d5c005SHervé Poussineau     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
15757d5c005SHervé Poussineau     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
15857d5c005SHervé Poussineau     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
15957d5c005SHervé Poussineau     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
16057d5c005SHervé Poussineau     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
16157d5c005SHervé Poussineau     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
16257d5c005SHervé Poussineau };
16357d5c005SHervé Poussineau 
164620775d1SDaniel P. Berrange static unsigned int ps2_modifier_bit(QKeyCode key)
165620775d1SDaniel P. Berrange {
166620775d1SDaniel P. Berrange     switch (key) {
167620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL:
168620775d1SDaniel P. Berrange         return MOD_CTRL_L;
169620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL_R:
170620775d1SDaniel P. Berrange         return MOD_CTRL_R;
171620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT:
172620775d1SDaniel P. Berrange         return MOD_SHIFT_L;
173620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT_R:
174620775d1SDaniel P. Berrange         return MOD_SHIFT_R;
175620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT:
176620775d1SDaniel P. Berrange         return MOD_ALT_L;
177620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT_R:
178620775d1SDaniel P. Berrange         return MOD_ALT_R;
179620775d1SDaniel P. Berrange     default:
180620775d1SDaniel P. Berrange         return 0;
181620775d1SDaniel P. Berrange     }
182620775d1SDaniel P. Berrange }
183620775d1SDaniel P. Berrange 
184954ee55bSGerd Hoffmann static void ps2_reset_queue(PS2State *s)
185954ee55bSGerd Hoffmann {
186954ee55bSGerd Hoffmann     PS2Queue *q = &s->queue;
187954ee55bSGerd Hoffmann 
188954ee55bSGerd Hoffmann     q->rptr = 0;
189954ee55bSGerd Hoffmann     q->wptr = 0;
190954ee55bSGerd Hoffmann     q->count = 0;
191954ee55bSGerd Hoffmann }
192954ee55bSGerd Hoffmann 
1937abe7eb2SGeoffrey McRae void ps2_queue_noirq(PS2State *s, int b)
1940e43e99cSbellard {
1950e43e99cSbellard     PS2Queue *q = &s->queue;
1960e43e99cSbellard 
1977abe7eb2SGeoffrey McRae     if (q->count == PS2_QUEUE_SIZE) {
1980e43e99cSbellard         return;
1997abe7eb2SGeoffrey McRae     }
2007abe7eb2SGeoffrey McRae 
2010e43e99cSbellard     q->data[q->wptr] = b;
2020e43e99cSbellard     if (++q->wptr == PS2_QUEUE_SIZE)
2030e43e99cSbellard         q->wptr = 0;
2040e43e99cSbellard     q->count++;
2057abe7eb2SGeoffrey McRae }
2067abe7eb2SGeoffrey McRae 
2077abe7eb2SGeoffrey McRae void ps2_raise_irq(PS2State *s)
2087abe7eb2SGeoffrey McRae {
2097abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2107abe7eb2SGeoffrey McRae }
2117abe7eb2SGeoffrey McRae 
2127abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
2137abe7eb2SGeoffrey McRae {
2147abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
2157abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2167abe7eb2SGeoffrey McRae }
2177abe7eb2SGeoffrey McRae 
2187abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
2197abe7eb2SGeoffrey McRae {
2207abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
2217abe7eb2SGeoffrey McRae         return;
2227abe7eb2SGeoffrey McRae     }
2237abe7eb2SGeoffrey McRae 
2247abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2257abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2267abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2277abe7eb2SGeoffrey McRae }
2287abe7eb2SGeoffrey McRae 
2297abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2307abe7eb2SGeoffrey McRae {
2317abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2327abe7eb2SGeoffrey McRae         return;
2337abe7eb2SGeoffrey McRae     }
2347abe7eb2SGeoffrey McRae 
2357abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2367abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2377abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2387abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2397abe7eb2SGeoffrey McRae }
2407abe7eb2SGeoffrey McRae 
2417abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2427abe7eb2SGeoffrey McRae {
2437abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2447abe7eb2SGeoffrey McRae         return;
2457abe7eb2SGeoffrey McRae     }
2467abe7eb2SGeoffrey McRae 
2477abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2487abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2497abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2507abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
2510e43e99cSbellard     s->update_irq(s->update_arg, 1);
2520e43e99cSbellard }
2530e43e99cSbellard 
25457d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
2550e43e99cSbellard static void ps2_put_keycode(void *opaque, int keycode)
2560e43e99cSbellard {
257f94f5d71Spbrook     PS2KbdState *s = opaque;
258e7d93956Saurel32 
2595edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
260fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
26157d5c005SHervé Poussineau 
26257d5c005SHervé Poussineau     if (s->translate) {
26357d5c005SHervé Poussineau         if (keycode == 0xf0) {
26457d5c005SHervé Poussineau             s->need_high_bit = true;
26557d5c005SHervé Poussineau         } else if (s->need_high_bit) {
26657d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode] | 0x80);
26757d5c005SHervé Poussineau             s->need_high_bit = false;
26857d5c005SHervé Poussineau         } else {
26957d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode]);
2707096a96dSRoy Tam         }
27157d5c005SHervé Poussineau     } else {
2720e43e99cSbellard         ps2_queue(&s->common, keycode);
2730e43e99cSbellard     }
27457d5c005SHervé Poussineau }
2750e43e99cSbellard 
27666e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
27766e6536eSGerd Hoffmann                                InputEvent *evt)
27866e6536eSGerd Hoffmann {
27966e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
28032bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
2818c10e0baSHervé Poussineau     int qcode;
282ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
283620775d1SDaniel P. Berrange     int mod;
28466e6536eSGerd Hoffmann 
285143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
286143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
287143c04c7SGeoffrey McRae         return;
288143c04c7SGeoffrey McRae     }
289143c04c7SGeoffrey McRae 
290fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
2918c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
2928c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
29357d5c005SHervé Poussineau 
294620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
295620775d1SDaniel P. Berrange     trace_ps2_keyboard_event(s, qcode, key->down, mod, s->modifiers);
296620775d1SDaniel P. Berrange     if (key->down) {
297620775d1SDaniel P. Berrange         s->modifiers |= mod;
298620775d1SDaniel P. Berrange     } else {
299620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
300620775d1SDaniel P. Berrange     }
301620775d1SDaniel P. Berrange 
3028c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3038c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
30429fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
30529fd23a5SDaniel P. Berrange                 if (key->down) {
30629fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
30729fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
30829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
30929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
31029fd23a5SDaniel P. Berrange                 }
31129fd23a5SDaniel P. Berrange             } else {
3128c10e0baSHervé Poussineau                 if (key->down) {
3138c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3148c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3158c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
316927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3178c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
3188c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
3198c10e0baSHervé Poussineau                 }
32029fd23a5SDaniel P. Berrange             }
3218c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
322620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
323620775d1SDaniel P. Berrange                 if (key->down) {
324620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
325620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
326620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
327620775d1SDaniel P. Berrange                 } else {
328620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
329620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
330620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
331620775d1SDaniel P. Berrange                 }
332620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
333620775d1SDaniel P. Berrange                 if (key->down) {
334620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
335620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
336620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
337620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
338620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
339620775d1SDaniel P. Berrange                 } else {
340620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
341620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
342620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
343620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
344620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
345620775d1SDaniel P. Berrange                 }
3468f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
3478f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
3488f63458fSDaniel P. Berrange                 if (key->down) {
3498f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3508f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
3518f63458fSDaniel P. Berrange                 } else {
3528f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3538f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
3548f63458fSDaniel P. Berrange                 }
355620775d1SDaniel P. Berrange             } else {
3568c10e0baSHervé Poussineau                 if (key->down) {
3578c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3588c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
3598c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3608c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
3618c10e0baSHervé Poussineau                 } else {
3628c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3638c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
3648c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3658c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
3668c10e0baSHervé Poussineau                 }
367620775d1SDaniel P. Berrange             }
3688c10e0baSHervé Poussineau         } else {
369ab8f9d49SDaniel P. Berrange             if (qcode < qemu_input_map_qcode_to_atset1_len)
370ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
3718c10e0baSHervé Poussineau             if (keycode) {
3728c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
3738c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
3748c10e0baSHervé Poussineau                 }
3758c10e0baSHervé Poussineau                 if (!key->down) {
3768c10e0baSHervé Poussineau                     keycode |= 0x80;
3778c10e0baSHervé Poussineau                 }
3788c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
3798c10e0baSHervé Poussineau             } else {
380ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
381ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
3828c10e0baSHervé Poussineau             }
3838c10e0baSHervé Poussineau         }
3848c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
3858c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
38629fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
38729fd23a5SDaniel P. Berrange                 if (key->down) {
38829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
38929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
39029fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
39129fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
39229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
39329fd23a5SDaniel P. Berrange                 }
39429fd23a5SDaniel P. Berrange             } else {
3958c10e0baSHervé Poussineau                 if (key->down) {
3968c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3978c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
3988c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
3998c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4008c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4018c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4028c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4038c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4048c10e0baSHervé Poussineau                 }
40529fd23a5SDaniel P. Berrange             }
4068c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
407620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
408620775d1SDaniel P. Berrange                 if (key->down) {
409620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
410620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
411620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
412620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
413620775d1SDaniel P. Berrange                 } else {
414620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
415620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
416620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
417620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
418620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
419620775d1SDaniel P. Berrange                 }
420620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
421620775d1SDaniel P. Berrange                 if (key->down) {
422620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
423620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
424620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
425620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
426620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
427620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
428620775d1SDaniel P. Berrange                 } else {
429620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
430620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
431620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
432620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
433620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
434620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
435620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
436620775d1SDaniel P. Berrange                 }
4378f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4388f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4398f63458fSDaniel P. Berrange                 if (key->down) {
4408f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4418f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4428f63458fSDaniel P. Berrange                 } else {
4438f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4448f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
4458f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4468f63458fSDaniel P. Berrange                 }
447620775d1SDaniel P. Berrange             } else {
4488c10e0baSHervé Poussineau                 if (key->down) {
4498c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4508c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4518c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4528c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4538c10e0baSHervé Poussineau                 } else {
4548c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4558c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4568c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4578c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4588c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4598c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4608c10e0baSHervé Poussineau                 }
461620775d1SDaniel P. Berrange             }
4628c10e0baSHervé Poussineau         } else {
463ab8f9d49SDaniel P. Berrange             if (qcode < qemu_input_map_qcode_to_atset2_len)
464ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
4658c10e0baSHervé Poussineau             if (keycode) {
4668c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4678c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4688c10e0baSHervé Poussineau                 }
4698c10e0baSHervé Poussineau                 if (!key->down) {
4708c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4718c10e0baSHervé Poussineau                 }
4728c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
47357d5c005SHervé Poussineau             } else {
474ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
475ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
47657d5c005SHervé Poussineau             }
47757d5c005SHervé Poussineau         }
47857d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
479ab8f9d49SDaniel P. Berrange         if (qcode < qemu_input_map_qcode_to_atset3_len)
480ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
4818c10e0baSHervé Poussineau         if (keycode) {
4828c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
4838c10e0baSHervé Poussineau             if (!key->down) {
4848c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
48557d5c005SHervé Poussineau             }
48657d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
4878c10e0baSHervé Poussineau         } else {
488ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
489ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
4908c10e0baSHervé Poussineau         }
49166e6536eSGerd Hoffmann     }
49266e6536eSGerd Hoffmann }
49366e6536eSGerd Hoffmann 
4948498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
4950e43e99cSbellard {
4960e43e99cSbellard     PS2Queue *q;
4970e43e99cSbellard     int val, index;
4980e43e99cSbellard 
4998498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
5000e43e99cSbellard     q = &s->queue;
5010e43e99cSbellard     if (q->count == 0) {
5020e43e99cSbellard         /* NOTE: if no data left, we return the last keyboard one
5030e43e99cSbellard            (needed for EMM386) */
5040e43e99cSbellard         /* XXX: need a timer to do things correctly */
5050e43e99cSbellard         index = q->rptr - 1;
5060e43e99cSbellard         if (index < 0)
5070e43e99cSbellard             index = PS2_QUEUE_SIZE - 1;
5080e43e99cSbellard         val = q->data[index];
5090e43e99cSbellard     } else {
5100e43e99cSbellard         val = q->data[q->rptr];
5110e43e99cSbellard         if (++q->rptr == PS2_QUEUE_SIZE)
5120e43e99cSbellard             q->rptr = 0;
5130e43e99cSbellard         q->count--;
5140e43e99cSbellard         /* reading deasserts IRQ */
5150e43e99cSbellard         s->update_irq(s->update_arg, 0);
5160e43e99cSbellard         /* reassert IRQs if data left */
5170e43e99cSbellard         s->update_irq(s->update_arg, q->count != 0);
5180e43e99cSbellard     }
5190e43e99cSbellard     return val;
5200e43e99cSbellard }
5210e43e99cSbellard 
5227f540ab5SChristophe Fergeau static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
5237f540ab5SChristophe Fergeau {
5245edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
5257f540ab5SChristophe Fergeau     s->ledstate = ledstate;
5267f540ab5SChristophe Fergeau     kbd_put_ledstate(ledstate);
5277f540ab5SChristophe Fergeau }
5287f540ab5SChristophe Fergeau 
5290e43e99cSbellard static void ps2_reset_keyboard(PS2KbdState *s)
5300e43e99cSbellard {
5315edab03dSDon Koch     trace_ps2_reset_keyboard(s);
5320e43e99cSbellard     s->scan_enabled = 1;
533e7d93956Saurel32     s->scancode_set = 2;
5346e24ee0cSGerd Hoffmann     ps2_reset_queue(&s->common);
5357f540ab5SChristophe Fergeau     ps2_set_ledstate(s, 0);
5360e43e99cSbellard }
5370e43e99cSbellard 
5380e43e99cSbellard void ps2_write_keyboard(void *opaque, int val)
5390e43e99cSbellard {
5400e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
5410e43e99cSbellard 
5425edab03dSDon Koch     trace_ps2_write_keyboard(opaque, val);
5430e43e99cSbellard     switch(s->common.write_cmd) {
5440e43e99cSbellard     default:
5450e43e99cSbellard     case -1:
5460e43e99cSbellard         switch(val) {
5470e43e99cSbellard         case 0x00:
5480e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_ACK);
5490e43e99cSbellard             break;
5500e43e99cSbellard         case 0x05:
5510e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_RESEND);
5520e43e99cSbellard             break;
5530e43e99cSbellard         case KBD_CMD_GET_ID:
554e7d93956Saurel32             /* We emulate a MF2 AT keyboard here */
55535c4d671Saurel32             if (s->translate)
5567abe7eb2SGeoffrey McRae                 ps2_queue_3(&s->common,
5577abe7eb2SGeoffrey McRae                     KBD_REPLY_ACK,
5587abe7eb2SGeoffrey McRae                     KBD_REPLY_ID,
5597abe7eb2SGeoffrey McRae                     0x41);
56035c4d671Saurel32             else
5617abe7eb2SGeoffrey McRae                 ps2_queue_3(&s->common,
5627abe7eb2SGeoffrey McRae                     KBD_REPLY_ACK,
5637abe7eb2SGeoffrey McRae                     KBD_REPLY_ID,
5647abe7eb2SGeoffrey McRae                     0x83);
5650e43e99cSbellard             break;
5660e43e99cSbellard         case KBD_CMD_ECHO:
5670e43e99cSbellard             ps2_queue(&s->common, KBD_CMD_ECHO);
5680e43e99cSbellard             break;
5690e43e99cSbellard         case KBD_CMD_ENABLE:
5700e43e99cSbellard             s->scan_enabled = 1;
5710e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_ACK);
5720e43e99cSbellard             break;
573e7d93956Saurel32         case KBD_CMD_SCANCODE:
5740e43e99cSbellard         case KBD_CMD_SET_LEDS:
5750e43e99cSbellard         case KBD_CMD_SET_RATE:
5760e43e99cSbellard             s->common.write_cmd = val;
5770e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_ACK);
5780e43e99cSbellard             break;
5790e43e99cSbellard         case KBD_CMD_RESET_DISABLE:
5800e43e99cSbellard             ps2_reset_keyboard(s);
5810e43e99cSbellard             s->scan_enabled = 0;
5820e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_ACK);
5830e43e99cSbellard             break;
5840e43e99cSbellard         case KBD_CMD_RESET_ENABLE:
5850e43e99cSbellard             ps2_reset_keyboard(s);
5860e43e99cSbellard             s->scan_enabled = 1;
5870e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_ACK);
5880e43e99cSbellard             break;
5890e43e99cSbellard         case KBD_CMD_RESET:
5900e43e99cSbellard             ps2_reset_keyboard(s);
5917abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
5927abe7eb2SGeoffrey McRae                 KBD_REPLY_ACK,
5937abe7eb2SGeoffrey McRae                 KBD_REPLY_POR);
5940e43e99cSbellard             break;
5950e43e99cSbellard         default:
59606b3611fSHervé Poussineau             ps2_queue(&s->common, KBD_REPLY_RESEND);
5970e43e99cSbellard             break;
5980e43e99cSbellard         }
5990e43e99cSbellard         break;
600e7d93956Saurel32     case KBD_CMD_SCANCODE:
601e7d93956Saurel32         if (val == 0) {
6027abe7eb2SGeoffrey McRae             if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) {
6034df23b64SHervé Poussineau                 ps2_queue(&s->common, KBD_REPLY_ACK);
60457d5c005SHervé Poussineau                 ps2_put_keycode(s, s->scancode_set);
6057abe7eb2SGeoffrey McRae             }
6064df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
607e7d93956Saurel32             s->scancode_set = val;
608e7d93956Saurel32             ps2_queue(&s->common, KBD_REPLY_ACK);
6094df23b64SHervé Poussineau         } else {
6104df23b64SHervé Poussineau             ps2_queue(&s->common, KBD_REPLY_RESEND);
611e7d93956Saurel32         }
612e7d93956Saurel32         s->common.write_cmd = -1;
613e7d93956Saurel32         break;
6140e43e99cSbellard     case KBD_CMD_SET_LEDS:
6157f540ab5SChristophe Fergeau         ps2_set_ledstate(s, val);
6160e43e99cSbellard         ps2_queue(&s->common, KBD_REPLY_ACK);
6170e43e99cSbellard         s->common.write_cmd = -1;
6180e43e99cSbellard         break;
6190e43e99cSbellard     case KBD_CMD_SET_RATE:
6200e43e99cSbellard         ps2_queue(&s->common, KBD_REPLY_ACK);
6210e43e99cSbellard         s->common.write_cmd = -1;
6220e43e99cSbellard         break;
6230e43e99cSbellard     }
6240e43e99cSbellard }
6250e43e99cSbellard 
626f94f5d71Spbrook /* Set the scancode translation mode.
627f94f5d71Spbrook    0 = raw scancodes.
628f94f5d71Spbrook    1 = translated scancodes (used by qemu internally).  */
629f94f5d71Spbrook 
630f94f5d71Spbrook void ps2_keyboard_set_translation(void *opaque, int mode)
631f94f5d71Spbrook {
632f94f5d71Spbrook     PS2KbdState *s = (PS2KbdState *)opaque;
6335edab03dSDon Koch     trace_ps2_keyboard_set_translation(opaque, mode);
634f94f5d71Spbrook     s->translate = mode;
635f94f5d71Spbrook }
636f94f5d71Spbrook 
6377abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
6380e43e99cSbellard {
6397abe7eb2SGeoffrey McRae     const int needed = 3 + (s->mouse_type - 2);
6400e43e99cSbellard     unsigned int b;
6410e43e99cSbellard     int dx1, dy1, dz1;
6420e43e99cSbellard 
6437abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->common.queue.count < needed) {
6447abe7eb2SGeoffrey McRae         return 0;
6457abe7eb2SGeoffrey McRae     }
6467abe7eb2SGeoffrey McRae 
6470e43e99cSbellard     dx1 = s->mouse_dx;
6480e43e99cSbellard     dy1 = s->mouse_dy;
6490e43e99cSbellard     dz1 = s->mouse_dz;
6500e43e99cSbellard     /* XXX: increase range to 8 bits ? */
6510e43e99cSbellard     if (dx1 > 127)
6520e43e99cSbellard         dx1 = 127;
6530e43e99cSbellard     else if (dx1 < -127)
6540e43e99cSbellard         dx1 = -127;
6550e43e99cSbellard     if (dy1 > 127)
6560e43e99cSbellard         dy1 = 127;
6570e43e99cSbellard     else if (dy1 < -127)
6580e43e99cSbellard         dy1 = -127;
6590e43e99cSbellard     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
6607abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, b);
6617abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dx1 & 0xff);
6627abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dy1 & 0xff);
6630e43e99cSbellard     /* extra byte for IMPS/2 or IMEX */
6640e43e99cSbellard     switch(s->mouse_type) {
6650e43e99cSbellard     default:
6660e43e99cSbellard         break;
6670e43e99cSbellard     case 3:
6680e43e99cSbellard         if (dz1 > 127)
6690e43e99cSbellard             dz1 = 127;
6700e43e99cSbellard         else if (dz1 < -127)
6710e43e99cSbellard                 dz1 = -127;
6727abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, dz1 & 0xff);
6730e43e99cSbellard         break;
6740e43e99cSbellard     case 4:
6750e43e99cSbellard         if (dz1 > 7)
6760e43e99cSbellard             dz1 = 7;
6770e43e99cSbellard         else if (dz1 < -7)
6780e43e99cSbellard             dz1 = -7;
6790e43e99cSbellard         b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
6807abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, b);
6810e43e99cSbellard         break;
6820e43e99cSbellard     }
6830e43e99cSbellard 
6847abe7eb2SGeoffrey McRae     ps2_raise_irq(&s->common);
6857abe7eb2SGeoffrey McRae 
6865edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
6870e43e99cSbellard     /* update deltas */
6880e43e99cSbellard     s->mouse_dx -= dx1;
6890e43e99cSbellard     s->mouse_dy -= dy1;
6900e43e99cSbellard     s->mouse_dz -= dz1;
6917abe7eb2SGeoffrey McRae 
6927abe7eb2SGeoffrey McRae     return 1;
6930e43e99cSbellard }
6940e43e99cSbellard 
6952a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
6962a766d29SGerd Hoffmann                             InputEvent *evt)
6970e43e99cSbellard {
6987fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
6998b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
7008b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
7018b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
7028b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
7038b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
7042a766d29SGerd Hoffmann     };
7052a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
706b5a1b443SEric Blake     InputMoveEvent *move;
707b5a1b443SEric Blake     InputBtnEvent *btn;
7080e43e99cSbellard 
7090e43e99cSbellard     /* check if deltas are recorded when disabled */
7100e43e99cSbellard     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
7110e43e99cSbellard         return;
7120e43e99cSbellard 
713568c73a4SEric Blake     switch (evt->type) {
7142a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
71532bafa8fSEric Blake         move = evt->u.rel.data;
716b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
717b5a1b443SEric Blake             s->mouse_dx += move->value;
718b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
719b5a1b443SEric Blake             s->mouse_dy -= move->value;
7202a766d29SGerd Hoffmann         }
7212a766d29SGerd Hoffmann         break;
7220e43e99cSbellard 
7232a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
72432bafa8fSEric Blake         btn = evt->u.btn.data;
725b5a1b443SEric Blake         if (btn->down) {
726b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
727b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
7282a766d29SGerd Hoffmann                 s->mouse_dz--;
729b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
7302a766d29SGerd Hoffmann                 s->mouse_dz++;
7312a766d29SGerd Hoffmann             }
7322a766d29SGerd Hoffmann         } else {
733b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
7342a766d29SGerd Hoffmann         }
7352a766d29SGerd Hoffmann         break;
7362a766d29SGerd Hoffmann 
7372a766d29SGerd Hoffmann     default:
7382a766d29SGerd Hoffmann         /* keep gcc happy */
7392a766d29SGerd Hoffmann         break;
7402a766d29SGerd Hoffmann     }
741fd214d18SGerd Hoffmann }
742fd214d18SGerd Hoffmann 
7432a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
7442a766d29SGerd Hoffmann {
7452a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
7462a766d29SGerd Hoffmann 
747143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
748143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
749143c04c7SGeoffrey McRae         return;
750143c04c7SGeoffrey McRae     }
751143c04c7SGeoffrey McRae 
7522a766d29SGerd Hoffmann     if (s->mouse_buttons) {
753fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
7542a766d29SGerd Hoffmann     }
7552858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
7560e43e99cSbellard         /* if not remote, send event. Multiple events are sent if
7570e43e99cSbellard            too big deltas */
7587abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
7590e43e99cSbellard             if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
7600e43e99cSbellard                 break;
7610e43e99cSbellard         }
7620e43e99cSbellard     }
7630e43e99cSbellard }
7640e43e99cSbellard 
765548df2acSths void ps2_mouse_fake_event(void *opaque)
766548df2acSths {
7672a766d29SGerd Hoffmann     PS2MouseState *s = opaque;
7685edab03dSDon Koch     trace_ps2_mouse_fake_event(opaque);
7692a766d29SGerd Hoffmann     s->mouse_dx++;
7702a766d29SGerd Hoffmann     ps2_mouse_sync(opaque);
771548df2acSths }
772548df2acSths 
7730e43e99cSbellard void ps2_write_mouse(void *opaque, int val)
7740e43e99cSbellard {
7750e43e99cSbellard     PS2MouseState *s = (PS2MouseState *)opaque;
7765edab03dSDon Koch 
7775edab03dSDon Koch     trace_ps2_write_mouse(opaque, val);
7780e43e99cSbellard #ifdef DEBUG_MOUSE
7790e43e99cSbellard     printf("kbd: write mouse 0x%02x\n", val);
7800e43e99cSbellard #endif
7810e43e99cSbellard     switch(s->common.write_cmd) {
7820e43e99cSbellard     default:
7830e43e99cSbellard     case -1:
7840e43e99cSbellard         /* mouse command */
7850e43e99cSbellard         if (s->mouse_wrap) {
7860e43e99cSbellard             if (val == AUX_RESET_WRAP) {
7870e43e99cSbellard                 s->mouse_wrap = 0;
7880e43e99cSbellard                 ps2_queue(&s->common, AUX_ACK);
7890e43e99cSbellard                 return;
7900e43e99cSbellard             } else if (val != AUX_RESET) {
7910e43e99cSbellard                 ps2_queue(&s->common, val);
7920e43e99cSbellard                 return;
7930e43e99cSbellard             }
7940e43e99cSbellard         }
7950e43e99cSbellard         switch(val) {
7960e43e99cSbellard         case AUX_SET_SCALE11:
7970e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
7980e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
7990e43e99cSbellard             break;
8000e43e99cSbellard         case AUX_SET_SCALE21:
8010e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_SCALE21;
8020e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8030e43e99cSbellard             break;
8040e43e99cSbellard         case AUX_SET_STREAM:
8050e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
8060e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8070e43e99cSbellard             break;
8080e43e99cSbellard         case AUX_SET_WRAP:
8090e43e99cSbellard             s->mouse_wrap = 1;
8100e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8110e43e99cSbellard             break;
8120e43e99cSbellard         case AUX_SET_REMOTE:
8130e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_REMOTE;
8140e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8150e43e99cSbellard             break;
8160e43e99cSbellard         case AUX_GET_TYPE:
8177abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
8187abe7eb2SGeoffrey McRae                 AUX_ACK,
8197abe7eb2SGeoffrey McRae                 s->mouse_type);
8200e43e99cSbellard             break;
8210e43e99cSbellard         case AUX_SET_RES:
8220e43e99cSbellard         case AUX_SET_SAMPLE:
8230e43e99cSbellard             s->common.write_cmd = val;
8240e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8250e43e99cSbellard             break;
8260e43e99cSbellard         case AUX_GET_SCALE:
8277abe7eb2SGeoffrey McRae             ps2_queue_4(&s->common,
8287abe7eb2SGeoffrey McRae                 AUX_ACK,
8297abe7eb2SGeoffrey McRae                 s->mouse_status,
8307abe7eb2SGeoffrey McRae                 s->mouse_resolution,
8317abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
8320e43e99cSbellard             break;
8330e43e99cSbellard         case AUX_POLL:
8340e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8350e43e99cSbellard             ps2_mouse_send_packet(s);
8360e43e99cSbellard             break;
8370e43e99cSbellard         case AUX_ENABLE_DEV:
8380e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_ENABLED;
8390e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8400e43e99cSbellard             break;
8410e43e99cSbellard         case AUX_DISABLE_DEV:
8420e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
8430e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8440e43e99cSbellard             break;
8450e43e99cSbellard         case AUX_SET_DEFAULT:
8460e43e99cSbellard             s->mouse_sample_rate = 100;
8470e43e99cSbellard             s->mouse_resolution = 2;
8480e43e99cSbellard             s->mouse_status = 0;
8490e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8500e43e99cSbellard             break;
8510e43e99cSbellard         case AUX_RESET:
8520e43e99cSbellard             s->mouse_sample_rate = 100;
8530e43e99cSbellard             s->mouse_resolution = 2;
8540e43e99cSbellard             s->mouse_status = 0;
8550e43e99cSbellard             s->mouse_type = 0;
856143c04c7SGeoffrey McRae             ps2_reset_queue(&s->common);
8577abe7eb2SGeoffrey McRae             ps2_queue_3(&s->common,
8587abe7eb2SGeoffrey McRae                 AUX_ACK,
8597abe7eb2SGeoffrey McRae                 0xaa,
8607abe7eb2SGeoffrey McRae                 s->mouse_type);
8610e43e99cSbellard             break;
8620e43e99cSbellard         default:
8630e43e99cSbellard             break;
8640e43e99cSbellard         }
8650e43e99cSbellard         break;
8660e43e99cSbellard     case AUX_SET_SAMPLE:
8670e43e99cSbellard         s->mouse_sample_rate = val;
8680e43e99cSbellard         /* detect IMPS/2 or IMEX */
8690e43e99cSbellard         switch(s->mouse_detect_state) {
8700e43e99cSbellard         default:
8710e43e99cSbellard         case 0:
8720e43e99cSbellard             if (val == 200)
8730e43e99cSbellard                 s->mouse_detect_state = 1;
8740e43e99cSbellard             break;
8750e43e99cSbellard         case 1:
8760e43e99cSbellard             if (val == 100)
8770e43e99cSbellard                 s->mouse_detect_state = 2;
8780e43e99cSbellard             else if (val == 200)
8790e43e99cSbellard                 s->mouse_detect_state = 3;
8800e43e99cSbellard             else
8810e43e99cSbellard                 s->mouse_detect_state = 0;
8820e43e99cSbellard             break;
8830e43e99cSbellard         case 2:
8840e43e99cSbellard             if (val == 80)
8850e43e99cSbellard                 s->mouse_type = 3; /* IMPS/2 */
8860e43e99cSbellard             s->mouse_detect_state = 0;
8870e43e99cSbellard             break;
8880e43e99cSbellard         case 3:
8890e43e99cSbellard             if (val == 80)
8900e43e99cSbellard                 s->mouse_type = 4; /* IMEX */
8910e43e99cSbellard             s->mouse_detect_state = 0;
8920e43e99cSbellard             break;
8930e43e99cSbellard         }
8940e43e99cSbellard         ps2_queue(&s->common, AUX_ACK);
8950e43e99cSbellard         s->common.write_cmd = -1;
8960e43e99cSbellard         break;
8970e43e99cSbellard     case AUX_SET_RES:
8980e43e99cSbellard         s->mouse_resolution = val;
8990e43e99cSbellard         ps2_queue(&s->common, AUX_ACK);
9000e43e99cSbellard         s->common.write_cmd = -1;
9010e43e99cSbellard         break;
9020e43e99cSbellard     }
9030e43e99cSbellard }
9040e43e99cSbellard 
905ef74679aSDinesh Subhraveti static void ps2_common_reset(PS2State *s)
9060e43e99cSbellard {
9070e43e99cSbellard     s->write_cmd = -1;
908954ee55bSGerd Hoffmann     ps2_reset_queue(s);
909deeccef3Saliguori     s->update_irq(s->update_arg, 0);
9100e43e99cSbellard }
9110e43e99cSbellard 
9122858ab09SGonglei static void ps2_common_post_load(PS2State *s)
9132858ab09SGonglei {
9142858ab09SGonglei     PS2Queue *q = &s->queue;
915802cbcb7SPrasad J Pandit     uint8_t i, size;
916802cbcb7SPrasad J Pandit     uint8_t tmp_data[PS2_QUEUE_SIZE];
9172858ab09SGonglei 
9182858ab09SGonglei     /* set the useful data buffer queue size, < PS2_QUEUE_SIZE */
919a1f2ed2aSPavel Dovgalyuk     size = q->count;
920a1f2ed2aSPavel Dovgalyuk     if (q->count < 0) {
921a1f2ed2aSPavel Dovgalyuk         size = 0;
922a1f2ed2aSPavel Dovgalyuk     } else if (q->count > PS2_QUEUE_SIZE) {
923a1f2ed2aSPavel Dovgalyuk         size = PS2_QUEUE_SIZE;
924a1f2ed2aSPavel Dovgalyuk     }
9252858ab09SGonglei 
9262858ab09SGonglei     /* move the queue elements to the start of data array */
9272858ab09SGonglei     for (i = 0; i < size; i++) {
928802cbcb7SPrasad J Pandit         if (q->rptr < 0 || q->rptr >= sizeof(q->data)) {
9292858ab09SGonglei             q->rptr = 0;
9302858ab09SGonglei         }
931802cbcb7SPrasad J Pandit         tmp_data[i] = q->data[q->rptr++];
9322858ab09SGonglei     }
9332858ab09SGonglei     memcpy(q->data, tmp_data, size);
934802cbcb7SPrasad J Pandit 
9352858ab09SGonglei     /* reset rptr/wptr/count */
9362858ab09SGonglei     q->rptr = 0;
937b55a06dfSliujunjie     q->wptr = (size == PS2_QUEUE_SIZE) ? 0 : size;
9382858ab09SGonglei     q->count = size;
9392858ab09SGonglei }
9402858ab09SGonglei 
941ef74679aSDinesh Subhraveti static void ps2_kbd_reset(void *opaque)
942ef74679aSDinesh Subhraveti {
943ef74679aSDinesh Subhraveti     PS2KbdState *s = (PS2KbdState *) opaque;
944ef74679aSDinesh Subhraveti 
9455edab03dSDon Koch     trace_ps2_kbd_reset(opaque);
946ef74679aSDinesh Subhraveti     ps2_common_reset(&s->common);
947d2e550a8SHervé Poussineau     s->scan_enabled = 1;
948ef74679aSDinesh Subhraveti     s->translate = 0;
949089adafdSHervé Poussineau     s->scancode_set = 2;
950620775d1SDaniel P. Berrange     s->modifiers = 0;
951ef74679aSDinesh Subhraveti }
952ef74679aSDinesh Subhraveti 
953ef74679aSDinesh Subhraveti static void ps2_mouse_reset(void *opaque)
954ef74679aSDinesh Subhraveti {
955ef74679aSDinesh Subhraveti     PS2MouseState *s = (PS2MouseState *) opaque;
956ef74679aSDinesh Subhraveti 
9575edab03dSDon Koch     trace_ps2_mouse_reset(opaque);
958ef74679aSDinesh Subhraveti     ps2_common_reset(&s->common);
959ef74679aSDinesh Subhraveti     s->mouse_status = 0;
960ef74679aSDinesh Subhraveti     s->mouse_resolution = 0;
961ef74679aSDinesh Subhraveti     s->mouse_sample_rate = 0;
962ef74679aSDinesh Subhraveti     s->mouse_wrap = 0;
963ef74679aSDinesh Subhraveti     s->mouse_type = 0;
964ef74679aSDinesh Subhraveti     s->mouse_detect_state = 0;
965ef74679aSDinesh Subhraveti     s->mouse_dx = 0;
966ef74679aSDinesh Subhraveti     s->mouse_dy = 0;
967ef74679aSDinesh Subhraveti     s->mouse_dz = 0;
968ef74679aSDinesh Subhraveti     s->mouse_buttons = 0;
969ef74679aSDinesh Subhraveti }
970ef74679aSDinesh Subhraveti 
971b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_common = {
972b31442c3SJuan Quintela     .name = "PS2 Common State",
973b31442c3SJuan Quintela     .version_id = 3,
974b31442c3SJuan Quintela     .minimum_version_id = 2,
975b31442c3SJuan Quintela     .fields = (VMStateField[]) {
976b31442c3SJuan Quintela         VMSTATE_INT32(write_cmd, PS2State),
977b31442c3SJuan Quintela         VMSTATE_INT32(queue.rptr, PS2State),
978b31442c3SJuan Quintela         VMSTATE_INT32(queue.wptr, PS2State),
979b31442c3SJuan Quintela         VMSTATE_INT32(queue.count, PS2State),
980b31442c3SJuan Quintela         VMSTATE_BUFFER(queue.data, PS2State),
981b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
9827783e9f0Spbrook     }
983b31442c3SJuan Quintela };
9847783e9f0Spbrook 
9857f540ab5SChristophe Fergeau static bool ps2_keyboard_ledstate_needed(void *opaque)
9867f540ab5SChristophe Fergeau {
9877f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
9887f540ab5SChristophe Fergeau 
9897f540ab5SChristophe Fergeau     return s->ledstate != 0; /* 0 is default state */
9907f540ab5SChristophe Fergeau }
9917f540ab5SChristophe Fergeau 
9927f540ab5SChristophe Fergeau static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
9937f540ab5SChristophe Fergeau {
9947f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
9957f540ab5SChristophe Fergeau 
9967f540ab5SChristophe Fergeau     kbd_put_ledstate(s->ledstate);
9977f540ab5SChristophe Fergeau     return 0;
9987f540ab5SChristophe Fergeau }
9997f540ab5SChristophe Fergeau 
10007f540ab5SChristophe Fergeau static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
10017f540ab5SChristophe Fergeau     .name = "ps2kbd/ledstate",
10027f540ab5SChristophe Fergeau     .version_id = 3,
10037f540ab5SChristophe Fergeau     .minimum_version_id = 2,
10047f540ab5SChristophe Fergeau     .post_load = ps2_kbd_ledstate_post_load,
10055cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
10067f540ab5SChristophe Fergeau     .fields = (VMStateField[]) {
10077f540ab5SChristophe Fergeau         VMSTATE_INT32(ledstate, PS2KbdState),
10087f540ab5SChristophe Fergeau         VMSTATE_END_OF_LIST()
10097f540ab5SChristophe Fergeau     }
10107f540ab5SChristophe Fergeau };
10117f540ab5SChristophe Fergeau 
101257d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
101357d5c005SHervé Poussineau {
101457d5c005SHervé Poussineau     PS2KbdState *s = opaque;
101557d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
101657d5c005SHervé Poussineau }
101757d5c005SHervé Poussineau 
101857d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
101957d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
102057d5c005SHervé Poussineau     .version_id = 1,
102157d5c005SHervé Poussineau     .minimum_version_id = 1,
102257d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
102357d5c005SHervé Poussineau     .fields = (VMStateField[]) {
102457d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
102557d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
102657d5c005SHervé Poussineau     }
102757d5c005SHervé Poussineau };
102857d5c005SHervé Poussineau 
1029db596c53SJuan Quintela static int ps2_kbd_post_load(void* opaque, int version_id)
10300e43e99cSbellard {
10310e43e99cSbellard     PS2KbdState *s = (PS2KbdState*)opaque;
10322858ab09SGonglei     PS2State *ps2 = &s->common;
10330e43e99cSbellard 
1034db596c53SJuan Quintela     if (version_id == 2)
1035e7d93956Saurel32         s->scancode_set=2;
10362858ab09SGonglei 
10372858ab09SGonglei     ps2_common_post_load(ps2);
10382858ab09SGonglei 
10390e43e99cSbellard     return 0;
10400e43e99cSbellard }
10410e43e99cSbellard 
104244b1ff31SDr. David Alan Gilbert static int ps2_kbd_pre_save(void *opaque)
10432858ab09SGonglei {
10442858ab09SGonglei     PS2KbdState *s = (PS2KbdState *)opaque;
10452858ab09SGonglei     PS2State *ps2 = &s->common;
10462858ab09SGonglei 
10472858ab09SGonglei     ps2_common_post_load(ps2);
104844b1ff31SDr. David Alan Gilbert 
104944b1ff31SDr. David Alan Gilbert     return 0;
10502858ab09SGonglei }
10512858ab09SGonglei 
1052b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_keyboard = {
1053b31442c3SJuan Quintela     .name = "ps2kbd",
1054b31442c3SJuan Quintela     .version_id = 3,
1055db596c53SJuan Quintela     .minimum_version_id = 2,
1056db596c53SJuan Quintela     .post_load = ps2_kbd_post_load,
10572858ab09SGonglei     .pre_save = ps2_kbd_pre_save,
1058b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1059b31442c3SJuan Quintela         VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
1060b31442c3SJuan Quintela         VMSTATE_INT32(scan_enabled, PS2KbdState),
1061b31442c3SJuan Quintela         VMSTATE_INT32(translate, PS2KbdState),
1062b31442c3SJuan Quintela         VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
1063b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
10647f540ab5SChristophe Fergeau     },
10655cd8cadaSJuan Quintela     .subsections = (const VMStateDescription*[]) {
10665cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
106757d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
10685cd8cadaSJuan Quintela         NULL
10690e43e99cSbellard     }
1070b31442c3SJuan Quintela };
1071b31442c3SJuan Quintela 
10722858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
10732858ab09SGonglei {
10742858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
10752858ab09SGonglei     PS2State *ps2 = &s->common;
10762858ab09SGonglei 
10772858ab09SGonglei     ps2_common_post_load(ps2);
10782858ab09SGonglei 
10792858ab09SGonglei     return 0;
10802858ab09SGonglei }
10812858ab09SGonglei 
108244b1ff31SDr. David Alan Gilbert static int ps2_mouse_pre_save(void *opaque)
10832858ab09SGonglei {
10842858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
10852858ab09SGonglei     PS2State *ps2 = &s->common;
10862858ab09SGonglei 
10872858ab09SGonglei     ps2_common_post_load(ps2);
108844b1ff31SDr. David Alan Gilbert 
108944b1ff31SDr. David Alan Gilbert     return 0;
10902858ab09SGonglei }
10912858ab09SGonglei 
1092b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_mouse = {
1093b31442c3SJuan Quintela     .name = "ps2mouse",
1094b31442c3SJuan Quintela     .version_id = 2,
1095b31442c3SJuan Quintela     .minimum_version_id = 2,
10962858ab09SGonglei     .post_load = ps2_mouse_post_load,
10972858ab09SGonglei     .pre_save = ps2_mouse_pre_save,
1098b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1099b31442c3SJuan Quintela         VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
1100b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_status, PS2MouseState),
1101b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
1102b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
1103b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
1104b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_type, PS2MouseState),
1105b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
1106b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dx, PS2MouseState),
1107b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dy, PS2MouseState),
1108b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dz, PS2MouseState),
1109b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
1110b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
1111b31442c3SJuan Quintela     }
1112b31442c3SJuan Quintela };
11130e43e99cSbellard 
111466e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
111566e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
111666e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
111766e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
111866e6536eSGerd Hoffmann };
111966e6536eSGerd Hoffmann 
11200e43e99cSbellard void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
11210e43e99cSbellard {
11227267c094SAnthony Liguori     PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState));
11230e43e99cSbellard 
11245edab03dSDon Koch     trace_ps2_kbd_init(s);
11250e43e99cSbellard     s->common.update_irq = update_irq;
11260e43e99cSbellard     s->common.update_arg = update_arg;
1127e7d93956Saurel32     s->scancode_set = 2;
11280be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
112966e6536eSGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
113066e6536eSGerd Hoffmann                                 &ps2_keyboard_handler);
1131ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_kbd_reset, s);
11320e43e99cSbellard     return s;
11330e43e99cSbellard }
11340e43e99cSbellard 
11352a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
11362a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
11372a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
11382a766d29SGerd Hoffmann     .event = ps2_mouse_event,
11392a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
11402a766d29SGerd Hoffmann };
11412a766d29SGerd Hoffmann 
11420e43e99cSbellard void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
11430e43e99cSbellard {
11447267c094SAnthony Liguori     PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState));
11450e43e99cSbellard 
11465edab03dSDon Koch     trace_ps2_mouse_init(s);
11470e43e99cSbellard     s->common.update_irq = update_irq;
11480e43e99cSbellard     s->common.update_arg = update_arg;
11490be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
11502a766d29SGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
11512a766d29SGerd Hoffmann                                 &ps2_mouse_handler);
1152ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_mouse_reset, s);
11530e43e99cSbellard     return s;
11540e43e99cSbellard }
1155