xref: /qemu/hw/input/ps2.c (revision 7704bb02dd73070b218fb091cdda79679dab2b8f)
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"
3254d31236SMarkus Armbruster #include "sysemu/runstate.h"
330e43e99cSbellard 
345edab03dSDon Koch #include "trace.h"
355edab03dSDon Koch 
360e43e99cSbellard /* Keyboard Commands */
370e43e99cSbellard #define KBD_CMD_SET_LEDS	0xED	/* Set keyboard leds */
380e43e99cSbellard #define KBD_CMD_ECHO     	0xEE
39e7d93956Saurel32 #define KBD_CMD_SCANCODE	0xF0	/* Get/set scancode set */
400e43e99cSbellard #define KBD_CMD_GET_ID 	        0xF2	/* get keyboard ID */
410e43e99cSbellard #define KBD_CMD_SET_RATE	0xF3	/* Set typematic rate */
420e43e99cSbellard #define KBD_CMD_ENABLE		0xF4	/* Enable scanning */
430e43e99cSbellard #define KBD_CMD_RESET_DISABLE	0xF5	/* reset and disable scanning */
440e43e99cSbellard #define KBD_CMD_RESET_ENABLE   	0xF6    /* reset and enable scanning */
450e43e99cSbellard #define KBD_CMD_RESET		0xFF	/* Reset */
46c56b6209SSven Schnelle #define KBD_CMD_SET_MAKE_BREAK  0xFC    /* Set Make and Break mode */
47c56b6209SSven Schnelle #define KBD_CMD_SET_TYPEMATIC   0xFA    /* Set Typematic Make and Break mode */
480e43e99cSbellard 
490e43e99cSbellard /* Keyboard Replies */
500e43e99cSbellard #define KBD_REPLY_POR		0xAA	/* Power on reset */
5135c4d671Saurel32 #define KBD_REPLY_ID		0xAB	/* Keyboard ID */
520e43e99cSbellard #define KBD_REPLY_ACK		0xFA	/* Command ACK */
530e43e99cSbellard #define KBD_REPLY_RESEND	0xFE	/* Command NACK, send the cmd again */
540e43e99cSbellard 
550e43e99cSbellard /* Mouse Commands */
560e43e99cSbellard #define AUX_SET_SCALE11		0xE6	/* Set 1:1 scaling */
570e43e99cSbellard #define AUX_SET_SCALE21		0xE7	/* Set 2:1 scaling */
580e43e99cSbellard #define AUX_SET_RES		0xE8	/* Set resolution */
590e43e99cSbellard #define AUX_GET_SCALE		0xE9	/* Get scaling factor */
600e43e99cSbellard #define AUX_SET_STREAM		0xEA	/* Set stream mode */
610e43e99cSbellard #define AUX_POLL		0xEB	/* Poll */
620e43e99cSbellard #define AUX_RESET_WRAP		0xEC	/* Reset wrap mode */
630e43e99cSbellard #define AUX_SET_WRAP		0xEE	/* Set wrap mode */
640e43e99cSbellard #define AUX_SET_REMOTE		0xF0	/* Set remote mode */
650e43e99cSbellard #define AUX_GET_TYPE		0xF2	/* Get type */
660e43e99cSbellard #define AUX_SET_SAMPLE		0xF3	/* Set sample rate */
670e43e99cSbellard #define AUX_ENABLE_DEV		0xF4	/* Enable aux device */
680e43e99cSbellard #define AUX_DISABLE_DEV		0xF5	/* Disable aux device */
690e43e99cSbellard #define AUX_SET_DEFAULT		0xF6
700e43e99cSbellard #define AUX_RESET		0xFF	/* Reset aux device */
710e43e99cSbellard #define AUX_ACK			0xFA	/* Command byte ACK. */
720e43e99cSbellard 
730e43e99cSbellard #define MOUSE_STATUS_REMOTE     0x40
740e43e99cSbellard #define MOUSE_STATUS_ENABLED    0x20
750e43e99cSbellard #define MOUSE_STATUS_SCALE21    0x10
760e43e99cSbellard 
772858ab09SGonglei #define PS2_QUEUE_SIZE 16  /* Buffer size required by PS/2 protocol */
780e43e99cSbellard 
79620775d1SDaniel P. Berrange /* Bits for 'modifiers' field in PS2KbdState */
80620775d1SDaniel P. Berrange #define MOD_CTRL_L  (1 << 0)
81620775d1SDaniel P. Berrange #define MOD_SHIFT_L (1 << 1)
82620775d1SDaniel P. Berrange #define MOD_ALT_L   (1 << 2)
83620775d1SDaniel P. Berrange #define MOD_CTRL_R  (1 << 3)
84620775d1SDaniel P. Berrange #define MOD_SHIFT_R (1 << 4)
85620775d1SDaniel P. Berrange #define MOD_ALT_R   (1 << 5)
86620775d1SDaniel P. Berrange 
870e43e99cSbellard typedef struct {
882858ab09SGonglei     /* Keep the data array 256 bytes long, which compatibility
892858ab09SGonglei      with older qemu versions. */
902858ab09SGonglei     uint8_t data[256];
910e43e99cSbellard     int rptr, wptr, count;
920e43e99cSbellard } PS2Queue;
930e43e99cSbellard 
948498bb8dSGerd Hoffmann struct PS2State {
950e43e99cSbellard     PS2Queue queue;
960e43e99cSbellard     int32_t write_cmd;
970e43e99cSbellard     void (*update_irq)(void *, int);
980e43e99cSbellard     void *update_arg;
998498bb8dSGerd Hoffmann };
1000e43e99cSbellard 
1010e43e99cSbellard typedef struct {
1020e43e99cSbellard     PS2State common;
1030e43e99cSbellard     int scan_enabled;
104f94f5d71Spbrook     int translate;
105e7d93956Saurel32     int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
1067f540ab5SChristophe Fergeau     int ledstate;
10757d5c005SHervé Poussineau     bool need_high_bit;
108620775d1SDaniel P. Berrange     unsigned int modifiers; /* bitmask of MOD_* constants above */
1090e43e99cSbellard } PS2KbdState;
1100e43e99cSbellard 
1110e43e99cSbellard typedef struct {
1120e43e99cSbellard     PS2State common;
1130e43e99cSbellard     uint8_t mouse_status;
1140e43e99cSbellard     uint8_t mouse_resolution;
1150e43e99cSbellard     uint8_t mouse_sample_rate;
1160e43e99cSbellard     uint8_t mouse_wrap;
1170e43e99cSbellard     uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
1180e43e99cSbellard     uint8_t mouse_detect_state;
1190e43e99cSbellard     int mouse_dx; /* current values, needed for 'poll' mode */
1200e43e99cSbellard     int mouse_dy;
1210e43e99cSbellard     int mouse_dz;
1220e43e99cSbellard     uint8_t mouse_buttons;
1230e43e99cSbellard } PS2MouseState;
1240e43e99cSbellard 
12557d5c005SHervé Poussineau static uint8_t translate_table[256] = {
12657d5c005SHervé Poussineau     0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
12757d5c005SHervé Poussineau     0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
12857d5c005SHervé Poussineau     0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
12957d5c005SHervé Poussineau     0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
13057d5c005SHervé Poussineau     0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
13157d5c005SHervé Poussineau     0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
13257d5c005SHervé Poussineau     0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
13357d5c005SHervé Poussineau     0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
13457d5c005SHervé Poussineau     0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
13557d5c005SHervé Poussineau     0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
13657d5c005SHervé Poussineau     0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
13757d5c005SHervé Poussineau     0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
13857d5c005SHervé Poussineau     0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
13957d5c005SHervé Poussineau     0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
14057d5c005SHervé Poussineau     0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
14157d5c005SHervé Poussineau     0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
14257d5c005SHervé Poussineau     0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
14357d5c005SHervé Poussineau     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
14457d5c005SHervé Poussineau     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
14557d5c005SHervé Poussineau     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
14657d5c005SHervé Poussineau     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
14757d5c005SHervé Poussineau     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
14857d5c005SHervé Poussineau     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
14957d5c005SHervé Poussineau     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
15057d5c005SHervé Poussineau     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
15157d5c005SHervé Poussineau     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
15257d5c005SHervé Poussineau     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
15357d5c005SHervé Poussineau     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
15457d5c005SHervé Poussineau     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
15557d5c005SHervé Poussineau     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
15657d5c005SHervé Poussineau     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
15757d5c005SHervé Poussineau     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
15857d5c005SHervé Poussineau };
15957d5c005SHervé Poussineau 
160620775d1SDaniel P. Berrange static unsigned int ps2_modifier_bit(QKeyCode key)
161620775d1SDaniel P. Berrange {
162620775d1SDaniel P. Berrange     switch (key) {
163620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL:
164620775d1SDaniel P. Berrange         return MOD_CTRL_L;
165620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL_R:
166620775d1SDaniel P. Berrange         return MOD_CTRL_R;
167620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT:
168620775d1SDaniel P. Berrange         return MOD_SHIFT_L;
169620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT_R:
170620775d1SDaniel P. Berrange         return MOD_SHIFT_R;
171620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT:
172620775d1SDaniel P. Berrange         return MOD_ALT_L;
173620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT_R:
174620775d1SDaniel P. Berrange         return MOD_ALT_R;
175620775d1SDaniel P. Berrange     default:
176620775d1SDaniel P. Berrange         return 0;
177620775d1SDaniel P. Berrange     }
178620775d1SDaniel P. Berrange }
179620775d1SDaniel P. Berrange 
180954ee55bSGerd Hoffmann static void ps2_reset_queue(PS2State *s)
181954ee55bSGerd Hoffmann {
182954ee55bSGerd Hoffmann     PS2Queue *q = &s->queue;
183954ee55bSGerd Hoffmann 
184954ee55bSGerd Hoffmann     q->rptr = 0;
185954ee55bSGerd Hoffmann     q->wptr = 0;
186954ee55bSGerd Hoffmann     q->count = 0;
187954ee55bSGerd Hoffmann }
188954ee55bSGerd Hoffmann 
1892a6505b0SSven Schnelle int ps2_queue_empty(PS2State *s)
1902a6505b0SSven Schnelle {
1912a6505b0SSven Schnelle     return s->queue.count == 0;
1922a6505b0SSven Schnelle }
1932a6505b0SSven Schnelle 
1947abe7eb2SGeoffrey McRae void ps2_queue_noirq(PS2State *s, int b)
1950e43e99cSbellard {
1960e43e99cSbellard     PS2Queue *q = &s->queue;
1970e43e99cSbellard 
1987abe7eb2SGeoffrey McRae     if (q->count == PS2_QUEUE_SIZE) {
1990e43e99cSbellard         return;
2007abe7eb2SGeoffrey McRae     }
2017abe7eb2SGeoffrey McRae 
2020e43e99cSbellard     q->data[q->wptr] = b;
2030e43e99cSbellard     if (++q->wptr == PS2_QUEUE_SIZE)
2040e43e99cSbellard         q->wptr = 0;
2050e43e99cSbellard     q->count++;
2067abe7eb2SGeoffrey McRae }
2077abe7eb2SGeoffrey McRae 
2087abe7eb2SGeoffrey McRae void ps2_raise_irq(PS2State *s)
2097abe7eb2SGeoffrey McRae {
2107abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2117abe7eb2SGeoffrey McRae }
2127abe7eb2SGeoffrey McRae 
2137abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
2147abe7eb2SGeoffrey McRae {
215*7704bb02SVolker Rümelin     if (PS2_QUEUE_SIZE - s->queue.count < 1) {
216*7704bb02SVolker Rümelin         return;
217*7704bb02SVolker Rümelin     }
218*7704bb02SVolker Rümelin 
2197abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
2207abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2217abe7eb2SGeoffrey McRae }
2227abe7eb2SGeoffrey McRae 
2237abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
2247abe7eb2SGeoffrey McRae {
2257abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
2267abe7eb2SGeoffrey McRae         return;
2277abe7eb2SGeoffrey McRae     }
2287abe7eb2SGeoffrey McRae 
2297abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2307abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2317abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2327abe7eb2SGeoffrey McRae }
2337abe7eb2SGeoffrey McRae 
2347abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2357abe7eb2SGeoffrey McRae {
2367abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2377abe7eb2SGeoffrey McRae         return;
2387abe7eb2SGeoffrey McRae     }
2397abe7eb2SGeoffrey McRae 
2407abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2417abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2427abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2437abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2447abe7eb2SGeoffrey McRae }
2457abe7eb2SGeoffrey McRae 
2467abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2477abe7eb2SGeoffrey McRae {
2487abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2497abe7eb2SGeoffrey McRae         return;
2507abe7eb2SGeoffrey McRae     }
2517abe7eb2SGeoffrey McRae 
2527abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2537abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2547abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2557abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
2560e43e99cSbellard     s->update_irq(s->update_arg, 1);
2570e43e99cSbellard }
2580e43e99cSbellard 
25957d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
2600e43e99cSbellard static void ps2_put_keycode(void *opaque, int keycode)
2610e43e99cSbellard {
262f94f5d71Spbrook     PS2KbdState *s = opaque;
263e7d93956Saurel32 
2645edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
265fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
26657d5c005SHervé Poussineau 
26757d5c005SHervé Poussineau     if (s->translate) {
26857d5c005SHervé Poussineau         if (keycode == 0xf0) {
26957d5c005SHervé Poussineau             s->need_high_bit = true;
27057d5c005SHervé Poussineau         } else if (s->need_high_bit) {
27157d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode] | 0x80);
27257d5c005SHervé Poussineau             s->need_high_bit = false;
27357d5c005SHervé Poussineau         } else {
27457d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode]);
2757096a96dSRoy Tam         }
27657d5c005SHervé Poussineau     } else {
2770e43e99cSbellard         ps2_queue(&s->common, keycode);
2780e43e99cSbellard     }
27957d5c005SHervé Poussineau }
2800e43e99cSbellard 
28166e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
28266e6536eSGerd Hoffmann                                InputEvent *evt)
28366e6536eSGerd Hoffmann {
28466e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
28532bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
2868c10e0baSHervé Poussineau     int qcode;
287ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
288620775d1SDaniel P. Berrange     int mod;
28966e6536eSGerd Hoffmann 
290143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
291143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
292143c04c7SGeoffrey McRae         return;
293143c04c7SGeoffrey McRae     }
294143c04c7SGeoffrey McRae 
295fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
2968c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
2978c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
29857d5c005SHervé Poussineau 
299620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
300644f66bfSDaniel P. Berrangé     trace_ps2_keyboard_event(s, qcode, key->down, mod,
301644f66bfSDaniel P. Berrangé                              s->modifiers, s->scancode_set, s->translate);
302620775d1SDaniel P. Berrange     if (key->down) {
303620775d1SDaniel P. Berrange         s->modifiers |= mod;
304620775d1SDaniel P. Berrange     } else {
305620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
306620775d1SDaniel P. Berrange     }
307620775d1SDaniel P. Berrange 
3088c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3098c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
31029fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
31129fd23a5SDaniel P. Berrange                 if (key->down) {
31229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
31329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
31429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
31529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
31629fd23a5SDaniel P. Berrange                 }
31729fd23a5SDaniel P. Berrange             } else {
3188c10e0baSHervé Poussineau                 if (key->down) {
3198c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3208c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3218c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
322927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3238c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
3248c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
3258c10e0baSHervé Poussineau                 }
32629fd23a5SDaniel P. Berrange             }
3278c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
328620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
329620775d1SDaniel P. Berrange                 if (key->down) {
330620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
331620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
332620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
333620775d1SDaniel P. Berrange                 } else {
334620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
335620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
336620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
337620775d1SDaniel P. Berrange                 }
338620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
339620775d1SDaniel P. Berrange                 if (key->down) {
340620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
341620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
342620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
343620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
344620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
345620775d1SDaniel P. Berrange                 } else {
346620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
347620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
348620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
349620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
350620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
351620775d1SDaniel P. Berrange                 }
3528f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
3538f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
3548f63458fSDaniel P. Berrange                 if (key->down) {
3558f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3568f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
3578f63458fSDaniel P. Berrange                 } else {
3588f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3598f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
3608f63458fSDaniel P. Berrange                 }
361620775d1SDaniel P. Berrange             } else {
3628c10e0baSHervé Poussineau                 if (key->down) {
3638c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3648c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
3658c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3668c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
3678c10e0baSHervé Poussineau                 } else {
3688c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3698c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
3708c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3718c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
3728c10e0baSHervé Poussineau                 }
373620775d1SDaniel P. Berrange             }
3748c10e0baSHervé Poussineau         } else {
375ab8f9d49SDaniel P. Berrange             if (qcode < qemu_input_map_qcode_to_atset1_len)
376ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
3778c10e0baSHervé Poussineau             if (keycode) {
3788c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
3798c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
3808c10e0baSHervé Poussineau                 }
3818c10e0baSHervé Poussineau                 if (!key->down) {
3828c10e0baSHervé Poussineau                     keycode |= 0x80;
3838c10e0baSHervé Poussineau                 }
3848c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
3858c10e0baSHervé Poussineau             } else {
386ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
387ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
3888c10e0baSHervé Poussineau             }
3898c10e0baSHervé Poussineau         }
3908c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
3918c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
39229fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
39329fd23a5SDaniel P. Berrange                 if (key->down) {
39429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
39529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
39629fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
39729fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
39829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
39929fd23a5SDaniel P. Berrange                 }
40029fd23a5SDaniel P. Berrange             } else {
4018c10e0baSHervé Poussineau                 if (key->down) {
4028c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4038c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4048c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4058c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4068c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4078c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4088c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4098c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4108c10e0baSHervé Poussineau                 }
41129fd23a5SDaniel P. Berrange             }
4128c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
413620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
414620775d1SDaniel P. Berrange                 if (key->down) {
415620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
416620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
417620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
418620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
419620775d1SDaniel P. Berrange                 } else {
420620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
421620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
422620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
423620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
424620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
425620775d1SDaniel P. Berrange                 }
426620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
427620775d1SDaniel P. Berrange                 if (key->down) {
428620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
429620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
430620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
431620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
432620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
433620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
434620775d1SDaniel P. Berrange                 } else {
435620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
436620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
437620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
438620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
439620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
440620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
441620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
442620775d1SDaniel P. Berrange                 }
4438f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4448f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4458f63458fSDaniel P. Berrange                 if (key->down) {
4468f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4478f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4488f63458fSDaniel P. Berrange                 } else {
4498f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4508f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
4518f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4528f63458fSDaniel P. Berrange                 }
453620775d1SDaniel P. Berrange             } else {
4548c10e0baSHervé Poussineau                 if (key->down) {
4558c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4568c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4578c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4588c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4598c10e0baSHervé Poussineau                 } else {
4608c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4618c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4628c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4638c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4648c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4658c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4668c10e0baSHervé Poussineau                 }
467620775d1SDaniel P. Berrange             }
4688c10e0baSHervé Poussineau         } else {
469ab8f9d49SDaniel P. Berrange             if (qcode < qemu_input_map_qcode_to_atset2_len)
470ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
4718c10e0baSHervé Poussineau             if (keycode) {
4728c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4738c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4748c10e0baSHervé Poussineau                 }
4758c10e0baSHervé Poussineau                 if (!key->down) {
4768c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4778c10e0baSHervé Poussineau                 }
4788c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
47957d5c005SHervé Poussineau             } else {
480ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
481ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
48257d5c005SHervé Poussineau             }
48357d5c005SHervé Poussineau         }
48457d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
485ab8f9d49SDaniel P. Berrange         if (qcode < qemu_input_map_qcode_to_atset3_len)
486ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
4878c10e0baSHervé Poussineau         if (keycode) {
4888c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
4898c10e0baSHervé Poussineau             if (!key->down) {
4908c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
49157d5c005SHervé Poussineau             }
49257d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
4938c10e0baSHervé Poussineau         } else {
494ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
495ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
4968c10e0baSHervé Poussineau         }
49766e6536eSGerd Hoffmann     }
49866e6536eSGerd Hoffmann }
49966e6536eSGerd Hoffmann 
5008498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
5010e43e99cSbellard {
5020e43e99cSbellard     PS2Queue *q;
5030e43e99cSbellard     int val, index;
5040e43e99cSbellard 
5058498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
5060e43e99cSbellard     q = &s->queue;
5070e43e99cSbellard     if (q->count == 0) {
5080e43e99cSbellard         /* NOTE: if no data left, we return the last keyboard one
5090e43e99cSbellard            (needed for EMM386) */
5100e43e99cSbellard         /* XXX: need a timer to do things correctly */
5110e43e99cSbellard         index = q->rptr - 1;
5120e43e99cSbellard         if (index < 0)
5130e43e99cSbellard             index = PS2_QUEUE_SIZE - 1;
5140e43e99cSbellard         val = q->data[index];
5150e43e99cSbellard     } else {
5160e43e99cSbellard         val = q->data[q->rptr];
5170e43e99cSbellard         if (++q->rptr == PS2_QUEUE_SIZE)
5180e43e99cSbellard             q->rptr = 0;
5190e43e99cSbellard         q->count--;
5200e43e99cSbellard         /* reading deasserts IRQ */
5210e43e99cSbellard         s->update_irq(s->update_arg, 0);
5220e43e99cSbellard         /* reassert IRQs if data left */
5230e43e99cSbellard         s->update_irq(s->update_arg, q->count != 0);
5240e43e99cSbellard     }
5250e43e99cSbellard     return val;
5260e43e99cSbellard }
5270e43e99cSbellard 
5287f540ab5SChristophe Fergeau static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
5297f540ab5SChristophe Fergeau {
5305edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
5317f540ab5SChristophe Fergeau     s->ledstate = ledstate;
5327f540ab5SChristophe Fergeau     kbd_put_ledstate(ledstate);
5337f540ab5SChristophe Fergeau }
5347f540ab5SChristophe Fergeau 
5350e43e99cSbellard static void ps2_reset_keyboard(PS2KbdState *s)
5360e43e99cSbellard {
5375edab03dSDon Koch     trace_ps2_reset_keyboard(s);
5380e43e99cSbellard     s->scan_enabled = 1;
539e7d93956Saurel32     s->scancode_set = 2;
5406e24ee0cSGerd Hoffmann     ps2_reset_queue(&s->common);
5417f540ab5SChristophe Fergeau     ps2_set_ledstate(s, 0);
5420e43e99cSbellard }
5430e43e99cSbellard 
5440e43e99cSbellard void ps2_write_keyboard(void *opaque, int val)
5450e43e99cSbellard {
5460e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
5470e43e99cSbellard 
5485edab03dSDon Koch     trace_ps2_write_keyboard(opaque, val);
5490e43e99cSbellard     switch(s->common.write_cmd) {
5500e43e99cSbellard     default:
5510e43e99cSbellard     case -1:
5520e43e99cSbellard         switch(val) {
5530e43e99cSbellard         case 0x00:
5540e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_ACK);
5550e43e99cSbellard             break;
5560e43e99cSbellard         case 0x05:
5570e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_RESEND);
5580e43e99cSbellard             break;
5590e43e99cSbellard         case KBD_CMD_GET_ID:
560e7d93956Saurel32             /* We emulate a MF2 AT keyboard here */
56135c4d671Saurel32             if (s->translate)
5627abe7eb2SGeoffrey McRae                 ps2_queue_3(&s->common,
5637abe7eb2SGeoffrey McRae                     KBD_REPLY_ACK,
5647abe7eb2SGeoffrey McRae                     KBD_REPLY_ID,
5657abe7eb2SGeoffrey McRae                     0x41);
56635c4d671Saurel32             else
5677abe7eb2SGeoffrey McRae                 ps2_queue_3(&s->common,
5687abe7eb2SGeoffrey McRae                     KBD_REPLY_ACK,
5697abe7eb2SGeoffrey McRae                     KBD_REPLY_ID,
5707abe7eb2SGeoffrey McRae                     0x83);
5710e43e99cSbellard             break;
5720e43e99cSbellard         case KBD_CMD_ECHO:
5730e43e99cSbellard             ps2_queue(&s->common, KBD_CMD_ECHO);
5740e43e99cSbellard             break;
5750e43e99cSbellard         case KBD_CMD_ENABLE:
5760e43e99cSbellard             s->scan_enabled = 1;
5770e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_ACK);
5780e43e99cSbellard             break;
579e7d93956Saurel32         case KBD_CMD_SCANCODE:
5800e43e99cSbellard         case KBD_CMD_SET_LEDS:
5810e43e99cSbellard         case KBD_CMD_SET_RATE:
582c56b6209SSven Schnelle         case KBD_CMD_SET_MAKE_BREAK:
5830e43e99cSbellard             s->common.write_cmd = val;
5840e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_ACK);
5850e43e99cSbellard             break;
5860e43e99cSbellard         case KBD_CMD_RESET_DISABLE:
5870e43e99cSbellard             ps2_reset_keyboard(s);
5880e43e99cSbellard             s->scan_enabled = 0;
5890e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_ACK);
5900e43e99cSbellard             break;
5910e43e99cSbellard         case KBD_CMD_RESET_ENABLE:
5920e43e99cSbellard             ps2_reset_keyboard(s);
5930e43e99cSbellard             s->scan_enabled = 1;
5940e43e99cSbellard             ps2_queue(&s->common, KBD_REPLY_ACK);
5950e43e99cSbellard             break;
5960e43e99cSbellard         case KBD_CMD_RESET:
5970e43e99cSbellard             ps2_reset_keyboard(s);
5987abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
5997abe7eb2SGeoffrey McRae                 KBD_REPLY_ACK,
6007abe7eb2SGeoffrey McRae                 KBD_REPLY_POR);
6010e43e99cSbellard             break;
602c56b6209SSven Schnelle         case KBD_CMD_SET_TYPEMATIC:
603c56b6209SSven Schnelle             ps2_queue(&s->common, KBD_REPLY_ACK);
604c56b6209SSven Schnelle             break;
6050e43e99cSbellard         default:
60606b3611fSHervé Poussineau             ps2_queue(&s->common, KBD_REPLY_RESEND);
6070e43e99cSbellard             break;
6080e43e99cSbellard         }
6090e43e99cSbellard         break;
610c56b6209SSven Schnelle     case KBD_CMD_SET_MAKE_BREAK:
611c56b6209SSven Schnelle         ps2_queue(&s->common, KBD_REPLY_ACK);
612c56b6209SSven Schnelle         s->common.write_cmd = -1;
613c56b6209SSven Schnelle         break;
614e7d93956Saurel32     case KBD_CMD_SCANCODE:
615e7d93956Saurel32         if (val == 0) {
6167abe7eb2SGeoffrey McRae             if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) {
6174df23b64SHervé Poussineau                 ps2_queue(&s->common, KBD_REPLY_ACK);
61857d5c005SHervé Poussineau                 ps2_put_keycode(s, s->scancode_set);
6197abe7eb2SGeoffrey McRae             }
6204df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
621e7d93956Saurel32             s->scancode_set = val;
622e7d93956Saurel32             ps2_queue(&s->common, KBD_REPLY_ACK);
6234df23b64SHervé Poussineau         } else {
6244df23b64SHervé Poussineau             ps2_queue(&s->common, KBD_REPLY_RESEND);
625e7d93956Saurel32         }
626e7d93956Saurel32         s->common.write_cmd = -1;
627e7d93956Saurel32         break;
6280e43e99cSbellard     case KBD_CMD_SET_LEDS:
6297f540ab5SChristophe Fergeau         ps2_set_ledstate(s, val);
6300e43e99cSbellard         ps2_queue(&s->common, KBD_REPLY_ACK);
6310e43e99cSbellard         s->common.write_cmd = -1;
6320e43e99cSbellard         break;
6330e43e99cSbellard     case KBD_CMD_SET_RATE:
6340e43e99cSbellard         ps2_queue(&s->common, KBD_REPLY_ACK);
6350e43e99cSbellard         s->common.write_cmd = -1;
6360e43e99cSbellard         break;
6370e43e99cSbellard     }
6380e43e99cSbellard }
6390e43e99cSbellard 
640f94f5d71Spbrook /* Set the scancode translation mode.
641f94f5d71Spbrook    0 = raw scancodes.
642f94f5d71Spbrook    1 = translated scancodes (used by qemu internally).  */
643f94f5d71Spbrook 
644f94f5d71Spbrook void ps2_keyboard_set_translation(void *opaque, int mode)
645f94f5d71Spbrook {
646f94f5d71Spbrook     PS2KbdState *s = (PS2KbdState *)opaque;
6475edab03dSDon Koch     trace_ps2_keyboard_set_translation(opaque, mode);
648f94f5d71Spbrook     s->translate = mode;
649f94f5d71Spbrook }
650f94f5d71Spbrook 
6517abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
6520e43e99cSbellard {
65376968101SVolker Rümelin     /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
65476968101SVolker Rümelin     const int needed = s->mouse_type ? 4 : 3;
6550e43e99cSbellard     unsigned int b;
6560e43e99cSbellard     int dx1, dy1, dz1;
6570e43e99cSbellard 
6587abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->common.queue.count < needed) {
6597abe7eb2SGeoffrey McRae         return 0;
6607abe7eb2SGeoffrey McRae     }
6617abe7eb2SGeoffrey McRae 
6620e43e99cSbellard     dx1 = s->mouse_dx;
6630e43e99cSbellard     dy1 = s->mouse_dy;
6640e43e99cSbellard     dz1 = s->mouse_dz;
6650e43e99cSbellard     /* XXX: increase range to 8 bits ? */
6660e43e99cSbellard     if (dx1 > 127)
6670e43e99cSbellard         dx1 = 127;
6680e43e99cSbellard     else if (dx1 < -127)
6690e43e99cSbellard         dx1 = -127;
6700e43e99cSbellard     if (dy1 > 127)
6710e43e99cSbellard         dy1 = 127;
6720e43e99cSbellard     else if (dy1 < -127)
6730e43e99cSbellard         dy1 = -127;
6740e43e99cSbellard     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
6757abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, b);
6767abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dx1 & 0xff);
6777abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dy1 & 0xff);
6780e43e99cSbellard     /* extra byte for IMPS/2 or IMEX */
6790e43e99cSbellard     switch(s->mouse_type) {
6800e43e99cSbellard     default:
6810e43e99cSbellard         break;
6820e43e99cSbellard     case 3:
6830e43e99cSbellard         if (dz1 > 127)
6840e43e99cSbellard             dz1 = 127;
6850e43e99cSbellard         else if (dz1 < -127)
6860e43e99cSbellard                 dz1 = -127;
6877abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, dz1 & 0xff);
6880e43e99cSbellard         break;
6890e43e99cSbellard     case 4:
6900e43e99cSbellard         if (dz1 > 7)
6910e43e99cSbellard             dz1 = 7;
6920e43e99cSbellard         else if (dz1 < -7)
6930e43e99cSbellard             dz1 = -7;
6940e43e99cSbellard         b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
6957abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, b);
6960e43e99cSbellard         break;
6970e43e99cSbellard     }
6980e43e99cSbellard 
6997abe7eb2SGeoffrey McRae     ps2_raise_irq(&s->common);
7007abe7eb2SGeoffrey McRae 
7015edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
7020e43e99cSbellard     /* update deltas */
7030e43e99cSbellard     s->mouse_dx -= dx1;
7040e43e99cSbellard     s->mouse_dy -= dy1;
7050e43e99cSbellard     s->mouse_dz -= dz1;
7067abe7eb2SGeoffrey McRae 
7077abe7eb2SGeoffrey McRae     return 1;
7080e43e99cSbellard }
7090e43e99cSbellard 
7102a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
7112a766d29SGerd Hoffmann                             InputEvent *evt)
7120e43e99cSbellard {
7137fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
7148b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
7158b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
7168b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
7178b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
7188b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
7192a766d29SGerd Hoffmann     };
7202a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
721b5a1b443SEric Blake     InputMoveEvent *move;
722b5a1b443SEric Blake     InputBtnEvent *btn;
7230e43e99cSbellard 
7240e43e99cSbellard     /* check if deltas are recorded when disabled */
7250e43e99cSbellard     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
7260e43e99cSbellard         return;
7270e43e99cSbellard 
728568c73a4SEric Blake     switch (evt->type) {
7292a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
73032bafa8fSEric Blake         move = evt->u.rel.data;
731b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
732b5a1b443SEric Blake             s->mouse_dx += move->value;
733b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
734b5a1b443SEric Blake             s->mouse_dy -= move->value;
7352a766d29SGerd Hoffmann         }
7362a766d29SGerd Hoffmann         break;
7370e43e99cSbellard 
7382a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
73932bafa8fSEric Blake         btn = evt->u.btn.data;
740b5a1b443SEric Blake         if (btn->down) {
741b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
742b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
7432a766d29SGerd Hoffmann                 s->mouse_dz--;
744b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
7452a766d29SGerd Hoffmann                 s->mouse_dz++;
7462a766d29SGerd Hoffmann             }
7472a766d29SGerd Hoffmann         } else {
748b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
7492a766d29SGerd Hoffmann         }
7502a766d29SGerd Hoffmann         break;
7512a766d29SGerd Hoffmann 
7522a766d29SGerd Hoffmann     default:
7532a766d29SGerd Hoffmann         /* keep gcc happy */
7542a766d29SGerd Hoffmann         break;
7552a766d29SGerd Hoffmann     }
756fd214d18SGerd Hoffmann }
757fd214d18SGerd Hoffmann 
7582a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
7592a766d29SGerd Hoffmann {
7602a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
7612a766d29SGerd Hoffmann 
762143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
763143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
764143c04c7SGeoffrey McRae         return;
765143c04c7SGeoffrey McRae     }
766143c04c7SGeoffrey McRae 
7672a766d29SGerd Hoffmann     if (s->mouse_buttons) {
768fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
7692a766d29SGerd Hoffmann     }
7702858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
7710e43e99cSbellard         /* if not remote, send event. Multiple events are sent if
7720e43e99cSbellard            too big deltas */
7737abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
7740e43e99cSbellard             if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
7750e43e99cSbellard                 break;
7760e43e99cSbellard         }
7770e43e99cSbellard     }
7780e43e99cSbellard }
7790e43e99cSbellard 
780548df2acSths void ps2_mouse_fake_event(void *opaque)
781548df2acSths {
7822a766d29SGerd Hoffmann     PS2MouseState *s = opaque;
7835edab03dSDon Koch     trace_ps2_mouse_fake_event(opaque);
7842a766d29SGerd Hoffmann     s->mouse_dx++;
7852a766d29SGerd Hoffmann     ps2_mouse_sync(opaque);
786548df2acSths }
787548df2acSths 
7880e43e99cSbellard void ps2_write_mouse(void *opaque, int val)
7890e43e99cSbellard {
7900e43e99cSbellard     PS2MouseState *s = (PS2MouseState *)opaque;
7915edab03dSDon Koch 
7925edab03dSDon Koch     trace_ps2_write_mouse(opaque, val);
7930e43e99cSbellard     switch(s->common.write_cmd) {
7940e43e99cSbellard     default:
7950e43e99cSbellard     case -1:
7960e43e99cSbellard         /* mouse command */
7970e43e99cSbellard         if (s->mouse_wrap) {
7980e43e99cSbellard             if (val == AUX_RESET_WRAP) {
7990e43e99cSbellard                 s->mouse_wrap = 0;
8000e43e99cSbellard                 ps2_queue(&s->common, AUX_ACK);
8010e43e99cSbellard                 return;
8020e43e99cSbellard             } else if (val != AUX_RESET) {
8030e43e99cSbellard                 ps2_queue(&s->common, val);
8040e43e99cSbellard                 return;
8050e43e99cSbellard             }
8060e43e99cSbellard         }
8070e43e99cSbellard         switch(val) {
8080e43e99cSbellard         case AUX_SET_SCALE11:
8090e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
8100e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8110e43e99cSbellard             break;
8120e43e99cSbellard         case AUX_SET_SCALE21:
8130e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_SCALE21;
8140e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8150e43e99cSbellard             break;
8160e43e99cSbellard         case AUX_SET_STREAM:
8170e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
8180e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8190e43e99cSbellard             break;
8200e43e99cSbellard         case AUX_SET_WRAP:
8210e43e99cSbellard             s->mouse_wrap = 1;
8220e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8230e43e99cSbellard             break;
8240e43e99cSbellard         case AUX_SET_REMOTE:
8250e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_REMOTE;
8260e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8270e43e99cSbellard             break;
8280e43e99cSbellard         case AUX_GET_TYPE:
8297abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
8307abe7eb2SGeoffrey McRae                 AUX_ACK,
8317abe7eb2SGeoffrey McRae                 s->mouse_type);
8320e43e99cSbellard             break;
8330e43e99cSbellard         case AUX_SET_RES:
8340e43e99cSbellard         case AUX_SET_SAMPLE:
8350e43e99cSbellard             s->common.write_cmd = val;
8360e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8370e43e99cSbellard             break;
8380e43e99cSbellard         case AUX_GET_SCALE:
8397abe7eb2SGeoffrey McRae             ps2_queue_4(&s->common,
8407abe7eb2SGeoffrey McRae                 AUX_ACK,
8417abe7eb2SGeoffrey McRae                 s->mouse_status,
8427abe7eb2SGeoffrey McRae                 s->mouse_resolution,
8437abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
8440e43e99cSbellard             break;
8450e43e99cSbellard         case AUX_POLL:
8460e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8470e43e99cSbellard             ps2_mouse_send_packet(s);
8480e43e99cSbellard             break;
8490e43e99cSbellard         case AUX_ENABLE_DEV:
8500e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_ENABLED;
8510e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8520e43e99cSbellard             break;
8530e43e99cSbellard         case AUX_DISABLE_DEV:
8540e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
8550e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8560e43e99cSbellard             break;
8570e43e99cSbellard         case AUX_SET_DEFAULT:
8580e43e99cSbellard             s->mouse_sample_rate = 100;
8590e43e99cSbellard             s->mouse_resolution = 2;
8600e43e99cSbellard             s->mouse_status = 0;
8610e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
8620e43e99cSbellard             break;
8630e43e99cSbellard         case AUX_RESET:
8640e43e99cSbellard             s->mouse_sample_rate = 100;
8650e43e99cSbellard             s->mouse_resolution = 2;
8660e43e99cSbellard             s->mouse_status = 0;
8670e43e99cSbellard             s->mouse_type = 0;
868143c04c7SGeoffrey McRae             ps2_reset_queue(&s->common);
8697abe7eb2SGeoffrey McRae             ps2_queue_3(&s->common,
8707abe7eb2SGeoffrey McRae                 AUX_ACK,
8717abe7eb2SGeoffrey McRae                 0xaa,
8727abe7eb2SGeoffrey McRae                 s->mouse_type);
8730e43e99cSbellard             break;
8740e43e99cSbellard         default:
8750e43e99cSbellard             break;
8760e43e99cSbellard         }
8770e43e99cSbellard         break;
8780e43e99cSbellard     case AUX_SET_SAMPLE:
8790e43e99cSbellard         s->mouse_sample_rate = val;
8800e43e99cSbellard         /* detect IMPS/2 or IMEX */
8810e43e99cSbellard         switch(s->mouse_detect_state) {
8820e43e99cSbellard         default:
8830e43e99cSbellard         case 0:
8840e43e99cSbellard             if (val == 200)
8850e43e99cSbellard                 s->mouse_detect_state = 1;
8860e43e99cSbellard             break;
8870e43e99cSbellard         case 1:
8880e43e99cSbellard             if (val == 100)
8890e43e99cSbellard                 s->mouse_detect_state = 2;
8900e43e99cSbellard             else if (val == 200)
8910e43e99cSbellard                 s->mouse_detect_state = 3;
8920e43e99cSbellard             else
8930e43e99cSbellard                 s->mouse_detect_state = 0;
8940e43e99cSbellard             break;
8950e43e99cSbellard         case 2:
8960e43e99cSbellard             if (val == 80)
8970e43e99cSbellard                 s->mouse_type = 3; /* IMPS/2 */
8980e43e99cSbellard             s->mouse_detect_state = 0;
8990e43e99cSbellard             break;
9000e43e99cSbellard         case 3:
9010e43e99cSbellard             if (val == 80)
9020e43e99cSbellard                 s->mouse_type = 4; /* IMEX */
9030e43e99cSbellard             s->mouse_detect_state = 0;
9040e43e99cSbellard             break;
9050e43e99cSbellard         }
9060e43e99cSbellard         ps2_queue(&s->common, AUX_ACK);
9070e43e99cSbellard         s->common.write_cmd = -1;
9080e43e99cSbellard         break;
9090e43e99cSbellard     case AUX_SET_RES:
9100e43e99cSbellard         s->mouse_resolution = val;
9110e43e99cSbellard         ps2_queue(&s->common, AUX_ACK);
9120e43e99cSbellard         s->common.write_cmd = -1;
9130e43e99cSbellard         break;
9140e43e99cSbellard     }
9150e43e99cSbellard }
9160e43e99cSbellard 
917ef74679aSDinesh Subhraveti static void ps2_common_reset(PS2State *s)
9180e43e99cSbellard {
9190e43e99cSbellard     s->write_cmd = -1;
920954ee55bSGerd Hoffmann     ps2_reset_queue(s);
921deeccef3Saliguori     s->update_irq(s->update_arg, 0);
9220e43e99cSbellard }
9230e43e99cSbellard 
9242858ab09SGonglei static void ps2_common_post_load(PS2State *s)
9252858ab09SGonglei {
9262858ab09SGonglei     PS2Queue *q = &s->queue;
927802cbcb7SPrasad J Pandit     uint8_t i, size;
928802cbcb7SPrasad J Pandit     uint8_t tmp_data[PS2_QUEUE_SIZE];
9292858ab09SGonglei 
9302858ab09SGonglei     /* set the useful data buffer queue size, < PS2_QUEUE_SIZE */
931a1f2ed2aSPavel Dovgalyuk     size = q->count;
932a1f2ed2aSPavel Dovgalyuk     if (q->count < 0) {
933a1f2ed2aSPavel Dovgalyuk         size = 0;
934a1f2ed2aSPavel Dovgalyuk     } else if (q->count > PS2_QUEUE_SIZE) {
935a1f2ed2aSPavel Dovgalyuk         size = PS2_QUEUE_SIZE;
936a1f2ed2aSPavel Dovgalyuk     }
9372858ab09SGonglei 
9382858ab09SGonglei     /* move the queue elements to the start of data array */
9392858ab09SGonglei     for (i = 0; i < size; i++) {
940802cbcb7SPrasad J Pandit         if (q->rptr < 0 || q->rptr >= sizeof(q->data)) {
9412858ab09SGonglei             q->rptr = 0;
9422858ab09SGonglei         }
943802cbcb7SPrasad J Pandit         tmp_data[i] = q->data[q->rptr++];
9442858ab09SGonglei     }
9452858ab09SGonglei     memcpy(q->data, tmp_data, size);
946802cbcb7SPrasad J Pandit 
9472858ab09SGonglei     /* reset rptr/wptr/count */
9482858ab09SGonglei     q->rptr = 0;
949b55a06dfSliujunjie     q->wptr = (size == PS2_QUEUE_SIZE) ? 0 : size;
9502858ab09SGonglei     q->count = size;
9512858ab09SGonglei }
9522858ab09SGonglei 
953ef74679aSDinesh Subhraveti static void ps2_kbd_reset(void *opaque)
954ef74679aSDinesh Subhraveti {
955ef74679aSDinesh Subhraveti     PS2KbdState *s = (PS2KbdState *) opaque;
956ef74679aSDinesh Subhraveti 
9575edab03dSDon Koch     trace_ps2_kbd_reset(opaque);
958ef74679aSDinesh Subhraveti     ps2_common_reset(&s->common);
959d2e550a8SHervé Poussineau     s->scan_enabled = 1;
960ef74679aSDinesh Subhraveti     s->translate = 0;
961089adafdSHervé Poussineau     s->scancode_set = 2;
962620775d1SDaniel P. Berrange     s->modifiers = 0;
963ef74679aSDinesh Subhraveti }
964ef74679aSDinesh Subhraveti 
965ef74679aSDinesh Subhraveti static void ps2_mouse_reset(void *opaque)
966ef74679aSDinesh Subhraveti {
967ef74679aSDinesh Subhraveti     PS2MouseState *s = (PS2MouseState *) opaque;
968ef74679aSDinesh Subhraveti 
9695edab03dSDon Koch     trace_ps2_mouse_reset(opaque);
970ef74679aSDinesh Subhraveti     ps2_common_reset(&s->common);
971ef74679aSDinesh Subhraveti     s->mouse_status = 0;
972ef74679aSDinesh Subhraveti     s->mouse_resolution = 0;
973ef74679aSDinesh Subhraveti     s->mouse_sample_rate = 0;
974ef74679aSDinesh Subhraveti     s->mouse_wrap = 0;
975ef74679aSDinesh Subhraveti     s->mouse_type = 0;
976ef74679aSDinesh Subhraveti     s->mouse_detect_state = 0;
977ef74679aSDinesh Subhraveti     s->mouse_dx = 0;
978ef74679aSDinesh Subhraveti     s->mouse_dy = 0;
979ef74679aSDinesh Subhraveti     s->mouse_dz = 0;
980ef74679aSDinesh Subhraveti     s->mouse_buttons = 0;
981ef74679aSDinesh Subhraveti }
982ef74679aSDinesh Subhraveti 
983b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_common = {
984b31442c3SJuan Quintela     .name = "PS2 Common State",
985b31442c3SJuan Quintela     .version_id = 3,
986b31442c3SJuan Quintela     .minimum_version_id = 2,
987b31442c3SJuan Quintela     .fields = (VMStateField[]) {
988b31442c3SJuan Quintela         VMSTATE_INT32(write_cmd, PS2State),
989b31442c3SJuan Quintela         VMSTATE_INT32(queue.rptr, PS2State),
990b31442c3SJuan Quintela         VMSTATE_INT32(queue.wptr, PS2State),
991b31442c3SJuan Quintela         VMSTATE_INT32(queue.count, PS2State),
992b31442c3SJuan Quintela         VMSTATE_BUFFER(queue.data, PS2State),
993b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
9947783e9f0Spbrook     }
995b31442c3SJuan Quintela };
9967783e9f0Spbrook 
9977f540ab5SChristophe Fergeau static bool ps2_keyboard_ledstate_needed(void *opaque)
9987f540ab5SChristophe Fergeau {
9997f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
10007f540ab5SChristophe Fergeau 
10017f540ab5SChristophe Fergeau     return s->ledstate != 0; /* 0 is default state */
10027f540ab5SChristophe Fergeau }
10037f540ab5SChristophe Fergeau 
10047f540ab5SChristophe Fergeau static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
10057f540ab5SChristophe Fergeau {
10067f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
10077f540ab5SChristophe Fergeau 
10087f540ab5SChristophe Fergeau     kbd_put_ledstate(s->ledstate);
10097f540ab5SChristophe Fergeau     return 0;
10107f540ab5SChristophe Fergeau }
10117f540ab5SChristophe Fergeau 
10127f540ab5SChristophe Fergeau static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
10137f540ab5SChristophe Fergeau     .name = "ps2kbd/ledstate",
10147f540ab5SChristophe Fergeau     .version_id = 3,
10157f540ab5SChristophe Fergeau     .minimum_version_id = 2,
10167f540ab5SChristophe Fergeau     .post_load = ps2_kbd_ledstate_post_load,
10175cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
10187f540ab5SChristophe Fergeau     .fields = (VMStateField[]) {
10197f540ab5SChristophe Fergeau         VMSTATE_INT32(ledstate, PS2KbdState),
10207f540ab5SChristophe Fergeau         VMSTATE_END_OF_LIST()
10217f540ab5SChristophe Fergeau     }
10227f540ab5SChristophe Fergeau };
10237f540ab5SChristophe Fergeau 
102457d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
102557d5c005SHervé Poussineau {
102657d5c005SHervé Poussineau     PS2KbdState *s = opaque;
102757d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
102857d5c005SHervé Poussineau }
102957d5c005SHervé Poussineau 
103057d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
103157d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
103257d5c005SHervé Poussineau     .version_id = 1,
103357d5c005SHervé Poussineau     .minimum_version_id = 1,
103457d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
103557d5c005SHervé Poussineau     .fields = (VMStateField[]) {
103657d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
103757d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
103857d5c005SHervé Poussineau     }
103957d5c005SHervé Poussineau };
104057d5c005SHervé Poussineau 
1041db596c53SJuan Quintela static int ps2_kbd_post_load(void* opaque, int version_id)
10420e43e99cSbellard {
10430e43e99cSbellard     PS2KbdState *s = (PS2KbdState*)opaque;
10442858ab09SGonglei     PS2State *ps2 = &s->common;
10450e43e99cSbellard 
1046db596c53SJuan Quintela     if (version_id == 2)
1047e7d93956Saurel32         s->scancode_set=2;
10482858ab09SGonglei 
10492858ab09SGonglei     ps2_common_post_load(ps2);
10502858ab09SGonglei 
10510e43e99cSbellard     return 0;
10520e43e99cSbellard }
10530e43e99cSbellard 
105444b1ff31SDr. David Alan Gilbert static int ps2_kbd_pre_save(void *opaque)
10552858ab09SGonglei {
10562858ab09SGonglei     PS2KbdState *s = (PS2KbdState *)opaque;
10572858ab09SGonglei     PS2State *ps2 = &s->common;
10582858ab09SGonglei 
10592858ab09SGonglei     ps2_common_post_load(ps2);
106044b1ff31SDr. David Alan Gilbert 
106144b1ff31SDr. David Alan Gilbert     return 0;
10622858ab09SGonglei }
10632858ab09SGonglei 
1064b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_keyboard = {
1065b31442c3SJuan Quintela     .name = "ps2kbd",
1066b31442c3SJuan Quintela     .version_id = 3,
1067db596c53SJuan Quintela     .minimum_version_id = 2,
1068db596c53SJuan Quintela     .post_load = ps2_kbd_post_load,
10692858ab09SGonglei     .pre_save = ps2_kbd_pre_save,
1070b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1071b31442c3SJuan Quintela         VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
1072b31442c3SJuan Quintela         VMSTATE_INT32(scan_enabled, PS2KbdState),
1073b31442c3SJuan Quintela         VMSTATE_INT32(translate, PS2KbdState),
1074b31442c3SJuan Quintela         VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
1075b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
10767f540ab5SChristophe Fergeau     },
10775cd8cadaSJuan Quintela     .subsections = (const VMStateDescription*[]) {
10785cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
107957d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
10805cd8cadaSJuan Quintela         NULL
10810e43e99cSbellard     }
1082b31442c3SJuan Quintela };
1083b31442c3SJuan Quintela 
10842858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
10852858ab09SGonglei {
10862858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
10872858ab09SGonglei     PS2State *ps2 = &s->common;
10882858ab09SGonglei 
10892858ab09SGonglei     ps2_common_post_load(ps2);
10902858ab09SGonglei 
10912858ab09SGonglei     return 0;
10922858ab09SGonglei }
10932858ab09SGonglei 
109444b1ff31SDr. David Alan Gilbert static int ps2_mouse_pre_save(void *opaque)
10952858ab09SGonglei {
10962858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
10972858ab09SGonglei     PS2State *ps2 = &s->common;
10982858ab09SGonglei 
10992858ab09SGonglei     ps2_common_post_load(ps2);
110044b1ff31SDr. David Alan Gilbert 
110144b1ff31SDr. David Alan Gilbert     return 0;
11022858ab09SGonglei }
11032858ab09SGonglei 
1104b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_mouse = {
1105b31442c3SJuan Quintela     .name = "ps2mouse",
1106b31442c3SJuan Quintela     .version_id = 2,
1107b31442c3SJuan Quintela     .minimum_version_id = 2,
11082858ab09SGonglei     .post_load = ps2_mouse_post_load,
11092858ab09SGonglei     .pre_save = ps2_mouse_pre_save,
1110b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1111b31442c3SJuan Quintela         VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
1112b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_status, PS2MouseState),
1113b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
1114b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
1115b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
1116b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_type, PS2MouseState),
1117b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
1118b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dx, PS2MouseState),
1119b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dy, PS2MouseState),
1120b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dz, PS2MouseState),
1121b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
1122b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
1123b31442c3SJuan Quintela     }
1124b31442c3SJuan Quintela };
11250e43e99cSbellard 
112666e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
112766e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
112866e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
112966e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
113066e6536eSGerd Hoffmann };
113166e6536eSGerd Hoffmann 
11320e43e99cSbellard void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
11330e43e99cSbellard {
11347267c094SAnthony Liguori     PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState));
11350e43e99cSbellard 
11365edab03dSDon Koch     trace_ps2_kbd_init(s);
11370e43e99cSbellard     s->common.update_irq = update_irq;
11380e43e99cSbellard     s->common.update_arg = update_arg;
1139e7d93956Saurel32     s->scancode_set = 2;
11400be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
114166e6536eSGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
114266e6536eSGerd Hoffmann                                 &ps2_keyboard_handler);
1143ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_kbd_reset, s);
11440e43e99cSbellard     return s;
11450e43e99cSbellard }
11460e43e99cSbellard 
11472a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
11482a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
11492a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
11502a766d29SGerd Hoffmann     .event = ps2_mouse_event,
11512a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
11522a766d29SGerd Hoffmann };
11532a766d29SGerd Hoffmann 
11540e43e99cSbellard void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
11550e43e99cSbellard {
11567267c094SAnthony Liguori     PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState));
11570e43e99cSbellard 
11585edab03dSDon Koch     trace_ps2_mouse_init(s);
11590e43e99cSbellard     s->common.update_irq = update_irq;
11600e43e99cSbellard     s->common.update_arg = update_arg;
11610be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
11622a766d29SGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
11632a766d29SGerd Hoffmann                                 &ps2_mouse_handler);
1164ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_mouse_reset, s);
11650e43e99cSbellard     return s;
11660e43e99cSbellard }
1167