xref: /qemu/hw/input/ps2.c (revision 545e5cf817c0e635479f202cbb2e0973aa6431d0)
10e43e99cSbellard /*
20e43e99cSbellard  * QEMU PS/2 keyboard/mouse emulation
30e43e99cSbellard  *
40e43e99cSbellard  * Copyright (c) 2003 Fabrice Bellard
50e43e99cSbellard  *
60e43e99cSbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
70e43e99cSbellard  * of this software and associated documentation files (the "Software"), to deal
80e43e99cSbellard  * in the Software without restriction, including without limitation the rights
90e43e99cSbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
100e43e99cSbellard  * copies of the Software, and to permit persons to whom the Software is
110e43e99cSbellard  * furnished to do so, subject to the following conditions:
120e43e99cSbellard  *
130e43e99cSbellard  * The above copyright notice and this permission notice shall be included in
140e43e99cSbellard  * all copies or substantial portions of the Software.
150e43e99cSbellard  *
160e43e99cSbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
170e43e99cSbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
180e43e99cSbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
190e43e99cSbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
200e43e99cSbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
210e43e99cSbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
220e43e99cSbellard  * THE SOFTWARE.
230e43e99cSbellard  */
2471e8a915SMarkus Armbruster 
250430891cSPeter Maydell #include "qemu/osdep.h"
26ec044a80SHervé Poussineau #include "qemu/log.h"
270d09e41aSPaolo Bonzini #include "hw/input/ps2.h"
28d6454270SMarkus Armbruster #include "migration/vmstate.h"
2928ecbaeeSPaolo Bonzini #include "ui/console.h"
3066e6536eSGerd Hoffmann #include "ui/input.h"
3171e8a915SMarkus Armbruster #include "sysemu/reset.h"
3254d31236SMarkus Armbruster #include "sysemu/runstate.h"
330e43e99cSbellard 
345edab03dSDon Koch #include "trace.h"
355edab03dSDon Koch 
360e43e99cSbellard /* Keyboard Commands */
370e43e99cSbellard #define KBD_CMD_SET_LEDS        0xED    /* Set keyboard leds */
380e43e99cSbellard #define KBD_CMD_ECHO            0xEE
39e7d93956Saurel32 #define KBD_CMD_SCANCODE        0xF0    /* Get/set scancode set */
400e43e99cSbellard #define KBD_CMD_GET_ID          0xF2    /* get keyboard ID */
410e43e99cSbellard #define KBD_CMD_SET_RATE        0xF3    /* Set typematic rate */
420e43e99cSbellard #define KBD_CMD_ENABLE          0xF4    /* Enable scanning */
430e43e99cSbellard #define KBD_CMD_RESET_DISABLE   0xF5    /* reset and disable scanning */
440e43e99cSbellard #define KBD_CMD_RESET_ENABLE    0xF6    /* reset and enable scanning */
450e43e99cSbellard #define KBD_CMD_RESET           0xFF    /* Reset */
46c56b6209SSven Schnelle #define KBD_CMD_SET_MAKE_BREAK  0xFC    /* Set Make and Break mode */
47c56b6209SSven Schnelle #define KBD_CMD_SET_TYPEMATIC   0xFA    /* Set Typematic Make and Break mode */
480e43e99cSbellard 
490e43e99cSbellard /* Keyboard Replies */
500e43e99cSbellard #define KBD_REPLY_POR       0xAA    /* Power on reset */
5135c4d671Saurel32 #define KBD_REPLY_ID        0xAB    /* Keyboard ID */
520e43e99cSbellard #define KBD_REPLY_ACK       0xFA    /* Command ACK */
530e43e99cSbellard #define KBD_REPLY_RESEND    0xFE    /* Command NACK, send the cmd again */
540e43e99cSbellard 
550e43e99cSbellard /* Mouse Commands */
560e43e99cSbellard #define AUX_SET_SCALE11     0xE6    /* Set 1:1 scaling */
570e43e99cSbellard #define AUX_SET_SCALE21     0xE7    /* Set 2:1 scaling */
580e43e99cSbellard #define AUX_SET_RES         0xE8    /* Set resolution */
590e43e99cSbellard #define AUX_GET_SCALE       0xE9    /* Get scaling factor */
600e43e99cSbellard #define AUX_SET_STREAM      0xEA    /* Set stream mode */
610e43e99cSbellard #define AUX_POLL            0xEB    /* Poll */
620e43e99cSbellard #define AUX_RESET_WRAP      0xEC    /* Reset wrap mode */
630e43e99cSbellard #define AUX_SET_WRAP        0xEE    /* Set wrap mode */
640e43e99cSbellard #define AUX_SET_REMOTE      0xF0    /* Set remote mode */
650e43e99cSbellard #define AUX_GET_TYPE        0xF2    /* Get type */
660e43e99cSbellard #define AUX_SET_SAMPLE      0xF3    /* Set sample rate */
670e43e99cSbellard #define AUX_ENABLE_DEV      0xF4    /* Enable aux device */
680e43e99cSbellard #define AUX_DISABLE_DEV     0xF5    /* Disable aux device */
690e43e99cSbellard #define AUX_SET_DEFAULT     0xF6
700e43e99cSbellard #define AUX_RESET           0xFF    /* Reset aux device */
710e43e99cSbellard #define AUX_ACK             0xFA    /* Command byte ACK. */
720e43e99cSbellard 
730e43e99cSbellard #define MOUSE_STATUS_REMOTE     0x40
740e43e99cSbellard #define MOUSE_STATUS_ENABLED    0x20
750e43e99cSbellard #define MOUSE_STATUS_SCALE21    0x10
760e43e99cSbellard 
7747db2432SVolker Rümelin /*
7847db2432SVolker Rümelin  * PS/2 buffer size. Keep 256 bytes for compatibility with
7947db2432SVolker Rümelin  * older QEMU versions.
8047db2432SVolker Rümelin  */
8147db2432SVolker Rümelin #define PS2_BUFFER_SIZE     256
8247db2432SVolker Rümelin #define PS2_QUEUE_SIZE      16  /* Queue size required by PS/2 protocol */
834e9bddcbSVolker Rümelin #define PS2_QUEUE_HEADROOM  8   /* Queue size for keyboard command replies */
840e43e99cSbellard 
85620775d1SDaniel P. Berrange /* Bits for 'modifiers' field in PS2KbdState */
86620775d1SDaniel P. Berrange #define MOD_CTRL_L  (1 << 0)
87620775d1SDaniel P. Berrange #define MOD_SHIFT_L (1 << 1)
88620775d1SDaniel P. Berrange #define MOD_ALT_L   (1 << 2)
89620775d1SDaniel P. Berrange #define MOD_CTRL_R  (1 << 3)
90620775d1SDaniel P. Berrange #define MOD_SHIFT_R (1 << 4)
91620775d1SDaniel P. Berrange #define MOD_ALT_R   (1 << 5)
92620775d1SDaniel P. Berrange 
930e43e99cSbellard typedef struct {
9447db2432SVolker Rümelin     uint8_t data[PS2_BUFFER_SIZE];
959e24b2ddSVolker Rümelin     int rptr, wptr, cwptr, count;
960e43e99cSbellard } PS2Queue;
970e43e99cSbellard 
988498bb8dSGerd Hoffmann struct PS2State {
990e43e99cSbellard     PS2Queue queue;
1000e43e99cSbellard     int32_t write_cmd;
1010e43e99cSbellard     void (*update_irq)(void *, int);
1020e43e99cSbellard     void *update_arg;
1038498bb8dSGerd Hoffmann };
1040e43e99cSbellard 
1050e43e99cSbellard typedef struct {
1060e43e99cSbellard     PS2State common;
1070e43e99cSbellard     int scan_enabled;
108f94f5d71Spbrook     int translate;
109e7d93956Saurel32     int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
1107f540ab5SChristophe Fergeau     int ledstate;
11157d5c005SHervé Poussineau     bool need_high_bit;
112620775d1SDaniel P. Berrange     unsigned int modifiers; /* bitmask of MOD_* constants above */
1130e43e99cSbellard } PS2KbdState;
1140e43e99cSbellard 
1150e43e99cSbellard typedef struct {
1160e43e99cSbellard     PS2State common;
1170e43e99cSbellard     uint8_t mouse_status;
1180e43e99cSbellard     uint8_t mouse_resolution;
1190e43e99cSbellard     uint8_t mouse_sample_rate;
1200e43e99cSbellard     uint8_t mouse_wrap;
1210e43e99cSbellard     uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
1220e43e99cSbellard     uint8_t mouse_detect_state;
1230e43e99cSbellard     int mouse_dx; /* current values, needed for 'poll' mode */
1240e43e99cSbellard     int mouse_dy;
1250e43e99cSbellard     int mouse_dz;
12664ebbb7dSDmitry Petrov     int mouse_dw;
1270e43e99cSbellard     uint8_t mouse_buttons;
1280e43e99cSbellard } PS2MouseState;
1290e43e99cSbellard 
13057d5c005SHervé Poussineau static uint8_t translate_table[256] = {
13157d5c005SHervé Poussineau     0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
13257d5c005SHervé Poussineau     0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
13357d5c005SHervé Poussineau     0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
13457d5c005SHervé Poussineau     0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
13557d5c005SHervé Poussineau     0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
13657d5c005SHervé Poussineau     0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
13757d5c005SHervé Poussineau     0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
13857d5c005SHervé Poussineau     0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
13957d5c005SHervé Poussineau     0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
14057d5c005SHervé Poussineau     0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
14157d5c005SHervé Poussineau     0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
14257d5c005SHervé Poussineau     0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
14357d5c005SHervé Poussineau     0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
14457d5c005SHervé Poussineau     0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
14557d5c005SHervé Poussineau     0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
14657d5c005SHervé Poussineau     0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
14757d5c005SHervé Poussineau     0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
14857d5c005SHervé Poussineau     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
14957d5c005SHervé Poussineau     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
15057d5c005SHervé Poussineau     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
15157d5c005SHervé Poussineau     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
15257d5c005SHervé Poussineau     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
15357d5c005SHervé Poussineau     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
15457d5c005SHervé Poussineau     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
15557d5c005SHervé Poussineau     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
15657d5c005SHervé Poussineau     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
15757d5c005SHervé Poussineau     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
15857d5c005SHervé Poussineau     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
15957d5c005SHervé Poussineau     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
16057d5c005SHervé Poussineau     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
16157d5c005SHervé Poussineau     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
16257d5c005SHervé Poussineau     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
16357d5c005SHervé Poussineau };
16457d5c005SHervé Poussineau 
165620775d1SDaniel P. Berrange static unsigned int ps2_modifier_bit(QKeyCode key)
166620775d1SDaniel P. Berrange {
167620775d1SDaniel P. Berrange     switch (key) {
168620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL:
169620775d1SDaniel P. Berrange         return MOD_CTRL_L;
170620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL_R:
171620775d1SDaniel P. Berrange         return MOD_CTRL_R;
172620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT:
173620775d1SDaniel P. Berrange         return MOD_SHIFT_L;
174620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT_R:
175620775d1SDaniel P. Berrange         return MOD_SHIFT_R;
176620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT:
177620775d1SDaniel P. Berrange         return MOD_ALT_L;
178620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT_R:
179620775d1SDaniel P. Berrange         return MOD_ALT_R;
180620775d1SDaniel P. Berrange     default:
181620775d1SDaniel P. Berrange         return 0;
182620775d1SDaniel P. Berrange     }
183620775d1SDaniel P. Berrange }
184620775d1SDaniel P. Berrange 
185954ee55bSGerd Hoffmann static void ps2_reset_queue(PS2State *s)
186954ee55bSGerd Hoffmann {
187954ee55bSGerd Hoffmann     PS2Queue *q = &s->queue;
188954ee55bSGerd Hoffmann 
189954ee55bSGerd Hoffmann     q->rptr = 0;
190954ee55bSGerd Hoffmann     q->wptr = 0;
1919e24b2ddSVolker Rümelin     q->cwptr = -1;
192954ee55bSGerd Hoffmann     q->count = 0;
193954ee55bSGerd Hoffmann }
194954ee55bSGerd Hoffmann 
1952a6505b0SSven Schnelle int ps2_queue_empty(PS2State *s)
1962a6505b0SSven Schnelle {
1972a6505b0SSven Schnelle     return s->queue.count == 0;
1982a6505b0SSven Schnelle }
1992a6505b0SSven Schnelle 
2007abe7eb2SGeoffrey McRae void ps2_queue_noirq(PS2State *s, int b)
2010e43e99cSbellard {
2020e43e99cSbellard     PS2Queue *q = &s->queue;
2030e43e99cSbellard 
2049e24b2ddSVolker Rümelin     if (q->count >= PS2_QUEUE_SIZE) {
2050e43e99cSbellard         return;
2067abe7eb2SGeoffrey McRae     }
2077abe7eb2SGeoffrey McRae 
2080e43e99cSbellard     q->data[q->wptr] = b;
20947db2432SVolker Rümelin     if (++q->wptr == PS2_BUFFER_SIZE) {
2100e43e99cSbellard         q->wptr = 0;
21147db2432SVolker Rümelin     }
2120e43e99cSbellard     q->count++;
2137abe7eb2SGeoffrey McRae }
2147abe7eb2SGeoffrey McRae 
2157abe7eb2SGeoffrey McRae void ps2_raise_irq(PS2State *s)
2167abe7eb2SGeoffrey McRae {
2177abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2187abe7eb2SGeoffrey McRae }
2197abe7eb2SGeoffrey McRae 
2207abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
2217abe7eb2SGeoffrey McRae {
2227704bb02SVolker Rümelin     if (PS2_QUEUE_SIZE - s->queue.count < 1) {
2237704bb02SVolker Rümelin         return;
2247704bb02SVolker Rümelin     }
2257704bb02SVolker Rümelin 
2267abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
22796376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2287abe7eb2SGeoffrey McRae }
2297abe7eb2SGeoffrey McRae 
2307abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
2317abe7eb2SGeoffrey McRae {
2327abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
2337abe7eb2SGeoffrey McRae         return;
2347abe7eb2SGeoffrey McRae     }
2357abe7eb2SGeoffrey McRae 
2367abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2377abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
23896376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2397abe7eb2SGeoffrey McRae }
2407abe7eb2SGeoffrey McRae 
2417abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2427abe7eb2SGeoffrey McRae {
2437abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2447abe7eb2SGeoffrey McRae         return;
2457abe7eb2SGeoffrey McRae     }
2467abe7eb2SGeoffrey McRae 
2477abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2487abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2497abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
25096376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2517abe7eb2SGeoffrey McRae }
2527abe7eb2SGeoffrey McRae 
2537abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2547abe7eb2SGeoffrey McRae {
2557abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2567abe7eb2SGeoffrey McRae         return;
2577abe7eb2SGeoffrey McRae     }
2587abe7eb2SGeoffrey McRae 
2597abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2607abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2617abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2627abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
26396376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2640e43e99cSbellard }
2650e43e99cSbellard 
2669e24b2ddSVolker Rümelin static void ps2_cqueue_data(PS2Queue *q, int b)
2679e24b2ddSVolker Rümelin {
2689e24b2ddSVolker Rümelin     q->data[q->cwptr] = b;
2699e24b2ddSVolker Rümelin     if (++q->cwptr >= PS2_BUFFER_SIZE) {
2709e24b2ddSVolker Rümelin         q->cwptr = 0;
2719e24b2ddSVolker Rümelin     }
2729e24b2ddSVolker Rümelin     q->count++;
2739e24b2ddSVolker Rümelin }
2749e24b2ddSVolker Rümelin 
2759e24b2ddSVolker Rümelin static void ps2_cqueue_1(PS2State *s, int b1)
2769e24b2ddSVolker Rümelin {
2779e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2789e24b2ddSVolker Rümelin 
2799e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 1) & (PS2_BUFFER_SIZE - 1);
2809e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2819e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2829e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2839e24b2ddSVolker Rümelin }
2849e24b2ddSVolker Rümelin 
2859e24b2ddSVolker Rümelin static void ps2_cqueue_2(PS2State *s, int b1, int b2)
2869e24b2ddSVolker Rümelin {
2879e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2889e24b2ddSVolker Rümelin 
2899e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 2) & (PS2_BUFFER_SIZE - 1);
2909e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2919e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2929e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
2939e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2949e24b2ddSVolker Rümelin }
2959e24b2ddSVolker Rümelin 
2969e24b2ddSVolker Rümelin static void ps2_cqueue_3(PS2State *s, int b1, int b2, int b3)
2979e24b2ddSVolker Rümelin {
2989e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2999e24b2ddSVolker Rümelin 
3009e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 3) & (PS2_BUFFER_SIZE - 1);
3019e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
3029e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
3039e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
3049e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b3);
3059e24b2ddSVolker Rümelin     ps2_raise_irq(s);
3069e24b2ddSVolker Rümelin }
3079e24b2ddSVolker Rümelin 
3089e24b2ddSVolker Rümelin static void ps2_cqueue_reset(PS2State *s)
3099e24b2ddSVolker Rümelin {
3109e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
3119e24b2ddSVolker Rümelin     int ccount;
3129e24b2ddSVolker Rümelin 
3139e24b2ddSVolker Rümelin     if (q->cwptr == -1) {
3149e24b2ddSVolker Rümelin         return;
3159e24b2ddSVolker Rümelin     }
3169e24b2ddSVolker Rümelin 
3179e24b2ddSVolker Rümelin     ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
3189e24b2ddSVolker Rümelin     q->count -= ccount;
3199e24b2ddSVolker Rümelin     q->rptr = q->cwptr;
3209e24b2ddSVolker Rümelin     q->cwptr = -1;
3219e24b2ddSVolker Rümelin }
3229e24b2ddSVolker Rümelin 
32357d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
3240e43e99cSbellard static void ps2_put_keycode(void *opaque, int keycode)
3250e43e99cSbellard {
326f94f5d71Spbrook     PS2KbdState *s = opaque;
327e7d93956Saurel32 
3285edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
329fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
33057d5c005SHervé Poussineau 
33157d5c005SHervé Poussineau     if (s->translate) {
33257d5c005SHervé Poussineau         if (keycode == 0xf0) {
33357d5c005SHervé Poussineau             s->need_high_bit = true;
33457d5c005SHervé Poussineau         } else if (s->need_high_bit) {
33557d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode] | 0x80);
33657d5c005SHervé Poussineau             s->need_high_bit = false;
33757d5c005SHervé Poussineau         } else {
33857d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode]);
3397096a96dSRoy Tam         }
34057d5c005SHervé Poussineau     } else {
3410e43e99cSbellard         ps2_queue(&s->common, keycode);
3420e43e99cSbellard     }
34357d5c005SHervé Poussineau }
3440e43e99cSbellard 
34566e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
34666e6536eSGerd Hoffmann                                InputEvent *evt)
34766e6536eSGerd Hoffmann {
34866e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
34932bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
3508c10e0baSHervé Poussineau     int qcode;
351ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
352620775d1SDaniel P. Berrange     int mod;
35366e6536eSGerd Hoffmann 
354143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
355143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
356143c04c7SGeoffrey McRae         return;
357143c04c7SGeoffrey McRae     }
358143c04c7SGeoffrey McRae 
359fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
3608c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
3618c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
36257d5c005SHervé Poussineau 
363620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
364644f66bfSDaniel P. Berrangé     trace_ps2_keyboard_event(s, qcode, key->down, mod,
365644f66bfSDaniel P. Berrangé                              s->modifiers, s->scancode_set, s->translate);
366620775d1SDaniel P. Berrange     if (key->down) {
367620775d1SDaniel P. Berrange         s->modifiers |= mod;
368620775d1SDaniel P. Berrange     } else {
369620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
370620775d1SDaniel P. Berrange     }
371620775d1SDaniel P. Berrange 
3728c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3738c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
37429fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
37529fd23a5SDaniel P. Berrange                 if (key->down) {
37629fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
37729fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
37829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
37929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
38029fd23a5SDaniel P. Berrange                 }
38129fd23a5SDaniel P. Berrange             } else {
3828c10e0baSHervé Poussineau                 if (key->down) {
3838c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3848c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3858c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
386927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3878c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
3888c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
3898c10e0baSHervé Poussineau                 }
39029fd23a5SDaniel P. Berrange             }
3918c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
392620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
393620775d1SDaniel P. Berrange                 if (key->down) {
394620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
395620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
396620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
397620775d1SDaniel P. Berrange                 } else {
398620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
399620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
400620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
401620775d1SDaniel P. Berrange                 }
402620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
403620775d1SDaniel P. Berrange                 if (key->down) {
404620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
405620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
406620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
407620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
408620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
409620775d1SDaniel P. Berrange                 } else {
410620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
411620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
412620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
413620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
414620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
415620775d1SDaniel P. Berrange                 }
4168f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4178f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4188f63458fSDaniel P. Berrange                 if (key->down) {
4198f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4208f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
4218f63458fSDaniel P. Berrange                 } else {
4228f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4238f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
4248f63458fSDaniel P. Berrange                 }
425620775d1SDaniel P. Berrange             } else {
4268c10e0baSHervé Poussineau                 if (key->down) {
4278c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4288c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
4298c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4308c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
4318c10e0baSHervé Poussineau                 } else {
4328c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4338c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
4348c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4358c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
4368c10e0baSHervé Poussineau                 }
437620775d1SDaniel P. Berrange             }
4388c10e0baSHervé Poussineau         } else {
439*545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset1_len) {
440ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
441*545e5cf8SMark Cave-Ayland             }
4428c10e0baSHervé Poussineau             if (keycode) {
4438c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4448c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4458c10e0baSHervé Poussineau                 }
4468c10e0baSHervé Poussineau                 if (!key->down) {
4478c10e0baSHervé Poussineau                     keycode |= 0x80;
4488c10e0baSHervé Poussineau                 }
4498c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
4508c10e0baSHervé Poussineau             } else {
451ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
452ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
4538c10e0baSHervé Poussineau             }
4548c10e0baSHervé Poussineau         }
4558c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
4568c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
45729fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
45829fd23a5SDaniel P. Berrange                 if (key->down) {
45929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
46029fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
46129fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
46229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
46329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
46429fd23a5SDaniel P. Berrange                 }
46529fd23a5SDaniel P. Berrange             } else {
4668c10e0baSHervé Poussineau                 if (key->down) {
4678c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4688c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4698c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4708c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4718c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4728c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4738c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4748c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4758c10e0baSHervé Poussineau                 }
47629fd23a5SDaniel P. Berrange             }
4778c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
478620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
479620775d1SDaniel P. Berrange                 if (key->down) {
480620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
481620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
482620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
483620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
484620775d1SDaniel P. Berrange                 } else {
485620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
486620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
487620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
488620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
489620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
490620775d1SDaniel P. Berrange                 }
491620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
492620775d1SDaniel P. Berrange                 if (key->down) {
493620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
494620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
495620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
496620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
497620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
498620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
499620775d1SDaniel P. Berrange                 } else {
500620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
501620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
502620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
503620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
504620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
505620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
506620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
507620775d1SDaniel P. Berrange                 }
5088f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
5098f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
5108f63458fSDaniel P. Berrange                 if (key->down) {
5118f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
5128f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
5138f63458fSDaniel P. Berrange                 } else {
5148f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
5158f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
5168f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
5178f63458fSDaniel P. Berrange                 }
518620775d1SDaniel P. Berrange             } else {
5198c10e0baSHervé Poussineau                 if (key->down) {
5208c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5218c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
5228c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5238c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
5248c10e0baSHervé Poussineau                 } else {
5258c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5268c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5278c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
5288c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5298c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5308c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
5318c10e0baSHervé Poussineau                 }
532620775d1SDaniel P. Berrange             }
5338c10e0baSHervé Poussineau         } else {
534*545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset2_len) {
535ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
536*545e5cf8SMark Cave-Ayland             }
5378c10e0baSHervé Poussineau             if (keycode) {
5388c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
5398c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
5408c10e0baSHervé Poussineau                 }
5418c10e0baSHervé Poussineau                 if (!key->down) {
5428c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5438c10e0baSHervé Poussineau                 }
5448c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
54557d5c005SHervé Poussineau             } else {
546ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
547ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
54857d5c005SHervé Poussineau             }
54957d5c005SHervé Poussineau         }
55057d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
551*545e5cf8SMark Cave-Ayland         if (qcode < qemu_input_map_qcode_to_atset3_len) {
552ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
553*545e5cf8SMark Cave-Ayland         }
5548c10e0baSHervé Poussineau         if (keycode) {
5558c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
5568c10e0baSHervé Poussineau             if (!key->down) {
5578c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
55857d5c005SHervé Poussineau             }
55957d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
5608c10e0baSHervé Poussineau         } else {
561ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
562ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
5638c10e0baSHervé Poussineau         }
56466e6536eSGerd Hoffmann     }
56566e6536eSGerd Hoffmann }
56666e6536eSGerd Hoffmann 
5678498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
5680e43e99cSbellard {
5690e43e99cSbellard     PS2Queue *q;
5700e43e99cSbellard     int val, index;
5710e43e99cSbellard 
5728498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
5730e43e99cSbellard     q = &s->queue;
5740e43e99cSbellard     if (q->count == 0) {
575*545e5cf8SMark Cave-Ayland         /*
576*545e5cf8SMark Cave-Ayland          * NOTE: if no data left, we return the last keyboard one
577*545e5cf8SMark Cave-Ayland          * (needed for EMM386)
578*545e5cf8SMark Cave-Ayland          */
5790e43e99cSbellard         /* XXX: need a timer to do things correctly */
5800e43e99cSbellard         index = q->rptr - 1;
58147db2432SVolker Rümelin         if (index < 0) {
58247db2432SVolker Rümelin             index = PS2_BUFFER_SIZE - 1;
58347db2432SVolker Rümelin         }
5840e43e99cSbellard         val = q->data[index];
5850e43e99cSbellard     } else {
5860e43e99cSbellard         val = q->data[q->rptr];
58747db2432SVolker Rümelin         if (++q->rptr == PS2_BUFFER_SIZE) {
5880e43e99cSbellard             q->rptr = 0;
58947db2432SVolker Rümelin         }
5900e43e99cSbellard         q->count--;
5919e24b2ddSVolker Rümelin         if (q->rptr == q->cwptr) {
5929e24b2ddSVolker Rümelin             /* command reply queue is empty */
5939e24b2ddSVolker Rümelin             q->cwptr = -1;
5949e24b2ddSVolker Rümelin         }
5950e43e99cSbellard         /* reading deasserts IRQ */
5960e43e99cSbellard         s->update_irq(s->update_arg, 0);
5970e43e99cSbellard         /* reassert IRQs if data left */
598cec32524SVolker Rümelin         if (q->count) {
599cec32524SVolker Rümelin             s->update_irq(s->update_arg, 1);
600cec32524SVolker Rümelin         }
6010e43e99cSbellard     }
6020e43e99cSbellard     return val;
6030e43e99cSbellard }
6040e43e99cSbellard 
6057f540ab5SChristophe Fergeau static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
6067f540ab5SChristophe Fergeau {
6075edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
6087f540ab5SChristophe Fergeau     s->ledstate = ledstate;
6097f540ab5SChristophe Fergeau     kbd_put_ledstate(ledstate);
6107f540ab5SChristophe Fergeau }
6117f540ab5SChristophe Fergeau 
6120e43e99cSbellard static void ps2_reset_keyboard(PS2KbdState *s)
6130e43e99cSbellard {
6145edab03dSDon Koch     trace_ps2_reset_keyboard(s);
6150e43e99cSbellard     s->scan_enabled = 1;
616e7d93956Saurel32     s->scancode_set = 2;
6176e24ee0cSGerd Hoffmann     ps2_reset_queue(&s->common);
6187f540ab5SChristophe Fergeau     ps2_set_ledstate(s, 0);
6190e43e99cSbellard }
6200e43e99cSbellard 
6210e43e99cSbellard void ps2_write_keyboard(void *opaque, int val)
6220e43e99cSbellard {
6230e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
6240e43e99cSbellard 
6255edab03dSDon Koch     trace_ps2_write_keyboard(opaque, val);
6269e24b2ddSVolker Rümelin     ps2_cqueue_reset(&s->common);
6270e43e99cSbellard     switch (s->common.write_cmd) {
6280e43e99cSbellard     default:
6290e43e99cSbellard     case -1:
6300e43e99cSbellard         switch (val) {
6310e43e99cSbellard         case 0x00:
6329e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6330e43e99cSbellard             break;
6340e43e99cSbellard         case 0x05:
6359e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_RESEND);
6360e43e99cSbellard             break;
6370e43e99cSbellard         case KBD_CMD_GET_ID:
638e7d93956Saurel32             /* We emulate a MF2 AT keyboard here */
6399e24b2ddSVolker Rümelin             ps2_cqueue_3(&s->common, KBD_REPLY_ACK, KBD_REPLY_ID,
6409e24b2ddSVolker Rümelin                          s->translate ? 0x41 : 0x83);
6410e43e99cSbellard             break;
6420e43e99cSbellard         case KBD_CMD_ECHO:
6439e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_CMD_ECHO);
6440e43e99cSbellard             break;
6450e43e99cSbellard         case KBD_CMD_ENABLE:
6460e43e99cSbellard             s->scan_enabled = 1;
6479e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6480e43e99cSbellard             break;
649e7d93956Saurel32         case KBD_CMD_SCANCODE:
6500e43e99cSbellard         case KBD_CMD_SET_LEDS:
6510e43e99cSbellard         case KBD_CMD_SET_RATE:
652c56b6209SSven Schnelle         case KBD_CMD_SET_MAKE_BREAK:
6530e43e99cSbellard             s->common.write_cmd = val;
6549e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6550e43e99cSbellard             break;
6560e43e99cSbellard         case KBD_CMD_RESET_DISABLE:
6570e43e99cSbellard             ps2_reset_keyboard(s);
6580e43e99cSbellard             s->scan_enabled = 0;
6599e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6600e43e99cSbellard             break;
6610e43e99cSbellard         case KBD_CMD_RESET_ENABLE:
6620e43e99cSbellard             ps2_reset_keyboard(s);
6630e43e99cSbellard             s->scan_enabled = 1;
6649e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6650e43e99cSbellard             break;
6660e43e99cSbellard         case KBD_CMD_RESET:
6670e43e99cSbellard             ps2_reset_keyboard(s);
6689e24b2ddSVolker Rümelin             ps2_cqueue_2(&s->common,
6697abe7eb2SGeoffrey McRae                          KBD_REPLY_ACK,
6707abe7eb2SGeoffrey McRae                          KBD_REPLY_POR);
6710e43e99cSbellard             break;
672c56b6209SSven Schnelle         case KBD_CMD_SET_TYPEMATIC:
6739e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
674c56b6209SSven Schnelle             break;
6750e43e99cSbellard         default:
6769e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_RESEND);
6770e43e99cSbellard             break;
6780e43e99cSbellard         }
6790e43e99cSbellard         break;
680c56b6209SSven Schnelle     case KBD_CMD_SET_MAKE_BREAK:
6819e24b2ddSVolker Rümelin         ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
682c56b6209SSven Schnelle         s->common.write_cmd = -1;
683c56b6209SSven Schnelle         break;
684e7d93956Saurel32     case KBD_CMD_SCANCODE:
685e7d93956Saurel32         if (val == 0) {
6869e24b2ddSVolker Rümelin             ps2_cqueue_2(&s->common, KBD_REPLY_ACK, s->translate ?
6879e24b2ddSVolker Rümelin                 translate_table[s->scancode_set] : s->scancode_set);
6884df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
689e7d93956Saurel32             s->scancode_set = val;
6909e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6914df23b64SHervé Poussineau         } else {
6929e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_RESEND);
693e7d93956Saurel32         }
694e7d93956Saurel32         s->common.write_cmd = -1;
695e7d93956Saurel32         break;
6960e43e99cSbellard     case KBD_CMD_SET_LEDS:
6977f540ab5SChristophe Fergeau         ps2_set_ledstate(s, val);
6989e24b2ddSVolker Rümelin         ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6990e43e99cSbellard         s->common.write_cmd = -1;
7000e43e99cSbellard         break;
7010e43e99cSbellard     case KBD_CMD_SET_RATE:
7029e24b2ddSVolker Rümelin         ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
7030e43e99cSbellard         s->common.write_cmd = -1;
7040e43e99cSbellard         break;
7050e43e99cSbellard     }
7060e43e99cSbellard }
7070e43e99cSbellard 
708*545e5cf8SMark Cave-Ayland /*
709*545e5cf8SMark Cave-Ayland  * Set the scancode translation mode.
710*545e5cf8SMark Cave-Ayland  * 0 = raw scancodes.
711*545e5cf8SMark Cave-Ayland  * 1 = translated scancodes (used by qemu internally).
712*545e5cf8SMark Cave-Ayland  */
713f94f5d71Spbrook 
714f94f5d71Spbrook void ps2_keyboard_set_translation(void *opaque, int mode)
715f94f5d71Spbrook {
716f94f5d71Spbrook     PS2KbdState *s = (PS2KbdState *)opaque;
7175edab03dSDon Koch     trace_ps2_keyboard_set_translation(opaque, mode);
718f94f5d71Spbrook     s->translate = mode;
719f94f5d71Spbrook }
720f94f5d71Spbrook 
7217abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
7220e43e99cSbellard {
72376968101SVolker Rümelin     /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
72476968101SVolker Rümelin     const int needed = s->mouse_type ? 4 : 3;
7250e43e99cSbellard     unsigned int b;
72664ebbb7dSDmitry Petrov     int dx1, dy1, dz1, dw1;
7270e43e99cSbellard 
7287abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->common.queue.count < needed) {
7297abe7eb2SGeoffrey McRae         return 0;
7307abe7eb2SGeoffrey McRae     }
7317abe7eb2SGeoffrey McRae 
7320e43e99cSbellard     dx1 = s->mouse_dx;
7330e43e99cSbellard     dy1 = s->mouse_dy;
7340e43e99cSbellard     dz1 = s->mouse_dz;
73564ebbb7dSDmitry Petrov     dw1 = s->mouse_dw;
7360e43e99cSbellard     /* XXX: increase range to 8 bits ? */
737*545e5cf8SMark Cave-Ayland     if (dx1 > 127) {
7380e43e99cSbellard         dx1 = 127;
739*545e5cf8SMark Cave-Ayland     } else if (dx1 < -127) {
7400e43e99cSbellard         dx1 = -127;
741*545e5cf8SMark Cave-Ayland     }
742*545e5cf8SMark Cave-Ayland     if (dy1 > 127) {
7430e43e99cSbellard         dy1 = 127;
744*545e5cf8SMark Cave-Ayland     } else if (dy1 < -127) {
7450e43e99cSbellard         dy1 = -127;
746*545e5cf8SMark Cave-Ayland     }
7470e43e99cSbellard     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
7487abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, b);
7497abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dx1 & 0xff);
7507abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dy1 & 0xff);
7510e43e99cSbellard     /* extra byte for IMPS/2 or IMEX */
7520e43e99cSbellard     switch (s->mouse_type) {
7530e43e99cSbellard     default:
75464ebbb7dSDmitry Petrov         /* Just ignore the wheels if not supported */
75564ebbb7dSDmitry Petrov         s->mouse_dz = 0;
75664ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7570e43e99cSbellard         break;
7580e43e99cSbellard     case 3:
759*545e5cf8SMark Cave-Ayland         if (dz1 > 127) {
7600e43e99cSbellard             dz1 = 127;
761*545e5cf8SMark Cave-Ayland         } else if (dz1 < -127) {
7620e43e99cSbellard             dz1 = -127;
763*545e5cf8SMark Cave-Ayland         }
7647abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, dz1 & 0xff);
76564ebbb7dSDmitry Petrov         s->mouse_dz -= dz1;
76664ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7670e43e99cSbellard         break;
7680e43e99cSbellard     case 4:
76964ebbb7dSDmitry Petrov         /*
77064ebbb7dSDmitry Petrov          * This matches what the Linux kernel expects for exps/2 in
77164ebbb7dSDmitry Petrov          * drivers/input/mouse/psmouse-base.c. Note, if you happen to
77264ebbb7dSDmitry Petrov          * press/release the 4th or 5th buttons at the same moment as a
77364ebbb7dSDmitry Petrov          * horizontal wheel scroll, those button presses will get lost. I'm not
77464ebbb7dSDmitry Petrov          * sure what to do about that, since by this point we don't know
77564ebbb7dSDmitry Petrov          * whether those buttons actually changed state.
77664ebbb7dSDmitry Petrov          */
77764ebbb7dSDmitry Petrov         if (dw1 != 0) {
77864ebbb7dSDmitry Petrov             if (dw1 > 31) {
77964ebbb7dSDmitry Petrov                 dw1 = 31;
78064ebbb7dSDmitry Petrov             } else if (dw1 < -31) {
78164ebbb7dSDmitry Petrov                 dw1 = -31;
78264ebbb7dSDmitry Petrov             }
78364ebbb7dSDmitry Petrov 
78464ebbb7dSDmitry Petrov             /*
78564ebbb7dSDmitry Petrov              * linux kernel expects first 6 bits to represent the value
78664ebbb7dSDmitry Petrov              * for horizontal scroll
78764ebbb7dSDmitry Petrov              */
78864ebbb7dSDmitry Petrov             b = (dw1 & 0x3f) | 0x40;
78964ebbb7dSDmitry Petrov             s->mouse_dw -= dw1;
79064ebbb7dSDmitry Petrov         } else {
79164ebbb7dSDmitry Petrov             if (dz1 > 7) {
7920e43e99cSbellard                 dz1 = 7;
79364ebbb7dSDmitry Petrov             } else if (dz1 < -7) {
7940e43e99cSbellard                 dz1 = -7;
79564ebbb7dSDmitry Petrov             }
79664ebbb7dSDmitry Petrov 
7970e43e99cSbellard             b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
79864ebbb7dSDmitry Petrov             s->mouse_dz -= dz1;
79964ebbb7dSDmitry Petrov         }
8007abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, b);
8010e43e99cSbellard         break;
8020e43e99cSbellard     }
8030e43e99cSbellard 
8047abe7eb2SGeoffrey McRae     ps2_raise_irq(&s->common);
8057abe7eb2SGeoffrey McRae 
8065edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
8070e43e99cSbellard     /* update deltas */
8080e43e99cSbellard     s->mouse_dx -= dx1;
8090e43e99cSbellard     s->mouse_dy -= dy1;
8107abe7eb2SGeoffrey McRae 
8117abe7eb2SGeoffrey McRae     return 1;
8120e43e99cSbellard }
8130e43e99cSbellard 
8142a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
8152a766d29SGerd Hoffmann                             InputEvent *evt)
8160e43e99cSbellard {
8177fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
8188b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
8198b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
8208b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
8218b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
8228b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
8232a766d29SGerd Hoffmann     };
8242a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
825b5a1b443SEric Blake     InputMoveEvent *move;
826b5a1b443SEric Blake     InputBtnEvent *btn;
8270e43e99cSbellard 
8280e43e99cSbellard     /* check if deltas are recorded when disabled */
829*545e5cf8SMark Cave-Ayland     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
8300e43e99cSbellard         return;
831*545e5cf8SMark Cave-Ayland     }
8320e43e99cSbellard 
833568c73a4SEric Blake     switch (evt->type) {
8342a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
83532bafa8fSEric Blake         move = evt->u.rel.data;
836b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
837b5a1b443SEric Blake             s->mouse_dx += move->value;
838b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
839b5a1b443SEric Blake             s->mouse_dy -= move->value;
8402a766d29SGerd Hoffmann         }
8412a766d29SGerd Hoffmann         break;
8420e43e99cSbellard 
8432a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
84432bafa8fSEric Blake         btn = evt->u.btn.data;
845b5a1b443SEric Blake         if (btn->down) {
846b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
847b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
8482a766d29SGerd Hoffmann                 s->mouse_dz--;
849b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
8502a766d29SGerd Hoffmann                 s->mouse_dz++;
8512a766d29SGerd Hoffmann             }
85264ebbb7dSDmitry Petrov 
85364ebbb7dSDmitry Petrov             if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
85464ebbb7dSDmitry Petrov                 s->mouse_dw--;
85564ebbb7dSDmitry Petrov             } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
85664ebbb7dSDmitry Petrov                 s->mouse_dw++;
85764ebbb7dSDmitry Petrov             }
8582a766d29SGerd Hoffmann         } else {
859b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
8602a766d29SGerd Hoffmann         }
8612a766d29SGerd Hoffmann         break;
8622a766d29SGerd Hoffmann 
8632a766d29SGerd Hoffmann     default:
8642a766d29SGerd Hoffmann         /* keep gcc happy */
8652a766d29SGerd Hoffmann         break;
8662a766d29SGerd Hoffmann     }
867fd214d18SGerd Hoffmann }
868fd214d18SGerd Hoffmann 
8692a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
8702a766d29SGerd Hoffmann {
8712a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
8722a766d29SGerd Hoffmann 
873143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
874143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
875143c04c7SGeoffrey McRae         return;
876143c04c7SGeoffrey McRae     }
877143c04c7SGeoffrey McRae 
8782a766d29SGerd Hoffmann     if (s->mouse_buttons) {
879fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
8802a766d29SGerd Hoffmann     }
8812858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
882*545e5cf8SMark Cave-Ayland         /*
883*545e5cf8SMark Cave-Ayland          * if not remote, send event. Multiple events are sent if
884*545e5cf8SMark Cave-Ayland          * too big deltas
885*545e5cf8SMark Cave-Ayland          */
8867abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
88764ebbb7dSDmitry Petrov             if (s->mouse_dx == 0 && s->mouse_dy == 0
88864ebbb7dSDmitry Petrov                     && s->mouse_dz == 0 && s->mouse_dw == 0) {
8890e43e99cSbellard                 break;
8900e43e99cSbellard             }
8910e43e99cSbellard         }
8920e43e99cSbellard     }
89364ebbb7dSDmitry Petrov }
8940e43e99cSbellard 
895548df2acSths void ps2_mouse_fake_event(void *opaque)
896548df2acSths {
8972a766d29SGerd Hoffmann     PS2MouseState *s = opaque;
8985edab03dSDon Koch     trace_ps2_mouse_fake_event(opaque);
8992a766d29SGerd Hoffmann     s->mouse_dx++;
9002a766d29SGerd Hoffmann     ps2_mouse_sync(opaque);
901548df2acSths }
902548df2acSths 
9030e43e99cSbellard void ps2_write_mouse(void *opaque, int val)
9040e43e99cSbellard {
9050e43e99cSbellard     PS2MouseState *s = (PS2MouseState *)opaque;
9065edab03dSDon Koch 
9075edab03dSDon Koch     trace_ps2_write_mouse(opaque, val);
9080e43e99cSbellard     switch (s->common.write_cmd) {
9090e43e99cSbellard     default:
9100e43e99cSbellard     case -1:
9110e43e99cSbellard         /* mouse command */
9120e43e99cSbellard         if (s->mouse_wrap) {
9130e43e99cSbellard             if (val == AUX_RESET_WRAP) {
9140e43e99cSbellard                 s->mouse_wrap = 0;
9150e43e99cSbellard                 ps2_queue(&s->common, AUX_ACK);
9160e43e99cSbellard                 return;
9170e43e99cSbellard             } else if (val != AUX_RESET) {
9180e43e99cSbellard                 ps2_queue(&s->common, val);
9190e43e99cSbellard                 return;
9200e43e99cSbellard             }
9210e43e99cSbellard         }
9220e43e99cSbellard         switch (val) {
9230e43e99cSbellard         case AUX_SET_SCALE11:
9240e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
9250e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9260e43e99cSbellard             break;
9270e43e99cSbellard         case AUX_SET_SCALE21:
9280e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_SCALE21;
9290e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9300e43e99cSbellard             break;
9310e43e99cSbellard         case AUX_SET_STREAM:
9320e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
9330e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9340e43e99cSbellard             break;
9350e43e99cSbellard         case AUX_SET_WRAP:
9360e43e99cSbellard             s->mouse_wrap = 1;
9370e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9380e43e99cSbellard             break;
9390e43e99cSbellard         case AUX_SET_REMOTE:
9400e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_REMOTE;
9410e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9420e43e99cSbellard             break;
9430e43e99cSbellard         case AUX_GET_TYPE:
9447abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
9457abe7eb2SGeoffrey McRae                 AUX_ACK,
9467abe7eb2SGeoffrey McRae                 s->mouse_type);
9470e43e99cSbellard             break;
9480e43e99cSbellard         case AUX_SET_RES:
9490e43e99cSbellard         case AUX_SET_SAMPLE:
9500e43e99cSbellard             s->common.write_cmd = val;
9510e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9520e43e99cSbellard             break;
9530e43e99cSbellard         case AUX_GET_SCALE:
9547abe7eb2SGeoffrey McRae             ps2_queue_4(&s->common,
9557abe7eb2SGeoffrey McRae                 AUX_ACK,
9567abe7eb2SGeoffrey McRae                 s->mouse_status,
9577abe7eb2SGeoffrey McRae                 s->mouse_resolution,
9587abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
9590e43e99cSbellard             break;
9600e43e99cSbellard         case AUX_POLL:
9610e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9620e43e99cSbellard             ps2_mouse_send_packet(s);
9630e43e99cSbellard             break;
9640e43e99cSbellard         case AUX_ENABLE_DEV:
9650e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_ENABLED;
9660e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9670e43e99cSbellard             break;
9680e43e99cSbellard         case AUX_DISABLE_DEV:
9690e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
9700e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9710e43e99cSbellard             break;
9720e43e99cSbellard         case AUX_SET_DEFAULT:
9730e43e99cSbellard             s->mouse_sample_rate = 100;
9740e43e99cSbellard             s->mouse_resolution = 2;
9750e43e99cSbellard             s->mouse_status = 0;
9760e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9770e43e99cSbellard             break;
9780e43e99cSbellard         case AUX_RESET:
9790e43e99cSbellard             s->mouse_sample_rate = 100;
9800e43e99cSbellard             s->mouse_resolution = 2;
9810e43e99cSbellard             s->mouse_status = 0;
9820e43e99cSbellard             s->mouse_type = 0;
983143c04c7SGeoffrey McRae             ps2_reset_queue(&s->common);
9847abe7eb2SGeoffrey McRae             ps2_queue_3(&s->common,
9857abe7eb2SGeoffrey McRae                 AUX_ACK,
9867abe7eb2SGeoffrey McRae                 0xaa,
9877abe7eb2SGeoffrey McRae                 s->mouse_type);
9880e43e99cSbellard             break;
9890e43e99cSbellard         default:
9900e43e99cSbellard             break;
9910e43e99cSbellard         }
9920e43e99cSbellard         break;
9930e43e99cSbellard     case AUX_SET_SAMPLE:
9940e43e99cSbellard         s->mouse_sample_rate = val;
9950e43e99cSbellard         /* detect IMPS/2 or IMEX */
9960e43e99cSbellard         switch (s->mouse_detect_state) {
9970e43e99cSbellard         default:
9980e43e99cSbellard         case 0:
999*545e5cf8SMark Cave-Ayland             if (val == 200) {
10000e43e99cSbellard                 s->mouse_detect_state = 1;
1001*545e5cf8SMark Cave-Ayland             }
10020e43e99cSbellard             break;
10030e43e99cSbellard         case 1:
1004*545e5cf8SMark Cave-Ayland             if (val == 100) {
10050e43e99cSbellard                 s->mouse_detect_state = 2;
1006*545e5cf8SMark Cave-Ayland             } else if (val == 200) {
10070e43e99cSbellard                 s->mouse_detect_state = 3;
1008*545e5cf8SMark Cave-Ayland             } else {
10090e43e99cSbellard                 s->mouse_detect_state = 0;
1010*545e5cf8SMark Cave-Ayland             }
10110e43e99cSbellard             break;
10120e43e99cSbellard         case 2:
1013*545e5cf8SMark Cave-Ayland             if (val == 80) {
10140e43e99cSbellard                 s->mouse_type = 3; /* IMPS/2 */
1015*545e5cf8SMark Cave-Ayland             }
10160e43e99cSbellard             s->mouse_detect_state = 0;
10170e43e99cSbellard             break;
10180e43e99cSbellard         case 3:
1019*545e5cf8SMark Cave-Ayland             if (val == 80) {
10200e43e99cSbellard                 s->mouse_type = 4; /* IMEX */
1021*545e5cf8SMark Cave-Ayland             }
10220e43e99cSbellard             s->mouse_detect_state = 0;
10230e43e99cSbellard             break;
10240e43e99cSbellard         }
10250e43e99cSbellard         ps2_queue(&s->common, AUX_ACK);
10260e43e99cSbellard         s->common.write_cmd = -1;
10270e43e99cSbellard         break;
10280e43e99cSbellard     case AUX_SET_RES:
10290e43e99cSbellard         s->mouse_resolution = val;
10300e43e99cSbellard         ps2_queue(&s->common, AUX_ACK);
10310e43e99cSbellard         s->common.write_cmd = -1;
10320e43e99cSbellard         break;
10330e43e99cSbellard     }
10340e43e99cSbellard }
10350e43e99cSbellard 
1036ef74679aSDinesh Subhraveti static void ps2_common_reset(PS2State *s)
10370e43e99cSbellard {
10380e43e99cSbellard     s->write_cmd = -1;
1039954ee55bSGerd Hoffmann     ps2_reset_queue(s);
1040deeccef3Saliguori     s->update_irq(s->update_arg, 0);
10410e43e99cSbellard }
10420e43e99cSbellard 
10432858ab09SGonglei static void ps2_common_post_load(PS2State *s)
10442858ab09SGonglei {
10452858ab09SGonglei     PS2Queue *q = &s->queue;
10464e9bddcbSVolker Rümelin     int ccount = 0;
10472858ab09SGonglei 
10484e9bddcbSVolker Rümelin     /* limit the number of queued command replies to PS2_QUEUE_HEADROOM */
10494e9bddcbSVolker Rümelin     if (q->cwptr != -1) {
10504e9bddcbSVolker Rümelin         ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
10514e9bddcbSVolker Rümelin         if (ccount > PS2_QUEUE_HEADROOM) {
10524e9bddcbSVolker Rümelin             ccount = PS2_QUEUE_HEADROOM;
10534e9bddcbSVolker Rümelin         }
1054a1f2ed2aSPavel Dovgalyuk     }
10552858ab09SGonglei 
10564e9bddcbSVolker Rümelin     /* limit the scancode queue size to PS2_QUEUE_SIZE */
10574e9bddcbSVolker Rümelin     if (q->count < ccount) {
10584e9bddcbSVolker Rümelin         q->count = ccount;
10594e9bddcbSVolker Rümelin     } else if (q->count > ccount + PS2_QUEUE_SIZE) {
10604e9bddcbSVolker Rümelin         q->count = ccount + PS2_QUEUE_SIZE;
10614e9bddcbSVolker Rümelin     }
10624e9bddcbSVolker Rümelin 
10634e9bddcbSVolker Rümelin     /* sanitize rptr and recalculate wptr and cwptr */
106447db2432SVolker Rümelin     q->rptr = q->rptr & (PS2_BUFFER_SIZE - 1);
106547db2432SVolker Rümelin     q->wptr = (q->rptr + q->count) & (PS2_BUFFER_SIZE - 1);
10664e9bddcbSVolker Rümelin     q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1;
10672858ab09SGonglei }
10682858ab09SGonglei 
1069ef74679aSDinesh Subhraveti static void ps2_kbd_reset(void *opaque)
1070ef74679aSDinesh Subhraveti {
1071ef74679aSDinesh Subhraveti     PS2KbdState *s = (PS2KbdState *) opaque;
1072ef74679aSDinesh Subhraveti 
10735edab03dSDon Koch     trace_ps2_kbd_reset(opaque);
1074ef74679aSDinesh Subhraveti     ps2_common_reset(&s->common);
1075d2e550a8SHervé Poussineau     s->scan_enabled = 1;
1076ef74679aSDinesh Subhraveti     s->translate = 0;
1077089adafdSHervé Poussineau     s->scancode_set = 2;
1078620775d1SDaniel P. Berrange     s->modifiers = 0;
1079ef74679aSDinesh Subhraveti }
1080ef74679aSDinesh Subhraveti 
1081ef74679aSDinesh Subhraveti static void ps2_mouse_reset(void *opaque)
1082ef74679aSDinesh Subhraveti {
1083ef74679aSDinesh Subhraveti     PS2MouseState *s = (PS2MouseState *) opaque;
1084ef74679aSDinesh Subhraveti 
10855edab03dSDon Koch     trace_ps2_mouse_reset(opaque);
1086ef74679aSDinesh Subhraveti     ps2_common_reset(&s->common);
1087ef74679aSDinesh Subhraveti     s->mouse_status = 0;
1088ef74679aSDinesh Subhraveti     s->mouse_resolution = 0;
1089ef74679aSDinesh Subhraveti     s->mouse_sample_rate = 0;
1090ef74679aSDinesh Subhraveti     s->mouse_wrap = 0;
1091ef74679aSDinesh Subhraveti     s->mouse_type = 0;
1092ef74679aSDinesh Subhraveti     s->mouse_detect_state = 0;
1093ef74679aSDinesh Subhraveti     s->mouse_dx = 0;
1094ef74679aSDinesh Subhraveti     s->mouse_dy = 0;
1095ef74679aSDinesh Subhraveti     s->mouse_dz = 0;
109664ebbb7dSDmitry Petrov     s->mouse_dw = 0;
1097ef74679aSDinesh Subhraveti     s->mouse_buttons = 0;
1098ef74679aSDinesh Subhraveti }
1099ef74679aSDinesh Subhraveti 
1100b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_common = {
1101b31442c3SJuan Quintela     .name = "PS2 Common State",
1102b31442c3SJuan Quintela     .version_id = 3,
1103b31442c3SJuan Quintela     .minimum_version_id = 2,
1104b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1105b31442c3SJuan Quintela         VMSTATE_INT32(write_cmd, PS2State),
1106b31442c3SJuan Quintela         VMSTATE_INT32(queue.rptr, PS2State),
1107b31442c3SJuan Quintela         VMSTATE_INT32(queue.wptr, PS2State),
1108b31442c3SJuan Quintela         VMSTATE_INT32(queue.count, PS2State),
1109b31442c3SJuan Quintela         VMSTATE_BUFFER(queue.data, PS2State),
1110b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
11117783e9f0Spbrook     }
1112b31442c3SJuan Quintela };
11137783e9f0Spbrook 
11147f540ab5SChristophe Fergeau static bool ps2_keyboard_ledstate_needed(void *opaque)
11157f540ab5SChristophe Fergeau {
11167f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
11177f540ab5SChristophe Fergeau 
11187f540ab5SChristophe Fergeau     return s->ledstate != 0; /* 0 is default state */
11197f540ab5SChristophe Fergeau }
11207f540ab5SChristophe Fergeau 
11217f540ab5SChristophe Fergeau static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
11227f540ab5SChristophe Fergeau {
11237f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
11247f540ab5SChristophe Fergeau 
11257f540ab5SChristophe Fergeau     kbd_put_ledstate(s->ledstate);
11267f540ab5SChristophe Fergeau     return 0;
11277f540ab5SChristophe Fergeau }
11287f540ab5SChristophe Fergeau 
11297f540ab5SChristophe Fergeau static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
11307f540ab5SChristophe Fergeau     .name = "ps2kbd/ledstate",
11317f540ab5SChristophe Fergeau     .version_id = 3,
11327f540ab5SChristophe Fergeau     .minimum_version_id = 2,
11337f540ab5SChristophe Fergeau     .post_load = ps2_kbd_ledstate_post_load,
11345cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
11357f540ab5SChristophe Fergeau     .fields = (VMStateField[]) {
11367f540ab5SChristophe Fergeau         VMSTATE_INT32(ledstate, PS2KbdState),
11377f540ab5SChristophe Fergeau         VMSTATE_END_OF_LIST()
11387f540ab5SChristophe Fergeau     }
11397f540ab5SChristophe Fergeau };
11407f540ab5SChristophe Fergeau 
114157d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
114257d5c005SHervé Poussineau {
114357d5c005SHervé Poussineau     PS2KbdState *s = opaque;
114457d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
114557d5c005SHervé Poussineau }
114657d5c005SHervé Poussineau 
114757d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
114857d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
114957d5c005SHervé Poussineau     .version_id = 1,
115057d5c005SHervé Poussineau     .minimum_version_id = 1,
115157d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
115257d5c005SHervé Poussineau     .fields = (VMStateField[]) {
115357d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
115457d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
115557d5c005SHervé Poussineau     }
115657d5c005SHervé Poussineau };
115757d5c005SHervé Poussineau 
11584e9bddcbSVolker Rümelin static bool ps2_keyboard_cqueue_needed(void *opaque)
11594e9bddcbSVolker Rümelin {
11604e9bddcbSVolker Rümelin     PS2KbdState *s = opaque;
11614e9bddcbSVolker Rümelin 
11624e9bddcbSVolker Rümelin     return s->common.queue.cwptr != -1; /* the queue is mostly empty */
11634e9bddcbSVolker Rümelin }
11644e9bddcbSVolker Rümelin 
11654e9bddcbSVolker Rümelin static const VMStateDescription vmstate_ps2_keyboard_cqueue = {
11664e9bddcbSVolker Rümelin     .name = "ps2kbd/command_reply_queue",
11674e9bddcbSVolker Rümelin     .needed = ps2_keyboard_cqueue_needed,
11684e9bddcbSVolker Rümelin     .fields = (VMStateField[]) {
11694e9bddcbSVolker Rümelin         VMSTATE_INT32(common.queue.cwptr, PS2KbdState),
11704e9bddcbSVolker Rümelin         VMSTATE_END_OF_LIST()
11714e9bddcbSVolker Rümelin     }
11724e9bddcbSVolker Rümelin };
11734e9bddcbSVolker Rümelin 
1174db596c53SJuan Quintela static int ps2_kbd_post_load(void *opaque, int version_id)
11750e43e99cSbellard {
11760e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
11772858ab09SGonglei     PS2State *ps2 = &s->common;
11780e43e99cSbellard 
1179*545e5cf8SMark Cave-Ayland     if (version_id == 2) {
1180e7d93956Saurel32         s->scancode_set = 2;
1181*545e5cf8SMark Cave-Ayland     }
11822858ab09SGonglei 
11832858ab09SGonglei     ps2_common_post_load(ps2);
11842858ab09SGonglei 
11850e43e99cSbellard     return 0;
11860e43e99cSbellard }
11870e43e99cSbellard 
1188b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_keyboard = {
1189b31442c3SJuan Quintela     .name = "ps2kbd",
1190b31442c3SJuan Quintela     .version_id = 3,
1191db596c53SJuan Quintela     .minimum_version_id = 2,
1192db596c53SJuan Quintela     .post_load = ps2_kbd_post_load,
1193b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1194b31442c3SJuan Quintela         VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
1195b31442c3SJuan Quintela         VMSTATE_INT32(scan_enabled, PS2KbdState),
1196b31442c3SJuan Quintela         VMSTATE_INT32(translate, PS2KbdState),
1197b31442c3SJuan Quintela         VMSTATE_INT32_V(scancode_set, PS2KbdState, 3),
1198b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
11997f540ab5SChristophe Fergeau     },
12005cd8cadaSJuan Quintela     .subsections = (const VMStateDescription * []) {
12015cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
120257d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
12034e9bddcbSVolker Rümelin         &vmstate_ps2_keyboard_cqueue,
12045cd8cadaSJuan Quintela         NULL
12050e43e99cSbellard     }
1206b31442c3SJuan Quintela };
1207b31442c3SJuan Quintela 
12082858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
12092858ab09SGonglei {
12102858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
12112858ab09SGonglei     PS2State *ps2 = &s->common;
12122858ab09SGonglei 
12132858ab09SGonglei     ps2_common_post_load(ps2);
12142858ab09SGonglei 
12152858ab09SGonglei     return 0;
12162858ab09SGonglei }
12172858ab09SGonglei 
1218b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_mouse = {
1219b31442c3SJuan Quintela     .name = "ps2mouse",
1220b31442c3SJuan Quintela     .version_id = 2,
1221b31442c3SJuan Quintela     .minimum_version_id = 2,
12222858ab09SGonglei     .post_load = ps2_mouse_post_load,
1223b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1224b31442c3SJuan Quintela         VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
1225b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_status, PS2MouseState),
1226b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
1227b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
1228b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
1229b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_type, PS2MouseState),
1230b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
1231b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dx, PS2MouseState),
1232b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dy, PS2MouseState),
1233b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dz, PS2MouseState),
1234b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
1235b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
1236b31442c3SJuan Quintela     }
1237b31442c3SJuan Quintela };
12380e43e99cSbellard 
123966e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
124066e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
124166e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
124266e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
124366e6536eSGerd Hoffmann };
124466e6536eSGerd Hoffmann 
12450e43e99cSbellard void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
12460e43e99cSbellard {
1247b21e2380SMarkus Armbruster     PS2KbdState *s = g_new0(PS2KbdState, 1);
12480e43e99cSbellard 
12495edab03dSDon Koch     trace_ps2_kbd_init(s);
12500e43e99cSbellard     s->common.update_irq = update_irq;
12510e43e99cSbellard     s->common.update_arg = update_arg;
1252e7d93956Saurel32     s->scancode_set = 2;
12530be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
125466e6536eSGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
125566e6536eSGerd Hoffmann                                 &ps2_keyboard_handler);
1256ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_kbd_reset, s);
12570e43e99cSbellard     return s;
12580e43e99cSbellard }
12590e43e99cSbellard 
12602a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
12612a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
12622a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
12632a766d29SGerd Hoffmann     .event = ps2_mouse_event,
12642a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
12652a766d29SGerd Hoffmann };
12662a766d29SGerd Hoffmann 
12670e43e99cSbellard void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
12680e43e99cSbellard {
1269b21e2380SMarkus Armbruster     PS2MouseState *s = g_new0(PS2MouseState, 1);
12700e43e99cSbellard 
12715edab03dSDon Koch     trace_ps2_mouse_init(s);
12720e43e99cSbellard     s->common.update_irq = update_irq;
12730e43e99cSbellard     s->common.update_arg = update_arg;
12740be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
12752a766d29SGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
12762a766d29SGerd Hoffmann                                 &ps2_mouse_handler);
1277ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_mouse_reset, s);
12780e43e99cSbellard     return s;
12790e43e99cSbellard }
1280