xref: /qemu/hw/input/ps2.c (revision 5cb6e55622570cfc2284dfe4c95f64aee428e63c)
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 
17552b28f76SMark Cave-Ayland static void ps2_raise_irq(PS2State *s)
1767abe7eb2SGeoffrey McRae {
1777abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
1787abe7eb2SGeoffrey McRae }
1797abe7eb2SGeoffrey McRae 
180*5cb6e556SMark Cave-Ayland static void ps2_lower_irq(PS2State *s)
181*5cb6e556SMark Cave-Ayland {
182*5cb6e556SMark Cave-Ayland     s->update_irq(s->update_arg, 0);
183*5cb6e556SMark Cave-Ayland }
184*5cb6e556SMark Cave-Ayland 
1857abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
1867abe7eb2SGeoffrey McRae {
1877704bb02SVolker Rümelin     if (PS2_QUEUE_SIZE - s->queue.count < 1) {
1887704bb02SVolker Rümelin         return;
1897704bb02SVolker Rümelin     }
1907704bb02SVolker Rümelin 
1917abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
19296376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
1937abe7eb2SGeoffrey McRae }
1947abe7eb2SGeoffrey McRae 
1957abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
1967abe7eb2SGeoffrey McRae {
1977abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
1987abe7eb2SGeoffrey McRae         return;
1997abe7eb2SGeoffrey McRae     }
2007abe7eb2SGeoffrey McRae 
2017abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2027abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
20396376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2047abe7eb2SGeoffrey McRae }
2057abe7eb2SGeoffrey McRae 
2067abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2077abe7eb2SGeoffrey McRae {
2087abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2097abe7eb2SGeoffrey McRae         return;
2107abe7eb2SGeoffrey McRae     }
2117abe7eb2SGeoffrey McRae 
2127abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2137abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2147abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
21596376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2167abe7eb2SGeoffrey McRae }
2177abe7eb2SGeoffrey McRae 
2187abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2197abe7eb2SGeoffrey McRae {
2207abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2217abe7eb2SGeoffrey McRae         return;
2227abe7eb2SGeoffrey McRae     }
2237abe7eb2SGeoffrey McRae 
2247abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2257abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2267abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2277abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
22896376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2290e43e99cSbellard }
2300e43e99cSbellard 
2319e24b2ddSVolker Rümelin static void ps2_cqueue_data(PS2Queue *q, int b)
2329e24b2ddSVolker Rümelin {
2339e24b2ddSVolker Rümelin     q->data[q->cwptr] = b;
2349e24b2ddSVolker Rümelin     if (++q->cwptr >= PS2_BUFFER_SIZE) {
2359e24b2ddSVolker Rümelin         q->cwptr = 0;
2369e24b2ddSVolker Rümelin     }
2379e24b2ddSVolker Rümelin     q->count++;
2389e24b2ddSVolker Rümelin }
2399e24b2ddSVolker Rümelin 
2409e24b2ddSVolker Rümelin static void ps2_cqueue_1(PS2State *s, int b1)
2419e24b2ddSVolker Rümelin {
2429e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2439e24b2ddSVolker Rümelin 
2449e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 1) & (PS2_BUFFER_SIZE - 1);
2459e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2469e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2479e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2489e24b2ddSVolker Rümelin }
2499e24b2ddSVolker Rümelin 
2509e24b2ddSVolker Rümelin static void ps2_cqueue_2(PS2State *s, int b1, int b2)
2519e24b2ddSVolker Rümelin {
2529e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2539e24b2ddSVolker Rümelin 
2549e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 2) & (PS2_BUFFER_SIZE - 1);
2559e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2569e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2579e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
2589e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2599e24b2ddSVolker Rümelin }
2609e24b2ddSVolker Rümelin 
2619e24b2ddSVolker Rümelin static void ps2_cqueue_3(PS2State *s, int b1, int b2, int b3)
2629e24b2ddSVolker Rümelin {
2639e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2649e24b2ddSVolker Rümelin 
2659e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 3) & (PS2_BUFFER_SIZE - 1);
2669e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2679e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2689e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
2699e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b3);
2709e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2719e24b2ddSVolker Rümelin }
2729e24b2ddSVolker Rümelin 
2739e24b2ddSVolker Rümelin static void ps2_cqueue_reset(PS2State *s)
2749e24b2ddSVolker Rümelin {
2759e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2769e24b2ddSVolker Rümelin     int ccount;
2779e24b2ddSVolker Rümelin 
2789e24b2ddSVolker Rümelin     if (q->cwptr == -1) {
2799e24b2ddSVolker Rümelin         return;
2809e24b2ddSVolker Rümelin     }
2819e24b2ddSVolker Rümelin 
2829e24b2ddSVolker Rümelin     ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
2839e24b2ddSVolker Rümelin     q->count -= ccount;
2849e24b2ddSVolker Rümelin     q->rptr = q->cwptr;
2859e24b2ddSVolker Rümelin     q->cwptr = -1;
2869e24b2ddSVolker Rümelin }
2879e24b2ddSVolker Rümelin 
28857d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
2890e43e99cSbellard static void ps2_put_keycode(void *opaque, int keycode)
2900e43e99cSbellard {
291f94f5d71Spbrook     PS2KbdState *s = opaque;
2928f84e53cSMark Cave-Ayland     PS2State *ps = PS2_DEVICE(s);
293e7d93956Saurel32 
2945edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
295fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
29657d5c005SHervé Poussineau 
29757d5c005SHervé Poussineau     if (s->translate) {
29857d5c005SHervé Poussineau         if (keycode == 0xf0) {
29957d5c005SHervé Poussineau             s->need_high_bit = true;
30057d5c005SHervé Poussineau         } else if (s->need_high_bit) {
3018f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode] | 0x80);
30257d5c005SHervé Poussineau             s->need_high_bit = false;
30357d5c005SHervé Poussineau         } else {
3048f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode]);
3057096a96dSRoy Tam         }
30657d5c005SHervé Poussineau     } else {
3078f84e53cSMark Cave-Ayland         ps2_queue(ps, keycode);
3080e43e99cSbellard     }
30957d5c005SHervé Poussineau }
3100e43e99cSbellard 
31166e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
31266e6536eSGerd Hoffmann                                InputEvent *evt)
31366e6536eSGerd Hoffmann {
31466e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
31532bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
3168c10e0baSHervé Poussineau     int qcode;
317ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
318620775d1SDaniel P. Berrange     int mod;
31966e6536eSGerd Hoffmann 
320143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
321143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
322143c04c7SGeoffrey McRae         return;
323143c04c7SGeoffrey McRae     }
324143c04c7SGeoffrey McRae 
325fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
3268c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
3278c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
32857d5c005SHervé Poussineau 
329620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
330644f66bfSDaniel P. Berrangé     trace_ps2_keyboard_event(s, qcode, key->down, mod,
331644f66bfSDaniel P. Berrangé                              s->modifiers, s->scancode_set, s->translate);
332620775d1SDaniel P. Berrange     if (key->down) {
333620775d1SDaniel P. Berrange         s->modifiers |= mod;
334620775d1SDaniel P. Berrange     } else {
335620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
336620775d1SDaniel P. Berrange     }
337620775d1SDaniel P. Berrange 
3388c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3398c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
34029fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
34129fd23a5SDaniel P. Berrange                 if (key->down) {
34229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
34329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
34429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
34529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
34629fd23a5SDaniel P. Berrange                 }
34729fd23a5SDaniel P. Berrange             } else {
3488c10e0baSHervé Poussineau                 if (key->down) {
3498c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3508c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3518c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
352927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3538c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
3548c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
3558c10e0baSHervé Poussineau                 }
35629fd23a5SDaniel P. Berrange             }
3578c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
358620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
359620775d1SDaniel P. Berrange                 if (key->down) {
360620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
361620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
362620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
363620775d1SDaniel P. Berrange                 } else {
364620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
365620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
366620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
367620775d1SDaniel P. Berrange                 }
368620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
369620775d1SDaniel P. Berrange                 if (key->down) {
370620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
371620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
372620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
373620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
374620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
375620775d1SDaniel P. Berrange                 } else {
376620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
377620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
378620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
379620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
380620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
381620775d1SDaniel P. Berrange                 }
3828f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
3838f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
3848f63458fSDaniel P. Berrange                 if (key->down) {
3858f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3868f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
3878f63458fSDaniel P. Berrange                 } else {
3888f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3898f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
3908f63458fSDaniel P. Berrange                 }
391620775d1SDaniel P. Berrange             } else {
3928c10e0baSHervé Poussineau                 if (key->down) {
3938c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3948c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
3958c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3968c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
3978c10e0baSHervé Poussineau                 } else {
3988c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3998c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
4008c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4018c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
4028c10e0baSHervé Poussineau                 }
403620775d1SDaniel P. Berrange             }
4048c10e0baSHervé Poussineau         } else {
405545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset1_len) {
406ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
407545e5cf8SMark Cave-Ayland             }
4088c10e0baSHervé Poussineau             if (keycode) {
4098c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4108c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4118c10e0baSHervé Poussineau                 }
4128c10e0baSHervé Poussineau                 if (!key->down) {
4138c10e0baSHervé Poussineau                     keycode |= 0x80;
4148c10e0baSHervé Poussineau                 }
4158c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
4168c10e0baSHervé Poussineau             } else {
417ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
418ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
4198c10e0baSHervé Poussineau             }
4208c10e0baSHervé Poussineau         }
4218c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
4228c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
42329fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
42429fd23a5SDaniel P. Berrange                 if (key->down) {
42529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
42629fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
42729fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
42829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
42929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
43029fd23a5SDaniel P. Berrange                 }
43129fd23a5SDaniel P. Berrange             } else {
4328c10e0baSHervé Poussineau                 if (key->down) {
4338c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4348c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4358c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4368c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4378c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4388c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4398c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4408c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4418c10e0baSHervé Poussineau                 }
44229fd23a5SDaniel P. Berrange             }
4438c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
444620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
445620775d1SDaniel P. Berrange                 if (key->down) {
446620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
447620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
448620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
449620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
450620775d1SDaniel P. Berrange                 } else {
451620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
452620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
453620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
454620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
455620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
456620775d1SDaniel P. Berrange                 }
457620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
458620775d1SDaniel P. Berrange                 if (key->down) {
459620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
460620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
461620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
462620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
463620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
464620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
465620775d1SDaniel P. Berrange                 } else {
466620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
467620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
468620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
469620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
470620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
471620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
472620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
473620775d1SDaniel P. Berrange                 }
4748f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4758f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4768f63458fSDaniel P. Berrange                 if (key->down) {
4778f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4788f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4798f63458fSDaniel P. Berrange                 } else {
4808f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4818f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
4828f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4838f63458fSDaniel P. Berrange                 }
484620775d1SDaniel P. Berrange             } else {
4858c10e0baSHervé Poussineau                 if (key->down) {
4868c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4878c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4888c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4898c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4908c10e0baSHervé Poussineau                 } else {
4918c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4928c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4938c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4948c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4958c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4968c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4978c10e0baSHervé Poussineau                 }
498620775d1SDaniel P. Berrange             }
4998c10e0baSHervé Poussineau         } else {
500545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset2_len) {
501ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
502545e5cf8SMark Cave-Ayland             }
5038c10e0baSHervé Poussineau             if (keycode) {
5048c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
5058c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
5068c10e0baSHervé Poussineau                 }
5078c10e0baSHervé Poussineau                 if (!key->down) {
5088c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5098c10e0baSHervé Poussineau                 }
5108c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
51157d5c005SHervé Poussineau             } else {
512ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
513ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
51457d5c005SHervé Poussineau             }
51557d5c005SHervé Poussineau         }
51657d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
517545e5cf8SMark Cave-Ayland         if (qcode < qemu_input_map_qcode_to_atset3_len) {
518ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
519545e5cf8SMark Cave-Ayland         }
5208c10e0baSHervé Poussineau         if (keycode) {
5218c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
5228c10e0baSHervé Poussineau             if (!key->down) {
5238c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
52457d5c005SHervé Poussineau             }
52557d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
5268c10e0baSHervé Poussineau         } else {
527ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
528ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
5298c10e0baSHervé Poussineau         }
53066e6536eSGerd Hoffmann     }
53166e6536eSGerd Hoffmann }
53266e6536eSGerd Hoffmann 
5338498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
5340e43e99cSbellard {
5350e43e99cSbellard     PS2Queue *q;
5360e43e99cSbellard     int val, index;
5370e43e99cSbellard 
5388498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
5390e43e99cSbellard     q = &s->queue;
5400e43e99cSbellard     if (q->count == 0) {
541545e5cf8SMark Cave-Ayland         /*
542545e5cf8SMark Cave-Ayland          * NOTE: if no data left, we return the last keyboard one
543545e5cf8SMark Cave-Ayland          * (needed for EMM386)
544545e5cf8SMark Cave-Ayland          */
5450e43e99cSbellard         /* XXX: need a timer to do things correctly */
5460e43e99cSbellard         index = q->rptr - 1;
54747db2432SVolker Rümelin         if (index < 0) {
54847db2432SVolker Rümelin             index = PS2_BUFFER_SIZE - 1;
54947db2432SVolker Rümelin         }
5500e43e99cSbellard         val = q->data[index];
5510e43e99cSbellard     } else {
5520e43e99cSbellard         val = q->data[q->rptr];
55347db2432SVolker Rümelin         if (++q->rptr == PS2_BUFFER_SIZE) {
5540e43e99cSbellard             q->rptr = 0;
55547db2432SVolker Rümelin         }
5560e43e99cSbellard         q->count--;
5579e24b2ddSVolker Rümelin         if (q->rptr == q->cwptr) {
5589e24b2ddSVolker Rümelin             /* command reply queue is empty */
5599e24b2ddSVolker Rümelin             q->cwptr = -1;
5609e24b2ddSVolker Rümelin         }
5610e43e99cSbellard         /* reading deasserts IRQ */
562*5cb6e556SMark Cave-Ayland         ps2_lower_irq(s);
5630e43e99cSbellard         /* reassert IRQs if data left */
564cec32524SVolker Rümelin         if (q->count) {
565892e9bbeSMark Cave-Ayland             ps2_raise_irq(s);
566cec32524SVolker Rümelin         }
5670e43e99cSbellard     }
5680e43e99cSbellard     return val;
5690e43e99cSbellard }
5700e43e99cSbellard 
5717f540ab5SChristophe Fergeau static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
5727f540ab5SChristophe Fergeau {
5735edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
5747f540ab5SChristophe Fergeau     s->ledstate = ledstate;
5757f540ab5SChristophe Fergeau     kbd_put_ledstate(ledstate);
5767f540ab5SChristophe Fergeau }
5777f540ab5SChristophe Fergeau 
5780e43e99cSbellard static void ps2_reset_keyboard(PS2KbdState *s)
5790e43e99cSbellard {
5808f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
5818f84e53cSMark Cave-Ayland 
5825edab03dSDon Koch     trace_ps2_reset_keyboard(s);
5830e43e99cSbellard     s->scan_enabled = 1;
584e7d93956Saurel32     s->scancode_set = 2;
5858f84e53cSMark Cave-Ayland     ps2_reset_queue(ps2);
5867f540ab5SChristophe Fergeau     ps2_set_ledstate(s, 0);
5870e43e99cSbellard }
5880e43e99cSbellard 
58954334e73SMark Cave-Ayland void ps2_write_keyboard(PS2KbdState *s, int val)
5900e43e99cSbellard {
5918f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
5920e43e99cSbellard 
59354334e73SMark Cave-Ayland     trace_ps2_write_keyboard(s, val);
5948f84e53cSMark Cave-Ayland     ps2_cqueue_reset(ps2);
5958f84e53cSMark Cave-Ayland     switch (ps2->write_cmd) {
5960e43e99cSbellard     default:
5970e43e99cSbellard     case -1:
5980e43e99cSbellard         switch (val) {
5990e43e99cSbellard         case 0x00:
6008f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6010e43e99cSbellard             break;
6020e43e99cSbellard         case 0x05:
6038f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
6040e43e99cSbellard             break;
6050e43e99cSbellard         case KBD_CMD_GET_ID:
606e7d93956Saurel32             /* We emulate a MF2 AT keyboard here */
6078f84e53cSMark Cave-Ayland             ps2_cqueue_3(ps2, KBD_REPLY_ACK, KBD_REPLY_ID,
6089e24b2ddSVolker Rümelin                          s->translate ? 0x41 : 0x83);
6090e43e99cSbellard             break;
6100e43e99cSbellard         case KBD_CMD_ECHO:
6118f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_CMD_ECHO);
6120e43e99cSbellard             break;
6130e43e99cSbellard         case KBD_CMD_ENABLE:
6140e43e99cSbellard             s->scan_enabled = 1;
6158f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6160e43e99cSbellard             break;
617e7d93956Saurel32         case KBD_CMD_SCANCODE:
6180e43e99cSbellard         case KBD_CMD_SET_LEDS:
6190e43e99cSbellard         case KBD_CMD_SET_RATE:
620c56b6209SSven Schnelle         case KBD_CMD_SET_MAKE_BREAK:
6218f84e53cSMark Cave-Ayland             ps2->write_cmd = val;
6228f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6230e43e99cSbellard             break;
6240e43e99cSbellard         case KBD_CMD_RESET_DISABLE:
6250e43e99cSbellard             ps2_reset_keyboard(s);
6260e43e99cSbellard             s->scan_enabled = 0;
6278f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6280e43e99cSbellard             break;
6290e43e99cSbellard         case KBD_CMD_RESET_ENABLE:
6300e43e99cSbellard             ps2_reset_keyboard(s);
6310e43e99cSbellard             s->scan_enabled = 1;
6328f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6330e43e99cSbellard             break;
6340e43e99cSbellard         case KBD_CMD_RESET:
6350e43e99cSbellard             ps2_reset_keyboard(s);
6368f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2,
6377abe7eb2SGeoffrey McRae                          KBD_REPLY_ACK,
6387abe7eb2SGeoffrey McRae                          KBD_REPLY_POR);
6390e43e99cSbellard             break;
640c56b6209SSven Schnelle         case KBD_CMD_SET_TYPEMATIC:
6418f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
642c56b6209SSven Schnelle             break;
6430e43e99cSbellard         default:
6448f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
6450e43e99cSbellard             break;
6460e43e99cSbellard         }
6470e43e99cSbellard         break;
648c56b6209SSven Schnelle     case KBD_CMD_SET_MAKE_BREAK:
6498f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6508f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
651c56b6209SSven Schnelle         break;
652e7d93956Saurel32     case KBD_CMD_SCANCODE:
653e7d93956Saurel32         if (val == 0) {
6548f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2, KBD_REPLY_ACK, s->translate ?
6559e24b2ddSVolker Rümelin                 translate_table[s->scancode_set] : s->scancode_set);
6564df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
657e7d93956Saurel32             s->scancode_set = val;
6588f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6594df23b64SHervé Poussineau         } else {
6608f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
661e7d93956Saurel32         }
6628f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
663e7d93956Saurel32         break;
6640e43e99cSbellard     case KBD_CMD_SET_LEDS:
6657f540ab5SChristophe Fergeau         ps2_set_ledstate(s, val);
6668f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6678f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
6680e43e99cSbellard         break;
6690e43e99cSbellard     case KBD_CMD_SET_RATE:
6708f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6718f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
6720e43e99cSbellard         break;
6730e43e99cSbellard     }
6740e43e99cSbellard }
6750e43e99cSbellard 
676545e5cf8SMark Cave-Ayland /*
677545e5cf8SMark Cave-Ayland  * Set the scancode translation mode.
678545e5cf8SMark Cave-Ayland  * 0 = raw scancodes.
679545e5cf8SMark Cave-Ayland  * 1 = translated scancodes (used by qemu internally).
680545e5cf8SMark Cave-Ayland  */
681f94f5d71Spbrook 
68254334e73SMark Cave-Ayland void ps2_keyboard_set_translation(PS2KbdState *s, int mode)
683f94f5d71Spbrook {
68454334e73SMark Cave-Ayland     trace_ps2_keyboard_set_translation(s, mode);
685f94f5d71Spbrook     s->translate = mode;
686f94f5d71Spbrook }
687f94f5d71Spbrook 
6887abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
6890e43e99cSbellard {
6902d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
69176968101SVolker Rümelin     /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
69276968101SVolker Rümelin     const int needed = s->mouse_type ? 4 : 3;
6930e43e99cSbellard     unsigned int b;
69464ebbb7dSDmitry Petrov     int dx1, dy1, dz1, dw1;
6950e43e99cSbellard 
6962d135409SMark Cave-Ayland     if (PS2_QUEUE_SIZE - ps2->queue.count < needed) {
6977abe7eb2SGeoffrey McRae         return 0;
6987abe7eb2SGeoffrey McRae     }
6997abe7eb2SGeoffrey McRae 
7000e43e99cSbellard     dx1 = s->mouse_dx;
7010e43e99cSbellard     dy1 = s->mouse_dy;
7020e43e99cSbellard     dz1 = s->mouse_dz;
70364ebbb7dSDmitry Petrov     dw1 = s->mouse_dw;
7040e43e99cSbellard     /* XXX: increase range to 8 bits ? */
705545e5cf8SMark Cave-Ayland     if (dx1 > 127) {
7060e43e99cSbellard         dx1 = 127;
707545e5cf8SMark Cave-Ayland     } else if (dx1 < -127) {
7080e43e99cSbellard         dx1 = -127;
709545e5cf8SMark Cave-Ayland     }
710545e5cf8SMark Cave-Ayland     if (dy1 > 127) {
7110e43e99cSbellard         dy1 = 127;
712545e5cf8SMark Cave-Ayland     } else if (dy1 < -127) {
7130e43e99cSbellard         dy1 = -127;
714545e5cf8SMark Cave-Ayland     }
7150e43e99cSbellard     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
7162d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, b);
7172d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, dx1 & 0xff);
7182d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, dy1 & 0xff);
7190e43e99cSbellard     /* extra byte for IMPS/2 or IMEX */
7200e43e99cSbellard     switch (s->mouse_type) {
7210e43e99cSbellard     default:
72264ebbb7dSDmitry Petrov         /* Just ignore the wheels if not supported */
72364ebbb7dSDmitry Petrov         s->mouse_dz = 0;
72464ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7250e43e99cSbellard         break;
7260e43e99cSbellard     case 3:
727545e5cf8SMark Cave-Ayland         if (dz1 > 127) {
7280e43e99cSbellard             dz1 = 127;
729545e5cf8SMark Cave-Ayland         } else if (dz1 < -127) {
7300e43e99cSbellard             dz1 = -127;
731545e5cf8SMark Cave-Ayland         }
7322d135409SMark Cave-Ayland         ps2_queue_noirq(ps2, dz1 & 0xff);
73364ebbb7dSDmitry Petrov         s->mouse_dz -= dz1;
73464ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7350e43e99cSbellard         break;
7360e43e99cSbellard     case 4:
73764ebbb7dSDmitry Petrov         /*
73864ebbb7dSDmitry Petrov          * This matches what the Linux kernel expects for exps/2 in
73964ebbb7dSDmitry Petrov          * drivers/input/mouse/psmouse-base.c. Note, if you happen to
74064ebbb7dSDmitry Petrov          * press/release the 4th or 5th buttons at the same moment as a
74164ebbb7dSDmitry Petrov          * horizontal wheel scroll, those button presses will get lost. I'm not
74264ebbb7dSDmitry Petrov          * sure what to do about that, since by this point we don't know
74364ebbb7dSDmitry Petrov          * whether those buttons actually changed state.
74464ebbb7dSDmitry Petrov          */
74564ebbb7dSDmitry Petrov         if (dw1 != 0) {
74664ebbb7dSDmitry Petrov             if (dw1 > 31) {
74764ebbb7dSDmitry Petrov                 dw1 = 31;
74864ebbb7dSDmitry Petrov             } else if (dw1 < -31) {
74964ebbb7dSDmitry Petrov                 dw1 = -31;
75064ebbb7dSDmitry Petrov             }
75164ebbb7dSDmitry Petrov 
75264ebbb7dSDmitry Petrov             /*
75364ebbb7dSDmitry Petrov              * linux kernel expects first 6 bits to represent the value
75464ebbb7dSDmitry Petrov              * for horizontal scroll
75564ebbb7dSDmitry Petrov              */
75664ebbb7dSDmitry Petrov             b = (dw1 & 0x3f) | 0x40;
75764ebbb7dSDmitry Petrov             s->mouse_dw -= dw1;
75864ebbb7dSDmitry Petrov         } else {
75964ebbb7dSDmitry Petrov             if (dz1 > 7) {
7600e43e99cSbellard                 dz1 = 7;
76164ebbb7dSDmitry Petrov             } else if (dz1 < -7) {
7620e43e99cSbellard                 dz1 = -7;
76364ebbb7dSDmitry Petrov             }
76464ebbb7dSDmitry Petrov 
7650e43e99cSbellard             b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
76664ebbb7dSDmitry Petrov             s->mouse_dz -= dz1;
76764ebbb7dSDmitry Petrov         }
7682d135409SMark Cave-Ayland         ps2_queue_noirq(ps2, b);
7690e43e99cSbellard         break;
7700e43e99cSbellard     }
7710e43e99cSbellard 
7722d135409SMark Cave-Ayland     ps2_raise_irq(ps2);
7737abe7eb2SGeoffrey McRae 
7745edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
7750e43e99cSbellard     /* update deltas */
7760e43e99cSbellard     s->mouse_dx -= dx1;
7770e43e99cSbellard     s->mouse_dy -= dy1;
7787abe7eb2SGeoffrey McRae 
7797abe7eb2SGeoffrey McRae     return 1;
7800e43e99cSbellard }
7810e43e99cSbellard 
7822a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
7832a766d29SGerd Hoffmann                             InputEvent *evt)
7840e43e99cSbellard {
7857fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
7868b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
7878b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
7888b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
7898b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
7908b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
7912a766d29SGerd Hoffmann     };
7922a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
793b5a1b443SEric Blake     InputMoveEvent *move;
794b5a1b443SEric Blake     InputBtnEvent *btn;
7950e43e99cSbellard 
7960e43e99cSbellard     /* check if deltas are recorded when disabled */
797545e5cf8SMark Cave-Ayland     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
7980e43e99cSbellard         return;
799545e5cf8SMark Cave-Ayland     }
8000e43e99cSbellard 
801568c73a4SEric Blake     switch (evt->type) {
8022a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
80332bafa8fSEric Blake         move = evt->u.rel.data;
804b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
805b5a1b443SEric Blake             s->mouse_dx += move->value;
806b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
807b5a1b443SEric Blake             s->mouse_dy -= move->value;
8082a766d29SGerd Hoffmann         }
8092a766d29SGerd Hoffmann         break;
8100e43e99cSbellard 
8112a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
81232bafa8fSEric Blake         btn = evt->u.btn.data;
813b5a1b443SEric Blake         if (btn->down) {
814b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
815b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
8162a766d29SGerd Hoffmann                 s->mouse_dz--;
817b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
8182a766d29SGerd Hoffmann                 s->mouse_dz++;
8192a766d29SGerd Hoffmann             }
82064ebbb7dSDmitry Petrov 
82164ebbb7dSDmitry Petrov             if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
82264ebbb7dSDmitry Petrov                 s->mouse_dw--;
82364ebbb7dSDmitry Petrov             } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
82464ebbb7dSDmitry Petrov                 s->mouse_dw++;
82564ebbb7dSDmitry Petrov             }
8262a766d29SGerd Hoffmann         } else {
827b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
8282a766d29SGerd Hoffmann         }
8292a766d29SGerd Hoffmann         break;
8302a766d29SGerd Hoffmann 
8312a766d29SGerd Hoffmann     default:
8322a766d29SGerd Hoffmann         /* keep gcc happy */
8332a766d29SGerd Hoffmann         break;
8342a766d29SGerd Hoffmann     }
835fd214d18SGerd Hoffmann }
836fd214d18SGerd Hoffmann 
8372a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
8382a766d29SGerd Hoffmann {
8392a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
8402a766d29SGerd Hoffmann 
841143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
842143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
843143c04c7SGeoffrey McRae         return;
844143c04c7SGeoffrey McRae     }
845143c04c7SGeoffrey McRae 
8462a766d29SGerd Hoffmann     if (s->mouse_buttons) {
847fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
8482a766d29SGerd Hoffmann     }
8492858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
850545e5cf8SMark Cave-Ayland         /*
851545e5cf8SMark Cave-Ayland          * if not remote, send event. Multiple events are sent if
852545e5cf8SMark Cave-Ayland          * too big deltas
853545e5cf8SMark Cave-Ayland          */
8547abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
85564ebbb7dSDmitry Petrov             if (s->mouse_dx == 0 && s->mouse_dy == 0
85664ebbb7dSDmitry Petrov                     && s->mouse_dz == 0 && s->mouse_dw == 0) {
8570e43e99cSbellard                 break;
8580e43e99cSbellard             }
8590e43e99cSbellard         }
8600e43e99cSbellard     }
86164ebbb7dSDmitry Petrov }
8620e43e99cSbellard 
86354334e73SMark Cave-Ayland void ps2_mouse_fake_event(PS2MouseState *s)
864548df2acSths {
86554334e73SMark Cave-Ayland     trace_ps2_mouse_fake_event(s);
8662a766d29SGerd Hoffmann     s->mouse_dx++;
86754334e73SMark Cave-Ayland     ps2_mouse_sync(DEVICE(s));
868548df2acSths }
869548df2acSths 
87054334e73SMark Cave-Ayland void ps2_write_mouse(PS2MouseState *s, int val)
8710e43e99cSbellard {
8722d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
8735edab03dSDon Koch 
87454334e73SMark Cave-Ayland     trace_ps2_write_mouse(s, val);
8752d135409SMark Cave-Ayland     switch (ps2->write_cmd) {
8760e43e99cSbellard     default:
8770e43e99cSbellard     case -1:
8780e43e99cSbellard         /* mouse command */
8790e43e99cSbellard         if (s->mouse_wrap) {
8800e43e99cSbellard             if (val == AUX_RESET_WRAP) {
8810e43e99cSbellard                 s->mouse_wrap = 0;
8822d135409SMark Cave-Ayland                 ps2_queue(ps2, AUX_ACK);
8830e43e99cSbellard                 return;
8840e43e99cSbellard             } else if (val != AUX_RESET) {
8852d135409SMark Cave-Ayland                 ps2_queue(ps2, val);
8860e43e99cSbellard                 return;
8870e43e99cSbellard             }
8880e43e99cSbellard         }
8890e43e99cSbellard         switch (val) {
8900e43e99cSbellard         case AUX_SET_SCALE11:
8910e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
8922d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
8930e43e99cSbellard             break;
8940e43e99cSbellard         case AUX_SET_SCALE21:
8950e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_SCALE21;
8962d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
8970e43e99cSbellard             break;
8980e43e99cSbellard         case AUX_SET_STREAM:
8990e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
9002d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9010e43e99cSbellard             break;
9020e43e99cSbellard         case AUX_SET_WRAP:
9030e43e99cSbellard             s->mouse_wrap = 1;
9042d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9050e43e99cSbellard             break;
9060e43e99cSbellard         case AUX_SET_REMOTE:
9070e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_REMOTE;
9082d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9090e43e99cSbellard             break;
9100e43e99cSbellard         case AUX_GET_TYPE:
9112d135409SMark Cave-Ayland             ps2_queue_2(ps2,
9127abe7eb2SGeoffrey McRae                 AUX_ACK,
9137abe7eb2SGeoffrey McRae                 s->mouse_type);
9140e43e99cSbellard             break;
9150e43e99cSbellard         case AUX_SET_RES:
9160e43e99cSbellard         case AUX_SET_SAMPLE:
9172d135409SMark Cave-Ayland             ps2->write_cmd = val;
9182d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9190e43e99cSbellard             break;
9200e43e99cSbellard         case AUX_GET_SCALE:
9212d135409SMark Cave-Ayland             ps2_queue_4(ps2,
9227abe7eb2SGeoffrey McRae                 AUX_ACK,
9237abe7eb2SGeoffrey McRae                 s->mouse_status,
9247abe7eb2SGeoffrey McRae                 s->mouse_resolution,
9257abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
9260e43e99cSbellard             break;
9270e43e99cSbellard         case AUX_POLL:
9282d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9290e43e99cSbellard             ps2_mouse_send_packet(s);
9300e43e99cSbellard             break;
9310e43e99cSbellard         case AUX_ENABLE_DEV:
9320e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_ENABLED;
9332d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9340e43e99cSbellard             break;
9350e43e99cSbellard         case AUX_DISABLE_DEV:
9360e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
9372d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9380e43e99cSbellard             break;
9390e43e99cSbellard         case AUX_SET_DEFAULT:
9400e43e99cSbellard             s->mouse_sample_rate = 100;
9410e43e99cSbellard             s->mouse_resolution = 2;
9420e43e99cSbellard             s->mouse_status = 0;
9432d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9440e43e99cSbellard             break;
9450e43e99cSbellard         case AUX_RESET:
9460e43e99cSbellard             s->mouse_sample_rate = 100;
9470e43e99cSbellard             s->mouse_resolution = 2;
9480e43e99cSbellard             s->mouse_status = 0;
9490e43e99cSbellard             s->mouse_type = 0;
9502d135409SMark Cave-Ayland             ps2_reset_queue(ps2);
9512d135409SMark Cave-Ayland             ps2_queue_3(ps2,
9527abe7eb2SGeoffrey McRae                 AUX_ACK,
9537abe7eb2SGeoffrey McRae                 0xaa,
9547abe7eb2SGeoffrey McRae                 s->mouse_type);
9550e43e99cSbellard             break;
9560e43e99cSbellard         default:
9570e43e99cSbellard             break;
9580e43e99cSbellard         }
9590e43e99cSbellard         break;
9600e43e99cSbellard     case AUX_SET_SAMPLE:
9610e43e99cSbellard         s->mouse_sample_rate = val;
9620e43e99cSbellard         /* detect IMPS/2 or IMEX */
9630e43e99cSbellard         switch (s->mouse_detect_state) {
9640e43e99cSbellard         default:
9650e43e99cSbellard         case 0:
966545e5cf8SMark Cave-Ayland             if (val == 200) {
9670e43e99cSbellard                 s->mouse_detect_state = 1;
968545e5cf8SMark Cave-Ayland             }
9690e43e99cSbellard             break;
9700e43e99cSbellard         case 1:
971545e5cf8SMark Cave-Ayland             if (val == 100) {
9720e43e99cSbellard                 s->mouse_detect_state = 2;
973545e5cf8SMark Cave-Ayland             } else if (val == 200) {
9740e43e99cSbellard                 s->mouse_detect_state = 3;
975545e5cf8SMark Cave-Ayland             } else {
9760e43e99cSbellard                 s->mouse_detect_state = 0;
977545e5cf8SMark Cave-Ayland             }
9780e43e99cSbellard             break;
9790e43e99cSbellard         case 2:
980545e5cf8SMark Cave-Ayland             if (val == 80) {
9810e43e99cSbellard                 s->mouse_type = 3; /* IMPS/2 */
982545e5cf8SMark Cave-Ayland             }
9830e43e99cSbellard             s->mouse_detect_state = 0;
9840e43e99cSbellard             break;
9850e43e99cSbellard         case 3:
986545e5cf8SMark Cave-Ayland             if (val == 80) {
9870e43e99cSbellard                 s->mouse_type = 4; /* IMEX */
988545e5cf8SMark Cave-Ayland             }
9890e43e99cSbellard             s->mouse_detect_state = 0;
9900e43e99cSbellard             break;
9910e43e99cSbellard         }
9922d135409SMark Cave-Ayland         ps2_queue(ps2, AUX_ACK);
9932d135409SMark Cave-Ayland         ps2->write_cmd = -1;
9940e43e99cSbellard         break;
9950e43e99cSbellard     case AUX_SET_RES:
9960e43e99cSbellard         s->mouse_resolution = val;
9972d135409SMark Cave-Ayland         ps2_queue(ps2, AUX_ACK);
9982d135409SMark Cave-Ayland         ps2->write_cmd = -1;
9990e43e99cSbellard         break;
10000e43e99cSbellard     }
10010e43e99cSbellard }
10020e43e99cSbellard 
1003108cb22eSMark Cave-Ayland static void ps2_reset(DeviceState *dev)
10040e43e99cSbellard {
1005108cb22eSMark Cave-Ayland     PS2State *s = PS2_DEVICE(dev);
1006108cb22eSMark Cave-Ayland 
10070e43e99cSbellard     s->write_cmd = -1;
1008954ee55bSGerd Hoffmann     ps2_reset_queue(s);
1009*5cb6e556SMark Cave-Ayland     ps2_lower_irq(s);
10100e43e99cSbellard }
10110e43e99cSbellard 
10122858ab09SGonglei static void ps2_common_post_load(PS2State *s)
10132858ab09SGonglei {
10142858ab09SGonglei     PS2Queue *q = &s->queue;
10154e9bddcbSVolker Rümelin     int ccount = 0;
10162858ab09SGonglei 
10174e9bddcbSVolker Rümelin     /* limit the number of queued command replies to PS2_QUEUE_HEADROOM */
10184e9bddcbSVolker Rümelin     if (q->cwptr != -1) {
10194e9bddcbSVolker Rümelin         ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
10204e9bddcbSVolker Rümelin         if (ccount > PS2_QUEUE_HEADROOM) {
10214e9bddcbSVolker Rümelin             ccount = PS2_QUEUE_HEADROOM;
10224e9bddcbSVolker Rümelin         }
1023a1f2ed2aSPavel Dovgalyuk     }
10242858ab09SGonglei 
10254e9bddcbSVolker Rümelin     /* limit the scancode queue size to PS2_QUEUE_SIZE */
10264e9bddcbSVolker Rümelin     if (q->count < ccount) {
10274e9bddcbSVolker Rümelin         q->count = ccount;
10284e9bddcbSVolker Rümelin     } else if (q->count > ccount + PS2_QUEUE_SIZE) {
10294e9bddcbSVolker Rümelin         q->count = ccount + PS2_QUEUE_SIZE;
10304e9bddcbSVolker Rümelin     }
10314e9bddcbSVolker Rümelin 
10324e9bddcbSVolker Rümelin     /* sanitize rptr and recalculate wptr and cwptr */
103347db2432SVolker Rümelin     q->rptr = q->rptr & (PS2_BUFFER_SIZE - 1);
103447db2432SVolker Rümelin     q->wptr = (q->rptr + q->count) & (PS2_BUFFER_SIZE - 1);
10354e9bddcbSVolker Rümelin     q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1;
10362858ab09SGonglei }
10372858ab09SGonglei 
1038108cb22eSMark Cave-Ayland static void ps2_kbd_reset(DeviceState *dev)
1039ef74679aSDinesh Subhraveti {
1040108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(dev);
1041108cb22eSMark Cave-Ayland     PS2KbdState *s = PS2_KBD_DEVICE(dev);
1042ef74679aSDinesh Subhraveti 
1043108cb22eSMark Cave-Ayland     trace_ps2_kbd_reset(s);
1044108cb22eSMark Cave-Ayland     ps2dc->parent_reset(dev);
1045108cb22eSMark Cave-Ayland 
1046d2e550a8SHervé Poussineau     s->scan_enabled = 1;
1047ef74679aSDinesh Subhraveti     s->translate = 0;
1048089adafdSHervé Poussineau     s->scancode_set = 2;
1049620775d1SDaniel P. Berrange     s->modifiers = 0;
1050ef74679aSDinesh Subhraveti }
1051ef74679aSDinesh Subhraveti 
1052108cb22eSMark Cave-Ayland static void ps2_mouse_reset(DeviceState *dev)
1053ef74679aSDinesh Subhraveti {
1054108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(dev);
1055108cb22eSMark Cave-Ayland     PS2MouseState *s = PS2_MOUSE_DEVICE(dev);
1056ef74679aSDinesh Subhraveti 
1057108cb22eSMark Cave-Ayland     trace_ps2_mouse_reset(s);
1058108cb22eSMark Cave-Ayland     ps2dc->parent_reset(dev);
1059108cb22eSMark Cave-Ayland 
1060ef74679aSDinesh Subhraveti     s->mouse_status = 0;
1061ef74679aSDinesh Subhraveti     s->mouse_resolution = 0;
1062ef74679aSDinesh Subhraveti     s->mouse_sample_rate = 0;
1063ef74679aSDinesh Subhraveti     s->mouse_wrap = 0;
1064ef74679aSDinesh Subhraveti     s->mouse_type = 0;
1065ef74679aSDinesh Subhraveti     s->mouse_detect_state = 0;
1066ef74679aSDinesh Subhraveti     s->mouse_dx = 0;
1067ef74679aSDinesh Subhraveti     s->mouse_dy = 0;
1068ef74679aSDinesh Subhraveti     s->mouse_dz = 0;
106964ebbb7dSDmitry Petrov     s->mouse_dw = 0;
1070ef74679aSDinesh Subhraveti     s->mouse_buttons = 0;
1071ef74679aSDinesh Subhraveti }
1072ef74679aSDinesh Subhraveti 
1073b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_common = {
1074b31442c3SJuan Quintela     .name = "PS2 Common State",
1075b31442c3SJuan Quintela     .version_id = 3,
1076b31442c3SJuan Quintela     .minimum_version_id = 2,
1077b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1078b31442c3SJuan Quintela         VMSTATE_INT32(write_cmd, PS2State),
1079b31442c3SJuan Quintela         VMSTATE_INT32(queue.rptr, PS2State),
1080b31442c3SJuan Quintela         VMSTATE_INT32(queue.wptr, PS2State),
1081b31442c3SJuan Quintela         VMSTATE_INT32(queue.count, PS2State),
1082b31442c3SJuan Quintela         VMSTATE_BUFFER(queue.data, PS2State),
1083b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
10847783e9f0Spbrook     }
1085b31442c3SJuan Quintela };
10867783e9f0Spbrook 
10877f540ab5SChristophe Fergeau static bool ps2_keyboard_ledstate_needed(void *opaque)
10887f540ab5SChristophe Fergeau {
10897f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
10907f540ab5SChristophe Fergeau 
10917f540ab5SChristophe Fergeau     return s->ledstate != 0; /* 0 is default state */
10927f540ab5SChristophe Fergeau }
10937f540ab5SChristophe Fergeau 
10947f540ab5SChristophe Fergeau static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
10957f540ab5SChristophe Fergeau {
10967f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
10977f540ab5SChristophe Fergeau 
10987f540ab5SChristophe Fergeau     kbd_put_ledstate(s->ledstate);
10997f540ab5SChristophe Fergeau     return 0;
11007f540ab5SChristophe Fergeau }
11017f540ab5SChristophe Fergeau 
11027f540ab5SChristophe Fergeau static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
11037f540ab5SChristophe Fergeau     .name = "ps2kbd/ledstate",
11047f540ab5SChristophe Fergeau     .version_id = 3,
11057f540ab5SChristophe Fergeau     .minimum_version_id = 2,
11067f540ab5SChristophe Fergeau     .post_load = ps2_kbd_ledstate_post_load,
11075cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
11087f540ab5SChristophe Fergeau     .fields = (VMStateField[]) {
11097f540ab5SChristophe Fergeau         VMSTATE_INT32(ledstate, PS2KbdState),
11107f540ab5SChristophe Fergeau         VMSTATE_END_OF_LIST()
11117f540ab5SChristophe Fergeau     }
11127f540ab5SChristophe Fergeau };
11137f540ab5SChristophe Fergeau 
111457d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
111557d5c005SHervé Poussineau {
111657d5c005SHervé Poussineau     PS2KbdState *s = opaque;
111757d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
111857d5c005SHervé Poussineau }
111957d5c005SHervé Poussineau 
112057d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
112157d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
112257d5c005SHervé Poussineau     .version_id = 1,
112357d5c005SHervé Poussineau     .minimum_version_id = 1,
112457d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
112557d5c005SHervé Poussineau     .fields = (VMStateField[]) {
112657d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
112757d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
112857d5c005SHervé Poussineau     }
112957d5c005SHervé Poussineau };
113057d5c005SHervé Poussineau 
11314e9bddcbSVolker Rümelin static bool ps2_keyboard_cqueue_needed(void *opaque)
11324e9bddcbSVolker Rümelin {
11334e9bddcbSVolker Rümelin     PS2KbdState *s = opaque;
11348f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11354e9bddcbSVolker Rümelin 
11368f84e53cSMark Cave-Ayland     return ps2->queue.cwptr != -1; /* the queue is mostly empty */
11374e9bddcbSVolker Rümelin }
11384e9bddcbSVolker Rümelin 
11394e9bddcbSVolker Rümelin static const VMStateDescription vmstate_ps2_keyboard_cqueue = {
11404e9bddcbSVolker Rümelin     .name = "ps2kbd/command_reply_queue",
11414e9bddcbSVolker Rümelin     .needed = ps2_keyboard_cqueue_needed,
11424e9bddcbSVolker Rümelin     .fields = (VMStateField[]) {
11438f84e53cSMark Cave-Ayland         VMSTATE_INT32(parent_obj.queue.cwptr, PS2KbdState),
11444e9bddcbSVolker Rümelin         VMSTATE_END_OF_LIST()
11454e9bddcbSVolker Rümelin     }
11464e9bddcbSVolker Rümelin };
11474e9bddcbSVolker Rümelin 
1148db596c53SJuan Quintela static int ps2_kbd_post_load(void *opaque, int version_id)
11490e43e99cSbellard {
11500e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
11518f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11520e43e99cSbellard 
1153545e5cf8SMark Cave-Ayland     if (version_id == 2) {
1154e7d93956Saurel32         s->scancode_set = 2;
1155545e5cf8SMark Cave-Ayland     }
11562858ab09SGonglei 
11572858ab09SGonglei     ps2_common_post_load(ps2);
11582858ab09SGonglei 
11590e43e99cSbellard     return 0;
11600e43e99cSbellard }
11610e43e99cSbellard 
1162b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_keyboard = {
1163b31442c3SJuan Quintela     .name = "ps2kbd",
1164b31442c3SJuan Quintela     .version_id = 3,
1165db596c53SJuan Quintela     .minimum_version_id = 2,
1166db596c53SJuan Quintela     .post_load = ps2_kbd_post_load,
1167b31442c3SJuan Quintela     .fields = (VMStateField[]) {
11688f84e53cSMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2KbdState, 0, vmstate_ps2_common,
11698f84e53cSMark Cave-Ayland                        PS2State),
1170b31442c3SJuan Quintela         VMSTATE_INT32(scan_enabled, PS2KbdState),
1171b31442c3SJuan Quintela         VMSTATE_INT32(translate, PS2KbdState),
1172b31442c3SJuan Quintela         VMSTATE_INT32_V(scancode_set, PS2KbdState, 3),
1173b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
11747f540ab5SChristophe Fergeau     },
11755cd8cadaSJuan Quintela     .subsections = (const VMStateDescription * []) {
11765cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
117757d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
11784e9bddcbSVolker Rümelin         &vmstate_ps2_keyboard_cqueue,
11795cd8cadaSJuan Quintela         NULL
11800e43e99cSbellard     }
1181b31442c3SJuan Quintela };
1182b31442c3SJuan Quintela 
11832858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
11842858ab09SGonglei {
11852858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
11862d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11872858ab09SGonglei 
11882858ab09SGonglei     ps2_common_post_load(ps2);
11892858ab09SGonglei 
11902858ab09SGonglei     return 0;
11912858ab09SGonglei }
11922858ab09SGonglei 
1193b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_mouse = {
1194b31442c3SJuan Quintela     .name = "ps2mouse",
1195b31442c3SJuan Quintela     .version_id = 2,
1196b31442c3SJuan Quintela     .minimum_version_id = 2,
11972858ab09SGonglei     .post_load = ps2_mouse_post_load,
1198b31442c3SJuan Quintela     .fields = (VMStateField[]) {
11992d135409SMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2MouseState, 0, vmstate_ps2_common,
12002d135409SMark Cave-Ayland                        PS2State),
1201b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_status, PS2MouseState),
1202b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
1203b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
1204b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
1205b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_type, PS2MouseState),
1206b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
1207b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dx, PS2MouseState),
1208b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dy, PS2MouseState),
1209b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dz, PS2MouseState),
1210b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
1211b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
1212b31442c3SJuan Quintela     }
1213b31442c3SJuan Quintela };
12140e43e99cSbellard 
121566e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
121666e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
121766e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
121866e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
121966e6536eSGerd Hoffmann };
122066e6536eSGerd Hoffmann 
1221ea247a0fSMark Cave-Ayland static void ps2_kbd_realize(DeviceState *dev, Error **errp)
1222ea247a0fSMark Cave-Ayland {
1223ea247a0fSMark Cave-Ayland     qemu_input_handler_register(dev, &ps2_keyboard_handler);
1224ea247a0fSMark Cave-Ayland }
1225ea247a0fSMark Cave-Ayland 
12260e43e99cSbellard void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
12270e43e99cSbellard {
12288f84e53cSMark Cave-Ayland     DeviceState *dev;
12298f84e53cSMark Cave-Ayland     PS2KbdState *s;
12308f84e53cSMark Cave-Ayland     PS2State *ps2;
12318f84e53cSMark Cave-Ayland 
12328f84e53cSMark Cave-Ayland     dev = qdev_new(TYPE_PS2_KBD_DEVICE);
12338f84e53cSMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
12348f84e53cSMark Cave-Ayland     s = PS2_KBD_DEVICE(dev);
12358f84e53cSMark Cave-Ayland     ps2 = PS2_DEVICE(s);
12360e43e99cSbellard 
12375edab03dSDon Koch     trace_ps2_kbd_init(s);
12388f84e53cSMark Cave-Ayland     ps2->update_irq = update_irq;
12398f84e53cSMark Cave-Ayland     ps2->update_arg = update_arg;
1240ea247a0fSMark Cave-Ayland 
12410e43e99cSbellard     return s;
12420e43e99cSbellard }
12430e43e99cSbellard 
12442a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
12452a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
12462a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
12472a766d29SGerd Hoffmann     .event = ps2_mouse_event,
12482a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
12492a766d29SGerd Hoffmann };
12502a766d29SGerd Hoffmann 
12514a68b482SMark Cave-Ayland static void ps2_mouse_realize(DeviceState *dev, Error **errp)
12524a68b482SMark Cave-Ayland {
12534a68b482SMark Cave-Ayland     qemu_input_handler_register(dev, &ps2_mouse_handler);
12544a68b482SMark Cave-Ayland }
12554a68b482SMark Cave-Ayland 
12560e43e99cSbellard void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
12570e43e99cSbellard {
12582d135409SMark Cave-Ayland     DeviceState *dev;
12592d135409SMark Cave-Ayland     PS2MouseState *s;
12602d135409SMark Cave-Ayland     PS2State *ps2;
12612d135409SMark Cave-Ayland 
12622d135409SMark Cave-Ayland     dev = qdev_new(TYPE_PS2_MOUSE_DEVICE);
12632d135409SMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
12642d135409SMark Cave-Ayland     s = PS2_MOUSE_DEVICE(dev);
12652d135409SMark Cave-Ayland     ps2 = PS2_DEVICE(s);
12660e43e99cSbellard 
12675edab03dSDon Koch     trace_ps2_mouse_init(s);
12682d135409SMark Cave-Ayland     ps2->update_irq = update_irq;
12692d135409SMark Cave-Ayland     ps2->update_arg = update_arg;
12700e43e99cSbellard     return s;
12710e43e99cSbellard }
127264bbdd13SMark Cave-Ayland 
1273108cb22eSMark Cave-Ayland static void ps2_kbd_class_init(ObjectClass *klass, void *data)
1274108cb22eSMark Cave-Ayland {
1275108cb22eSMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
1276108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass);
1277108cb22eSMark Cave-Ayland 
1278ea247a0fSMark Cave-Ayland     dc->realize = ps2_kbd_realize;
1279108cb22eSMark Cave-Ayland     device_class_set_parent_reset(dc, ps2_kbd_reset, &ps2dc->parent_reset);
1280f055f507SMark Cave-Ayland     dc->vmsd = &vmstate_ps2_keyboard;
1281108cb22eSMark Cave-Ayland }
1282108cb22eSMark Cave-Ayland 
12838f84e53cSMark Cave-Ayland static const TypeInfo ps2_kbd_info = {
12848f84e53cSMark Cave-Ayland     .name          = TYPE_PS2_KBD_DEVICE,
12858f84e53cSMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
12868f84e53cSMark Cave-Ayland     .instance_size = sizeof(PS2KbdState),
1287108cb22eSMark Cave-Ayland     .class_init    = ps2_kbd_class_init
12888f84e53cSMark Cave-Ayland };
12898f84e53cSMark Cave-Ayland 
1290108cb22eSMark Cave-Ayland static void ps2_mouse_class_init(ObjectClass *klass, void *data)
1291108cb22eSMark Cave-Ayland {
1292108cb22eSMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
1293108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass);
1294108cb22eSMark Cave-Ayland 
12954a68b482SMark Cave-Ayland     dc->realize = ps2_mouse_realize;
1296108cb22eSMark Cave-Ayland     device_class_set_parent_reset(dc, ps2_mouse_reset,
1297108cb22eSMark Cave-Ayland                                   &ps2dc->parent_reset);
129897259e70SMark Cave-Ayland     dc->vmsd = &vmstate_ps2_mouse;
1299108cb22eSMark Cave-Ayland }
1300108cb22eSMark Cave-Ayland 
13012d135409SMark Cave-Ayland static const TypeInfo ps2_mouse_info = {
13022d135409SMark Cave-Ayland     .name          = TYPE_PS2_MOUSE_DEVICE,
13032d135409SMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
13042d135409SMark Cave-Ayland     .instance_size = sizeof(PS2MouseState),
1305108cb22eSMark Cave-Ayland     .class_init    = ps2_mouse_class_init
13062d135409SMark Cave-Ayland };
13072d135409SMark Cave-Ayland 
130864bbdd13SMark Cave-Ayland static void ps2_class_init(ObjectClass *klass, void *data)
130964bbdd13SMark Cave-Ayland {
131064bbdd13SMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
131164bbdd13SMark Cave-Ayland 
1312108cb22eSMark Cave-Ayland     dc->reset = ps2_reset;
131364bbdd13SMark Cave-Ayland     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
131464bbdd13SMark Cave-Ayland }
131564bbdd13SMark Cave-Ayland 
131664bbdd13SMark Cave-Ayland static const TypeInfo ps2_info = {
131764bbdd13SMark Cave-Ayland     .name          = TYPE_PS2_DEVICE,
131864bbdd13SMark Cave-Ayland     .parent        = TYPE_SYS_BUS_DEVICE,
131964bbdd13SMark Cave-Ayland     .instance_size = sizeof(PS2State),
132064bbdd13SMark Cave-Ayland     .class_init    = ps2_class_init,
1321494145b2SMark Cave-Ayland     .class_size    = sizeof(PS2DeviceClass),
132264bbdd13SMark Cave-Ayland     .abstract      = true
132364bbdd13SMark Cave-Ayland };
132464bbdd13SMark Cave-Ayland 
132564bbdd13SMark Cave-Ayland static void ps2_register_types(void)
132664bbdd13SMark Cave-Ayland {
132764bbdd13SMark Cave-Ayland     type_register_static(&ps2_info);
13288f84e53cSMark Cave-Ayland     type_register_static(&ps2_kbd_info);
13292d135409SMark Cave-Ayland     type_register_static(&ps2_mouse_info);
133064bbdd13SMark Cave-Ayland }
133164bbdd13SMark Cave-Ayland 
133264bbdd13SMark Cave-Ayland type_init(ps2_register_types)
1333