xref: /qemu/hw/input/ps2.c (revision ea247a0f3696a0ccdbc7ab818c1c15e5b53e4c31)
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"
2764bbdd13SMark Cave-Ayland #include "hw/sysbus.h"
280d09e41aSPaolo Bonzini #include "hw/input/ps2.h"
29d6454270SMarkus Armbruster #include "migration/vmstate.h"
3028ecbaeeSPaolo Bonzini #include "ui/console.h"
3166e6536eSGerd Hoffmann #include "ui/input.h"
3271e8a915SMarkus Armbruster #include "sysemu/reset.h"
3354d31236SMarkus Armbruster #include "sysemu/runstate.h"
348f84e53cSMark Cave-Ayland #include "qapi/error.h"
350e43e99cSbellard 
365edab03dSDon Koch #include "trace.h"
375edab03dSDon Koch 
380e43e99cSbellard /* Keyboard Commands */
390e43e99cSbellard #define KBD_CMD_SET_LEDS        0xED    /* Set keyboard leds */
400e43e99cSbellard #define KBD_CMD_ECHO            0xEE
41e7d93956Saurel32 #define KBD_CMD_SCANCODE        0xF0    /* Get/set scancode set */
420e43e99cSbellard #define KBD_CMD_GET_ID          0xF2    /* get keyboard ID */
430e43e99cSbellard #define KBD_CMD_SET_RATE        0xF3    /* Set typematic rate */
440e43e99cSbellard #define KBD_CMD_ENABLE          0xF4    /* Enable scanning */
450e43e99cSbellard #define KBD_CMD_RESET_DISABLE   0xF5    /* reset and disable scanning */
460e43e99cSbellard #define KBD_CMD_RESET_ENABLE    0xF6    /* reset and enable scanning */
470e43e99cSbellard #define KBD_CMD_RESET           0xFF    /* Reset */
48c56b6209SSven Schnelle #define KBD_CMD_SET_MAKE_BREAK  0xFC    /* Set Make and Break mode */
49c56b6209SSven Schnelle #define KBD_CMD_SET_TYPEMATIC   0xFA    /* Set Typematic Make and Break mode */
500e43e99cSbellard 
510e43e99cSbellard /* Keyboard Replies */
520e43e99cSbellard #define KBD_REPLY_POR       0xAA    /* Power on reset */
5335c4d671Saurel32 #define KBD_REPLY_ID        0xAB    /* Keyboard ID */
540e43e99cSbellard #define KBD_REPLY_ACK       0xFA    /* Command ACK */
550e43e99cSbellard #define KBD_REPLY_RESEND    0xFE    /* Command NACK, send the cmd again */
560e43e99cSbellard 
570e43e99cSbellard /* Mouse Commands */
580e43e99cSbellard #define AUX_SET_SCALE11     0xE6    /* Set 1:1 scaling */
590e43e99cSbellard #define AUX_SET_SCALE21     0xE7    /* Set 2:1 scaling */
600e43e99cSbellard #define AUX_SET_RES         0xE8    /* Set resolution */
610e43e99cSbellard #define AUX_GET_SCALE       0xE9    /* Get scaling factor */
620e43e99cSbellard #define AUX_SET_STREAM      0xEA    /* Set stream mode */
630e43e99cSbellard #define AUX_POLL            0xEB    /* Poll */
640e43e99cSbellard #define AUX_RESET_WRAP      0xEC    /* Reset wrap mode */
650e43e99cSbellard #define AUX_SET_WRAP        0xEE    /* Set wrap mode */
660e43e99cSbellard #define AUX_SET_REMOTE      0xF0    /* Set remote mode */
670e43e99cSbellard #define AUX_GET_TYPE        0xF2    /* Get type */
680e43e99cSbellard #define AUX_SET_SAMPLE      0xF3    /* Set sample rate */
690e43e99cSbellard #define AUX_ENABLE_DEV      0xF4    /* Enable aux device */
700e43e99cSbellard #define AUX_DISABLE_DEV     0xF5    /* Disable aux device */
710e43e99cSbellard #define AUX_SET_DEFAULT     0xF6
720e43e99cSbellard #define AUX_RESET           0xFF    /* Reset aux device */
730e43e99cSbellard #define AUX_ACK             0xFA    /* Command byte ACK. */
740e43e99cSbellard 
750e43e99cSbellard #define MOUSE_STATUS_REMOTE     0x40
760e43e99cSbellard #define MOUSE_STATUS_ENABLED    0x20
770e43e99cSbellard #define MOUSE_STATUS_SCALE21    0x10
780e43e99cSbellard 
7947db2432SVolker Rümelin #define PS2_QUEUE_SIZE      16  /* Queue size required by PS/2 protocol */
804e9bddcbSVolker Rümelin #define PS2_QUEUE_HEADROOM  8   /* Queue size for keyboard command replies */
810e43e99cSbellard 
82620775d1SDaniel P. Berrange /* Bits for 'modifiers' field in PS2KbdState */
83620775d1SDaniel P. Berrange #define MOD_CTRL_L  (1 << 0)
84620775d1SDaniel P. Berrange #define MOD_SHIFT_L (1 << 1)
85620775d1SDaniel P. Berrange #define MOD_ALT_L   (1 << 2)
86620775d1SDaniel P. Berrange #define MOD_CTRL_R  (1 << 3)
87620775d1SDaniel P. Berrange #define MOD_SHIFT_R (1 << 4)
88620775d1SDaniel P. Berrange #define MOD_ALT_R   (1 << 5)
89620775d1SDaniel P. Berrange 
9057d5c005SHervé Poussineau static uint8_t translate_table[256] = {
9157d5c005SHervé Poussineau     0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
9257d5c005SHervé Poussineau     0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
9357d5c005SHervé Poussineau     0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
9457d5c005SHervé Poussineau     0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
9557d5c005SHervé Poussineau     0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
9657d5c005SHervé Poussineau     0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
9757d5c005SHervé Poussineau     0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
9857d5c005SHervé Poussineau     0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
9957d5c005SHervé Poussineau     0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
10057d5c005SHervé Poussineau     0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
10157d5c005SHervé Poussineau     0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
10257d5c005SHervé Poussineau     0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
10357d5c005SHervé Poussineau     0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
10457d5c005SHervé Poussineau     0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
10557d5c005SHervé Poussineau     0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
10657d5c005SHervé Poussineau     0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
10757d5c005SHervé Poussineau     0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
10857d5c005SHervé Poussineau     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
10957d5c005SHervé Poussineau     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
11057d5c005SHervé Poussineau     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
11157d5c005SHervé Poussineau     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
11257d5c005SHervé Poussineau     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
11357d5c005SHervé Poussineau     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
11457d5c005SHervé Poussineau     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
11557d5c005SHervé Poussineau     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
11657d5c005SHervé Poussineau     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
11757d5c005SHervé Poussineau     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
11857d5c005SHervé Poussineau     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
11957d5c005SHervé Poussineau     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
12057d5c005SHervé Poussineau     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
12157d5c005SHervé Poussineau     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
12257d5c005SHervé Poussineau     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
12357d5c005SHervé Poussineau };
12457d5c005SHervé Poussineau 
125620775d1SDaniel P. Berrange static unsigned int ps2_modifier_bit(QKeyCode key)
126620775d1SDaniel P. Berrange {
127620775d1SDaniel P. Berrange     switch (key) {
128620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL:
129620775d1SDaniel P. Berrange         return MOD_CTRL_L;
130620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL_R:
131620775d1SDaniel P. Berrange         return MOD_CTRL_R;
132620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT:
133620775d1SDaniel P. Berrange         return MOD_SHIFT_L;
134620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT_R:
135620775d1SDaniel P. Berrange         return MOD_SHIFT_R;
136620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT:
137620775d1SDaniel P. Berrange         return MOD_ALT_L;
138620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT_R:
139620775d1SDaniel P. Berrange         return MOD_ALT_R;
140620775d1SDaniel P. Berrange     default:
141620775d1SDaniel P. Berrange         return 0;
142620775d1SDaniel P. Berrange     }
143620775d1SDaniel P. Berrange }
144620775d1SDaniel P. Berrange 
145954ee55bSGerd Hoffmann static void ps2_reset_queue(PS2State *s)
146954ee55bSGerd Hoffmann {
147954ee55bSGerd Hoffmann     PS2Queue *q = &s->queue;
148954ee55bSGerd Hoffmann 
149954ee55bSGerd Hoffmann     q->rptr = 0;
150954ee55bSGerd Hoffmann     q->wptr = 0;
1519e24b2ddSVolker Rümelin     q->cwptr = -1;
152954ee55bSGerd Hoffmann     q->count = 0;
153954ee55bSGerd Hoffmann }
154954ee55bSGerd Hoffmann 
1552a6505b0SSven Schnelle int ps2_queue_empty(PS2State *s)
1562a6505b0SSven Schnelle {
1572a6505b0SSven Schnelle     return s->queue.count == 0;
1582a6505b0SSven Schnelle }
1592a6505b0SSven Schnelle 
1607abe7eb2SGeoffrey McRae void ps2_queue_noirq(PS2State *s, int b)
1610e43e99cSbellard {
1620e43e99cSbellard     PS2Queue *q = &s->queue;
1630e43e99cSbellard 
1649e24b2ddSVolker Rümelin     if (q->count >= PS2_QUEUE_SIZE) {
1650e43e99cSbellard         return;
1667abe7eb2SGeoffrey McRae     }
1677abe7eb2SGeoffrey McRae 
1680e43e99cSbellard     q->data[q->wptr] = b;
16947db2432SVolker Rümelin     if (++q->wptr == PS2_BUFFER_SIZE) {
1700e43e99cSbellard         q->wptr = 0;
17147db2432SVolker Rümelin     }
1720e43e99cSbellard     q->count++;
1737abe7eb2SGeoffrey McRae }
1747abe7eb2SGeoffrey McRae 
1757abe7eb2SGeoffrey McRae void ps2_raise_irq(PS2State *s)
1767abe7eb2SGeoffrey McRae {
1777abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
1787abe7eb2SGeoffrey McRae }
1797abe7eb2SGeoffrey McRae 
1807abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
1817abe7eb2SGeoffrey McRae {
1827704bb02SVolker Rümelin     if (PS2_QUEUE_SIZE - s->queue.count < 1) {
1837704bb02SVolker Rümelin         return;
1847704bb02SVolker Rümelin     }
1857704bb02SVolker Rümelin 
1867abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
18796376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
1887abe7eb2SGeoffrey McRae }
1897abe7eb2SGeoffrey McRae 
1907abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
1917abe7eb2SGeoffrey McRae {
1927abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
1937abe7eb2SGeoffrey McRae         return;
1947abe7eb2SGeoffrey McRae     }
1957abe7eb2SGeoffrey McRae 
1967abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
1977abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
19896376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
1997abe7eb2SGeoffrey McRae }
2007abe7eb2SGeoffrey McRae 
2017abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2027abe7eb2SGeoffrey McRae {
2037abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2047abe7eb2SGeoffrey McRae         return;
2057abe7eb2SGeoffrey McRae     }
2067abe7eb2SGeoffrey McRae 
2077abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2087abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2097abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
21096376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2117abe7eb2SGeoffrey McRae }
2127abe7eb2SGeoffrey McRae 
2137abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2147abe7eb2SGeoffrey McRae {
2157abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2167abe7eb2SGeoffrey McRae         return;
2177abe7eb2SGeoffrey McRae     }
2187abe7eb2SGeoffrey McRae 
2197abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2207abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2217abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2227abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
22396376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2240e43e99cSbellard }
2250e43e99cSbellard 
2269e24b2ddSVolker Rümelin static void ps2_cqueue_data(PS2Queue *q, int b)
2279e24b2ddSVolker Rümelin {
2289e24b2ddSVolker Rümelin     q->data[q->cwptr] = b;
2299e24b2ddSVolker Rümelin     if (++q->cwptr >= PS2_BUFFER_SIZE) {
2309e24b2ddSVolker Rümelin         q->cwptr = 0;
2319e24b2ddSVolker Rümelin     }
2329e24b2ddSVolker Rümelin     q->count++;
2339e24b2ddSVolker Rümelin }
2349e24b2ddSVolker Rümelin 
2359e24b2ddSVolker Rümelin static void ps2_cqueue_1(PS2State *s, int b1)
2369e24b2ddSVolker Rümelin {
2379e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2389e24b2ddSVolker Rümelin 
2399e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 1) & (PS2_BUFFER_SIZE - 1);
2409e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2419e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2429e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2439e24b2ddSVolker Rümelin }
2449e24b2ddSVolker Rümelin 
2459e24b2ddSVolker Rümelin static void ps2_cqueue_2(PS2State *s, int b1, int b2)
2469e24b2ddSVolker Rümelin {
2479e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2489e24b2ddSVolker Rümelin 
2499e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 2) & (PS2_BUFFER_SIZE - 1);
2509e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2519e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2529e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
2539e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2549e24b2ddSVolker Rümelin }
2559e24b2ddSVolker Rümelin 
2569e24b2ddSVolker Rümelin static void ps2_cqueue_3(PS2State *s, int b1, int b2, int b3)
2579e24b2ddSVolker Rümelin {
2589e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2599e24b2ddSVolker Rümelin 
2609e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 3) & (PS2_BUFFER_SIZE - 1);
2619e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2629e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2639e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
2649e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b3);
2659e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2669e24b2ddSVolker Rümelin }
2679e24b2ddSVolker Rümelin 
2689e24b2ddSVolker Rümelin static void ps2_cqueue_reset(PS2State *s)
2699e24b2ddSVolker Rümelin {
2709e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2719e24b2ddSVolker Rümelin     int ccount;
2729e24b2ddSVolker Rümelin 
2739e24b2ddSVolker Rümelin     if (q->cwptr == -1) {
2749e24b2ddSVolker Rümelin         return;
2759e24b2ddSVolker Rümelin     }
2769e24b2ddSVolker Rümelin 
2779e24b2ddSVolker Rümelin     ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
2789e24b2ddSVolker Rümelin     q->count -= ccount;
2799e24b2ddSVolker Rümelin     q->rptr = q->cwptr;
2809e24b2ddSVolker Rümelin     q->cwptr = -1;
2819e24b2ddSVolker Rümelin }
2829e24b2ddSVolker Rümelin 
28357d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
2840e43e99cSbellard static void ps2_put_keycode(void *opaque, int keycode)
2850e43e99cSbellard {
286f94f5d71Spbrook     PS2KbdState *s = opaque;
2878f84e53cSMark Cave-Ayland     PS2State *ps = PS2_DEVICE(s);
288e7d93956Saurel32 
2895edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
290fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
29157d5c005SHervé Poussineau 
29257d5c005SHervé Poussineau     if (s->translate) {
29357d5c005SHervé Poussineau         if (keycode == 0xf0) {
29457d5c005SHervé Poussineau             s->need_high_bit = true;
29557d5c005SHervé Poussineau         } else if (s->need_high_bit) {
2968f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode] | 0x80);
29757d5c005SHervé Poussineau             s->need_high_bit = false;
29857d5c005SHervé Poussineau         } else {
2998f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode]);
3007096a96dSRoy Tam         }
30157d5c005SHervé Poussineau     } else {
3028f84e53cSMark Cave-Ayland         ps2_queue(ps, keycode);
3030e43e99cSbellard     }
30457d5c005SHervé Poussineau }
3050e43e99cSbellard 
30666e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
30766e6536eSGerd Hoffmann                                InputEvent *evt)
30866e6536eSGerd Hoffmann {
30966e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
31032bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
3118c10e0baSHervé Poussineau     int qcode;
312ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
313620775d1SDaniel P. Berrange     int mod;
31466e6536eSGerd Hoffmann 
315143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
316143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
317143c04c7SGeoffrey McRae         return;
318143c04c7SGeoffrey McRae     }
319143c04c7SGeoffrey McRae 
320fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
3218c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
3228c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
32357d5c005SHervé Poussineau 
324620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
325644f66bfSDaniel P. Berrangé     trace_ps2_keyboard_event(s, qcode, key->down, mod,
326644f66bfSDaniel P. Berrangé                              s->modifiers, s->scancode_set, s->translate);
327620775d1SDaniel P. Berrange     if (key->down) {
328620775d1SDaniel P. Berrange         s->modifiers |= mod;
329620775d1SDaniel P. Berrange     } else {
330620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
331620775d1SDaniel P. Berrange     }
332620775d1SDaniel P. Berrange 
3338c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3348c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
33529fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
33629fd23a5SDaniel P. Berrange                 if (key->down) {
33729fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
33829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
33929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
34029fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
34129fd23a5SDaniel P. Berrange                 }
34229fd23a5SDaniel P. Berrange             } else {
3438c10e0baSHervé Poussineau                 if (key->down) {
3448c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3458c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3468c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
347927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3488c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
3498c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
3508c10e0baSHervé Poussineau                 }
35129fd23a5SDaniel P. Berrange             }
3528c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
353620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
354620775d1SDaniel P. Berrange                 if (key->down) {
355620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
356620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
357620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
358620775d1SDaniel P. Berrange                 } else {
359620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
360620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
361620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
362620775d1SDaniel P. Berrange                 }
363620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
364620775d1SDaniel P. Berrange                 if (key->down) {
365620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
366620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
367620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
368620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
369620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
370620775d1SDaniel P. Berrange                 } else {
371620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
372620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
373620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
374620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
375620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
376620775d1SDaniel P. Berrange                 }
3778f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
3788f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
3798f63458fSDaniel P. Berrange                 if (key->down) {
3808f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3818f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
3828f63458fSDaniel P. Berrange                 } else {
3838f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3848f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
3858f63458fSDaniel P. Berrange                 }
386620775d1SDaniel P. Berrange             } else {
3878c10e0baSHervé Poussineau                 if (key->down) {
3888c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3898c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
3908c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3918c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
3928c10e0baSHervé Poussineau                 } else {
3938c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3948c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
3958c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3968c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
3978c10e0baSHervé Poussineau                 }
398620775d1SDaniel P. Berrange             }
3998c10e0baSHervé Poussineau         } else {
400545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset1_len) {
401ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
402545e5cf8SMark Cave-Ayland             }
4038c10e0baSHervé Poussineau             if (keycode) {
4048c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4058c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4068c10e0baSHervé Poussineau                 }
4078c10e0baSHervé Poussineau                 if (!key->down) {
4088c10e0baSHervé Poussineau                     keycode |= 0x80;
4098c10e0baSHervé Poussineau                 }
4108c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
4118c10e0baSHervé Poussineau             } else {
412ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
413ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
4148c10e0baSHervé Poussineau             }
4158c10e0baSHervé Poussineau         }
4168c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
4178c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
41829fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
41929fd23a5SDaniel P. Berrange                 if (key->down) {
42029fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
42129fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
42229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
42329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
42429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
42529fd23a5SDaniel P. Berrange                 }
42629fd23a5SDaniel P. Berrange             } else {
4278c10e0baSHervé Poussineau                 if (key->down) {
4288c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4298c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4308c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4318c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4328c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4338c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4348c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4358c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4368c10e0baSHervé Poussineau                 }
43729fd23a5SDaniel P. Berrange             }
4388c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
439620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
440620775d1SDaniel P. Berrange                 if (key->down) {
441620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
442620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
443620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
444620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
445620775d1SDaniel P. Berrange                 } else {
446620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
447620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
448620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
449620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
450620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
451620775d1SDaniel P. Berrange                 }
452620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
453620775d1SDaniel P. Berrange                 if (key->down) {
454620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
455620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
456620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
457620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
458620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
459620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
460620775d1SDaniel P. Berrange                 } else {
461620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
462620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
463620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
464620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
465620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
466620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
467620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
468620775d1SDaniel P. Berrange                 }
4698f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4708f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4718f63458fSDaniel P. Berrange                 if (key->down) {
4728f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4738f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4748f63458fSDaniel P. Berrange                 } else {
4758f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4768f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
4778f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4788f63458fSDaniel P. Berrange                 }
479620775d1SDaniel P. Berrange             } else {
4808c10e0baSHervé Poussineau                 if (key->down) {
4818c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4828c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4838c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4848c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4858c10e0baSHervé Poussineau                 } else {
4868c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4878c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4888c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4898c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4908c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4918c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4928c10e0baSHervé Poussineau                 }
493620775d1SDaniel P. Berrange             }
4948c10e0baSHervé Poussineau         } else {
495545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset2_len) {
496ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
497545e5cf8SMark Cave-Ayland             }
4988c10e0baSHervé Poussineau             if (keycode) {
4998c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
5008c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
5018c10e0baSHervé Poussineau                 }
5028c10e0baSHervé Poussineau                 if (!key->down) {
5038c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5048c10e0baSHervé Poussineau                 }
5058c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
50657d5c005SHervé Poussineau             } else {
507ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
508ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
50957d5c005SHervé Poussineau             }
51057d5c005SHervé Poussineau         }
51157d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
512545e5cf8SMark Cave-Ayland         if (qcode < qemu_input_map_qcode_to_atset3_len) {
513ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
514545e5cf8SMark Cave-Ayland         }
5158c10e0baSHervé Poussineau         if (keycode) {
5168c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
5178c10e0baSHervé Poussineau             if (!key->down) {
5188c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
51957d5c005SHervé Poussineau             }
52057d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
5218c10e0baSHervé Poussineau         } else {
522ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
523ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
5248c10e0baSHervé Poussineau         }
52566e6536eSGerd Hoffmann     }
52666e6536eSGerd Hoffmann }
52766e6536eSGerd Hoffmann 
5288498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
5290e43e99cSbellard {
5300e43e99cSbellard     PS2Queue *q;
5310e43e99cSbellard     int val, index;
5320e43e99cSbellard 
5338498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
5340e43e99cSbellard     q = &s->queue;
5350e43e99cSbellard     if (q->count == 0) {
536545e5cf8SMark Cave-Ayland         /*
537545e5cf8SMark Cave-Ayland          * NOTE: if no data left, we return the last keyboard one
538545e5cf8SMark Cave-Ayland          * (needed for EMM386)
539545e5cf8SMark Cave-Ayland          */
5400e43e99cSbellard         /* XXX: need a timer to do things correctly */
5410e43e99cSbellard         index = q->rptr - 1;
54247db2432SVolker Rümelin         if (index < 0) {
54347db2432SVolker Rümelin             index = PS2_BUFFER_SIZE - 1;
54447db2432SVolker Rümelin         }
5450e43e99cSbellard         val = q->data[index];
5460e43e99cSbellard     } else {
5470e43e99cSbellard         val = q->data[q->rptr];
54847db2432SVolker Rümelin         if (++q->rptr == PS2_BUFFER_SIZE) {
5490e43e99cSbellard             q->rptr = 0;
55047db2432SVolker Rümelin         }
5510e43e99cSbellard         q->count--;
5529e24b2ddSVolker Rümelin         if (q->rptr == q->cwptr) {
5539e24b2ddSVolker Rümelin             /* command reply queue is empty */
5549e24b2ddSVolker Rümelin             q->cwptr = -1;
5559e24b2ddSVolker Rümelin         }
5560e43e99cSbellard         /* reading deasserts IRQ */
5570e43e99cSbellard         s->update_irq(s->update_arg, 0);
5580e43e99cSbellard         /* reassert IRQs if data left */
559cec32524SVolker Rümelin         if (q->count) {
560cec32524SVolker Rümelin             s->update_irq(s->update_arg, 1);
561cec32524SVolker Rümelin         }
5620e43e99cSbellard     }
5630e43e99cSbellard     return val;
5640e43e99cSbellard }
5650e43e99cSbellard 
5667f540ab5SChristophe Fergeau static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
5677f540ab5SChristophe Fergeau {
5685edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
5697f540ab5SChristophe Fergeau     s->ledstate = ledstate;
5707f540ab5SChristophe Fergeau     kbd_put_ledstate(ledstate);
5717f540ab5SChristophe Fergeau }
5727f540ab5SChristophe Fergeau 
5730e43e99cSbellard static void ps2_reset_keyboard(PS2KbdState *s)
5740e43e99cSbellard {
5758f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
5768f84e53cSMark Cave-Ayland 
5775edab03dSDon Koch     trace_ps2_reset_keyboard(s);
5780e43e99cSbellard     s->scan_enabled = 1;
579e7d93956Saurel32     s->scancode_set = 2;
5808f84e53cSMark Cave-Ayland     ps2_reset_queue(ps2);
5817f540ab5SChristophe Fergeau     ps2_set_ledstate(s, 0);
5820e43e99cSbellard }
5830e43e99cSbellard 
58454334e73SMark Cave-Ayland void ps2_write_keyboard(PS2KbdState *s, int val)
5850e43e99cSbellard {
5868f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
5870e43e99cSbellard 
58854334e73SMark Cave-Ayland     trace_ps2_write_keyboard(s, val);
5898f84e53cSMark Cave-Ayland     ps2_cqueue_reset(ps2);
5908f84e53cSMark Cave-Ayland     switch (ps2->write_cmd) {
5910e43e99cSbellard     default:
5920e43e99cSbellard     case -1:
5930e43e99cSbellard         switch (val) {
5940e43e99cSbellard         case 0x00:
5958f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
5960e43e99cSbellard             break;
5970e43e99cSbellard         case 0x05:
5988f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
5990e43e99cSbellard             break;
6000e43e99cSbellard         case KBD_CMD_GET_ID:
601e7d93956Saurel32             /* We emulate a MF2 AT keyboard here */
6028f84e53cSMark Cave-Ayland             ps2_cqueue_3(ps2, KBD_REPLY_ACK, KBD_REPLY_ID,
6039e24b2ddSVolker Rümelin                          s->translate ? 0x41 : 0x83);
6040e43e99cSbellard             break;
6050e43e99cSbellard         case KBD_CMD_ECHO:
6068f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_CMD_ECHO);
6070e43e99cSbellard             break;
6080e43e99cSbellard         case KBD_CMD_ENABLE:
6090e43e99cSbellard             s->scan_enabled = 1;
6108f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6110e43e99cSbellard             break;
612e7d93956Saurel32         case KBD_CMD_SCANCODE:
6130e43e99cSbellard         case KBD_CMD_SET_LEDS:
6140e43e99cSbellard         case KBD_CMD_SET_RATE:
615c56b6209SSven Schnelle         case KBD_CMD_SET_MAKE_BREAK:
6168f84e53cSMark Cave-Ayland             ps2->write_cmd = val;
6178f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6180e43e99cSbellard             break;
6190e43e99cSbellard         case KBD_CMD_RESET_DISABLE:
6200e43e99cSbellard             ps2_reset_keyboard(s);
6210e43e99cSbellard             s->scan_enabled = 0;
6228f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6230e43e99cSbellard             break;
6240e43e99cSbellard         case KBD_CMD_RESET_ENABLE:
6250e43e99cSbellard             ps2_reset_keyboard(s);
6260e43e99cSbellard             s->scan_enabled = 1;
6278f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6280e43e99cSbellard             break;
6290e43e99cSbellard         case KBD_CMD_RESET:
6300e43e99cSbellard             ps2_reset_keyboard(s);
6318f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2,
6327abe7eb2SGeoffrey McRae                          KBD_REPLY_ACK,
6337abe7eb2SGeoffrey McRae                          KBD_REPLY_POR);
6340e43e99cSbellard             break;
635c56b6209SSven Schnelle         case KBD_CMD_SET_TYPEMATIC:
6368f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
637c56b6209SSven Schnelle             break;
6380e43e99cSbellard         default:
6398f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
6400e43e99cSbellard             break;
6410e43e99cSbellard         }
6420e43e99cSbellard         break;
643c56b6209SSven Schnelle     case KBD_CMD_SET_MAKE_BREAK:
6448f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6458f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
646c56b6209SSven Schnelle         break;
647e7d93956Saurel32     case KBD_CMD_SCANCODE:
648e7d93956Saurel32         if (val == 0) {
6498f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2, KBD_REPLY_ACK, s->translate ?
6509e24b2ddSVolker Rümelin                 translate_table[s->scancode_set] : s->scancode_set);
6514df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
652e7d93956Saurel32             s->scancode_set = val;
6538f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6544df23b64SHervé Poussineau         } else {
6558f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
656e7d93956Saurel32         }
6578f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
658e7d93956Saurel32         break;
6590e43e99cSbellard     case KBD_CMD_SET_LEDS:
6607f540ab5SChristophe Fergeau         ps2_set_ledstate(s, val);
6618f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6628f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
6630e43e99cSbellard         break;
6640e43e99cSbellard     case KBD_CMD_SET_RATE:
6658f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6668f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
6670e43e99cSbellard         break;
6680e43e99cSbellard     }
6690e43e99cSbellard }
6700e43e99cSbellard 
671545e5cf8SMark Cave-Ayland /*
672545e5cf8SMark Cave-Ayland  * Set the scancode translation mode.
673545e5cf8SMark Cave-Ayland  * 0 = raw scancodes.
674545e5cf8SMark Cave-Ayland  * 1 = translated scancodes (used by qemu internally).
675545e5cf8SMark Cave-Ayland  */
676f94f5d71Spbrook 
67754334e73SMark Cave-Ayland void ps2_keyboard_set_translation(PS2KbdState *s, int mode)
678f94f5d71Spbrook {
67954334e73SMark Cave-Ayland     trace_ps2_keyboard_set_translation(s, mode);
680f94f5d71Spbrook     s->translate = mode;
681f94f5d71Spbrook }
682f94f5d71Spbrook 
6837abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
6840e43e99cSbellard {
6852d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
68676968101SVolker Rümelin     /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
68776968101SVolker Rümelin     const int needed = s->mouse_type ? 4 : 3;
6880e43e99cSbellard     unsigned int b;
68964ebbb7dSDmitry Petrov     int dx1, dy1, dz1, dw1;
6900e43e99cSbellard 
6912d135409SMark Cave-Ayland     if (PS2_QUEUE_SIZE - ps2->queue.count < needed) {
6927abe7eb2SGeoffrey McRae         return 0;
6937abe7eb2SGeoffrey McRae     }
6947abe7eb2SGeoffrey McRae 
6950e43e99cSbellard     dx1 = s->mouse_dx;
6960e43e99cSbellard     dy1 = s->mouse_dy;
6970e43e99cSbellard     dz1 = s->mouse_dz;
69864ebbb7dSDmitry Petrov     dw1 = s->mouse_dw;
6990e43e99cSbellard     /* XXX: increase range to 8 bits ? */
700545e5cf8SMark Cave-Ayland     if (dx1 > 127) {
7010e43e99cSbellard         dx1 = 127;
702545e5cf8SMark Cave-Ayland     } else if (dx1 < -127) {
7030e43e99cSbellard         dx1 = -127;
704545e5cf8SMark Cave-Ayland     }
705545e5cf8SMark Cave-Ayland     if (dy1 > 127) {
7060e43e99cSbellard         dy1 = 127;
707545e5cf8SMark Cave-Ayland     } else if (dy1 < -127) {
7080e43e99cSbellard         dy1 = -127;
709545e5cf8SMark Cave-Ayland     }
7100e43e99cSbellard     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
7112d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, b);
7122d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, dx1 & 0xff);
7132d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, dy1 & 0xff);
7140e43e99cSbellard     /* extra byte for IMPS/2 or IMEX */
7150e43e99cSbellard     switch (s->mouse_type) {
7160e43e99cSbellard     default:
71764ebbb7dSDmitry Petrov         /* Just ignore the wheels if not supported */
71864ebbb7dSDmitry Petrov         s->mouse_dz = 0;
71964ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7200e43e99cSbellard         break;
7210e43e99cSbellard     case 3:
722545e5cf8SMark Cave-Ayland         if (dz1 > 127) {
7230e43e99cSbellard             dz1 = 127;
724545e5cf8SMark Cave-Ayland         } else if (dz1 < -127) {
7250e43e99cSbellard             dz1 = -127;
726545e5cf8SMark Cave-Ayland         }
7272d135409SMark Cave-Ayland         ps2_queue_noirq(ps2, dz1 & 0xff);
72864ebbb7dSDmitry Petrov         s->mouse_dz -= dz1;
72964ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7300e43e99cSbellard         break;
7310e43e99cSbellard     case 4:
73264ebbb7dSDmitry Petrov         /*
73364ebbb7dSDmitry Petrov          * This matches what the Linux kernel expects for exps/2 in
73464ebbb7dSDmitry Petrov          * drivers/input/mouse/psmouse-base.c. Note, if you happen to
73564ebbb7dSDmitry Petrov          * press/release the 4th or 5th buttons at the same moment as a
73664ebbb7dSDmitry Petrov          * horizontal wheel scroll, those button presses will get lost. I'm not
73764ebbb7dSDmitry Petrov          * sure what to do about that, since by this point we don't know
73864ebbb7dSDmitry Petrov          * whether those buttons actually changed state.
73964ebbb7dSDmitry Petrov          */
74064ebbb7dSDmitry Petrov         if (dw1 != 0) {
74164ebbb7dSDmitry Petrov             if (dw1 > 31) {
74264ebbb7dSDmitry Petrov                 dw1 = 31;
74364ebbb7dSDmitry Petrov             } else if (dw1 < -31) {
74464ebbb7dSDmitry Petrov                 dw1 = -31;
74564ebbb7dSDmitry Petrov             }
74664ebbb7dSDmitry Petrov 
74764ebbb7dSDmitry Petrov             /*
74864ebbb7dSDmitry Petrov              * linux kernel expects first 6 bits to represent the value
74964ebbb7dSDmitry Petrov              * for horizontal scroll
75064ebbb7dSDmitry Petrov              */
75164ebbb7dSDmitry Petrov             b = (dw1 & 0x3f) | 0x40;
75264ebbb7dSDmitry Petrov             s->mouse_dw -= dw1;
75364ebbb7dSDmitry Petrov         } else {
75464ebbb7dSDmitry Petrov             if (dz1 > 7) {
7550e43e99cSbellard                 dz1 = 7;
75664ebbb7dSDmitry Petrov             } else if (dz1 < -7) {
7570e43e99cSbellard                 dz1 = -7;
75864ebbb7dSDmitry Petrov             }
75964ebbb7dSDmitry Petrov 
7600e43e99cSbellard             b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
76164ebbb7dSDmitry Petrov             s->mouse_dz -= dz1;
76264ebbb7dSDmitry Petrov         }
7632d135409SMark Cave-Ayland         ps2_queue_noirq(ps2, b);
7640e43e99cSbellard         break;
7650e43e99cSbellard     }
7660e43e99cSbellard 
7672d135409SMark Cave-Ayland     ps2_raise_irq(ps2);
7687abe7eb2SGeoffrey McRae 
7695edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
7700e43e99cSbellard     /* update deltas */
7710e43e99cSbellard     s->mouse_dx -= dx1;
7720e43e99cSbellard     s->mouse_dy -= dy1;
7737abe7eb2SGeoffrey McRae 
7747abe7eb2SGeoffrey McRae     return 1;
7750e43e99cSbellard }
7760e43e99cSbellard 
7772a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
7782a766d29SGerd Hoffmann                             InputEvent *evt)
7790e43e99cSbellard {
7807fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
7818b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
7828b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
7838b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
7848b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
7858b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
7862a766d29SGerd Hoffmann     };
7872a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
788b5a1b443SEric Blake     InputMoveEvent *move;
789b5a1b443SEric Blake     InputBtnEvent *btn;
7900e43e99cSbellard 
7910e43e99cSbellard     /* check if deltas are recorded when disabled */
792545e5cf8SMark Cave-Ayland     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
7930e43e99cSbellard         return;
794545e5cf8SMark Cave-Ayland     }
7950e43e99cSbellard 
796568c73a4SEric Blake     switch (evt->type) {
7972a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
79832bafa8fSEric Blake         move = evt->u.rel.data;
799b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
800b5a1b443SEric Blake             s->mouse_dx += move->value;
801b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
802b5a1b443SEric Blake             s->mouse_dy -= move->value;
8032a766d29SGerd Hoffmann         }
8042a766d29SGerd Hoffmann         break;
8050e43e99cSbellard 
8062a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
80732bafa8fSEric Blake         btn = evt->u.btn.data;
808b5a1b443SEric Blake         if (btn->down) {
809b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
810b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
8112a766d29SGerd Hoffmann                 s->mouse_dz--;
812b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
8132a766d29SGerd Hoffmann                 s->mouse_dz++;
8142a766d29SGerd Hoffmann             }
81564ebbb7dSDmitry Petrov 
81664ebbb7dSDmitry Petrov             if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
81764ebbb7dSDmitry Petrov                 s->mouse_dw--;
81864ebbb7dSDmitry Petrov             } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
81964ebbb7dSDmitry Petrov                 s->mouse_dw++;
82064ebbb7dSDmitry Petrov             }
8212a766d29SGerd Hoffmann         } else {
822b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
8232a766d29SGerd Hoffmann         }
8242a766d29SGerd Hoffmann         break;
8252a766d29SGerd Hoffmann 
8262a766d29SGerd Hoffmann     default:
8272a766d29SGerd Hoffmann         /* keep gcc happy */
8282a766d29SGerd Hoffmann         break;
8292a766d29SGerd Hoffmann     }
830fd214d18SGerd Hoffmann }
831fd214d18SGerd Hoffmann 
8322a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
8332a766d29SGerd Hoffmann {
8342a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
8352a766d29SGerd Hoffmann 
836143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
837143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
838143c04c7SGeoffrey McRae         return;
839143c04c7SGeoffrey McRae     }
840143c04c7SGeoffrey McRae 
8412a766d29SGerd Hoffmann     if (s->mouse_buttons) {
842fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
8432a766d29SGerd Hoffmann     }
8442858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
845545e5cf8SMark Cave-Ayland         /*
846545e5cf8SMark Cave-Ayland          * if not remote, send event. Multiple events are sent if
847545e5cf8SMark Cave-Ayland          * too big deltas
848545e5cf8SMark Cave-Ayland          */
8497abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
85064ebbb7dSDmitry Petrov             if (s->mouse_dx == 0 && s->mouse_dy == 0
85164ebbb7dSDmitry Petrov                     && s->mouse_dz == 0 && s->mouse_dw == 0) {
8520e43e99cSbellard                 break;
8530e43e99cSbellard             }
8540e43e99cSbellard         }
8550e43e99cSbellard     }
85664ebbb7dSDmitry Petrov }
8570e43e99cSbellard 
85854334e73SMark Cave-Ayland void ps2_mouse_fake_event(PS2MouseState *s)
859548df2acSths {
86054334e73SMark Cave-Ayland     trace_ps2_mouse_fake_event(s);
8612a766d29SGerd Hoffmann     s->mouse_dx++;
86254334e73SMark Cave-Ayland     ps2_mouse_sync(DEVICE(s));
863548df2acSths }
864548df2acSths 
86554334e73SMark Cave-Ayland void ps2_write_mouse(PS2MouseState *s, int val)
8660e43e99cSbellard {
8672d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
8685edab03dSDon Koch 
86954334e73SMark Cave-Ayland     trace_ps2_write_mouse(s, val);
8702d135409SMark Cave-Ayland     switch (ps2->write_cmd) {
8710e43e99cSbellard     default:
8720e43e99cSbellard     case -1:
8730e43e99cSbellard         /* mouse command */
8740e43e99cSbellard         if (s->mouse_wrap) {
8750e43e99cSbellard             if (val == AUX_RESET_WRAP) {
8760e43e99cSbellard                 s->mouse_wrap = 0;
8772d135409SMark Cave-Ayland                 ps2_queue(ps2, AUX_ACK);
8780e43e99cSbellard                 return;
8790e43e99cSbellard             } else if (val != AUX_RESET) {
8802d135409SMark Cave-Ayland                 ps2_queue(ps2, val);
8810e43e99cSbellard                 return;
8820e43e99cSbellard             }
8830e43e99cSbellard         }
8840e43e99cSbellard         switch (val) {
8850e43e99cSbellard         case AUX_SET_SCALE11:
8860e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
8872d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
8880e43e99cSbellard             break;
8890e43e99cSbellard         case AUX_SET_SCALE21:
8900e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_SCALE21;
8912d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
8920e43e99cSbellard             break;
8930e43e99cSbellard         case AUX_SET_STREAM:
8940e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
8952d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
8960e43e99cSbellard             break;
8970e43e99cSbellard         case AUX_SET_WRAP:
8980e43e99cSbellard             s->mouse_wrap = 1;
8992d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9000e43e99cSbellard             break;
9010e43e99cSbellard         case AUX_SET_REMOTE:
9020e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_REMOTE;
9032d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9040e43e99cSbellard             break;
9050e43e99cSbellard         case AUX_GET_TYPE:
9062d135409SMark Cave-Ayland             ps2_queue_2(ps2,
9077abe7eb2SGeoffrey McRae                 AUX_ACK,
9087abe7eb2SGeoffrey McRae                 s->mouse_type);
9090e43e99cSbellard             break;
9100e43e99cSbellard         case AUX_SET_RES:
9110e43e99cSbellard         case AUX_SET_SAMPLE:
9122d135409SMark Cave-Ayland             ps2->write_cmd = val;
9132d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9140e43e99cSbellard             break;
9150e43e99cSbellard         case AUX_GET_SCALE:
9162d135409SMark Cave-Ayland             ps2_queue_4(ps2,
9177abe7eb2SGeoffrey McRae                 AUX_ACK,
9187abe7eb2SGeoffrey McRae                 s->mouse_status,
9197abe7eb2SGeoffrey McRae                 s->mouse_resolution,
9207abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
9210e43e99cSbellard             break;
9220e43e99cSbellard         case AUX_POLL:
9232d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9240e43e99cSbellard             ps2_mouse_send_packet(s);
9250e43e99cSbellard             break;
9260e43e99cSbellard         case AUX_ENABLE_DEV:
9270e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_ENABLED;
9282d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9290e43e99cSbellard             break;
9300e43e99cSbellard         case AUX_DISABLE_DEV:
9310e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
9322d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9330e43e99cSbellard             break;
9340e43e99cSbellard         case AUX_SET_DEFAULT:
9350e43e99cSbellard             s->mouse_sample_rate = 100;
9360e43e99cSbellard             s->mouse_resolution = 2;
9370e43e99cSbellard             s->mouse_status = 0;
9382d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9390e43e99cSbellard             break;
9400e43e99cSbellard         case AUX_RESET:
9410e43e99cSbellard             s->mouse_sample_rate = 100;
9420e43e99cSbellard             s->mouse_resolution = 2;
9430e43e99cSbellard             s->mouse_status = 0;
9440e43e99cSbellard             s->mouse_type = 0;
9452d135409SMark Cave-Ayland             ps2_reset_queue(ps2);
9462d135409SMark Cave-Ayland             ps2_queue_3(ps2,
9477abe7eb2SGeoffrey McRae                 AUX_ACK,
9487abe7eb2SGeoffrey McRae                 0xaa,
9497abe7eb2SGeoffrey McRae                 s->mouse_type);
9500e43e99cSbellard             break;
9510e43e99cSbellard         default:
9520e43e99cSbellard             break;
9530e43e99cSbellard         }
9540e43e99cSbellard         break;
9550e43e99cSbellard     case AUX_SET_SAMPLE:
9560e43e99cSbellard         s->mouse_sample_rate = val;
9570e43e99cSbellard         /* detect IMPS/2 or IMEX */
9580e43e99cSbellard         switch (s->mouse_detect_state) {
9590e43e99cSbellard         default:
9600e43e99cSbellard         case 0:
961545e5cf8SMark Cave-Ayland             if (val == 200) {
9620e43e99cSbellard                 s->mouse_detect_state = 1;
963545e5cf8SMark Cave-Ayland             }
9640e43e99cSbellard             break;
9650e43e99cSbellard         case 1:
966545e5cf8SMark Cave-Ayland             if (val == 100) {
9670e43e99cSbellard                 s->mouse_detect_state = 2;
968545e5cf8SMark Cave-Ayland             } else if (val == 200) {
9690e43e99cSbellard                 s->mouse_detect_state = 3;
970545e5cf8SMark Cave-Ayland             } else {
9710e43e99cSbellard                 s->mouse_detect_state = 0;
972545e5cf8SMark Cave-Ayland             }
9730e43e99cSbellard             break;
9740e43e99cSbellard         case 2:
975545e5cf8SMark Cave-Ayland             if (val == 80) {
9760e43e99cSbellard                 s->mouse_type = 3; /* IMPS/2 */
977545e5cf8SMark Cave-Ayland             }
9780e43e99cSbellard             s->mouse_detect_state = 0;
9790e43e99cSbellard             break;
9800e43e99cSbellard         case 3:
981545e5cf8SMark Cave-Ayland             if (val == 80) {
9820e43e99cSbellard                 s->mouse_type = 4; /* IMEX */
983545e5cf8SMark Cave-Ayland             }
9840e43e99cSbellard             s->mouse_detect_state = 0;
9850e43e99cSbellard             break;
9860e43e99cSbellard         }
9872d135409SMark Cave-Ayland         ps2_queue(ps2, AUX_ACK);
9882d135409SMark Cave-Ayland         ps2->write_cmd = -1;
9890e43e99cSbellard         break;
9900e43e99cSbellard     case AUX_SET_RES:
9910e43e99cSbellard         s->mouse_resolution = val;
9922d135409SMark Cave-Ayland         ps2_queue(ps2, AUX_ACK);
9932d135409SMark Cave-Ayland         ps2->write_cmd = -1;
9940e43e99cSbellard         break;
9950e43e99cSbellard     }
9960e43e99cSbellard }
9970e43e99cSbellard 
998108cb22eSMark Cave-Ayland static void ps2_reset(DeviceState *dev)
9990e43e99cSbellard {
1000108cb22eSMark Cave-Ayland     PS2State *s = PS2_DEVICE(dev);
1001108cb22eSMark Cave-Ayland 
10020e43e99cSbellard     s->write_cmd = -1;
1003954ee55bSGerd Hoffmann     ps2_reset_queue(s);
1004deeccef3Saliguori     s->update_irq(s->update_arg, 0);
10050e43e99cSbellard }
10060e43e99cSbellard 
10072858ab09SGonglei static void ps2_common_post_load(PS2State *s)
10082858ab09SGonglei {
10092858ab09SGonglei     PS2Queue *q = &s->queue;
10104e9bddcbSVolker Rümelin     int ccount = 0;
10112858ab09SGonglei 
10124e9bddcbSVolker Rümelin     /* limit the number of queued command replies to PS2_QUEUE_HEADROOM */
10134e9bddcbSVolker Rümelin     if (q->cwptr != -1) {
10144e9bddcbSVolker Rümelin         ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
10154e9bddcbSVolker Rümelin         if (ccount > PS2_QUEUE_HEADROOM) {
10164e9bddcbSVolker Rümelin             ccount = PS2_QUEUE_HEADROOM;
10174e9bddcbSVolker Rümelin         }
1018a1f2ed2aSPavel Dovgalyuk     }
10192858ab09SGonglei 
10204e9bddcbSVolker Rümelin     /* limit the scancode queue size to PS2_QUEUE_SIZE */
10214e9bddcbSVolker Rümelin     if (q->count < ccount) {
10224e9bddcbSVolker Rümelin         q->count = ccount;
10234e9bddcbSVolker Rümelin     } else if (q->count > ccount + PS2_QUEUE_SIZE) {
10244e9bddcbSVolker Rümelin         q->count = ccount + PS2_QUEUE_SIZE;
10254e9bddcbSVolker Rümelin     }
10264e9bddcbSVolker Rümelin 
10274e9bddcbSVolker Rümelin     /* sanitize rptr and recalculate wptr and cwptr */
102847db2432SVolker Rümelin     q->rptr = q->rptr & (PS2_BUFFER_SIZE - 1);
102947db2432SVolker Rümelin     q->wptr = (q->rptr + q->count) & (PS2_BUFFER_SIZE - 1);
10304e9bddcbSVolker Rümelin     q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1;
10312858ab09SGonglei }
10322858ab09SGonglei 
1033108cb22eSMark Cave-Ayland static void ps2_kbd_reset(DeviceState *dev)
1034ef74679aSDinesh Subhraveti {
1035108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(dev);
1036108cb22eSMark Cave-Ayland     PS2KbdState *s = PS2_KBD_DEVICE(dev);
1037ef74679aSDinesh Subhraveti 
1038108cb22eSMark Cave-Ayland     trace_ps2_kbd_reset(s);
1039108cb22eSMark Cave-Ayland     ps2dc->parent_reset(dev);
1040108cb22eSMark Cave-Ayland 
1041d2e550a8SHervé Poussineau     s->scan_enabled = 1;
1042ef74679aSDinesh Subhraveti     s->translate = 0;
1043089adafdSHervé Poussineau     s->scancode_set = 2;
1044620775d1SDaniel P. Berrange     s->modifiers = 0;
1045ef74679aSDinesh Subhraveti }
1046ef74679aSDinesh Subhraveti 
1047108cb22eSMark Cave-Ayland static void ps2_mouse_reset(DeviceState *dev)
1048ef74679aSDinesh Subhraveti {
1049108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(dev);
1050108cb22eSMark Cave-Ayland     PS2MouseState *s = PS2_MOUSE_DEVICE(dev);
1051ef74679aSDinesh Subhraveti 
1052108cb22eSMark Cave-Ayland     trace_ps2_mouse_reset(s);
1053108cb22eSMark Cave-Ayland     ps2dc->parent_reset(dev);
1054108cb22eSMark Cave-Ayland 
1055ef74679aSDinesh Subhraveti     s->mouse_status = 0;
1056ef74679aSDinesh Subhraveti     s->mouse_resolution = 0;
1057ef74679aSDinesh Subhraveti     s->mouse_sample_rate = 0;
1058ef74679aSDinesh Subhraveti     s->mouse_wrap = 0;
1059ef74679aSDinesh Subhraveti     s->mouse_type = 0;
1060ef74679aSDinesh Subhraveti     s->mouse_detect_state = 0;
1061ef74679aSDinesh Subhraveti     s->mouse_dx = 0;
1062ef74679aSDinesh Subhraveti     s->mouse_dy = 0;
1063ef74679aSDinesh Subhraveti     s->mouse_dz = 0;
106464ebbb7dSDmitry Petrov     s->mouse_dw = 0;
1065ef74679aSDinesh Subhraveti     s->mouse_buttons = 0;
1066ef74679aSDinesh Subhraveti }
1067ef74679aSDinesh Subhraveti 
1068b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_common = {
1069b31442c3SJuan Quintela     .name = "PS2 Common State",
1070b31442c3SJuan Quintela     .version_id = 3,
1071b31442c3SJuan Quintela     .minimum_version_id = 2,
1072b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1073b31442c3SJuan Quintela         VMSTATE_INT32(write_cmd, PS2State),
1074b31442c3SJuan Quintela         VMSTATE_INT32(queue.rptr, PS2State),
1075b31442c3SJuan Quintela         VMSTATE_INT32(queue.wptr, PS2State),
1076b31442c3SJuan Quintela         VMSTATE_INT32(queue.count, PS2State),
1077b31442c3SJuan Quintela         VMSTATE_BUFFER(queue.data, PS2State),
1078b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
10797783e9f0Spbrook     }
1080b31442c3SJuan Quintela };
10817783e9f0Spbrook 
10827f540ab5SChristophe Fergeau static bool ps2_keyboard_ledstate_needed(void *opaque)
10837f540ab5SChristophe Fergeau {
10847f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
10857f540ab5SChristophe Fergeau 
10867f540ab5SChristophe Fergeau     return s->ledstate != 0; /* 0 is default state */
10877f540ab5SChristophe Fergeau }
10887f540ab5SChristophe Fergeau 
10897f540ab5SChristophe Fergeau static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
10907f540ab5SChristophe Fergeau {
10917f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
10927f540ab5SChristophe Fergeau 
10937f540ab5SChristophe Fergeau     kbd_put_ledstate(s->ledstate);
10947f540ab5SChristophe Fergeau     return 0;
10957f540ab5SChristophe Fergeau }
10967f540ab5SChristophe Fergeau 
10977f540ab5SChristophe Fergeau static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
10987f540ab5SChristophe Fergeau     .name = "ps2kbd/ledstate",
10997f540ab5SChristophe Fergeau     .version_id = 3,
11007f540ab5SChristophe Fergeau     .minimum_version_id = 2,
11017f540ab5SChristophe Fergeau     .post_load = ps2_kbd_ledstate_post_load,
11025cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
11037f540ab5SChristophe Fergeau     .fields = (VMStateField[]) {
11047f540ab5SChristophe Fergeau         VMSTATE_INT32(ledstate, PS2KbdState),
11057f540ab5SChristophe Fergeau         VMSTATE_END_OF_LIST()
11067f540ab5SChristophe Fergeau     }
11077f540ab5SChristophe Fergeau };
11087f540ab5SChristophe Fergeau 
110957d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
111057d5c005SHervé Poussineau {
111157d5c005SHervé Poussineau     PS2KbdState *s = opaque;
111257d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
111357d5c005SHervé Poussineau }
111457d5c005SHervé Poussineau 
111557d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
111657d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
111757d5c005SHervé Poussineau     .version_id = 1,
111857d5c005SHervé Poussineau     .minimum_version_id = 1,
111957d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
112057d5c005SHervé Poussineau     .fields = (VMStateField[]) {
112157d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
112257d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
112357d5c005SHervé Poussineau     }
112457d5c005SHervé Poussineau };
112557d5c005SHervé Poussineau 
11264e9bddcbSVolker Rümelin static bool ps2_keyboard_cqueue_needed(void *opaque)
11274e9bddcbSVolker Rümelin {
11284e9bddcbSVolker Rümelin     PS2KbdState *s = opaque;
11298f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11304e9bddcbSVolker Rümelin 
11318f84e53cSMark Cave-Ayland     return ps2->queue.cwptr != -1; /* the queue is mostly empty */
11324e9bddcbSVolker Rümelin }
11334e9bddcbSVolker Rümelin 
11344e9bddcbSVolker Rümelin static const VMStateDescription vmstate_ps2_keyboard_cqueue = {
11354e9bddcbSVolker Rümelin     .name = "ps2kbd/command_reply_queue",
11364e9bddcbSVolker Rümelin     .needed = ps2_keyboard_cqueue_needed,
11374e9bddcbSVolker Rümelin     .fields = (VMStateField[]) {
11388f84e53cSMark Cave-Ayland         VMSTATE_INT32(parent_obj.queue.cwptr, PS2KbdState),
11394e9bddcbSVolker Rümelin         VMSTATE_END_OF_LIST()
11404e9bddcbSVolker Rümelin     }
11414e9bddcbSVolker Rümelin };
11424e9bddcbSVolker Rümelin 
1143db596c53SJuan Quintela static int ps2_kbd_post_load(void *opaque, int version_id)
11440e43e99cSbellard {
11450e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
11468f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11470e43e99cSbellard 
1148545e5cf8SMark Cave-Ayland     if (version_id == 2) {
1149e7d93956Saurel32         s->scancode_set = 2;
1150545e5cf8SMark Cave-Ayland     }
11512858ab09SGonglei 
11522858ab09SGonglei     ps2_common_post_load(ps2);
11532858ab09SGonglei 
11540e43e99cSbellard     return 0;
11550e43e99cSbellard }
11560e43e99cSbellard 
1157b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_keyboard = {
1158b31442c3SJuan Quintela     .name = "ps2kbd",
1159b31442c3SJuan Quintela     .version_id = 3,
1160db596c53SJuan Quintela     .minimum_version_id = 2,
1161db596c53SJuan Quintela     .post_load = ps2_kbd_post_load,
1162b31442c3SJuan Quintela     .fields = (VMStateField[]) {
11638f84e53cSMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2KbdState, 0, vmstate_ps2_common,
11648f84e53cSMark Cave-Ayland                        PS2State),
1165b31442c3SJuan Quintela         VMSTATE_INT32(scan_enabled, PS2KbdState),
1166b31442c3SJuan Quintela         VMSTATE_INT32(translate, PS2KbdState),
1167b31442c3SJuan Quintela         VMSTATE_INT32_V(scancode_set, PS2KbdState, 3),
1168b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
11697f540ab5SChristophe Fergeau     },
11705cd8cadaSJuan Quintela     .subsections = (const VMStateDescription * []) {
11715cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
117257d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
11734e9bddcbSVolker Rümelin         &vmstate_ps2_keyboard_cqueue,
11745cd8cadaSJuan Quintela         NULL
11750e43e99cSbellard     }
1176b31442c3SJuan Quintela };
1177b31442c3SJuan Quintela 
11782858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
11792858ab09SGonglei {
11802858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
11812d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11822858ab09SGonglei 
11832858ab09SGonglei     ps2_common_post_load(ps2);
11842858ab09SGonglei 
11852858ab09SGonglei     return 0;
11862858ab09SGonglei }
11872858ab09SGonglei 
1188b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_mouse = {
1189b31442c3SJuan Quintela     .name = "ps2mouse",
1190b31442c3SJuan Quintela     .version_id = 2,
1191b31442c3SJuan Quintela     .minimum_version_id = 2,
11922858ab09SGonglei     .post_load = ps2_mouse_post_load,
1193b31442c3SJuan Quintela     .fields = (VMStateField[]) {
11942d135409SMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2MouseState, 0, vmstate_ps2_common,
11952d135409SMark Cave-Ayland                        PS2State),
1196b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_status, PS2MouseState),
1197b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
1198b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
1199b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
1200b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_type, PS2MouseState),
1201b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
1202b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dx, PS2MouseState),
1203b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dy, PS2MouseState),
1204b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dz, PS2MouseState),
1205b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
1206b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
1207b31442c3SJuan Quintela     }
1208b31442c3SJuan Quintela };
12090e43e99cSbellard 
121066e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
121166e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
121266e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
121366e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
121466e6536eSGerd Hoffmann };
121566e6536eSGerd Hoffmann 
1216*ea247a0fSMark Cave-Ayland static void ps2_kbd_realize(DeviceState *dev, Error **errp)
1217*ea247a0fSMark Cave-Ayland {
1218*ea247a0fSMark Cave-Ayland     qemu_input_handler_register(dev, &ps2_keyboard_handler);
1219*ea247a0fSMark Cave-Ayland }
1220*ea247a0fSMark Cave-Ayland 
12210e43e99cSbellard void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
12220e43e99cSbellard {
12238f84e53cSMark Cave-Ayland     DeviceState *dev;
12248f84e53cSMark Cave-Ayland     PS2KbdState *s;
12258f84e53cSMark Cave-Ayland     PS2State *ps2;
12268f84e53cSMark Cave-Ayland 
12278f84e53cSMark Cave-Ayland     dev = qdev_new(TYPE_PS2_KBD_DEVICE);
12288f84e53cSMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
12298f84e53cSMark Cave-Ayland     s = PS2_KBD_DEVICE(dev);
12308f84e53cSMark Cave-Ayland     ps2 = PS2_DEVICE(s);
12310e43e99cSbellard 
12325edab03dSDon Koch     trace_ps2_kbd_init(s);
12338f84e53cSMark Cave-Ayland     ps2->update_irq = update_irq;
12348f84e53cSMark Cave-Ayland     ps2->update_arg = update_arg;
12350be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
1236*ea247a0fSMark Cave-Ayland 
12370e43e99cSbellard     return s;
12380e43e99cSbellard }
12390e43e99cSbellard 
12402a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
12412a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
12422a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
12432a766d29SGerd Hoffmann     .event = ps2_mouse_event,
12442a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
12452a766d29SGerd Hoffmann };
12462a766d29SGerd Hoffmann 
12470e43e99cSbellard void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
12480e43e99cSbellard {
12492d135409SMark Cave-Ayland     DeviceState *dev;
12502d135409SMark Cave-Ayland     PS2MouseState *s;
12512d135409SMark Cave-Ayland     PS2State *ps2;
12522d135409SMark Cave-Ayland 
12532d135409SMark Cave-Ayland     dev = qdev_new(TYPE_PS2_MOUSE_DEVICE);
12542d135409SMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
12552d135409SMark Cave-Ayland     s = PS2_MOUSE_DEVICE(dev);
12562d135409SMark Cave-Ayland     ps2 = PS2_DEVICE(s);
12570e43e99cSbellard 
12585edab03dSDon Koch     trace_ps2_mouse_init(s);
12592d135409SMark Cave-Ayland     ps2->update_irq = update_irq;
12602d135409SMark Cave-Ayland     ps2->update_arg = update_arg;
12610be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
12622a766d29SGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
12632a766d29SGerd Hoffmann                                 &ps2_mouse_handler);
12640e43e99cSbellard     return s;
12650e43e99cSbellard }
126664bbdd13SMark Cave-Ayland 
1267108cb22eSMark Cave-Ayland static void ps2_kbd_class_init(ObjectClass *klass, void *data)
1268108cb22eSMark Cave-Ayland {
1269108cb22eSMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
1270108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass);
1271108cb22eSMark Cave-Ayland 
1272*ea247a0fSMark Cave-Ayland     dc->realize = ps2_kbd_realize;
1273108cb22eSMark Cave-Ayland     device_class_set_parent_reset(dc, ps2_kbd_reset, &ps2dc->parent_reset);
1274108cb22eSMark Cave-Ayland }
1275108cb22eSMark Cave-Ayland 
12768f84e53cSMark Cave-Ayland static const TypeInfo ps2_kbd_info = {
12778f84e53cSMark Cave-Ayland     .name          = TYPE_PS2_KBD_DEVICE,
12788f84e53cSMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
12798f84e53cSMark Cave-Ayland     .instance_size = sizeof(PS2KbdState),
1280108cb22eSMark Cave-Ayland     .class_init    = ps2_kbd_class_init
12818f84e53cSMark Cave-Ayland };
12828f84e53cSMark Cave-Ayland 
1283108cb22eSMark Cave-Ayland static void ps2_mouse_class_init(ObjectClass *klass, void *data)
1284108cb22eSMark Cave-Ayland {
1285108cb22eSMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
1286108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass);
1287108cb22eSMark Cave-Ayland 
1288108cb22eSMark Cave-Ayland     device_class_set_parent_reset(dc, ps2_mouse_reset,
1289108cb22eSMark Cave-Ayland                                   &ps2dc->parent_reset);
1290108cb22eSMark Cave-Ayland }
1291108cb22eSMark Cave-Ayland 
12922d135409SMark Cave-Ayland static const TypeInfo ps2_mouse_info = {
12932d135409SMark Cave-Ayland     .name          = TYPE_PS2_MOUSE_DEVICE,
12942d135409SMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
12952d135409SMark Cave-Ayland     .instance_size = sizeof(PS2MouseState),
1296108cb22eSMark Cave-Ayland     .class_init    = ps2_mouse_class_init
12972d135409SMark Cave-Ayland };
12982d135409SMark Cave-Ayland 
129964bbdd13SMark Cave-Ayland static void ps2_class_init(ObjectClass *klass, void *data)
130064bbdd13SMark Cave-Ayland {
130164bbdd13SMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
130264bbdd13SMark Cave-Ayland 
1303108cb22eSMark Cave-Ayland     dc->reset = ps2_reset;
130464bbdd13SMark Cave-Ayland     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
130564bbdd13SMark Cave-Ayland }
130664bbdd13SMark Cave-Ayland 
130764bbdd13SMark Cave-Ayland static const TypeInfo ps2_info = {
130864bbdd13SMark Cave-Ayland     .name          = TYPE_PS2_DEVICE,
130964bbdd13SMark Cave-Ayland     .parent        = TYPE_SYS_BUS_DEVICE,
131064bbdd13SMark Cave-Ayland     .instance_size = sizeof(PS2State),
131164bbdd13SMark Cave-Ayland     .class_init    = ps2_class_init,
1312494145b2SMark Cave-Ayland     .class_size    = sizeof(PS2DeviceClass),
131364bbdd13SMark Cave-Ayland     .abstract      = true
131464bbdd13SMark Cave-Ayland };
131564bbdd13SMark Cave-Ayland 
131664bbdd13SMark Cave-Ayland static void ps2_register_types(void)
131764bbdd13SMark Cave-Ayland {
131864bbdd13SMark Cave-Ayland     type_register_static(&ps2_info);
13198f84e53cSMark Cave-Ayland     type_register_static(&ps2_kbd_info);
13202d135409SMark Cave-Ayland     type_register_static(&ps2_mouse_info);
132164bbdd13SMark Cave-Ayland }
132264bbdd13SMark Cave-Ayland 
132364bbdd13SMark Cave-Ayland type_init(ps2_register_types)
1324