xref: /qemu/hw/input/ps2.c (revision 6beb79e1)
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"
27*6beb79e1SMark 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 
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 
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 
1562a6505b0SSven Schnelle int ps2_queue_empty(PS2State *s)
1572a6505b0SSven Schnelle {
1582a6505b0SSven Schnelle     return s->queue.count == 0;
1592a6505b0SSven Schnelle }
1602a6505b0SSven Schnelle 
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 
17652b28f76SMark Cave-Ayland static void ps2_raise_irq(PS2State *s)
1777abe7eb2SGeoffrey McRae {
178*6beb79e1SMark Cave-Ayland     if (qemu_irq_is_connected(s->irq)) {
179*6beb79e1SMark Cave-Ayland         qemu_set_irq(s->irq, 1);
180*6beb79e1SMark Cave-Ayland     } else {
1817abe7eb2SGeoffrey McRae         s->update_irq(s->update_arg, 1);
1827abe7eb2SGeoffrey McRae     }
183*6beb79e1SMark Cave-Ayland }
1847abe7eb2SGeoffrey McRae 
1855cb6e556SMark Cave-Ayland static void ps2_lower_irq(PS2State *s)
1865cb6e556SMark Cave-Ayland {
187*6beb79e1SMark Cave-Ayland     if (qemu_irq_is_connected(s->irq)) {
188*6beb79e1SMark Cave-Ayland         qemu_set_irq(s->irq, 0);
189*6beb79e1SMark Cave-Ayland     } else {
1905cb6e556SMark Cave-Ayland         s->update_irq(s->update_arg, 0);
1915cb6e556SMark Cave-Ayland     }
192*6beb79e1SMark Cave-Ayland }
1935cb6e556SMark Cave-Ayland 
1947abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
1957abe7eb2SGeoffrey McRae {
1967704bb02SVolker Rümelin     if (PS2_QUEUE_SIZE - s->queue.count < 1) {
1977704bb02SVolker Rümelin         return;
1987704bb02SVolker Rümelin     }
1997704bb02SVolker Rümelin 
2007abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
20196376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2027abe7eb2SGeoffrey McRae }
2037abe7eb2SGeoffrey McRae 
2047abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
2057abe7eb2SGeoffrey McRae {
2067abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
2077abe7eb2SGeoffrey McRae         return;
2087abe7eb2SGeoffrey McRae     }
2097abe7eb2SGeoffrey McRae 
2107abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2117abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
21296376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2137abe7eb2SGeoffrey McRae }
2147abe7eb2SGeoffrey McRae 
2157abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2167abe7eb2SGeoffrey McRae {
2177abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2187abe7eb2SGeoffrey McRae         return;
2197abe7eb2SGeoffrey McRae     }
2207abe7eb2SGeoffrey McRae 
2217abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2227abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2237abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
22496376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2257abe7eb2SGeoffrey McRae }
2267abe7eb2SGeoffrey McRae 
2277abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2287abe7eb2SGeoffrey McRae {
2297abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2307abe7eb2SGeoffrey McRae         return;
2317abe7eb2SGeoffrey McRae     }
2327abe7eb2SGeoffrey McRae 
2337abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2347abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2357abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2367abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
23796376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
23849ab747fSPaolo Bonzini }
23949ab747fSPaolo Bonzini 
2409e24b2ddSVolker Rümelin static void ps2_cqueue_data(PS2Queue *q, int b)
2419e24b2ddSVolker Rümelin {
2429e24b2ddSVolker Rümelin     q->data[q->cwptr] = b;
2439e24b2ddSVolker Rümelin     if (++q->cwptr >= PS2_BUFFER_SIZE) {
2449e24b2ddSVolker Rümelin         q->cwptr = 0;
2459e24b2ddSVolker Rümelin     }
2469e24b2ddSVolker Rümelin     q->count++;
2479e24b2ddSVolker Rümelin }
2489e24b2ddSVolker Rümelin 
2499e24b2ddSVolker Rümelin static void ps2_cqueue_1(PS2State *s, int b1)
2509e24b2ddSVolker Rümelin {
2519e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2529e24b2ddSVolker Rümelin 
2539e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 1) & (PS2_BUFFER_SIZE - 1);
2549e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2559e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2569e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2579e24b2ddSVolker Rümelin }
2589e24b2ddSVolker Rümelin 
2599e24b2ddSVolker Rümelin static void ps2_cqueue_2(PS2State *s, int b1, int b2)
2609e24b2ddSVolker Rümelin {
2619e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2629e24b2ddSVolker Rümelin 
2639e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 2) & (PS2_BUFFER_SIZE - 1);
2649e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2659e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2669e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
2679e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2689e24b2ddSVolker Rümelin }
2699e24b2ddSVolker Rümelin 
2709e24b2ddSVolker Rümelin static void ps2_cqueue_3(PS2State *s, int b1, int b2, int b3)
2719e24b2ddSVolker Rümelin {
2729e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2739e24b2ddSVolker Rümelin 
2749e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 3) & (PS2_BUFFER_SIZE - 1);
2759e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2769e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2779e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
2789e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b3);
2799e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2809e24b2ddSVolker Rümelin }
2819e24b2ddSVolker Rümelin 
2829e24b2ddSVolker Rümelin static void ps2_cqueue_reset(PS2State *s)
2839e24b2ddSVolker Rümelin {
2849e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2859e24b2ddSVolker Rümelin     int ccount;
2869e24b2ddSVolker Rümelin 
2879e24b2ddSVolker Rümelin     if (q->cwptr == -1) {
2889e24b2ddSVolker Rümelin         return;
2899e24b2ddSVolker Rümelin     }
2909e24b2ddSVolker Rümelin 
2919e24b2ddSVolker Rümelin     ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
2929e24b2ddSVolker Rümelin     q->count -= ccount;
2939e24b2ddSVolker Rümelin     q->rptr = q->cwptr;
2949e24b2ddSVolker Rümelin     q->cwptr = -1;
2959e24b2ddSVolker Rümelin }
2969e24b2ddSVolker Rümelin 
29757d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
29849ab747fSPaolo Bonzini static void ps2_put_keycode(void *opaque, int keycode)
29949ab747fSPaolo Bonzini {
30049ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
3018f84e53cSMark Cave-Ayland     PS2State *ps = PS2_DEVICE(s);
30249ab747fSPaolo Bonzini 
3035edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
304fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
30557d5c005SHervé Poussineau 
30657d5c005SHervé Poussineau     if (s->translate) {
30757d5c005SHervé Poussineau         if (keycode == 0xf0) {
30857d5c005SHervé Poussineau             s->need_high_bit = true;
30957d5c005SHervé Poussineau         } else if (s->need_high_bit) {
3108f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode] | 0x80);
31157d5c005SHervé Poussineau             s->need_high_bit = false;
31257d5c005SHervé Poussineau         } else {
3138f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode]);
31449ab747fSPaolo Bonzini         }
31557d5c005SHervé Poussineau     } else {
3168f84e53cSMark Cave-Ayland         ps2_queue(ps, keycode);
31749ab747fSPaolo Bonzini     }
31857d5c005SHervé Poussineau }
31949ab747fSPaolo Bonzini 
32066e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
32166e6536eSGerd Hoffmann                                InputEvent *evt)
32266e6536eSGerd Hoffmann {
32366e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
32432bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
3258c10e0baSHervé Poussineau     int qcode;
326ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
327620775d1SDaniel P. Berrange     int mod;
32866e6536eSGerd Hoffmann 
329143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
330143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
331143c04c7SGeoffrey McRae         return;
332143c04c7SGeoffrey McRae     }
333143c04c7SGeoffrey McRae 
334fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
3358c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
3368c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
33757d5c005SHervé Poussineau 
338620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
339644f66bfSDaniel P. Berrangé     trace_ps2_keyboard_event(s, qcode, key->down, mod,
340644f66bfSDaniel P. Berrangé                              s->modifiers, s->scancode_set, s->translate);
341620775d1SDaniel P. Berrange     if (key->down) {
342620775d1SDaniel P. Berrange         s->modifiers |= mod;
343620775d1SDaniel P. Berrange     } else {
344620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
345620775d1SDaniel P. Berrange     }
346620775d1SDaniel P. Berrange 
3478c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3488c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
34929fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
35029fd23a5SDaniel P. Berrange                 if (key->down) {
35129fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
35229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
35329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
35429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
35529fd23a5SDaniel P. Berrange                 }
35629fd23a5SDaniel P. Berrange             } else {
3578c10e0baSHervé Poussineau                 if (key->down) {
3588c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3598c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3608c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
361927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3628c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
3638c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
3648c10e0baSHervé Poussineau                 }
36529fd23a5SDaniel P. Berrange             }
3668c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
367620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
368620775d1SDaniel P. Berrange                 if (key->down) {
369620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
370620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
371620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
372620775d1SDaniel P. Berrange                 } else {
373620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
374620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
375620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
376620775d1SDaniel P. Berrange                 }
377620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
378620775d1SDaniel P. Berrange                 if (key->down) {
379620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
380620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
381620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
382620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
383620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
384620775d1SDaniel P. Berrange                 } else {
385620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
386620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
387620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
388620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
389620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
390620775d1SDaniel P. Berrange                 }
3918f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
3928f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
3938f63458fSDaniel P. Berrange                 if (key->down) {
3948f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3958f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
3968f63458fSDaniel P. Berrange                 } else {
3978f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
3988f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
3998f63458fSDaniel P. Berrange                 }
400620775d1SDaniel P. Berrange             } else {
4018c10e0baSHervé Poussineau                 if (key->down) {
4028c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4038c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
4048c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4058c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
4068c10e0baSHervé Poussineau                 } else {
4078c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4088c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
4098c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4108c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
4118c10e0baSHervé Poussineau                 }
412620775d1SDaniel P. Berrange             }
4138c10e0baSHervé Poussineau         } else {
414545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset1_len) {
415ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
416545e5cf8SMark Cave-Ayland             }
4178c10e0baSHervé Poussineau             if (keycode) {
4188c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4198c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4208c10e0baSHervé Poussineau                 }
4218c10e0baSHervé Poussineau                 if (!key->down) {
4228c10e0baSHervé Poussineau                     keycode |= 0x80;
4238c10e0baSHervé Poussineau                 }
4248c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
4258c10e0baSHervé Poussineau             } else {
426ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
427ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
4288c10e0baSHervé Poussineau             }
4298c10e0baSHervé Poussineau         }
4308c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
4318c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
43229fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
43329fd23a5SDaniel P. Berrange                 if (key->down) {
43429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
43529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
43629fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
43729fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
43829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
43929fd23a5SDaniel P. Berrange                 }
44029fd23a5SDaniel P. Berrange             } else {
4418c10e0baSHervé Poussineau                 if (key->down) {
4428c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4438c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4448c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4458c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4468c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4478c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4488c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4498c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4508c10e0baSHervé Poussineau                 }
45129fd23a5SDaniel P. Berrange             }
4528c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
453620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
454620775d1SDaniel P. Berrange                 if (key->down) {
455620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
456620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
457620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
458620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
459620775d1SDaniel P. Berrange                 } else {
460620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
461620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
462620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
463620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
464620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
465620775d1SDaniel P. Berrange                 }
466620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
467620775d1SDaniel P. Berrange                 if (key->down) {
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                     ps2_put_keycode(s, 0x84);
474620775d1SDaniel P. Berrange                 } else {
475620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
476620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
477620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
478620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
479620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
480620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
481620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
482620775d1SDaniel P. Berrange                 }
4838f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4848f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4858f63458fSDaniel P. Berrange                 if (key->down) {
4868f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4878f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4888f63458fSDaniel P. Berrange                 } else {
4898f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4908f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
4918f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
4928f63458fSDaniel P. Berrange                 }
493620775d1SDaniel P. Berrange             } else {
4948c10e0baSHervé Poussineau                 if (key->down) {
4958c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4968c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
4978c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4988c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
4998c10e0baSHervé Poussineau                 } else {
5008c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5018c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5028c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
5038c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5048c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5058c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
5068c10e0baSHervé Poussineau                 }
507620775d1SDaniel P. Berrange             }
5088c10e0baSHervé Poussineau         } else {
509545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset2_len) {
510ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
511545e5cf8SMark Cave-Ayland             }
5128c10e0baSHervé Poussineau             if (keycode) {
5138c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
5148c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
5158c10e0baSHervé Poussineau                 }
5168c10e0baSHervé Poussineau                 if (!key->down) {
5178c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5188c10e0baSHervé Poussineau                 }
5198c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
52057d5c005SHervé Poussineau             } else {
521ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
522ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
52357d5c005SHervé Poussineau             }
52457d5c005SHervé Poussineau         }
52557d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
526545e5cf8SMark Cave-Ayland         if (qcode < qemu_input_map_qcode_to_atset3_len) {
527ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
528545e5cf8SMark Cave-Ayland         }
5298c10e0baSHervé Poussineau         if (keycode) {
5308c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
5318c10e0baSHervé Poussineau             if (!key->down) {
5328c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
53357d5c005SHervé Poussineau             }
53457d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
5358c10e0baSHervé Poussineau         } else {
536ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
537ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
5388c10e0baSHervé Poussineau         }
53966e6536eSGerd Hoffmann     }
54066e6536eSGerd Hoffmann }
54166e6536eSGerd Hoffmann 
5428498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
54349ab747fSPaolo Bonzini {
54449ab747fSPaolo Bonzini     PS2Queue *q;
54549ab747fSPaolo Bonzini     int val, index;
54649ab747fSPaolo Bonzini 
5478498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
54849ab747fSPaolo Bonzini     q = &s->queue;
54949ab747fSPaolo Bonzini     if (q->count == 0) {
550545e5cf8SMark Cave-Ayland         /*
551545e5cf8SMark Cave-Ayland          * NOTE: if no data left, we return the last keyboard one
552545e5cf8SMark Cave-Ayland          * (needed for EMM386)
553545e5cf8SMark Cave-Ayland          */
55449ab747fSPaolo Bonzini         /* XXX: need a timer to do things correctly */
55549ab747fSPaolo Bonzini         index = q->rptr - 1;
55647db2432SVolker Rümelin         if (index < 0) {
55747db2432SVolker Rümelin             index = PS2_BUFFER_SIZE - 1;
55847db2432SVolker Rümelin         }
55949ab747fSPaolo Bonzini         val = q->data[index];
56049ab747fSPaolo Bonzini     } else {
56149ab747fSPaolo Bonzini         val = q->data[q->rptr];
56247db2432SVolker Rümelin         if (++q->rptr == PS2_BUFFER_SIZE) {
56349ab747fSPaolo Bonzini             q->rptr = 0;
56447db2432SVolker Rümelin         }
56549ab747fSPaolo Bonzini         q->count--;
5669e24b2ddSVolker Rümelin         if (q->rptr == q->cwptr) {
5679e24b2ddSVolker Rümelin             /* command reply queue is empty */
5689e24b2ddSVolker Rümelin             q->cwptr = -1;
5699e24b2ddSVolker Rümelin         }
57049ab747fSPaolo Bonzini         /* reading deasserts IRQ */
5715cb6e556SMark Cave-Ayland         ps2_lower_irq(s);
57249ab747fSPaolo Bonzini         /* reassert IRQs if data left */
573cec32524SVolker Rümelin         if (q->count) {
574892e9bbeSMark Cave-Ayland             ps2_raise_irq(s);
575cec32524SVolker Rümelin         }
57649ab747fSPaolo Bonzini     }
57749ab747fSPaolo Bonzini     return val;
57849ab747fSPaolo Bonzini }
57949ab747fSPaolo Bonzini 
58049ab747fSPaolo Bonzini static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
58149ab747fSPaolo Bonzini {
5825edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
58349ab747fSPaolo Bonzini     s->ledstate = ledstate;
58449ab747fSPaolo Bonzini     kbd_put_ledstate(ledstate);
58549ab747fSPaolo Bonzini }
58649ab747fSPaolo Bonzini 
58749ab747fSPaolo Bonzini static void ps2_reset_keyboard(PS2KbdState *s)
58849ab747fSPaolo Bonzini {
5898f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
5908f84e53cSMark Cave-Ayland 
5915edab03dSDon Koch     trace_ps2_reset_keyboard(s);
59249ab747fSPaolo Bonzini     s->scan_enabled = 1;
59349ab747fSPaolo Bonzini     s->scancode_set = 2;
5948f84e53cSMark Cave-Ayland     ps2_reset_queue(ps2);
59549ab747fSPaolo Bonzini     ps2_set_ledstate(s, 0);
59649ab747fSPaolo Bonzini }
59749ab747fSPaolo Bonzini 
59854334e73SMark Cave-Ayland void ps2_write_keyboard(PS2KbdState *s, int val)
59949ab747fSPaolo Bonzini {
6008f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
60149ab747fSPaolo Bonzini 
60254334e73SMark Cave-Ayland     trace_ps2_write_keyboard(s, val);
6038f84e53cSMark Cave-Ayland     ps2_cqueue_reset(ps2);
6048f84e53cSMark Cave-Ayland     switch (ps2->write_cmd) {
60549ab747fSPaolo Bonzini     default:
60649ab747fSPaolo Bonzini     case -1:
60749ab747fSPaolo Bonzini         switch (val) {
60849ab747fSPaolo Bonzini         case 0x00:
6098f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
61049ab747fSPaolo Bonzini             break;
61149ab747fSPaolo Bonzini         case 0x05:
6128f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
61349ab747fSPaolo Bonzini             break;
61449ab747fSPaolo Bonzini         case KBD_CMD_GET_ID:
61549ab747fSPaolo Bonzini             /* We emulate a MF2 AT keyboard here */
6168f84e53cSMark Cave-Ayland             ps2_cqueue_3(ps2, KBD_REPLY_ACK, KBD_REPLY_ID,
6179e24b2ddSVolker Rümelin                          s->translate ? 0x41 : 0x83);
61849ab747fSPaolo Bonzini             break;
61949ab747fSPaolo Bonzini         case KBD_CMD_ECHO:
6208f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_CMD_ECHO);
62149ab747fSPaolo Bonzini             break;
62249ab747fSPaolo Bonzini         case KBD_CMD_ENABLE:
62349ab747fSPaolo Bonzini             s->scan_enabled = 1;
6248f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
62549ab747fSPaolo Bonzini             break;
62649ab747fSPaolo Bonzini         case KBD_CMD_SCANCODE:
62749ab747fSPaolo Bonzini         case KBD_CMD_SET_LEDS:
62849ab747fSPaolo Bonzini         case KBD_CMD_SET_RATE:
629c56b6209SSven Schnelle         case KBD_CMD_SET_MAKE_BREAK:
6308f84e53cSMark Cave-Ayland             ps2->write_cmd = val;
6318f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
63249ab747fSPaolo Bonzini             break;
63349ab747fSPaolo Bonzini         case KBD_CMD_RESET_DISABLE:
63449ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
63549ab747fSPaolo Bonzini             s->scan_enabled = 0;
6368f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
63749ab747fSPaolo Bonzini             break;
63849ab747fSPaolo Bonzini         case KBD_CMD_RESET_ENABLE:
63949ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
64049ab747fSPaolo Bonzini             s->scan_enabled = 1;
6418f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
64249ab747fSPaolo Bonzini             break;
64349ab747fSPaolo Bonzini         case KBD_CMD_RESET:
64449ab747fSPaolo Bonzini             ps2_reset_keyboard(s);
6458f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2,
6467abe7eb2SGeoffrey McRae                          KBD_REPLY_ACK,
6477abe7eb2SGeoffrey McRae                          KBD_REPLY_POR);
64849ab747fSPaolo Bonzini             break;
649c56b6209SSven Schnelle         case KBD_CMD_SET_TYPEMATIC:
6508f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
651c56b6209SSven Schnelle             break;
65249ab747fSPaolo Bonzini         default:
6538f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
65449ab747fSPaolo Bonzini             break;
65549ab747fSPaolo Bonzini         }
65649ab747fSPaolo Bonzini         break;
657c56b6209SSven Schnelle     case KBD_CMD_SET_MAKE_BREAK:
6588f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6598f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
660c56b6209SSven Schnelle         break;
66149ab747fSPaolo Bonzini     case KBD_CMD_SCANCODE:
66249ab747fSPaolo Bonzini         if (val == 0) {
6638f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2, KBD_REPLY_ACK, s->translate ?
6649e24b2ddSVolker Rümelin                 translate_table[s->scancode_set] : s->scancode_set);
6654df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
66649ab747fSPaolo Bonzini             s->scancode_set = val;
6678f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6684df23b64SHervé Poussineau         } else {
6698f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
67049ab747fSPaolo Bonzini         }
6718f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
67249ab747fSPaolo Bonzini         break;
67349ab747fSPaolo Bonzini     case KBD_CMD_SET_LEDS:
67449ab747fSPaolo Bonzini         ps2_set_ledstate(s, val);
6758f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6768f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
67749ab747fSPaolo Bonzini         break;
67849ab747fSPaolo Bonzini     case KBD_CMD_SET_RATE:
6798f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6808f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
68149ab747fSPaolo Bonzini         break;
68249ab747fSPaolo Bonzini     }
68349ab747fSPaolo Bonzini }
68449ab747fSPaolo Bonzini 
685545e5cf8SMark Cave-Ayland /*
686545e5cf8SMark Cave-Ayland  * Set the scancode translation mode.
687545e5cf8SMark Cave-Ayland  * 0 = raw scancodes.
688545e5cf8SMark Cave-Ayland  * 1 = translated scancodes (used by qemu internally).
689545e5cf8SMark Cave-Ayland  */
69049ab747fSPaolo Bonzini 
69154334e73SMark Cave-Ayland void ps2_keyboard_set_translation(PS2KbdState *s, int mode)
69249ab747fSPaolo Bonzini {
69354334e73SMark Cave-Ayland     trace_ps2_keyboard_set_translation(s, mode);
69449ab747fSPaolo Bonzini     s->translate = mode;
69549ab747fSPaolo Bonzini }
69649ab747fSPaolo Bonzini 
6977abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
69849ab747fSPaolo Bonzini {
6992d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
70076968101SVolker Rümelin     /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
70176968101SVolker Rümelin     const int needed = s->mouse_type ? 4 : 3;
70249ab747fSPaolo Bonzini     unsigned int b;
70364ebbb7dSDmitry Petrov     int dx1, dy1, dz1, dw1;
70449ab747fSPaolo Bonzini 
7052d135409SMark Cave-Ayland     if (PS2_QUEUE_SIZE - ps2->queue.count < needed) {
7067abe7eb2SGeoffrey McRae         return 0;
7077abe7eb2SGeoffrey McRae     }
7087abe7eb2SGeoffrey McRae 
70949ab747fSPaolo Bonzini     dx1 = s->mouse_dx;
71049ab747fSPaolo Bonzini     dy1 = s->mouse_dy;
71149ab747fSPaolo Bonzini     dz1 = s->mouse_dz;
71264ebbb7dSDmitry Petrov     dw1 = s->mouse_dw;
71349ab747fSPaolo Bonzini     /* XXX: increase range to 8 bits ? */
714545e5cf8SMark Cave-Ayland     if (dx1 > 127) {
71549ab747fSPaolo Bonzini         dx1 = 127;
716545e5cf8SMark Cave-Ayland     } else if (dx1 < -127) {
71749ab747fSPaolo Bonzini         dx1 = -127;
718545e5cf8SMark Cave-Ayland     }
719545e5cf8SMark Cave-Ayland     if (dy1 > 127) {
72049ab747fSPaolo Bonzini         dy1 = 127;
721545e5cf8SMark Cave-Ayland     } else if (dy1 < -127) {
72249ab747fSPaolo Bonzini         dy1 = -127;
723545e5cf8SMark Cave-Ayland     }
72449ab747fSPaolo Bonzini     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
7252d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, b);
7262d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, dx1 & 0xff);
7272d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, dy1 & 0xff);
72849ab747fSPaolo Bonzini     /* extra byte for IMPS/2 or IMEX */
72949ab747fSPaolo Bonzini     switch (s->mouse_type) {
73049ab747fSPaolo Bonzini     default:
73164ebbb7dSDmitry Petrov         /* Just ignore the wheels if not supported */
73264ebbb7dSDmitry Petrov         s->mouse_dz = 0;
73364ebbb7dSDmitry Petrov         s->mouse_dw = 0;
73449ab747fSPaolo Bonzini         break;
73549ab747fSPaolo Bonzini     case 3:
736545e5cf8SMark Cave-Ayland         if (dz1 > 127) {
73749ab747fSPaolo Bonzini             dz1 = 127;
738545e5cf8SMark Cave-Ayland         } else if (dz1 < -127) {
73949ab747fSPaolo Bonzini             dz1 = -127;
740545e5cf8SMark Cave-Ayland         }
7412d135409SMark Cave-Ayland         ps2_queue_noirq(ps2, dz1 & 0xff);
74264ebbb7dSDmitry Petrov         s->mouse_dz -= dz1;
74364ebbb7dSDmitry Petrov         s->mouse_dw = 0;
74449ab747fSPaolo Bonzini         break;
74549ab747fSPaolo Bonzini     case 4:
74664ebbb7dSDmitry Petrov         /*
74764ebbb7dSDmitry Petrov          * This matches what the Linux kernel expects for exps/2 in
74864ebbb7dSDmitry Petrov          * drivers/input/mouse/psmouse-base.c. Note, if you happen to
74964ebbb7dSDmitry Petrov          * press/release the 4th or 5th buttons at the same moment as a
75064ebbb7dSDmitry Petrov          * horizontal wheel scroll, those button presses will get lost. I'm not
75164ebbb7dSDmitry Petrov          * sure what to do about that, since by this point we don't know
75264ebbb7dSDmitry Petrov          * whether those buttons actually changed state.
75364ebbb7dSDmitry Petrov          */
75464ebbb7dSDmitry Petrov         if (dw1 != 0) {
75564ebbb7dSDmitry Petrov             if (dw1 > 31) {
75664ebbb7dSDmitry Petrov                 dw1 = 31;
75764ebbb7dSDmitry Petrov             } else if (dw1 < -31) {
75864ebbb7dSDmitry Petrov                 dw1 = -31;
75964ebbb7dSDmitry Petrov             }
76064ebbb7dSDmitry Petrov 
76164ebbb7dSDmitry Petrov             /*
76264ebbb7dSDmitry Petrov              * linux kernel expects first 6 bits to represent the value
76364ebbb7dSDmitry Petrov              * for horizontal scroll
76464ebbb7dSDmitry Petrov              */
76564ebbb7dSDmitry Petrov             b = (dw1 & 0x3f) | 0x40;
76664ebbb7dSDmitry Petrov             s->mouse_dw -= dw1;
76764ebbb7dSDmitry Petrov         } else {
76864ebbb7dSDmitry Petrov             if (dz1 > 7) {
76949ab747fSPaolo Bonzini                 dz1 = 7;
77064ebbb7dSDmitry Petrov             } else if (dz1 < -7) {
77149ab747fSPaolo Bonzini                 dz1 = -7;
77264ebbb7dSDmitry Petrov             }
77364ebbb7dSDmitry Petrov 
77449ab747fSPaolo Bonzini             b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
77564ebbb7dSDmitry Petrov             s->mouse_dz -= dz1;
77664ebbb7dSDmitry Petrov         }
7772d135409SMark Cave-Ayland         ps2_queue_noirq(ps2, b);
77849ab747fSPaolo Bonzini         break;
77949ab747fSPaolo Bonzini     }
78049ab747fSPaolo Bonzini 
7812d135409SMark Cave-Ayland     ps2_raise_irq(ps2);
7827abe7eb2SGeoffrey McRae 
7835edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
78449ab747fSPaolo Bonzini     /* update deltas */
78549ab747fSPaolo Bonzini     s->mouse_dx -= dx1;
78649ab747fSPaolo Bonzini     s->mouse_dy -= dy1;
7877abe7eb2SGeoffrey McRae 
7887abe7eb2SGeoffrey McRae     return 1;
78949ab747fSPaolo Bonzini }
79049ab747fSPaolo Bonzini 
7912a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
7922a766d29SGerd Hoffmann                             InputEvent *evt)
79349ab747fSPaolo Bonzini {
7947fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
7958b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
7968b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
7978b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
7988b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
7998b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
8002a766d29SGerd Hoffmann     };
8012a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
802b5a1b443SEric Blake     InputMoveEvent *move;
803b5a1b443SEric Blake     InputBtnEvent *btn;
80449ab747fSPaolo Bonzini 
80549ab747fSPaolo Bonzini     /* check if deltas are recorded when disabled */
806545e5cf8SMark Cave-Ayland     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
80749ab747fSPaolo Bonzini         return;
808545e5cf8SMark Cave-Ayland     }
80949ab747fSPaolo Bonzini 
810568c73a4SEric Blake     switch (evt->type) {
8112a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
81232bafa8fSEric Blake         move = evt->u.rel.data;
813b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
814b5a1b443SEric Blake             s->mouse_dx += move->value;
815b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
816b5a1b443SEric Blake             s->mouse_dy -= move->value;
8172a766d29SGerd Hoffmann         }
8182a766d29SGerd Hoffmann         break;
81949ab747fSPaolo Bonzini 
8202a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
82132bafa8fSEric Blake         btn = evt->u.btn.data;
822b5a1b443SEric Blake         if (btn->down) {
823b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
824b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
8252a766d29SGerd Hoffmann                 s->mouse_dz--;
826b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
8272a766d29SGerd Hoffmann                 s->mouse_dz++;
8282a766d29SGerd Hoffmann             }
82964ebbb7dSDmitry Petrov 
83064ebbb7dSDmitry Petrov             if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
83164ebbb7dSDmitry Petrov                 s->mouse_dw--;
83264ebbb7dSDmitry Petrov             } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
83364ebbb7dSDmitry Petrov                 s->mouse_dw++;
83464ebbb7dSDmitry Petrov             }
8352a766d29SGerd Hoffmann         } else {
836b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
8372a766d29SGerd Hoffmann         }
8382a766d29SGerd Hoffmann         break;
8392a766d29SGerd Hoffmann 
8402a766d29SGerd Hoffmann     default:
8412a766d29SGerd Hoffmann         /* keep gcc happy */
8422a766d29SGerd Hoffmann         break;
8432a766d29SGerd Hoffmann     }
84449ab747fSPaolo Bonzini }
84549ab747fSPaolo Bonzini 
8462a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
8472a766d29SGerd Hoffmann {
8482a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
8492a766d29SGerd Hoffmann 
850143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
851143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
852143c04c7SGeoffrey McRae         return;
853143c04c7SGeoffrey McRae     }
854143c04c7SGeoffrey McRae 
8552a766d29SGerd Hoffmann     if (s->mouse_buttons) {
856fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
8572a766d29SGerd Hoffmann     }
8582858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
859545e5cf8SMark Cave-Ayland         /*
860545e5cf8SMark Cave-Ayland          * if not remote, send event. Multiple events are sent if
861545e5cf8SMark Cave-Ayland          * too big deltas
862545e5cf8SMark Cave-Ayland          */
8637abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
86464ebbb7dSDmitry Petrov             if (s->mouse_dx == 0 && s->mouse_dy == 0
86564ebbb7dSDmitry Petrov                     && s->mouse_dz == 0 && s->mouse_dw == 0) {
86649ab747fSPaolo Bonzini                 break;
86749ab747fSPaolo Bonzini             }
86849ab747fSPaolo Bonzini         }
86949ab747fSPaolo Bonzini     }
87064ebbb7dSDmitry Petrov }
87149ab747fSPaolo Bonzini 
87254334e73SMark Cave-Ayland void ps2_mouse_fake_event(PS2MouseState *s)
87349ab747fSPaolo Bonzini {
87454334e73SMark Cave-Ayland     trace_ps2_mouse_fake_event(s);
8752a766d29SGerd Hoffmann     s->mouse_dx++;
87654334e73SMark Cave-Ayland     ps2_mouse_sync(DEVICE(s));
87749ab747fSPaolo Bonzini }
87849ab747fSPaolo Bonzini 
87954334e73SMark Cave-Ayland void ps2_write_mouse(PS2MouseState *s, int val)
88049ab747fSPaolo Bonzini {
8812d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
8825edab03dSDon Koch 
88354334e73SMark Cave-Ayland     trace_ps2_write_mouse(s, val);
8842d135409SMark Cave-Ayland     switch (ps2->write_cmd) {
88549ab747fSPaolo Bonzini     default:
88649ab747fSPaolo Bonzini     case -1:
88749ab747fSPaolo Bonzini         /* mouse command */
88849ab747fSPaolo Bonzini         if (s->mouse_wrap) {
88949ab747fSPaolo Bonzini             if (val == AUX_RESET_WRAP) {
89049ab747fSPaolo Bonzini                 s->mouse_wrap = 0;
8912d135409SMark Cave-Ayland                 ps2_queue(ps2, AUX_ACK);
89249ab747fSPaolo Bonzini                 return;
89349ab747fSPaolo Bonzini             } else if (val != AUX_RESET) {
8942d135409SMark Cave-Ayland                 ps2_queue(ps2, val);
89549ab747fSPaolo Bonzini                 return;
89649ab747fSPaolo Bonzini             }
89749ab747fSPaolo Bonzini         }
89849ab747fSPaolo Bonzini         switch (val) {
89949ab747fSPaolo Bonzini         case AUX_SET_SCALE11:
90049ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
9012d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
90249ab747fSPaolo Bonzini             break;
90349ab747fSPaolo Bonzini         case AUX_SET_SCALE21:
90449ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_SCALE21;
9052d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
90649ab747fSPaolo Bonzini             break;
90749ab747fSPaolo Bonzini         case AUX_SET_STREAM:
90849ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
9092d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
91049ab747fSPaolo Bonzini             break;
91149ab747fSPaolo Bonzini         case AUX_SET_WRAP:
91249ab747fSPaolo Bonzini             s->mouse_wrap = 1;
9132d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
91449ab747fSPaolo Bonzini             break;
91549ab747fSPaolo Bonzini         case AUX_SET_REMOTE:
91649ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_REMOTE;
9172d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
91849ab747fSPaolo Bonzini             break;
91949ab747fSPaolo Bonzini         case AUX_GET_TYPE:
9202d135409SMark Cave-Ayland             ps2_queue_2(ps2,
9217abe7eb2SGeoffrey McRae                 AUX_ACK,
9227abe7eb2SGeoffrey McRae                 s->mouse_type);
92349ab747fSPaolo Bonzini             break;
92449ab747fSPaolo Bonzini         case AUX_SET_RES:
92549ab747fSPaolo Bonzini         case AUX_SET_SAMPLE:
9262d135409SMark Cave-Ayland             ps2->write_cmd = val;
9272d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
92849ab747fSPaolo Bonzini             break;
92949ab747fSPaolo Bonzini         case AUX_GET_SCALE:
9302d135409SMark Cave-Ayland             ps2_queue_4(ps2,
9317abe7eb2SGeoffrey McRae                 AUX_ACK,
9327abe7eb2SGeoffrey McRae                 s->mouse_status,
9337abe7eb2SGeoffrey McRae                 s->mouse_resolution,
9347abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
93549ab747fSPaolo Bonzini             break;
93649ab747fSPaolo Bonzini         case AUX_POLL:
9372d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
93849ab747fSPaolo Bonzini             ps2_mouse_send_packet(s);
93949ab747fSPaolo Bonzini             break;
94049ab747fSPaolo Bonzini         case AUX_ENABLE_DEV:
94149ab747fSPaolo Bonzini             s->mouse_status |= MOUSE_STATUS_ENABLED;
9422d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
94349ab747fSPaolo Bonzini             break;
94449ab747fSPaolo Bonzini         case AUX_DISABLE_DEV:
94549ab747fSPaolo Bonzini             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
9462d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
94749ab747fSPaolo Bonzini             break;
94849ab747fSPaolo Bonzini         case AUX_SET_DEFAULT:
94949ab747fSPaolo Bonzini             s->mouse_sample_rate = 100;
95049ab747fSPaolo Bonzini             s->mouse_resolution = 2;
95149ab747fSPaolo Bonzini             s->mouse_status = 0;
9522d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
95349ab747fSPaolo Bonzini             break;
95449ab747fSPaolo Bonzini         case AUX_RESET:
95549ab747fSPaolo Bonzini             s->mouse_sample_rate = 100;
95649ab747fSPaolo Bonzini             s->mouse_resolution = 2;
95749ab747fSPaolo Bonzini             s->mouse_status = 0;
95849ab747fSPaolo Bonzini             s->mouse_type = 0;
9592d135409SMark Cave-Ayland             ps2_reset_queue(ps2);
9602d135409SMark Cave-Ayland             ps2_queue_3(ps2,
9617abe7eb2SGeoffrey McRae                 AUX_ACK,
9627abe7eb2SGeoffrey McRae                 0xaa,
9637abe7eb2SGeoffrey McRae                 s->mouse_type);
96449ab747fSPaolo Bonzini             break;
96549ab747fSPaolo Bonzini         default:
96649ab747fSPaolo Bonzini             break;
96749ab747fSPaolo Bonzini         }
96849ab747fSPaolo Bonzini         break;
96949ab747fSPaolo Bonzini     case AUX_SET_SAMPLE:
97049ab747fSPaolo Bonzini         s->mouse_sample_rate = val;
97149ab747fSPaolo Bonzini         /* detect IMPS/2 or IMEX */
97249ab747fSPaolo Bonzini         switch (s->mouse_detect_state) {
97349ab747fSPaolo Bonzini         default:
97449ab747fSPaolo Bonzini         case 0:
975545e5cf8SMark Cave-Ayland             if (val == 200) {
97649ab747fSPaolo Bonzini                 s->mouse_detect_state = 1;
977545e5cf8SMark Cave-Ayland             }
97849ab747fSPaolo Bonzini             break;
97949ab747fSPaolo Bonzini         case 1:
980545e5cf8SMark Cave-Ayland             if (val == 100) {
98149ab747fSPaolo Bonzini                 s->mouse_detect_state = 2;
982545e5cf8SMark Cave-Ayland             } else if (val == 200) {
98349ab747fSPaolo Bonzini                 s->mouse_detect_state = 3;
984545e5cf8SMark Cave-Ayland             } else {
98549ab747fSPaolo Bonzini                 s->mouse_detect_state = 0;
986545e5cf8SMark Cave-Ayland             }
98749ab747fSPaolo Bonzini             break;
98849ab747fSPaolo Bonzini         case 2:
989545e5cf8SMark Cave-Ayland             if (val == 80) {
99049ab747fSPaolo Bonzini                 s->mouse_type = 3; /* IMPS/2 */
991545e5cf8SMark Cave-Ayland             }
99249ab747fSPaolo Bonzini             s->mouse_detect_state = 0;
99349ab747fSPaolo Bonzini             break;
99449ab747fSPaolo Bonzini         case 3:
995545e5cf8SMark Cave-Ayland             if (val == 80) {
99649ab747fSPaolo Bonzini                 s->mouse_type = 4; /* IMEX */
997545e5cf8SMark Cave-Ayland             }
99849ab747fSPaolo Bonzini             s->mouse_detect_state = 0;
99949ab747fSPaolo Bonzini             break;
100049ab747fSPaolo Bonzini         }
10012d135409SMark Cave-Ayland         ps2_queue(ps2, AUX_ACK);
10022d135409SMark Cave-Ayland         ps2->write_cmd = -1;
100349ab747fSPaolo Bonzini         break;
100449ab747fSPaolo Bonzini     case AUX_SET_RES:
100549ab747fSPaolo Bonzini         s->mouse_resolution = val;
10062d135409SMark Cave-Ayland         ps2_queue(ps2, AUX_ACK);
10072d135409SMark Cave-Ayland         ps2->write_cmd = -1;
100849ab747fSPaolo Bonzini         break;
100949ab747fSPaolo Bonzini     }
101049ab747fSPaolo Bonzini }
101149ab747fSPaolo Bonzini 
1012108cb22eSMark Cave-Ayland static void ps2_reset(DeviceState *dev)
101349ab747fSPaolo Bonzini {
1014108cb22eSMark Cave-Ayland     PS2State *s = PS2_DEVICE(dev);
1015108cb22eSMark Cave-Ayland 
101649ab747fSPaolo Bonzini     s->write_cmd = -1;
1017954ee55bSGerd Hoffmann     ps2_reset_queue(s);
10185cb6e556SMark Cave-Ayland     ps2_lower_irq(s);
101949ab747fSPaolo Bonzini }
102049ab747fSPaolo Bonzini 
10212858ab09SGonglei static void ps2_common_post_load(PS2State *s)
10222858ab09SGonglei {
10232858ab09SGonglei     PS2Queue *q = &s->queue;
10244e9bddcbSVolker Rümelin     int ccount = 0;
10252858ab09SGonglei 
10264e9bddcbSVolker Rümelin     /* limit the number of queued command replies to PS2_QUEUE_HEADROOM */
10274e9bddcbSVolker Rümelin     if (q->cwptr != -1) {
10284e9bddcbSVolker Rümelin         ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
10294e9bddcbSVolker Rümelin         if (ccount > PS2_QUEUE_HEADROOM) {
10304e9bddcbSVolker Rümelin             ccount = PS2_QUEUE_HEADROOM;
10314e9bddcbSVolker Rümelin         }
1032a1f2ed2aSPavel Dovgalyuk     }
10332858ab09SGonglei 
10344e9bddcbSVolker Rümelin     /* limit the scancode queue size to PS2_QUEUE_SIZE */
10354e9bddcbSVolker Rümelin     if (q->count < ccount) {
10364e9bddcbSVolker Rümelin         q->count = ccount;
10374e9bddcbSVolker Rümelin     } else if (q->count > ccount + PS2_QUEUE_SIZE) {
10384e9bddcbSVolker Rümelin         q->count = ccount + PS2_QUEUE_SIZE;
10394e9bddcbSVolker Rümelin     }
10404e9bddcbSVolker Rümelin 
10414e9bddcbSVolker Rümelin     /* sanitize rptr and recalculate wptr and cwptr */
104247db2432SVolker Rümelin     q->rptr = q->rptr & (PS2_BUFFER_SIZE - 1);
104347db2432SVolker Rümelin     q->wptr = (q->rptr + q->count) & (PS2_BUFFER_SIZE - 1);
10444e9bddcbSVolker Rümelin     q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1;
10452858ab09SGonglei }
10462858ab09SGonglei 
1047108cb22eSMark Cave-Ayland static void ps2_kbd_reset(DeviceState *dev)
104849ab747fSPaolo Bonzini {
1049108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(dev);
1050108cb22eSMark Cave-Ayland     PS2KbdState *s = PS2_KBD_DEVICE(dev);
105149ab747fSPaolo Bonzini 
1052108cb22eSMark Cave-Ayland     trace_ps2_kbd_reset(s);
1053108cb22eSMark Cave-Ayland     ps2dc->parent_reset(dev);
1054108cb22eSMark Cave-Ayland 
1055d2e550a8SHervé Poussineau     s->scan_enabled = 1;
105649ab747fSPaolo Bonzini     s->translate = 0;
1057089adafdSHervé Poussineau     s->scancode_set = 2;
1058620775d1SDaniel P. Berrange     s->modifiers = 0;
105949ab747fSPaolo Bonzini }
106049ab747fSPaolo Bonzini 
1061108cb22eSMark Cave-Ayland static void ps2_mouse_reset(DeviceState *dev)
106249ab747fSPaolo Bonzini {
1063108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(dev);
1064108cb22eSMark Cave-Ayland     PS2MouseState *s = PS2_MOUSE_DEVICE(dev);
106549ab747fSPaolo Bonzini 
1066108cb22eSMark Cave-Ayland     trace_ps2_mouse_reset(s);
1067108cb22eSMark Cave-Ayland     ps2dc->parent_reset(dev);
1068108cb22eSMark Cave-Ayland 
106949ab747fSPaolo Bonzini     s->mouse_status = 0;
107049ab747fSPaolo Bonzini     s->mouse_resolution = 0;
107149ab747fSPaolo Bonzini     s->mouse_sample_rate = 0;
107249ab747fSPaolo Bonzini     s->mouse_wrap = 0;
107349ab747fSPaolo Bonzini     s->mouse_type = 0;
107449ab747fSPaolo Bonzini     s->mouse_detect_state = 0;
107549ab747fSPaolo Bonzini     s->mouse_dx = 0;
107649ab747fSPaolo Bonzini     s->mouse_dy = 0;
107749ab747fSPaolo Bonzini     s->mouse_dz = 0;
107864ebbb7dSDmitry Petrov     s->mouse_dw = 0;
107949ab747fSPaolo Bonzini     s->mouse_buttons = 0;
108049ab747fSPaolo Bonzini }
108149ab747fSPaolo Bonzini 
108249ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_common = {
108349ab747fSPaolo Bonzini     .name = "PS2 Common State",
108449ab747fSPaolo Bonzini     .version_id = 3,
108549ab747fSPaolo Bonzini     .minimum_version_id = 2,
108649ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
108749ab747fSPaolo Bonzini         VMSTATE_INT32(write_cmd, PS2State),
108849ab747fSPaolo Bonzini         VMSTATE_INT32(queue.rptr, PS2State),
108949ab747fSPaolo Bonzini         VMSTATE_INT32(queue.wptr, PS2State),
109049ab747fSPaolo Bonzini         VMSTATE_INT32(queue.count, PS2State),
109149ab747fSPaolo Bonzini         VMSTATE_BUFFER(queue.data, PS2State),
109249ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
109349ab747fSPaolo Bonzini     }
109449ab747fSPaolo Bonzini };
109549ab747fSPaolo Bonzini 
109649ab747fSPaolo Bonzini static bool ps2_keyboard_ledstate_needed(void *opaque)
109749ab747fSPaolo Bonzini {
109849ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
109949ab747fSPaolo Bonzini 
110049ab747fSPaolo Bonzini     return s->ledstate != 0; /* 0 is default state */
110149ab747fSPaolo Bonzini }
110249ab747fSPaolo Bonzini 
110349ab747fSPaolo Bonzini static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
110449ab747fSPaolo Bonzini {
110549ab747fSPaolo Bonzini     PS2KbdState *s = opaque;
110649ab747fSPaolo Bonzini 
110749ab747fSPaolo Bonzini     kbd_put_ledstate(s->ledstate);
110849ab747fSPaolo Bonzini     return 0;
110949ab747fSPaolo Bonzini }
111049ab747fSPaolo Bonzini 
111149ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
111249ab747fSPaolo Bonzini     .name = "ps2kbd/ledstate",
111349ab747fSPaolo Bonzini     .version_id = 3,
111449ab747fSPaolo Bonzini     .minimum_version_id = 2,
111549ab747fSPaolo Bonzini     .post_load = ps2_kbd_ledstate_post_load,
11165cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
111749ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
111849ab747fSPaolo Bonzini         VMSTATE_INT32(ledstate, PS2KbdState),
111949ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
112049ab747fSPaolo Bonzini     }
112149ab747fSPaolo Bonzini };
112249ab747fSPaolo Bonzini 
112357d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
112457d5c005SHervé Poussineau {
112557d5c005SHervé Poussineau     PS2KbdState *s = opaque;
112657d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
112757d5c005SHervé Poussineau }
112857d5c005SHervé Poussineau 
112957d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
113057d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
113157d5c005SHervé Poussineau     .version_id = 1,
113257d5c005SHervé Poussineau     .minimum_version_id = 1,
113357d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
113457d5c005SHervé Poussineau     .fields = (VMStateField[]) {
113557d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
113657d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
113757d5c005SHervé Poussineau     }
113857d5c005SHervé Poussineau };
113957d5c005SHervé Poussineau 
11404e9bddcbSVolker Rümelin static bool ps2_keyboard_cqueue_needed(void *opaque)
11414e9bddcbSVolker Rümelin {
11424e9bddcbSVolker Rümelin     PS2KbdState *s = opaque;
11438f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11444e9bddcbSVolker Rümelin 
11458f84e53cSMark Cave-Ayland     return ps2->queue.cwptr != -1; /* the queue is mostly empty */
11464e9bddcbSVolker Rümelin }
11474e9bddcbSVolker Rümelin 
11484e9bddcbSVolker Rümelin static const VMStateDescription vmstate_ps2_keyboard_cqueue = {
11494e9bddcbSVolker Rümelin     .name = "ps2kbd/command_reply_queue",
11504e9bddcbSVolker Rümelin     .needed = ps2_keyboard_cqueue_needed,
11514e9bddcbSVolker Rümelin     .fields = (VMStateField[]) {
11528f84e53cSMark Cave-Ayland         VMSTATE_INT32(parent_obj.queue.cwptr, PS2KbdState),
11534e9bddcbSVolker Rümelin         VMSTATE_END_OF_LIST()
11544e9bddcbSVolker Rümelin     }
11554e9bddcbSVolker Rümelin };
11564e9bddcbSVolker Rümelin 
115749ab747fSPaolo Bonzini static int ps2_kbd_post_load(void *opaque, int version_id)
115849ab747fSPaolo Bonzini {
115949ab747fSPaolo Bonzini     PS2KbdState *s = (PS2KbdState *)opaque;
11608f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
116149ab747fSPaolo Bonzini 
1162545e5cf8SMark Cave-Ayland     if (version_id == 2) {
116349ab747fSPaolo Bonzini         s->scancode_set = 2;
1164545e5cf8SMark Cave-Ayland     }
11652858ab09SGonglei 
11662858ab09SGonglei     ps2_common_post_load(ps2);
11672858ab09SGonglei 
116849ab747fSPaolo Bonzini     return 0;
116949ab747fSPaolo Bonzini }
117049ab747fSPaolo Bonzini 
117149ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_keyboard = {
117249ab747fSPaolo Bonzini     .name = "ps2kbd",
117349ab747fSPaolo Bonzini     .version_id = 3,
117449ab747fSPaolo Bonzini     .minimum_version_id = 2,
117549ab747fSPaolo Bonzini     .post_load = ps2_kbd_post_load,
117649ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
11778f84e53cSMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2KbdState, 0, vmstate_ps2_common,
11788f84e53cSMark Cave-Ayland                        PS2State),
117949ab747fSPaolo Bonzini         VMSTATE_INT32(scan_enabled, PS2KbdState),
118049ab747fSPaolo Bonzini         VMSTATE_INT32(translate, PS2KbdState),
118149ab747fSPaolo Bonzini         VMSTATE_INT32_V(scancode_set, PS2KbdState, 3),
118249ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
118349ab747fSPaolo Bonzini     },
11845cd8cadaSJuan Quintela     .subsections = (const VMStateDescription * []) {
11855cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
118657d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
11874e9bddcbSVolker Rümelin         &vmstate_ps2_keyboard_cqueue,
11885cd8cadaSJuan Quintela         NULL
118949ab747fSPaolo Bonzini     }
119049ab747fSPaolo Bonzini };
119149ab747fSPaolo Bonzini 
11922858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
11932858ab09SGonglei {
11942858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
11952d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11962858ab09SGonglei 
11972858ab09SGonglei     ps2_common_post_load(ps2);
11982858ab09SGonglei 
11992858ab09SGonglei     return 0;
12002858ab09SGonglei }
12012858ab09SGonglei 
120249ab747fSPaolo Bonzini static const VMStateDescription vmstate_ps2_mouse = {
120349ab747fSPaolo Bonzini     .name = "ps2mouse",
120449ab747fSPaolo Bonzini     .version_id = 2,
120549ab747fSPaolo Bonzini     .minimum_version_id = 2,
12062858ab09SGonglei     .post_load = ps2_mouse_post_load,
120749ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
12082d135409SMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2MouseState, 0, vmstate_ps2_common,
12092d135409SMark Cave-Ayland                        PS2State),
121049ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_status, PS2MouseState),
121149ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
121249ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
121349ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
121449ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_type, PS2MouseState),
121549ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
121649ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dx, PS2MouseState),
121749ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dy, PS2MouseState),
121849ab747fSPaolo Bonzini         VMSTATE_INT32(mouse_dz, PS2MouseState),
121949ab747fSPaolo Bonzini         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
122049ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
122149ab747fSPaolo Bonzini     }
122249ab747fSPaolo Bonzini };
122349ab747fSPaolo Bonzini 
122466e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
122566e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
122666e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
122766e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
122866e6536eSGerd Hoffmann };
122966e6536eSGerd Hoffmann 
1230ea247a0fSMark Cave-Ayland static void ps2_kbd_realize(DeviceState *dev, Error **errp)
1231ea247a0fSMark Cave-Ayland {
1232ea247a0fSMark Cave-Ayland     qemu_input_handler_register(dev, &ps2_keyboard_handler);
1233ea247a0fSMark Cave-Ayland }
1234ea247a0fSMark Cave-Ayland 
123549ab747fSPaolo Bonzini void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
123649ab747fSPaolo Bonzini {
12378f84e53cSMark Cave-Ayland     DeviceState *dev;
12388f84e53cSMark Cave-Ayland     PS2KbdState *s;
12398f84e53cSMark Cave-Ayland     PS2State *ps2;
12408f84e53cSMark Cave-Ayland 
12418f84e53cSMark Cave-Ayland     dev = qdev_new(TYPE_PS2_KBD_DEVICE);
12428f84e53cSMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
12438f84e53cSMark Cave-Ayland     s = PS2_KBD_DEVICE(dev);
12448f84e53cSMark Cave-Ayland     ps2 = PS2_DEVICE(s);
124549ab747fSPaolo Bonzini 
12465edab03dSDon Koch     trace_ps2_kbd_init(s);
12478f84e53cSMark Cave-Ayland     ps2->update_irq = update_irq;
12488f84e53cSMark Cave-Ayland     ps2->update_arg = update_arg;
1249ea247a0fSMark Cave-Ayland 
125049ab747fSPaolo Bonzini     return s;
125149ab747fSPaolo Bonzini }
125249ab747fSPaolo Bonzini 
12532a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
12542a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
12552a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
12562a766d29SGerd Hoffmann     .event = ps2_mouse_event,
12572a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
12582a766d29SGerd Hoffmann };
12592a766d29SGerd Hoffmann 
12604a68b482SMark Cave-Ayland static void ps2_mouse_realize(DeviceState *dev, Error **errp)
12614a68b482SMark Cave-Ayland {
12624a68b482SMark Cave-Ayland     qemu_input_handler_register(dev, &ps2_mouse_handler);
12634a68b482SMark Cave-Ayland }
12644a68b482SMark Cave-Ayland 
126549ab747fSPaolo Bonzini void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
126649ab747fSPaolo Bonzini {
12672d135409SMark Cave-Ayland     DeviceState *dev;
12682d135409SMark Cave-Ayland     PS2MouseState *s;
12692d135409SMark Cave-Ayland     PS2State *ps2;
12702d135409SMark Cave-Ayland 
12712d135409SMark Cave-Ayland     dev = qdev_new(TYPE_PS2_MOUSE_DEVICE);
12722d135409SMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
12732d135409SMark Cave-Ayland     s = PS2_MOUSE_DEVICE(dev);
12742d135409SMark Cave-Ayland     ps2 = PS2_DEVICE(s);
127549ab747fSPaolo Bonzini 
12765edab03dSDon Koch     trace_ps2_mouse_init(s);
12772d135409SMark Cave-Ayland     ps2->update_irq = update_irq;
12782d135409SMark Cave-Ayland     ps2->update_arg = update_arg;
127949ab747fSPaolo Bonzini     return s;
128049ab747fSPaolo Bonzini }
128164bbdd13SMark Cave-Ayland 
1282108cb22eSMark Cave-Ayland static void ps2_kbd_class_init(ObjectClass *klass, void *data)
1283108cb22eSMark Cave-Ayland {
1284108cb22eSMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
1285108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass);
1286108cb22eSMark Cave-Ayland 
1287ea247a0fSMark Cave-Ayland     dc->realize = ps2_kbd_realize;
1288108cb22eSMark Cave-Ayland     device_class_set_parent_reset(dc, ps2_kbd_reset, &ps2dc->parent_reset);
1289f055f507SMark Cave-Ayland     dc->vmsd = &vmstate_ps2_keyboard;
1290108cb22eSMark Cave-Ayland }
1291108cb22eSMark Cave-Ayland 
12928f84e53cSMark Cave-Ayland static const TypeInfo ps2_kbd_info = {
12938f84e53cSMark Cave-Ayland     .name          = TYPE_PS2_KBD_DEVICE,
12948f84e53cSMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
12958f84e53cSMark Cave-Ayland     .instance_size = sizeof(PS2KbdState),
1296108cb22eSMark Cave-Ayland     .class_init    = ps2_kbd_class_init
12978f84e53cSMark Cave-Ayland };
12988f84e53cSMark Cave-Ayland 
1299108cb22eSMark Cave-Ayland static void ps2_mouse_class_init(ObjectClass *klass, void *data)
1300108cb22eSMark Cave-Ayland {
1301108cb22eSMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
1302108cb22eSMark Cave-Ayland     PS2DeviceClass *ps2dc = PS2_DEVICE_CLASS(klass);
1303108cb22eSMark Cave-Ayland 
13044a68b482SMark Cave-Ayland     dc->realize = ps2_mouse_realize;
1305108cb22eSMark Cave-Ayland     device_class_set_parent_reset(dc, ps2_mouse_reset,
1306108cb22eSMark Cave-Ayland                                   &ps2dc->parent_reset);
130797259e70SMark Cave-Ayland     dc->vmsd = &vmstate_ps2_mouse;
1308108cb22eSMark Cave-Ayland }
1309108cb22eSMark Cave-Ayland 
13102d135409SMark Cave-Ayland static const TypeInfo ps2_mouse_info = {
13112d135409SMark Cave-Ayland     .name          = TYPE_PS2_MOUSE_DEVICE,
13122d135409SMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
13132d135409SMark Cave-Ayland     .instance_size = sizeof(PS2MouseState),
1314108cb22eSMark Cave-Ayland     .class_init    = ps2_mouse_class_init
13152d135409SMark Cave-Ayland };
13162d135409SMark Cave-Ayland 
1317*6beb79e1SMark Cave-Ayland static void ps2_init(Object *obj)
1318*6beb79e1SMark Cave-Ayland {
1319*6beb79e1SMark Cave-Ayland     PS2State *s = PS2_DEVICE(obj);
1320*6beb79e1SMark Cave-Ayland 
1321*6beb79e1SMark Cave-Ayland     qdev_init_gpio_out(DEVICE(obj), &s->irq, 1);
1322*6beb79e1SMark Cave-Ayland }
1323*6beb79e1SMark Cave-Ayland 
132464bbdd13SMark Cave-Ayland static void ps2_class_init(ObjectClass *klass, void *data)
132564bbdd13SMark Cave-Ayland {
132664bbdd13SMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
132764bbdd13SMark Cave-Ayland 
1328108cb22eSMark Cave-Ayland     dc->reset = ps2_reset;
132964bbdd13SMark Cave-Ayland     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
133064bbdd13SMark Cave-Ayland }
133164bbdd13SMark Cave-Ayland 
133264bbdd13SMark Cave-Ayland static const TypeInfo ps2_info = {
133364bbdd13SMark Cave-Ayland     .name          = TYPE_PS2_DEVICE,
133464bbdd13SMark Cave-Ayland     .parent        = TYPE_SYS_BUS_DEVICE,
1335*6beb79e1SMark Cave-Ayland     .instance_init = ps2_init,
133664bbdd13SMark Cave-Ayland     .instance_size = sizeof(PS2State),
133764bbdd13SMark Cave-Ayland     .class_init    = ps2_class_init,
1338494145b2SMark Cave-Ayland     .class_size    = sizeof(PS2DeviceClass),
133964bbdd13SMark Cave-Ayland     .abstract      = true
134064bbdd13SMark Cave-Ayland };
134164bbdd13SMark Cave-Ayland 
134264bbdd13SMark Cave-Ayland static void ps2_register_types(void)
134364bbdd13SMark Cave-Ayland {
134464bbdd13SMark Cave-Ayland     type_register_static(&ps2_info);
13458f84e53cSMark Cave-Ayland     type_register_static(&ps2_kbd_info);
13462d135409SMark Cave-Ayland     type_register_static(&ps2_mouse_info);
134764bbdd13SMark Cave-Ayland }
134864bbdd13SMark Cave-Ayland 
134964bbdd13SMark Cave-Ayland type_init(ps2_register_types)
1350