xref: /qemu/hw/input/ps2.c (revision ad80e367)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU PS/2 keyboard/mouse emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2003 Fabrice Bellard
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
749ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
849ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
949ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1149ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1449ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249ab747fSPaolo Bonzini  * THE SOFTWARE.
2349ab747fSPaolo Bonzini  */
2471e8a915SMarkus Armbruster 
250430891cSPeter Maydell #include "qemu/osdep.h"
26ec044a80SHervé Poussineau #include "qemu/log.h"
276beb79e1SMark Cave-Ayland #include "hw/irq.h"
2864bbdd13SMark Cave-Ayland #include "hw/sysbus.h"
2949ab747fSPaolo Bonzini #include "hw/input/ps2.h"
30d6454270SMarkus Armbruster #include "migration/vmstate.h"
3149ab747fSPaolo Bonzini #include "ui/console.h"
3266e6536eSGerd Hoffmann #include "ui/input.h"
3371e8a915SMarkus Armbruster #include "sysemu/reset.h"
3454d31236SMarkus Armbruster #include "sysemu/runstate.h"
358f84e53cSMark Cave-Ayland #include "qapi/error.h"
3649ab747fSPaolo Bonzini 
375edab03dSDon Koch #include "trace.h"
385edab03dSDon Koch 
3949ab747fSPaolo Bonzini /* Keyboard Commands */
4049ab747fSPaolo Bonzini #define KBD_CMD_SET_LEDS        0xED    /* Set keyboard leds */
4149ab747fSPaolo Bonzini #define KBD_CMD_ECHO            0xEE
4249ab747fSPaolo Bonzini #define KBD_CMD_SCANCODE        0xF0    /* Get/set scancode set */
4349ab747fSPaolo Bonzini #define KBD_CMD_GET_ID          0xF2    /* get keyboard ID */
4449ab747fSPaolo Bonzini #define KBD_CMD_SET_RATE        0xF3    /* Set typematic rate */
4549ab747fSPaolo Bonzini #define KBD_CMD_ENABLE          0xF4    /* Enable scanning */
4649ab747fSPaolo Bonzini #define KBD_CMD_RESET_DISABLE   0xF5    /* reset and disable scanning */
4749ab747fSPaolo Bonzini #define KBD_CMD_RESET_ENABLE    0xF6    /* reset and enable scanning */
4849ab747fSPaolo Bonzini #define KBD_CMD_RESET           0xFF    /* Reset */
49c56b6209SSven Schnelle #define KBD_CMD_SET_MAKE_BREAK  0xFC    /* Set Make and Break mode */
50c56b6209SSven Schnelle #define KBD_CMD_SET_TYPEMATIC   0xFA    /* Set Typematic Make and Break mode */
5149ab747fSPaolo Bonzini 
5249ab747fSPaolo Bonzini /* Keyboard Replies */
5349ab747fSPaolo Bonzini #define KBD_REPLY_POR       0xAA    /* Power on reset */
5449ab747fSPaolo Bonzini #define KBD_REPLY_ID        0xAB    /* Keyboard ID */
5549ab747fSPaolo Bonzini #define KBD_REPLY_ACK       0xFA    /* Command ACK */
5649ab747fSPaolo Bonzini #define KBD_REPLY_RESEND    0xFE    /* Command NACK, send the cmd again */
5749ab747fSPaolo Bonzini 
5849ab747fSPaolo Bonzini /* Mouse Commands */
5949ab747fSPaolo Bonzini #define AUX_SET_SCALE11     0xE6    /* Set 1:1 scaling */
6049ab747fSPaolo Bonzini #define AUX_SET_SCALE21     0xE7    /* Set 2:1 scaling */
6149ab747fSPaolo Bonzini #define AUX_SET_RES         0xE8    /* Set resolution */
6249ab747fSPaolo Bonzini #define AUX_GET_SCALE       0xE9    /* Get scaling factor */
6349ab747fSPaolo Bonzini #define AUX_SET_STREAM      0xEA    /* Set stream mode */
6449ab747fSPaolo Bonzini #define AUX_POLL            0xEB    /* Poll */
6549ab747fSPaolo Bonzini #define AUX_RESET_WRAP      0xEC    /* Reset wrap mode */
6649ab747fSPaolo Bonzini #define AUX_SET_WRAP        0xEE    /* Set wrap mode */
6749ab747fSPaolo Bonzini #define AUX_SET_REMOTE      0xF0    /* Set remote mode */
6849ab747fSPaolo Bonzini #define AUX_GET_TYPE        0xF2    /* Get type */
6949ab747fSPaolo Bonzini #define AUX_SET_SAMPLE      0xF3    /* Set sample rate */
7049ab747fSPaolo Bonzini #define AUX_ENABLE_DEV      0xF4    /* Enable aux device */
7149ab747fSPaolo Bonzini #define AUX_DISABLE_DEV     0xF5    /* Disable aux device */
7249ab747fSPaolo Bonzini #define AUX_SET_DEFAULT     0xF6
7349ab747fSPaolo Bonzini #define AUX_RESET           0xFF    /* Reset aux device */
7449ab747fSPaolo Bonzini #define AUX_ACK             0xFA    /* Command byte ACK. */
7549ab747fSPaolo Bonzini 
7649ab747fSPaolo Bonzini #define MOUSE_STATUS_REMOTE     0x40
7749ab747fSPaolo Bonzini #define MOUSE_STATUS_ENABLED    0x20
7849ab747fSPaolo Bonzini #define MOUSE_STATUS_SCALE21    0x10
7949ab747fSPaolo Bonzini 
8047db2432SVolker Rümelin #define PS2_QUEUE_SIZE      16  /* Queue size required by PS/2 protocol */
814e9bddcbSVolker Rümelin #define PS2_QUEUE_HEADROOM  8   /* Queue size for keyboard command replies */
8249ab747fSPaolo Bonzini 
83620775d1SDaniel P. Berrange /* Bits for 'modifiers' field in PS2KbdState */
84620775d1SDaniel P. Berrange #define MOD_CTRL_L  (1 << 0)
85620775d1SDaniel P. Berrange #define MOD_SHIFT_L (1 << 1)
86620775d1SDaniel P. Berrange #define MOD_ALT_L   (1 << 2)
87620775d1SDaniel P. Berrange #define MOD_CTRL_R  (1 << 3)
88620775d1SDaniel P. Berrange #define MOD_SHIFT_R (1 << 4)
89620775d1SDaniel P. Berrange #define MOD_ALT_R   (1 << 5)
90620775d1SDaniel P. Berrange 
9157d5c005SHervé Poussineau static uint8_t translate_table[256] = {
9257d5c005SHervé Poussineau     0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
9357d5c005SHervé Poussineau     0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
9457d5c005SHervé Poussineau     0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
9557d5c005SHervé Poussineau     0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
9657d5c005SHervé Poussineau     0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
9757d5c005SHervé Poussineau     0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
9857d5c005SHervé Poussineau     0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
9957d5c005SHervé Poussineau     0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
10057d5c005SHervé Poussineau     0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
10157d5c005SHervé Poussineau     0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
10257d5c005SHervé Poussineau     0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
10357d5c005SHervé Poussineau     0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
10457d5c005SHervé Poussineau     0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
10557d5c005SHervé Poussineau     0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
10657d5c005SHervé Poussineau     0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
10757d5c005SHervé Poussineau     0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
10857d5c005SHervé Poussineau     0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
10957d5c005SHervé Poussineau     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
11057d5c005SHervé Poussineau     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
11157d5c005SHervé Poussineau     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
11257d5c005SHervé Poussineau     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
11357d5c005SHervé Poussineau     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
11457d5c005SHervé Poussineau     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
11557d5c005SHervé Poussineau     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
11657d5c005SHervé Poussineau     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
11757d5c005SHervé Poussineau     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
11857d5c005SHervé Poussineau     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
11957d5c005SHervé Poussineau     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
12057d5c005SHervé Poussineau     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
12157d5c005SHervé Poussineau     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
12257d5c005SHervé Poussineau     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
12357d5c005SHervé Poussineau     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
12457d5c005SHervé Poussineau };
12557d5c005SHervé Poussineau 
ps2_modifier_bit(QKeyCode key)126620775d1SDaniel P. Berrange static unsigned int ps2_modifier_bit(QKeyCode key)
127620775d1SDaniel P. Berrange {
128620775d1SDaniel P. Berrange     switch (key) {
129620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL:
130620775d1SDaniel P. Berrange         return MOD_CTRL_L;
131620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL_R:
132620775d1SDaniel P. Berrange         return MOD_CTRL_R;
133620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT:
134620775d1SDaniel P. Berrange         return MOD_SHIFT_L;
135620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT_R:
136620775d1SDaniel P. Berrange         return MOD_SHIFT_R;
137620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT:
138620775d1SDaniel P. Berrange         return MOD_ALT_L;
139620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT_R:
140620775d1SDaniel P. Berrange         return MOD_ALT_R;
141620775d1SDaniel P. Berrange     default:
142620775d1SDaniel P. Berrange         return 0;
143620775d1SDaniel P. Berrange     }
144620775d1SDaniel P. Berrange }
145620775d1SDaniel P. Berrange 
ps2_reset_queue(PS2State * s)146954ee55bSGerd Hoffmann static void ps2_reset_queue(PS2State *s)
147954ee55bSGerd Hoffmann {
148954ee55bSGerd Hoffmann     PS2Queue *q = &s->queue;
149954ee55bSGerd Hoffmann 
150954ee55bSGerd Hoffmann     q->rptr = 0;
151954ee55bSGerd Hoffmann     q->wptr = 0;
1529e24b2ddSVolker Rümelin     q->cwptr = -1;
153954ee55bSGerd Hoffmann     q->count = 0;
154954ee55bSGerd Hoffmann }
155954ee55bSGerd Hoffmann 
ps2_queue_empty(PS2State * s)1562a6505b0SSven Schnelle int ps2_queue_empty(PS2State *s)
1572a6505b0SSven Schnelle {
1582a6505b0SSven Schnelle     return s->queue.count == 0;
1592a6505b0SSven Schnelle }
1602a6505b0SSven Schnelle 
ps2_queue_noirq(PS2State * s,int b)1617abe7eb2SGeoffrey McRae void ps2_queue_noirq(PS2State *s, int b)
16249ab747fSPaolo Bonzini {
16349ab747fSPaolo Bonzini     PS2Queue *q = &s->queue;
16449ab747fSPaolo Bonzini 
1659e24b2ddSVolker Rümelin     if (q->count >= PS2_QUEUE_SIZE) {
16649ab747fSPaolo Bonzini         return;
1677abe7eb2SGeoffrey McRae     }
1687abe7eb2SGeoffrey McRae 
16949ab747fSPaolo Bonzini     q->data[q->wptr] = b;
17047db2432SVolker Rümelin     if (++q->wptr == PS2_BUFFER_SIZE) {
17149ab747fSPaolo Bonzini         q->wptr = 0;
17247db2432SVolker Rümelin     }
17349ab747fSPaolo Bonzini     q->count++;
1747abe7eb2SGeoffrey McRae }
1757abe7eb2SGeoffrey McRae 
ps2_raise_irq(PS2State * s)17652b28f76SMark Cave-Ayland static void ps2_raise_irq(PS2State *s)
1777abe7eb2SGeoffrey McRae {
1786beb79e1SMark Cave-Ayland     qemu_set_irq(s->irq, 1);
1796beb79e1SMark Cave-Ayland }
1807abe7eb2SGeoffrey McRae 
ps2_lower_irq(PS2State * s)1815cb6e556SMark Cave-Ayland static void ps2_lower_irq(PS2State *s)
1825cb6e556SMark Cave-Ayland {
1836beb79e1SMark Cave-Ayland     qemu_set_irq(s->irq, 0);
1846beb79e1SMark Cave-Ayland }
1855cb6e556SMark Cave-Ayland 
ps2_queue(PS2State * s,int b)1867abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
1877abe7eb2SGeoffrey McRae {
1887704bb02SVolker Rümelin     if (PS2_QUEUE_SIZE - s->queue.count < 1) {
1897704bb02SVolker Rümelin         return;
1907704bb02SVolker Rümelin     }
1917704bb02SVolker Rümelin 
1927abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
19396376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
1947abe7eb2SGeoffrey McRae }
1957abe7eb2SGeoffrey McRae 
ps2_queue_2(PS2State * s,int b1,int b2)1967abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
1977abe7eb2SGeoffrey McRae {
1987abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
1997abe7eb2SGeoffrey McRae         return;
2007abe7eb2SGeoffrey McRae     }
2017abe7eb2SGeoffrey McRae 
2027abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2037abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
20496376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2057abe7eb2SGeoffrey McRae }
2067abe7eb2SGeoffrey McRae 
ps2_queue_3(PS2State * s,int b1,int b2,int b3)2077abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2087abe7eb2SGeoffrey McRae {
2097abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2107abe7eb2SGeoffrey McRae         return;
2117abe7eb2SGeoffrey McRae     }
2127abe7eb2SGeoffrey McRae 
2137abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2147abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2157abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
21696376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2177abe7eb2SGeoffrey McRae }
2187abe7eb2SGeoffrey McRae 
ps2_queue_4(PS2State * s,int b1,int b2,int b3,int b4)2197abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2207abe7eb2SGeoffrey McRae {
2217abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2227abe7eb2SGeoffrey McRae         return;
2237abe7eb2SGeoffrey McRae     }
2247abe7eb2SGeoffrey McRae 
2257abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2267abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2277abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2287abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
22996376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
23049ab747fSPaolo Bonzini }
23149ab747fSPaolo Bonzini 
ps2_cqueue_data(PS2Queue * q,int b)2329e24b2ddSVolker Rümelin static void ps2_cqueue_data(PS2Queue *q, int b)
2339e24b2ddSVolker Rümelin {
2349e24b2ddSVolker Rümelin     q->data[q->cwptr] = b;
2359e24b2ddSVolker Rümelin     if (++q->cwptr >= PS2_BUFFER_SIZE) {
2369e24b2ddSVolker Rümelin         q->cwptr = 0;
2379e24b2ddSVolker Rümelin     }
2389e24b2ddSVolker Rümelin     q->count++;
2399e24b2ddSVolker Rümelin }
2409e24b2ddSVolker Rümelin 
ps2_cqueue_1(PS2State * s,int b1)2419e24b2ddSVolker Rümelin static void ps2_cqueue_1(PS2State *s, int b1)
2429e24b2ddSVolker Rümelin {
2439e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2449e24b2ddSVolker Rümelin 
2459e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 1) & (PS2_BUFFER_SIZE - 1);
2469e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2479e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2489e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2499e24b2ddSVolker Rümelin }
2509e24b2ddSVolker Rümelin 
ps2_cqueue_2(PS2State * s,int b1,int b2)2519e24b2ddSVolker Rümelin static void ps2_cqueue_2(PS2State *s, int b1, int b2)
2529e24b2ddSVolker Rümelin {
2539e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2549e24b2ddSVolker Rümelin 
2559e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 2) & (PS2_BUFFER_SIZE - 1);
2569e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2579e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2589e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
2599e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2609e24b2ddSVolker Rümelin }
2619e24b2ddSVolker Rümelin 
ps2_cqueue_3(PS2State * s,int b1,int b2,int b3)2629e24b2ddSVolker Rümelin static void ps2_cqueue_3(PS2State *s, int b1, int b2, int b3)
2639e24b2ddSVolker Rümelin {
2649e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2659e24b2ddSVolker Rümelin 
2669e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 3) & (PS2_BUFFER_SIZE - 1);
2679e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2689e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2699e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
2709e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b3);
2719e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2729e24b2ddSVolker Rümelin }
2739e24b2ddSVolker Rümelin 
ps2_cqueue_reset(PS2State * s)2749e24b2ddSVolker Rümelin static void ps2_cqueue_reset(PS2State *s)
2759e24b2ddSVolker Rümelin {
2769e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2779e24b2ddSVolker Rümelin     int ccount;
2789e24b2ddSVolker Rümelin 
2799e24b2ddSVolker Rümelin     if (q->cwptr == -1) {
2809e24b2ddSVolker Rümelin         return;
2819e24b2ddSVolker Rümelin     }
2829e24b2ddSVolker Rümelin 
2839e24b2ddSVolker Rümelin     ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
2849e24b2ddSVolker Rümelin     q->count -= ccount;
2859e24b2ddSVolker Rümelin     q->rptr = q->cwptr;
2869e24b2ddSVolker Rümelin     q->cwptr = -1;
2879e24b2ddSVolker Rümelin }
2889e24b2ddSVolker Rümelin 
28957d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
ps2_put_keycode(void * opaque,int keycode)29049ab747fSPaolo Bonzini static void ps2_put_keycode(void *opaque, int keycode)
29149ab747fSPaolo Bonzini {
29249ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
2938f84e53cSMark Cave-Ayland     PS2State *ps = PS2_DEVICE(s);
29449ab747fSPaolo Bonzini 
2955edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
296fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
29757d5c005SHervé Poussineau 
29857d5c005SHervé Poussineau     if (s->translate) {
29957d5c005SHervé Poussineau         if (keycode == 0xf0) {
30057d5c005SHervé Poussineau             s->need_high_bit = true;
30157d5c005SHervé Poussineau         } else if (s->need_high_bit) {
3028f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode] | 0x80);
30357d5c005SHervé Poussineau             s->need_high_bit = false;
30457d5c005SHervé Poussineau         } else {
3058f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode]);
30649ab747fSPaolo Bonzini         }
30757d5c005SHervé Poussineau     } else {
3088f84e53cSMark Cave-Ayland         ps2_queue(ps, keycode);
30949ab747fSPaolo Bonzini     }
31057d5c005SHervé Poussineau }
31149ab747fSPaolo Bonzini 
ps2_keyboard_event(DeviceState * dev,QemuConsole * src,InputEvent * evt)31266e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
31366e6536eSGerd Hoffmann                                InputEvent *evt)
31466e6536eSGerd Hoffmann {
31566e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
31632bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
3178c10e0baSHervé Poussineau     int qcode;
318ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
319620775d1SDaniel P. Berrange     int mod;
32066e6536eSGerd Hoffmann 
321143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
322143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
323143c04c7SGeoffrey McRae         return;
324143c04c7SGeoffrey McRae     }
325143c04c7SGeoffrey McRae 
326fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
3278c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
3288c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
32957d5c005SHervé Poussineau 
330620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
331644f66bfSDaniel P. Berrangé     trace_ps2_keyboard_event(s, qcode, key->down, mod,
332644f66bfSDaniel P. Berrangé                              s->modifiers, s->scancode_set, s->translate);
333620775d1SDaniel P. Berrange     if (key->down) {
334620775d1SDaniel P. Berrange         s->modifiers |= mod;
335620775d1SDaniel P. Berrange     } else {
336620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
337620775d1SDaniel P. Berrange     }
338620775d1SDaniel P. Berrange 
3398c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3408c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
34129fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
34229fd23a5SDaniel P. Berrange                 if (key->down) {
34329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
34429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
34529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
34629fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
34729fd23a5SDaniel P. Berrange                 }
34829fd23a5SDaniel P. Berrange             } else {
3498c10e0baSHervé Poussineau                 if (key->down) {
3508c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3518c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3528c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
353927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3548c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
3558c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
3568c10e0baSHervé Poussineau                 }
35729fd23a5SDaniel P. Berrange             }
3588c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
359620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
360620775d1SDaniel P. Berrange                 if (key->down) {
361620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
362620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
363620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
364620775d1SDaniel P. Berrange                 } else {
365620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
366620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
367620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
368620775d1SDaniel P. Berrange                 }
369620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
370620775d1SDaniel P. Berrange                 if (key->down) {
371620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
372620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
373620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
374620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
375620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
376620775d1SDaniel P. Berrange                 } else {
377620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
378620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
379620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
380620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
381620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
382620775d1SDaniel P. Berrange                 }
3838f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
3848f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
3858f63458fSDaniel P. Berrange                 if (key->down) {
3868f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3878f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
3888f63458fSDaniel P. Berrange                 } else {
3898f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3908f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
3918f63458fSDaniel P. Berrange                 }
392620775d1SDaniel P. Berrange             } else {
3938c10e0baSHervé Poussineau                 if (key->down) {
3948c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3958c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
3968c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
3978c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
3988c10e0baSHervé Poussineau                 } else {
3998c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4008c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
4018c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4028c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
4038c10e0baSHervé Poussineau                 }
404620775d1SDaniel P. Berrange             }
40592f4a21dSRoss Lagerwall         } else if ((qcode == Q_KEY_CODE_LANG1 || qcode == Q_KEY_CODE_LANG2)
40692f4a21dSRoss Lagerwall                    && !key->down) {
40792f4a21dSRoss Lagerwall             /* Ignore release for these keys */
4088c10e0baSHervé Poussineau         } else {
409545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset1_len) {
410ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
411545e5cf8SMark Cave-Ayland             }
4128c10e0baSHervé Poussineau             if (keycode) {
4138c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4148c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4158c10e0baSHervé Poussineau                 }
4168c10e0baSHervé Poussineau                 if (!key->down) {
4178c10e0baSHervé Poussineau                     keycode |= 0x80;
4188c10e0baSHervé Poussineau                 }
4198c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
4208c10e0baSHervé Poussineau             } else {
421ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
422ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
4238c10e0baSHervé Poussineau             }
4248c10e0baSHervé Poussineau         }
4258c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
4268c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
42729fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
42829fd23a5SDaniel P. Berrange                 if (key->down) {
42929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
43029fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
43129fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
43229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
43329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
43429fd23a5SDaniel P. Berrange                 }
43529fd23a5SDaniel P. Berrange             } else {
4368c10e0baSHervé Poussineau                 if (key->down) {
4378c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4388c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4398c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4408c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4418c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4428c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4438c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4448c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4458c10e0baSHervé Poussineau                 }
44629fd23a5SDaniel P. Berrange             }
4478c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
448620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
449620775d1SDaniel P. Berrange                 if (key->down) {
450620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
451620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
452620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
453620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
454620775d1SDaniel P. Berrange                 } else {
455620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
456620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
457620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
458620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
459620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
460620775d1SDaniel P. Berrange                 }
461620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
462620775d1SDaniel P. Berrange                 if (key->down) {
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                     ps2_put_keycode(s, 0x84);
469620775d1SDaniel P. Berrange                 } else {
470620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
471620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
472620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
473620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
474620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
475620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
476620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
477620775d1SDaniel P. Berrange                 }
4788f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4798f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4808f63458fSDaniel P. Berrange                 if (key->down) {
4818f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4828f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4838f63458fSDaniel P. Berrange                 } else {
4848f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4858f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
4868f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4878f63458fSDaniel P. Berrange                 }
488620775d1SDaniel P. Berrange             } else {
4898c10e0baSHervé Poussineau                 if (key->down) {
4908c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4918c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4928c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4938c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4948c10e0baSHervé Poussineau                 } else {
4958c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4968c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4978c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4988c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4998c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5008c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
5018c10e0baSHervé Poussineau                 }
502620775d1SDaniel P. Berrange             }
50392f4a21dSRoss Lagerwall         } else if ((qcode == Q_KEY_CODE_LANG1 || qcode == Q_KEY_CODE_LANG2) &&
50492f4a21dSRoss Lagerwall                    !key->down) {
50592f4a21dSRoss Lagerwall             /* Ignore release for these keys */
5068c10e0baSHervé Poussineau         } else {
507545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset2_len) {
508ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
509545e5cf8SMark Cave-Ayland             }
5108c10e0baSHervé Poussineau             if (keycode) {
5118c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
5128c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
5138c10e0baSHervé Poussineau                 }
5148c10e0baSHervé Poussineau                 if (!key->down) {
5158c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5168c10e0baSHervé Poussineau                 }
5178c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
51857d5c005SHervé Poussineau             } else {
519ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
520ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
52157d5c005SHervé Poussineau             }
52257d5c005SHervé Poussineau         }
52357d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
524545e5cf8SMark Cave-Ayland         if (qcode < qemu_input_map_qcode_to_atset3_len) {
525ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
526545e5cf8SMark Cave-Ayland         }
5278c10e0baSHervé Poussineau         if (keycode) {
5288c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
5298c10e0baSHervé Poussineau             if (!key->down) {
5308c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
53157d5c005SHervé Poussineau             }
53257d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
5338c10e0baSHervé Poussineau         } else {
534ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
535ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
5368c10e0baSHervé Poussineau         }
53766e6536eSGerd Hoffmann     }
53866e6536eSGerd Hoffmann }
53966e6536eSGerd Hoffmann 
ps2_read_data(PS2State * s)5408498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
54149ab747fSPaolo Bonzini {
54249ab747fSPaolo Bonzini     PS2Queue *q;
54349ab747fSPaolo Bonzini     int val, index;
54449ab747fSPaolo Bonzini 
5458498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
54649ab747fSPaolo Bonzini     q = &s->queue;
54749ab747fSPaolo Bonzini     if (q->count == 0) {
548545e5cf8SMark Cave-Ayland         /*
549545e5cf8SMark Cave-Ayland          * NOTE: if no data left, we return the last keyboard one
550545e5cf8SMark Cave-Ayland          * (needed for EMM386)
551545e5cf8SMark Cave-Ayland          */
55249ab747fSPaolo Bonzini         /* XXX: need a timer to do things correctly */
55349ab747fSPaolo Bonzini         index = q->rptr - 1;
55447db2432SVolker Rümelin         if (index < 0) {
55547db2432SVolker Rümelin             index = PS2_BUFFER_SIZE - 1;
55647db2432SVolker Rümelin         }
55749ab747fSPaolo Bonzini         val = q->data[index];
55849ab747fSPaolo Bonzini     } else {
55949ab747fSPaolo Bonzini         val = q->data[q->rptr];
56047db2432SVolker Rümelin         if (++q->rptr == PS2_BUFFER_SIZE) {
56149ab747fSPaolo Bonzini             q->rptr = 0;
56247db2432SVolker Rümelin         }
56349ab747fSPaolo Bonzini         q->count--;
5649e24b2ddSVolker Rümelin         if (q->rptr == q->cwptr) {
5659e24b2ddSVolker Rümelin             /* command reply queue is empty */
5669e24b2ddSVolker Rümelin             q->cwptr = -1;
5679e24b2ddSVolker Rümelin         }
56849ab747fSPaolo Bonzini         /* reading deasserts IRQ */
5695cb6e556SMark Cave-Ayland         ps2_lower_irq(s);
57049ab747fSPaolo Bonzini         /* reassert IRQs if data left */
571cec32524SVolker Rümelin         if (q->count) {
572892e9bbeSMark Cave-Ayland             ps2_raise_irq(s);
573cec32524SVolker Rümelin         }
57449ab747fSPaolo Bonzini     }
57549ab747fSPaolo Bonzini     return val;
57649ab747fSPaolo Bonzini }
57749ab747fSPaolo Bonzini 
ps2_set_ledstate(PS2KbdState * s,int ledstate)57849ab747fSPaolo Bonzini static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
57949ab747fSPaolo Bonzini {
5805edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
58149ab747fSPaolo Bonzini     s->ledstate = ledstate;
58249ab747fSPaolo Bonzini     kbd_put_ledstate(ledstate);
58349ab747fSPaolo Bonzini }
58449ab747fSPaolo Bonzini 
ps2_reset_keyboard(PS2KbdState * s)58549ab747fSPaolo Bonzini static void ps2_reset_keyboard(PS2KbdState *s)
58649ab747fSPaolo Bonzini {
5878f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
5888f84e53cSMark Cave-Ayland 
5895edab03dSDon Koch     trace_ps2_reset_keyboard(s);
59049ab747fSPaolo Bonzini     s->scan_enabled = 1;
59149ab747fSPaolo Bonzini     s->scancode_set = 2;
5928f84e53cSMark Cave-Ayland     ps2_reset_queue(ps2);
59349ab747fSPaolo Bonzini     ps2_set_ledstate(s, 0);
59449ab747fSPaolo Bonzini }
59549ab747fSPaolo Bonzini 
ps2_write_keyboard(PS2KbdState * s,int val)59654334e73SMark Cave-Ayland void ps2_write_keyboard(PS2KbdState *s, int val)
59749ab747fSPaolo Bonzini {
5988f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
59949ab747fSPaolo Bonzini 
60054334e73SMark Cave-Ayland     trace_ps2_write_keyboard(s, val);
6018f84e53cSMark Cave-Ayland     ps2_cqueue_reset(ps2);
6028f84e53cSMark Cave-Ayland     switch (ps2->write_cmd) {
60349ab747fSPaolo Bonzini     default:
60449ab747fSPaolo Bonzini     case -1:
60549ab747fSPaolo Bonzini         switch (val) {
60649ab747fSPaolo Bonzini         case 0x00:
6078f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
60849ab747fSPaolo Bonzini             break;
60949ab747fSPaolo Bonzini         case 0x05:
6108f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
61149ab747fSPaolo Bonzini             break;
61249ab747fSPaolo Bonzini         case KBD_CMD_GET_ID:
61349ab747fSPaolo Bonzini             /* We emulate a MF2 AT keyboard here */
6148f84e53cSMark Cave-Ayland             ps2_cqueue_3(ps2, KBD_REPLY_ACK, KBD_REPLY_ID,
6159e24b2ddSVolker Rümelin                          s->translate ? 0x41 : 0x83);
61649ab747fSPaolo Bonzini             break;
61749ab747fSPaolo Bonzini         case KBD_CMD_ECHO:
6188f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_CMD_ECHO);
61949ab747fSPaolo Bonzini             break;
62049ab747fSPaolo Bonzini         case KBD_CMD_ENABLE:
62149ab747fSPaolo Bonzini             s->scan_enabled = 1;
6228f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
62349ab747fSPaolo Bonzini             break;
62449ab747fSPaolo Bonzini         case KBD_CMD_SCANCODE:
62549ab747fSPaolo Bonzini         case KBD_CMD_SET_LEDS:
62649ab747fSPaolo Bonzini         case KBD_CMD_SET_RATE:
627c56b6209SSven Schnelle         case KBD_CMD_SET_MAKE_BREAK:
6288f84e53cSMark Cave-Ayland             ps2->write_cmd = val;
6298f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
63049ab747fSPaolo Bonzini             break;
63149ab747fSPaolo Bonzini         case KBD_CMD_RESET_DISABLE:
63249ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
63349ab747fSPaolo Bonzini             s->scan_enabled = 0;
6348f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
63549ab747fSPaolo Bonzini             break;
63649ab747fSPaolo Bonzini         case KBD_CMD_RESET_ENABLE:
63749ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
63849ab747fSPaolo Bonzini             s->scan_enabled = 1;
6398f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
64049ab747fSPaolo Bonzini             break;
64149ab747fSPaolo Bonzini         case KBD_CMD_RESET:
64249ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
6438f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2,
6447abe7eb2SGeoffrey McRae                          KBD_REPLY_ACK,
6457abe7eb2SGeoffrey McRae                          KBD_REPLY_POR);
64649ab747fSPaolo Bonzini             break;
647c56b6209SSven Schnelle         case KBD_CMD_SET_TYPEMATIC:
6488f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
649c56b6209SSven Schnelle             break;
65049ab747fSPaolo Bonzini         default:
6518f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
65249ab747fSPaolo Bonzini             break;
65349ab747fSPaolo Bonzini         }
65449ab747fSPaolo Bonzini         break;
655c56b6209SSven Schnelle     case KBD_CMD_SET_MAKE_BREAK:
6568f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6578f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
658c56b6209SSven Schnelle         break;
65949ab747fSPaolo Bonzini     case KBD_CMD_SCANCODE:
66049ab747fSPaolo Bonzini         if (val == 0) {
6618f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2, KBD_REPLY_ACK, s->translate ?
6629e24b2ddSVolker Rümelin                 translate_table[s->scancode_set] : s->scancode_set);
6634df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
66449ab747fSPaolo Bonzini             s->scancode_set = val;
6658f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6664df23b64SHervé Poussineau         } else {
6678f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
66849ab747fSPaolo Bonzini         }
6698f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
67049ab747fSPaolo Bonzini         break;
67149ab747fSPaolo Bonzini     case KBD_CMD_SET_LEDS:
67249ab747fSPaolo Bonzini         ps2_set_ledstate(s, val);
6738f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6748f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
67549ab747fSPaolo Bonzini         break;
67649ab747fSPaolo Bonzini     case KBD_CMD_SET_RATE:
6778f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6788f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
67949ab747fSPaolo Bonzini         break;
68049ab747fSPaolo Bonzini     }
68149ab747fSPaolo Bonzini }
68249ab747fSPaolo Bonzini 
683545e5cf8SMark Cave-Ayland /*
684545e5cf8SMark Cave-Ayland  * Set the scancode translation mode.
685545e5cf8SMark Cave-Ayland  * 0 = raw scancodes.
686545e5cf8SMark Cave-Ayland  * 1 = translated scancodes (used by qemu internally).
687545e5cf8SMark Cave-Ayland  */
68849ab747fSPaolo Bonzini 
ps2_keyboard_set_translation(PS2KbdState * s,int mode)68954334e73SMark Cave-Ayland void ps2_keyboard_set_translation(PS2KbdState *s, int mode)
69049ab747fSPaolo Bonzini {
69154334e73SMark Cave-Ayland     trace_ps2_keyboard_set_translation(s, mode);
69249ab747fSPaolo Bonzini     s->translate = mode;
69349ab747fSPaolo Bonzini }
69449ab747fSPaolo Bonzini 
ps2_mouse_send_packet(PS2MouseState * s)6957abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
69649ab747fSPaolo Bonzini {
6972d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
69876968101SVolker Rümelin     /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
69976968101SVolker Rümelin     const int needed = s->mouse_type ? 4 : 3;
70049ab747fSPaolo Bonzini     unsigned int b;
70164ebbb7dSDmitry Petrov     int dx1, dy1, dz1, dw1;
70249ab747fSPaolo Bonzini 
7032d135409SMark Cave-Ayland     if (PS2_QUEUE_SIZE - ps2->queue.count < needed) {
7047abe7eb2SGeoffrey McRae         return 0;
7057abe7eb2SGeoffrey McRae     }
7067abe7eb2SGeoffrey McRae 
70749ab747fSPaolo Bonzini     dx1 = s->mouse_dx;
70849ab747fSPaolo Bonzini     dy1 = s->mouse_dy;
70949ab747fSPaolo Bonzini     dz1 = s->mouse_dz;
71064ebbb7dSDmitry Petrov     dw1 = s->mouse_dw;
71149ab747fSPaolo Bonzini     /* XXX: increase range to 8 bits ? */
712545e5cf8SMark Cave-Ayland     if (dx1 > 127) {
71349ab747fSPaolo Bonzini         dx1 = 127;
714545e5cf8SMark Cave-Ayland     } else if (dx1 < -127) {
71549ab747fSPaolo Bonzini         dx1 = -127;
716545e5cf8SMark Cave-Ayland     }
717545e5cf8SMark Cave-Ayland     if (dy1 > 127) {
71849ab747fSPaolo Bonzini         dy1 = 127;
719545e5cf8SMark Cave-Ayland     } else if (dy1 < -127) {
72049ab747fSPaolo Bonzini         dy1 = -127;
721545e5cf8SMark Cave-Ayland     }
72249ab747fSPaolo Bonzini     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
7232d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, b);
7242d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, dx1 & 0xff);
7252d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, dy1 & 0xff);
72649ab747fSPaolo Bonzini     /* extra byte for IMPS/2 or IMEX */
72749ab747fSPaolo Bonzini     switch (s->mouse_type) {
72849ab747fSPaolo Bonzini     default:
72964ebbb7dSDmitry Petrov         /* Just ignore the wheels if not supported */
73064ebbb7dSDmitry Petrov         s->mouse_dz = 0;
73164ebbb7dSDmitry Petrov         s->mouse_dw = 0;
73249ab747fSPaolo Bonzini         break;
73349ab747fSPaolo Bonzini     case 3:
734545e5cf8SMark Cave-Ayland         if (dz1 > 127) {
73549ab747fSPaolo Bonzini             dz1 = 127;
736545e5cf8SMark Cave-Ayland         } else if (dz1 < -127) {
73749ab747fSPaolo Bonzini             dz1 = -127;
738545e5cf8SMark Cave-Ayland         }
7392d135409SMark Cave-Ayland         ps2_queue_noirq(ps2, dz1 & 0xff);
74064ebbb7dSDmitry Petrov         s->mouse_dz -= dz1;
74164ebbb7dSDmitry Petrov         s->mouse_dw = 0;
74249ab747fSPaolo Bonzini         break;
74349ab747fSPaolo Bonzini     case 4:
74464ebbb7dSDmitry Petrov         /*
74564ebbb7dSDmitry Petrov          * This matches what the Linux kernel expects for exps/2 in
74664ebbb7dSDmitry Petrov          * drivers/input/mouse/psmouse-base.c. Note, if you happen to
74764ebbb7dSDmitry Petrov          * press/release the 4th or 5th buttons at the same moment as a
74864ebbb7dSDmitry Petrov          * horizontal wheel scroll, those button presses will get lost. I'm not
74964ebbb7dSDmitry Petrov          * sure what to do about that, since by this point we don't know
75064ebbb7dSDmitry Petrov          * whether those buttons actually changed state.
75164ebbb7dSDmitry Petrov          */
75264ebbb7dSDmitry Petrov         if (dw1 != 0) {
75364ebbb7dSDmitry Petrov             if (dw1 > 31) {
75464ebbb7dSDmitry Petrov                 dw1 = 31;
75564ebbb7dSDmitry Petrov             } else if (dw1 < -31) {
75664ebbb7dSDmitry Petrov                 dw1 = -31;
75764ebbb7dSDmitry Petrov             }
75864ebbb7dSDmitry Petrov 
75964ebbb7dSDmitry Petrov             /*
76064ebbb7dSDmitry Petrov              * linux kernel expects first 6 bits to represent the value
76164ebbb7dSDmitry Petrov              * for horizontal scroll
76264ebbb7dSDmitry Petrov              */
76364ebbb7dSDmitry Petrov             b = (dw1 & 0x3f) | 0x40;
76464ebbb7dSDmitry Petrov             s->mouse_dw -= dw1;
76564ebbb7dSDmitry Petrov         } else {
76664ebbb7dSDmitry Petrov             if (dz1 > 7) {
76749ab747fSPaolo Bonzini                 dz1 = 7;
76864ebbb7dSDmitry Petrov             } else if (dz1 < -7) {
76949ab747fSPaolo Bonzini                 dz1 = -7;
77064ebbb7dSDmitry Petrov             }
77164ebbb7dSDmitry Petrov 
77249ab747fSPaolo Bonzini             b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
77364ebbb7dSDmitry Petrov             s->mouse_dz -= dz1;
77464ebbb7dSDmitry Petrov         }
7752d135409SMark Cave-Ayland         ps2_queue_noirq(ps2, b);
77649ab747fSPaolo Bonzini         break;
77749ab747fSPaolo Bonzini     }
77849ab747fSPaolo Bonzini 
7792d135409SMark Cave-Ayland     ps2_raise_irq(ps2);
7807abe7eb2SGeoffrey McRae 
7815edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
78249ab747fSPaolo Bonzini     /* update deltas */
78349ab747fSPaolo Bonzini     s->mouse_dx -= dx1;
78449ab747fSPaolo Bonzini     s->mouse_dy -= dy1;
7857abe7eb2SGeoffrey McRae 
7867abe7eb2SGeoffrey McRae     return 1;
78749ab747fSPaolo Bonzini }
78849ab747fSPaolo Bonzini 
ps2_mouse_event(DeviceState * dev,QemuConsole * src,InputEvent * evt)7892a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
7902a766d29SGerd Hoffmann                             InputEvent *evt)
79149ab747fSPaolo Bonzini {
7927fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
7938b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
7948b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
7958b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
7968b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
7978b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
7982a766d29SGerd Hoffmann     };
7992a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
800b5a1b443SEric Blake     InputMoveEvent *move;
801b5a1b443SEric Blake     InputBtnEvent *btn;
80249ab747fSPaolo Bonzini 
80349ab747fSPaolo Bonzini     /* check if deltas are recorded when disabled */
804545e5cf8SMark Cave-Ayland     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
80549ab747fSPaolo Bonzini         return;
806545e5cf8SMark Cave-Ayland     }
80749ab747fSPaolo Bonzini 
808568c73a4SEric Blake     switch (evt->type) {
8092a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
81032bafa8fSEric Blake         move = evt->u.rel.data;
811b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
812b5a1b443SEric Blake             s->mouse_dx += move->value;
813b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
814b5a1b443SEric Blake             s->mouse_dy -= move->value;
8152a766d29SGerd Hoffmann         }
8162a766d29SGerd Hoffmann         break;
81749ab747fSPaolo Bonzini 
8182a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
81932bafa8fSEric Blake         btn = evt->u.btn.data;
820b5a1b443SEric Blake         if (btn->down) {
821b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
822b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
8232a766d29SGerd Hoffmann                 s->mouse_dz--;
824b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
8252a766d29SGerd Hoffmann                 s->mouse_dz++;
8262a766d29SGerd Hoffmann             }
82764ebbb7dSDmitry Petrov 
82864ebbb7dSDmitry Petrov             if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
82964ebbb7dSDmitry Petrov                 s->mouse_dw--;
83064ebbb7dSDmitry Petrov             } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
83164ebbb7dSDmitry Petrov                 s->mouse_dw++;
83264ebbb7dSDmitry Petrov             }
8332a766d29SGerd Hoffmann         } else {
834b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
8352a766d29SGerd Hoffmann         }
8362a766d29SGerd Hoffmann         break;
8372a766d29SGerd Hoffmann 
8382a766d29SGerd Hoffmann     default:
8392a766d29SGerd Hoffmann         /* keep gcc happy */
8402a766d29SGerd Hoffmann         break;
8412a766d29SGerd Hoffmann     }
84249ab747fSPaolo Bonzini }
84349ab747fSPaolo Bonzini 
ps2_mouse_sync(DeviceState * dev)8442a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
8452a766d29SGerd Hoffmann {
8462a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
8472a766d29SGerd Hoffmann 
848143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
849143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
850143c04c7SGeoffrey McRae         return;
851143c04c7SGeoffrey McRae     }
852143c04c7SGeoffrey McRae 
8532a766d29SGerd Hoffmann     if (s->mouse_buttons) {
854fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
8552a766d29SGerd Hoffmann     }
8562858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
857545e5cf8SMark Cave-Ayland         /*
858545e5cf8SMark Cave-Ayland          * if not remote, send event. Multiple events are sent if
859545e5cf8SMark Cave-Ayland          * too big deltas
860545e5cf8SMark Cave-Ayland          */
8617abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
86264ebbb7dSDmitry Petrov             if (s->mouse_dx == 0 && s->mouse_dy == 0
86364ebbb7dSDmitry Petrov                     && s->mouse_dz == 0 && s->mouse_dw == 0) {
86449ab747fSPaolo Bonzini                 break;
86549ab747fSPaolo Bonzini             }
86649ab747fSPaolo Bonzini         }
86749ab747fSPaolo Bonzini     }
86864ebbb7dSDmitry Petrov }
86949ab747fSPaolo Bonzini 
ps2_mouse_fake_event(PS2MouseState * s)87054334e73SMark Cave-Ayland void ps2_mouse_fake_event(PS2MouseState *s)
87149ab747fSPaolo Bonzini {
87254334e73SMark Cave-Ayland     trace_ps2_mouse_fake_event(s);
8732a766d29SGerd Hoffmann     s->mouse_dx++;
87454334e73SMark Cave-Ayland     ps2_mouse_sync(DEVICE(s));
87549ab747fSPaolo Bonzini }
87649ab747fSPaolo Bonzini 
ps2_write_mouse(PS2MouseState * s,int val)87754334e73SMark Cave-Ayland void ps2_write_mouse(PS2MouseState *s, int val)
87849ab747fSPaolo Bonzini {
8792d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
8805edab03dSDon Koch 
88154334e73SMark Cave-Ayland     trace_ps2_write_mouse(s, val);
8822d135409SMark Cave-Ayland     switch (ps2->write_cmd) {
88349ab747fSPaolo Bonzini     default:
88449ab747fSPaolo Bonzini     case -1:
88549ab747fSPaolo Bonzini         /* mouse command */
88649ab747fSPaolo Bonzini         if (s->mouse_wrap) {
88749ab747fSPaolo Bonzini             if (val == AUX_RESET_WRAP) {
88849ab747fSPaolo Bonzini                 s->mouse_wrap = 0;
8892d135409SMark Cave-Ayland                 ps2_queue(ps2, AUX_ACK);
89049ab747fSPaolo Bonzini                 return;
89149ab747fSPaolo Bonzini             } else if (val != AUX_RESET) {
8922d135409SMark Cave-Ayland                 ps2_queue(ps2, val);
89349ab747fSPaolo Bonzini                 return;
89449ab747fSPaolo Bonzini             }
89549ab747fSPaolo Bonzini         }
89649ab747fSPaolo Bonzini         switch (val) {
89749ab747fSPaolo Bonzini         case AUX_SET_SCALE11:
89849ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
8992d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
90049ab747fSPaolo Bonzini             break;
90149ab747fSPaolo Bonzini         case AUX_SET_SCALE21:
90249ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_SCALE21;
9032d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
90449ab747fSPaolo Bonzini             break;
90549ab747fSPaolo Bonzini         case AUX_SET_STREAM:
90649ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
9072d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
90849ab747fSPaolo Bonzini             break;
90949ab747fSPaolo Bonzini         case AUX_SET_WRAP:
91049ab747fSPaolo Bonzini             s->mouse_wrap = 1;
9112d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
91249ab747fSPaolo Bonzini             break;
91349ab747fSPaolo Bonzini         case AUX_SET_REMOTE:
91449ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_REMOTE;
9152d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
91649ab747fSPaolo Bonzini             break;
91749ab747fSPaolo Bonzini         case AUX_GET_TYPE:
9182d135409SMark Cave-Ayland             ps2_queue_2(ps2,
9197abe7eb2SGeoffrey McRae                 AUX_ACK,
9207abe7eb2SGeoffrey McRae                 s->mouse_type);
92149ab747fSPaolo Bonzini             break;
92249ab747fSPaolo Bonzini         case AUX_SET_RES:
92349ab747fSPaolo Bonzini         case AUX_SET_SAMPLE:
9242d135409SMark Cave-Ayland             ps2->write_cmd = val;
9252d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
92649ab747fSPaolo Bonzini             break;
92749ab747fSPaolo Bonzini         case AUX_GET_SCALE:
9282d135409SMark Cave-Ayland             ps2_queue_4(ps2,
9297abe7eb2SGeoffrey McRae                 AUX_ACK,
9307abe7eb2SGeoffrey McRae                 s->mouse_status,
9317abe7eb2SGeoffrey McRae                 s->mouse_resolution,
9327abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
93349ab747fSPaolo Bonzini             break;
93449ab747fSPaolo Bonzini         case AUX_POLL:
9352d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
93649ab747fSPaolo Bonzini             ps2_mouse_send_packet(s);
93749ab747fSPaolo Bonzini             break;
93849ab747fSPaolo Bonzini         case AUX_ENABLE_DEV:
93949ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_ENABLED;
9402d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
94149ab747fSPaolo Bonzini             break;
94249ab747fSPaolo Bonzini         case AUX_DISABLE_DEV:
94349ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
9442d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
94549ab747fSPaolo Bonzini             break;
94649ab747fSPaolo Bonzini         case AUX_SET_DEFAULT:
94749ab747fSPaolo Bonzini             s->mouse_sample_rate = 100;
94849ab747fSPaolo Bonzini             s->mouse_resolution = 2;
94949ab747fSPaolo Bonzini             s->mouse_status = 0;
9502d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
95149ab747fSPaolo Bonzini             break;
95249ab747fSPaolo Bonzini         case AUX_RESET:
95349ab747fSPaolo Bonzini             s->mouse_sample_rate = 100;
95449ab747fSPaolo Bonzini             s->mouse_resolution = 2;
95549ab747fSPaolo Bonzini             s->mouse_status = 0;
95649ab747fSPaolo Bonzini             s->mouse_type = 0;
9572d135409SMark Cave-Ayland             ps2_reset_queue(ps2);
9582d135409SMark Cave-Ayland             ps2_queue_3(ps2,
9597abe7eb2SGeoffrey McRae                 AUX_ACK,
9607abe7eb2SGeoffrey McRae                 0xaa,
9617abe7eb2SGeoffrey McRae                 s->mouse_type);
96249ab747fSPaolo Bonzini             break;
96349ab747fSPaolo Bonzini         default:
96449ab747fSPaolo Bonzini             break;
96549ab747fSPaolo Bonzini         }
96649ab747fSPaolo Bonzini         break;
96749ab747fSPaolo Bonzini     case AUX_SET_SAMPLE:
96849ab747fSPaolo Bonzini         s->mouse_sample_rate = val;
96949ab747fSPaolo Bonzini         /* detect IMPS/2 or IMEX */
97049ab747fSPaolo Bonzini         switch (s->mouse_detect_state) {
97149ab747fSPaolo Bonzini         default:
97249ab747fSPaolo Bonzini         case 0:
973545e5cf8SMark Cave-Ayland             if (val == 200) {
97449ab747fSPaolo Bonzini                 s->mouse_detect_state = 1;
975545e5cf8SMark Cave-Ayland             }
97649ab747fSPaolo Bonzini             break;
97749ab747fSPaolo Bonzini         case 1:
978545e5cf8SMark Cave-Ayland             if (val == 100) {
97949ab747fSPaolo Bonzini                 s->mouse_detect_state = 2;
980545e5cf8SMark Cave-Ayland             } else if (val == 200) {
98149ab747fSPaolo Bonzini                 s->mouse_detect_state = 3;
982545e5cf8SMark Cave-Ayland             } else {
98349ab747fSPaolo Bonzini                 s->mouse_detect_state = 0;
984545e5cf8SMark Cave-Ayland             }
98549ab747fSPaolo Bonzini             break;
98649ab747fSPaolo Bonzini         case 2:
987545e5cf8SMark Cave-Ayland             if (val == 80) {
98849ab747fSPaolo Bonzini                 s->mouse_type = 3; /* IMPS/2 */
989545e5cf8SMark Cave-Ayland             }
99049ab747fSPaolo Bonzini             s->mouse_detect_state = 0;
99149ab747fSPaolo Bonzini             break;
99249ab747fSPaolo Bonzini         case 3:
993545e5cf8SMark Cave-Ayland             if (val == 80) {
99449ab747fSPaolo Bonzini                 s->mouse_type = 4; /* IMEX */
995545e5cf8SMark Cave-Ayland             }
99649ab747fSPaolo Bonzini             s->mouse_detect_state = 0;
99749ab747fSPaolo Bonzini             break;
99849ab747fSPaolo Bonzini         }
9992d135409SMark Cave-Ayland         ps2_queue(ps2, AUX_ACK);
10002d135409SMark Cave-Ayland         ps2->write_cmd = -1;
100149ab747fSPaolo Bonzini         break;
100249ab747fSPaolo Bonzini     case AUX_SET_RES:
100349ab747fSPaolo Bonzini         s->mouse_resolution = val;
10042d135409SMark Cave-Ayland         ps2_queue(ps2, AUX_ACK);
10052d135409SMark Cave-Ayland         ps2->write_cmd = -1;
100649ab747fSPaolo Bonzini         break;
100749ab747fSPaolo Bonzini     }
100849ab747fSPaolo Bonzini }
100949ab747fSPaolo Bonzini 
ps2_reset_hold(Object * obj,ResetType type)1010ad80e367SPeter Maydell static void ps2_reset_hold(Object *obj, ResetType type)
101149ab747fSPaolo Bonzini {
10122bb3f930SPeter Maydell     PS2State *s = PS2_DEVICE(obj);
1013108cb22eSMark Cave-Ayland 
101449ab747fSPaolo Bonzini     s->write_cmd = -1;
1015954ee55bSGerd Hoffmann     ps2_reset_queue(s);
10162bb3f930SPeter Maydell }
10172bb3f930SPeter Maydell 
ps2_reset_exit(Object * obj,ResetType type)1018ad80e367SPeter Maydell static void ps2_reset_exit(Object *obj, ResetType type)
10192bb3f930SPeter Maydell {
10202bb3f930SPeter Maydell     PS2State *s = PS2_DEVICE(obj);
10212bb3f930SPeter Maydell 
10225cb6e556SMark Cave-Ayland     ps2_lower_irq(s);
102349ab747fSPaolo Bonzini }
102449ab747fSPaolo Bonzini 
ps2_common_post_load(PS2State * s)10252858ab09SGonglei static void ps2_common_post_load(PS2State *s)
10262858ab09SGonglei {
10272858ab09SGonglei     PS2Queue *q = &s->queue;
10284e9bddcbSVolker Rümelin     int ccount = 0;
10292858ab09SGonglei 
10304e9bddcbSVolker Rümelin     /* limit the number of queued command replies to PS2_QUEUE_HEADROOM */
10314e9bddcbSVolker Rümelin     if (q->cwptr != -1) {
10324e9bddcbSVolker Rümelin         ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
10334e9bddcbSVolker Rümelin         if (ccount > PS2_QUEUE_HEADROOM) {
10344e9bddcbSVolker Rümelin             ccount = PS2_QUEUE_HEADROOM;
10354e9bddcbSVolker Rümelin         }
1036a1f2ed2aSPavel Dovgalyuk     }
10372858ab09SGonglei 
10384e9bddcbSVolker Rümelin     /* limit the scancode queue size to PS2_QUEUE_SIZE */
10394e9bddcbSVolker Rümelin     if (q->count < ccount) {
10404e9bddcbSVolker Rümelin         q->count = ccount;
10414e9bddcbSVolker Rümelin     } else if (q->count > ccount + PS2_QUEUE_SIZE) {
10424e9bddcbSVolker Rümelin         q->count = ccount + PS2_QUEUE_SIZE;
10434e9bddcbSVolker Rümelin     }
10444e9bddcbSVolker Rümelin 
10454e9bddcbSVolker Rümelin     /* sanitize rptr and recalculate wptr and cwptr */
104647db2432SVolker Rümelin     q->rptr = q->rptr & (PS2_BUFFER_SIZE - 1);
104747db2432SVolker Rümelin     q->wptr = (q->rptr + q->count) & (PS2_BUFFER_SIZE - 1);
10484e9bddcbSVolker Rümelin     q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1;
10492858ab09SGonglei }
10502858ab09SGonglei 
ps2_kbd_reset_hold(Object * obj,ResetType type)1051ad80e367SPeter Maydell static void ps2_kbd_reset_hold(Object *obj, ResetType type)
105249ab747fSPaolo Bonzini {
1053fc2fc3c1SPeter Maydell     PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj);
1054fc2fc3c1SPeter Maydell     PS2KbdState *s = PS2_KBD_DEVICE(obj);
105549ab747fSPaolo Bonzini 
1056108cb22eSMark Cave-Ayland     trace_ps2_kbd_reset(s);
1057fc2fc3c1SPeter Maydell 
1058fc2fc3c1SPeter Maydell     if (ps2dc->parent_phases.hold) {
1059ad80e367SPeter Maydell         ps2dc->parent_phases.hold(obj, type);
1060fc2fc3c1SPeter Maydell     }
1061108cb22eSMark Cave-Ayland 
1062d2e550a8SHervé Poussineau     s->scan_enabled = 1;
106349ab747fSPaolo Bonzini     s->translate = 0;
1064089adafdSHervé Poussineau     s->scancode_set = 2;
1065620775d1SDaniel P. Berrange     s->modifiers = 0;
106649ab747fSPaolo Bonzini }
106749ab747fSPaolo Bonzini 
ps2_mouse_reset_hold(Object * obj,ResetType type)1068ad80e367SPeter Maydell static void ps2_mouse_reset_hold(Object *obj, ResetType type)
106949ab747fSPaolo Bonzini {
1070fc2fc3c1SPeter Maydell     PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj);
1071fc2fc3c1SPeter Maydell     PS2MouseState *s = PS2_MOUSE_DEVICE(obj);
107249ab747fSPaolo Bonzini 
1073108cb22eSMark Cave-Ayland     trace_ps2_mouse_reset(s);
1074fc2fc3c1SPeter Maydell 
1075fc2fc3c1SPeter Maydell     if (ps2dc->parent_phases.hold) {
1076ad80e367SPeter Maydell         ps2dc->parent_phases.hold(obj, type);
1077fc2fc3c1SPeter Maydell     }
1078108cb22eSMark Cave-Ayland 
107949ab747fSPaolo Bonzini     s->mouse_status = 0;
108049ab747fSPaolo Bonzini     s->mouse_resolution = 0;
108149ab747fSPaolo Bonzini     s->mouse_sample_rate = 0;
108249ab747fSPaolo Bonzini     s->mouse_wrap = 0;
108349ab747fSPaolo Bonzini     s->mouse_type = 0;
108449ab747fSPaolo Bonzini     s->mouse_detect_state = 0;
108549ab747fSPaolo Bonzini     s->mouse_dx = 0;
108649ab747fSPaolo Bonzini     s->mouse_dy = 0;
108749ab747fSPaolo Bonzini     s->mouse_dz = 0;
108864ebbb7dSDmitry Petrov     s->mouse_dw = 0;
108949ab747fSPaolo Bonzini     s->mouse_buttons = 0;
109049ab747fSPaolo Bonzini }
109149ab747fSPaolo Bonzini 
109249ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_common = {
109349ab747fSPaolo Bonzini     .name = "PS2 Common State",
109449ab747fSPaolo Bonzini     .version_id = 3,
109549ab747fSPaolo Bonzini     .minimum_version_id = 2,
1096af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
109749ab747fSPaolo Bonzini         VMSTATE_INT32(write_cmd, PS2State),
109849ab747fSPaolo Bonzini         VMSTATE_INT32(queue.rptr, PS2State),
109949ab747fSPaolo Bonzini         VMSTATE_INT32(queue.wptr, PS2State),
110049ab747fSPaolo Bonzini         VMSTATE_INT32(queue.count, PS2State),
110149ab747fSPaolo Bonzini         VMSTATE_BUFFER(queue.data, PS2State),
110249ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
110349ab747fSPaolo Bonzini     }
110449ab747fSPaolo Bonzini };
110549ab747fSPaolo Bonzini 
ps2_keyboard_ledstate_needed(void * opaque)110649ab747fSPaolo Bonzini static bool ps2_keyboard_ledstate_needed(void *opaque)
110749ab747fSPaolo Bonzini {
110849ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
110949ab747fSPaolo Bonzini 
111049ab747fSPaolo Bonzini     return s->ledstate != 0; /* 0 is default state */
111149ab747fSPaolo Bonzini }
111249ab747fSPaolo Bonzini 
ps2_kbd_ledstate_post_load(void * opaque,int version_id)111349ab747fSPaolo Bonzini static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
111449ab747fSPaolo Bonzini {
111549ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
111649ab747fSPaolo Bonzini 
111749ab747fSPaolo Bonzini     kbd_put_ledstate(s->ledstate);
111849ab747fSPaolo Bonzini     return 0;
111949ab747fSPaolo Bonzini }
112049ab747fSPaolo Bonzini 
112149ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
112249ab747fSPaolo Bonzini     .name = "ps2kbd/ledstate",
112349ab747fSPaolo Bonzini     .version_id = 3,
112449ab747fSPaolo Bonzini     .minimum_version_id = 2,
112549ab747fSPaolo Bonzini     .post_load = ps2_kbd_ledstate_post_load,
11265cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
1127af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
112849ab747fSPaolo Bonzini         VMSTATE_INT32(ledstate, PS2KbdState),
112949ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
113049ab747fSPaolo Bonzini     }
113149ab747fSPaolo Bonzini };
113249ab747fSPaolo Bonzini 
ps2_keyboard_need_high_bit_needed(void * opaque)113357d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
113457d5c005SHervé Poussineau {
113557d5c005SHervé Poussineau     PS2KbdState *s = opaque;
113657d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
113757d5c005SHervé Poussineau }
113857d5c005SHervé Poussineau 
113957d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
114057d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
114157d5c005SHervé Poussineau     .version_id = 1,
114257d5c005SHervé Poussineau     .minimum_version_id = 1,
114357d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
1144af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
114557d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
114657d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
114757d5c005SHervé Poussineau     }
114857d5c005SHervé Poussineau };
114957d5c005SHervé Poussineau 
ps2_keyboard_cqueue_needed(void * opaque)11504e9bddcbSVolker Rümelin static bool ps2_keyboard_cqueue_needed(void *opaque)
11514e9bddcbSVolker Rümelin {
11524e9bddcbSVolker Rümelin     PS2KbdState *s = opaque;
11538f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11544e9bddcbSVolker Rümelin 
11558f84e53cSMark Cave-Ayland     return ps2->queue.cwptr != -1; /* the queue is mostly empty */
11564e9bddcbSVolker Rümelin }
11574e9bddcbSVolker Rümelin 
11584e9bddcbSVolker Rümelin static const VMStateDescription vmstate_ps2_keyboard_cqueue = {
11594e9bddcbSVolker Rümelin     .name = "ps2kbd/command_reply_queue",
11604e9bddcbSVolker Rümelin     .needed = ps2_keyboard_cqueue_needed,
1161af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
11628f84e53cSMark Cave-Ayland         VMSTATE_INT32(parent_obj.queue.cwptr, PS2KbdState),
11634e9bddcbSVolker Rümelin         VMSTATE_END_OF_LIST()
11644e9bddcbSVolker Rümelin     }
11654e9bddcbSVolker Rümelin };
11664e9bddcbSVolker Rümelin 
ps2_kbd_post_load(void * opaque,int version_id)116749ab747fSPaolo Bonzini static int ps2_kbd_post_load(void *opaque, int version_id)
116849ab747fSPaolo Bonzini {
116949ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)opaque;
11708f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
117149ab747fSPaolo Bonzini 
1172545e5cf8SMark Cave-Ayland     if (version_id == 2) {
117349ab747fSPaolo Bonzini         s->scancode_set = 2;
1174545e5cf8SMark Cave-Ayland     }
11752858ab09SGonglei 
11762858ab09SGonglei     ps2_common_post_load(ps2);
11772858ab09SGonglei 
117849ab747fSPaolo Bonzini     return 0;
117949ab747fSPaolo Bonzini }
118049ab747fSPaolo Bonzini 
118149ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_keyboard = {
118249ab747fSPaolo Bonzini     .name = "ps2kbd",
118349ab747fSPaolo Bonzini     .version_id = 3,
118449ab747fSPaolo Bonzini     .minimum_version_id = 2,
118549ab747fSPaolo Bonzini     .post_load = ps2_kbd_post_load,
1186af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
11878f84e53cSMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2KbdState, 0, vmstate_ps2_common,
11888f84e53cSMark Cave-Ayland                        PS2State),
118949ab747fSPaolo Bonzini         VMSTATE_INT32(scan_enabled, PS2KbdState),
119049ab747fSPaolo Bonzini         VMSTATE_INT32(translate, PS2KbdState),
119149ab747fSPaolo Bonzini         VMSTATE_INT32_V(scancode_set, PS2KbdState, 3),
119249ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
119349ab747fSPaolo Bonzini     },
1194af0f07dfSRichard Henderson     .subsections = (const VMStateDescription * const []) {
11955cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
119657d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
11974e9bddcbSVolker Rümelin         &vmstate_ps2_keyboard_cqueue,
11985cd8cadaSJuan Quintela         NULL
119949ab747fSPaolo Bonzini     }
120049ab747fSPaolo Bonzini };
120149ab747fSPaolo Bonzini 
ps2_mouse_post_load(void * opaque,int version_id)12022858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
12032858ab09SGonglei {
12042858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
12052d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
12062858ab09SGonglei 
12072858ab09SGonglei     ps2_common_post_load(ps2);
12082858ab09SGonglei 
12092858ab09SGonglei     return 0;
12102858ab09SGonglei }
12112858ab09SGonglei 
121249ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_mouse = {
121349ab747fSPaolo Bonzini     .name = "ps2mouse",
121449ab747fSPaolo Bonzini     .version_id = 2,
121549ab747fSPaolo Bonzini     .minimum_version_id = 2,
12162858ab09SGonglei     .post_load = ps2_mouse_post_load,
1217af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
12182d135409SMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2MouseState, 0, vmstate_ps2_common,
12192d135409SMark Cave-Ayland                        PS2State),
122049ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_status, PS2MouseState),
122149ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
122249ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
122349ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
122449ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_type, PS2MouseState),
122549ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
122649ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dx, PS2MouseState),
122749ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dy, PS2MouseState),
122849ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dz, PS2MouseState),
122949ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
123049ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
123149ab747fSPaolo Bonzini     }
123249ab747fSPaolo Bonzini };
123349ab747fSPaolo Bonzini 
1234b1be65f6SPhilippe Mathieu-Daudé static const QemuInputHandler ps2_keyboard_handler = {
123566e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
123666e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
123766e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
123866e6536eSGerd Hoffmann };
123966e6536eSGerd Hoffmann 
ps2_kbd_realize(DeviceState * dev,Error ** errp)1240ea247a0fSMark Cave-Ayland static void ps2_kbd_realize(DeviceState *dev, Error **errp)
1241ea247a0fSMark Cave-Ayland {
1242ea247a0fSMark Cave-Ayland     qemu_input_handler_register(dev, &ps2_keyboard_handler);
1243ea247a0fSMark Cave-Ayland }
1244ea247a0fSMark Cave-Ayland 
1245b1be65f6SPhilippe Mathieu-Daudé static const QemuInputHandler ps2_mouse_handler = {
12462a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
12472a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
12482a766d29SGerd Hoffmann     .event = ps2_mouse_event,
12492a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
12502a766d29SGerd Hoffmann };
12512a766d29SGerd Hoffmann 
ps2_mouse_realize(DeviceState * dev,Error ** errp)12524a68b482SMark Cave-Ayland static void ps2_mouse_realize(DeviceState *dev, Error **errp)
12534a68b482SMark Cave-Ayland {
12544a68b482SMark Cave-Ayland     qemu_input_handler_register(dev, &ps2_mouse_handler);
12554a68b482SMark Cave-Ayland }
12564a68b482SMark Cave-Ayland 
ps2_kbd_class_init(ObjectClass * klass,void * data)1257108cb22eSMark Cave-Ayland static void ps2_kbd_class_init(ObjectClass *klass, void *data)
1258108cb22eSMark Cave-Ayland {
1259108cb22eSMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
1260fc2fc3c1SPeter Maydell     ResettableClass *rc = RESETTABLE_CLASS(klass);
1261108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass);
1262108cb22eSMark Cave-Ayland 
1263ea247a0fSMark Cave-Ayland     dc->realize = ps2_kbd_realize;
1264fc2fc3c1SPeter Maydell     resettable_class_set_parent_phases(rc, NULL, ps2_kbd_reset_hold, NULL,
1265fc2fc3c1SPeter Maydell                                        &ps2dc->parent_phases);
1266f055f507SMark Cave-Ayland     dc->vmsd = &vmstate_ps2_keyboard;
1267108cb22eSMark Cave-Ayland }
1268108cb22eSMark Cave-Ayland 
12698f84e53cSMark Cave-Ayland static const TypeInfo ps2_kbd_info = {
12708f84e53cSMark Cave-Ayland     .name          = TYPE_PS2_KBD_DEVICE,
12718f84e53cSMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
12728f84e53cSMark Cave-Ayland     .instance_size = sizeof(PS2KbdState),
1273108cb22eSMark Cave-Ayland     .class_init    = ps2_kbd_class_init
12748f84e53cSMark Cave-Ayland };
12758f84e53cSMark Cave-Ayland 
ps2_mouse_class_init(ObjectClass * klass,void * data)1276108cb22eSMark Cave-Ayland static void ps2_mouse_class_init(ObjectClass *klass, void *data)
1277108cb22eSMark Cave-Ayland {
1278108cb22eSMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
1279fc2fc3c1SPeter Maydell     ResettableClass *rc = RESETTABLE_CLASS(klass);
1280108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass);
1281108cb22eSMark Cave-Ayland 
12824a68b482SMark Cave-Ayland     dc->realize = ps2_mouse_realize;
1283fc2fc3c1SPeter Maydell     resettable_class_set_parent_phases(rc, NULL, ps2_mouse_reset_hold, NULL,
1284fc2fc3c1SPeter Maydell                                        &ps2dc->parent_phases);
128597259e70SMark Cave-Ayland     dc->vmsd = &vmstate_ps2_mouse;
1286108cb22eSMark Cave-Ayland }
1287108cb22eSMark Cave-Ayland 
12882d135409SMark Cave-Ayland static const TypeInfo ps2_mouse_info = {
12892d135409SMark Cave-Ayland     .name          = TYPE_PS2_MOUSE_DEVICE,
12902d135409SMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
12912d135409SMark Cave-Ayland     .instance_size = sizeof(PS2MouseState),
1292108cb22eSMark Cave-Ayland     .class_init    = ps2_mouse_class_init
12932d135409SMark Cave-Ayland };
12942d135409SMark Cave-Ayland 
ps2_init(Object * obj)12956beb79e1SMark Cave-Ayland static void ps2_init(Object *obj)
12966beb79e1SMark Cave-Ayland {
12976beb79e1SMark Cave-Ayland     PS2State *s = PS2_DEVICE(obj);
12986beb79e1SMark Cave-Ayland 
12996beb79e1SMark Cave-Ayland     qdev_init_gpio_out(DEVICE(obj), &s->irq, 1);
13006beb79e1SMark Cave-Ayland }
13016beb79e1SMark Cave-Ayland 
ps2_class_init(ObjectClass * klass,void * data)130264bbdd13SMark Cave-Ayland static void ps2_class_init(ObjectClass *klass, void *data)
130364bbdd13SMark Cave-Ayland {
130464bbdd13SMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
13052bb3f930SPeter Maydell     ResettableClass *rc = RESETTABLE_CLASS(klass);
130664bbdd13SMark Cave-Ayland 
13072bb3f930SPeter Maydell     rc->phases.hold = ps2_reset_hold;
13082bb3f930SPeter Maydell     rc->phases.exit = ps2_reset_exit;
130964bbdd13SMark Cave-Ayland     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
131064bbdd13SMark Cave-Ayland }
131164bbdd13SMark Cave-Ayland 
131264bbdd13SMark Cave-Ayland static const TypeInfo ps2_info = {
131364bbdd13SMark Cave-Ayland     .name          = TYPE_PS2_DEVICE,
131464bbdd13SMark Cave-Ayland     .parent        = TYPE_SYS_BUS_DEVICE,
13156beb79e1SMark Cave-Ayland     .instance_init = ps2_init,
131664bbdd13SMark Cave-Ayland     .instance_size = sizeof(PS2State),
131764bbdd13SMark Cave-Ayland     .class_init    = ps2_class_init,
1318494145b2SMark Cave-Ayland     .class_size    = sizeof(PS2DeviceClass),
131964bbdd13SMark Cave-Ayland     .abstract      = true
132064bbdd13SMark Cave-Ayland };
132164bbdd13SMark Cave-Ayland 
ps2_register_types(void)132264bbdd13SMark Cave-Ayland static void ps2_register_types(void)
132364bbdd13SMark Cave-Ayland {
132464bbdd13SMark Cave-Ayland     type_register_static(&ps2_info);
13258f84e53cSMark Cave-Ayland     type_register_static(&ps2_kbd_info);
13262d135409SMark Cave-Ayland     type_register_static(&ps2_mouse_info);
132764bbdd13SMark Cave-Ayland }
132864bbdd13SMark Cave-Ayland 
132964bbdd13SMark Cave-Ayland type_init(ps2_register_types)
1330