xref: /qemu/hw/input/ps2.c (revision 64bbdd138a1e40b71d54c71d25653361329d69fe)
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"
27*64bbdd13SMark Cave-Ayland #include "hw/sysbus.h"
280d09e41aSPaolo Bonzini #include "hw/input/ps2.h"
29d6454270SMarkus Armbruster #include "migration/vmstate.h"
3028ecbaeeSPaolo Bonzini #include "ui/console.h"
3166e6536eSGerd Hoffmann #include "ui/input.h"
3271e8a915SMarkus Armbruster #include "sysemu/reset.h"
3354d31236SMarkus Armbruster #include "sysemu/runstate.h"
340e43e99cSbellard 
355edab03dSDon Koch #include "trace.h"
365edab03dSDon Koch 
370e43e99cSbellard /* Keyboard Commands */
380e43e99cSbellard #define KBD_CMD_SET_LEDS        0xED    /* Set keyboard leds */
390e43e99cSbellard #define KBD_CMD_ECHO            0xEE
40e7d93956Saurel32 #define KBD_CMD_SCANCODE        0xF0    /* Get/set scancode set */
410e43e99cSbellard #define KBD_CMD_GET_ID          0xF2    /* get keyboard ID */
420e43e99cSbellard #define KBD_CMD_SET_RATE        0xF3    /* Set typematic rate */
430e43e99cSbellard #define KBD_CMD_ENABLE          0xF4    /* Enable scanning */
440e43e99cSbellard #define KBD_CMD_RESET_DISABLE   0xF5    /* reset and disable scanning */
450e43e99cSbellard #define KBD_CMD_RESET_ENABLE    0xF6    /* reset and enable scanning */
460e43e99cSbellard #define KBD_CMD_RESET           0xFF    /* Reset */
47c56b6209SSven Schnelle #define KBD_CMD_SET_MAKE_BREAK  0xFC    /* Set Make and Break mode */
48c56b6209SSven Schnelle #define KBD_CMD_SET_TYPEMATIC   0xFA    /* Set Typematic Make and Break mode */
490e43e99cSbellard 
500e43e99cSbellard /* Keyboard Replies */
510e43e99cSbellard #define KBD_REPLY_POR       0xAA    /* Power on reset */
5235c4d671Saurel32 #define KBD_REPLY_ID        0xAB    /* Keyboard ID */
530e43e99cSbellard #define KBD_REPLY_ACK       0xFA    /* Command ACK */
540e43e99cSbellard #define KBD_REPLY_RESEND    0xFE    /* Command NACK, send the cmd again */
550e43e99cSbellard 
560e43e99cSbellard /* Mouse Commands */
570e43e99cSbellard #define AUX_SET_SCALE11     0xE6    /* Set 1:1 scaling */
580e43e99cSbellard #define AUX_SET_SCALE21     0xE7    /* Set 2:1 scaling */
590e43e99cSbellard #define AUX_SET_RES         0xE8    /* Set resolution */
600e43e99cSbellard #define AUX_GET_SCALE       0xE9    /* Get scaling factor */
610e43e99cSbellard #define AUX_SET_STREAM      0xEA    /* Set stream mode */
620e43e99cSbellard #define AUX_POLL            0xEB    /* Poll */
630e43e99cSbellard #define AUX_RESET_WRAP      0xEC    /* Reset wrap mode */
640e43e99cSbellard #define AUX_SET_WRAP        0xEE    /* Set wrap mode */
650e43e99cSbellard #define AUX_SET_REMOTE      0xF0    /* Set remote mode */
660e43e99cSbellard #define AUX_GET_TYPE        0xF2    /* Get type */
670e43e99cSbellard #define AUX_SET_SAMPLE      0xF3    /* Set sample rate */
680e43e99cSbellard #define AUX_ENABLE_DEV      0xF4    /* Enable aux device */
690e43e99cSbellard #define AUX_DISABLE_DEV     0xF5    /* Disable aux device */
700e43e99cSbellard #define AUX_SET_DEFAULT     0xF6
710e43e99cSbellard #define AUX_RESET           0xFF    /* Reset aux device */
720e43e99cSbellard #define AUX_ACK             0xFA    /* Command byte ACK. */
730e43e99cSbellard 
740e43e99cSbellard #define MOUSE_STATUS_REMOTE     0x40
750e43e99cSbellard #define MOUSE_STATUS_ENABLED    0x20
760e43e99cSbellard #define MOUSE_STATUS_SCALE21    0x10
770e43e99cSbellard 
7847db2432SVolker Rümelin /*
7947db2432SVolker Rümelin  * PS/2 buffer size. Keep 256 bytes for compatibility with
8047db2432SVolker Rümelin  * older QEMU versions.
8147db2432SVolker Rümelin  */
8247db2432SVolker Rümelin #define PS2_BUFFER_SIZE     256
8347db2432SVolker Rümelin #define PS2_QUEUE_SIZE      16  /* Queue size required by PS/2 protocol */
844e9bddcbSVolker Rümelin #define PS2_QUEUE_HEADROOM  8   /* Queue size for keyboard command replies */
850e43e99cSbellard 
86620775d1SDaniel P. Berrange /* Bits for 'modifiers' field in PS2KbdState */
87620775d1SDaniel P. Berrange #define MOD_CTRL_L  (1 << 0)
88620775d1SDaniel P. Berrange #define MOD_SHIFT_L (1 << 1)
89620775d1SDaniel P. Berrange #define MOD_ALT_L   (1 << 2)
90620775d1SDaniel P. Berrange #define MOD_CTRL_R  (1 << 3)
91620775d1SDaniel P. Berrange #define MOD_SHIFT_R (1 << 4)
92620775d1SDaniel P. Berrange #define MOD_ALT_R   (1 << 5)
93620775d1SDaniel P. Berrange 
940e43e99cSbellard typedef struct {
9547db2432SVolker Rümelin     uint8_t data[PS2_BUFFER_SIZE];
969e24b2ddSVolker Rümelin     int rptr, wptr, cwptr, count;
970e43e99cSbellard } PS2Queue;
980e43e99cSbellard 
998498bb8dSGerd Hoffmann struct PS2State {
100*64bbdd13SMark Cave-Ayland     SysBusDevice parent_obj;
101*64bbdd13SMark Cave-Ayland 
1020e43e99cSbellard     PS2Queue queue;
1030e43e99cSbellard     int32_t write_cmd;
1040e43e99cSbellard     void (*update_irq)(void *, int);
1050e43e99cSbellard     void *update_arg;
1068498bb8dSGerd Hoffmann };
1070e43e99cSbellard 
108*64bbdd13SMark Cave-Ayland #define TYPE_PS2_DEVICE "ps2-device"
109*64bbdd13SMark Cave-Ayland OBJECT_DECLARE_SIMPLE_TYPE(PS2State, PS2_DEVICE)
110*64bbdd13SMark Cave-Ayland 
1110e43e99cSbellard typedef struct {
1120e43e99cSbellard     PS2State common;
1130e43e99cSbellard     int scan_enabled;
114f94f5d71Spbrook     int translate;
115e7d93956Saurel32     int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
1167f540ab5SChristophe Fergeau     int ledstate;
11757d5c005SHervé Poussineau     bool need_high_bit;
118620775d1SDaniel P. Berrange     unsigned int modifiers; /* bitmask of MOD_* constants above */
1190e43e99cSbellard } PS2KbdState;
1200e43e99cSbellard 
1210e43e99cSbellard typedef struct {
1220e43e99cSbellard     PS2State common;
1230e43e99cSbellard     uint8_t mouse_status;
1240e43e99cSbellard     uint8_t mouse_resolution;
1250e43e99cSbellard     uint8_t mouse_sample_rate;
1260e43e99cSbellard     uint8_t mouse_wrap;
1270e43e99cSbellard     uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
1280e43e99cSbellard     uint8_t mouse_detect_state;
1290e43e99cSbellard     int mouse_dx; /* current values, needed for 'poll' mode */
1300e43e99cSbellard     int mouse_dy;
1310e43e99cSbellard     int mouse_dz;
13264ebbb7dSDmitry Petrov     int mouse_dw;
1330e43e99cSbellard     uint8_t mouse_buttons;
1340e43e99cSbellard } PS2MouseState;
1350e43e99cSbellard 
13657d5c005SHervé Poussineau static uint8_t translate_table[256] = {
13757d5c005SHervé Poussineau     0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
13857d5c005SHervé Poussineau     0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
13957d5c005SHervé Poussineau     0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
14057d5c005SHervé Poussineau     0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
14157d5c005SHervé Poussineau     0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
14257d5c005SHervé Poussineau     0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
14357d5c005SHervé Poussineau     0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
14457d5c005SHervé Poussineau     0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
14557d5c005SHervé Poussineau     0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
14657d5c005SHervé Poussineau     0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
14757d5c005SHervé Poussineau     0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
14857d5c005SHervé Poussineau     0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
14957d5c005SHervé Poussineau     0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
15057d5c005SHervé Poussineau     0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
15157d5c005SHervé Poussineau     0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
15257d5c005SHervé Poussineau     0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
15357d5c005SHervé Poussineau     0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
15457d5c005SHervé Poussineau     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
15557d5c005SHervé Poussineau     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
15657d5c005SHervé Poussineau     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
15757d5c005SHervé Poussineau     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
15857d5c005SHervé Poussineau     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
15957d5c005SHervé Poussineau     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
16057d5c005SHervé Poussineau     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
16157d5c005SHervé Poussineau     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
16257d5c005SHervé Poussineau     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
16357d5c005SHervé Poussineau     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
16457d5c005SHervé Poussineau     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
16557d5c005SHervé Poussineau     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
16657d5c005SHervé Poussineau     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
16757d5c005SHervé Poussineau     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
16857d5c005SHervé Poussineau     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
16957d5c005SHervé Poussineau };
17057d5c005SHervé Poussineau 
171620775d1SDaniel P. Berrange static unsigned int ps2_modifier_bit(QKeyCode key)
172620775d1SDaniel P. Berrange {
173620775d1SDaniel P. Berrange     switch (key) {
174620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL:
175620775d1SDaniel P. Berrange         return MOD_CTRL_L;
176620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL_R:
177620775d1SDaniel P. Berrange         return MOD_CTRL_R;
178620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT:
179620775d1SDaniel P. Berrange         return MOD_SHIFT_L;
180620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT_R:
181620775d1SDaniel P. Berrange         return MOD_SHIFT_R;
182620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT:
183620775d1SDaniel P. Berrange         return MOD_ALT_L;
184620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT_R:
185620775d1SDaniel P. Berrange         return MOD_ALT_R;
186620775d1SDaniel P. Berrange     default:
187620775d1SDaniel P. Berrange         return 0;
188620775d1SDaniel P. Berrange     }
189620775d1SDaniel P. Berrange }
190620775d1SDaniel P. Berrange 
191954ee55bSGerd Hoffmann static void ps2_reset_queue(PS2State *s)
192954ee55bSGerd Hoffmann {
193954ee55bSGerd Hoffmann     PS2Queue *q = &s->queue;
194954ee55bSGerd Hoffmann 
195954ee55bSGerd Hoffmann     q->rptr = 0;
196954ee55bSGerd Hoffmann     q->wptr = 0;
1979e24b2ddSVolker Rümelin     q->cwptr = -1;
198954ee55bSGerd Hoffmann     q->count = 0;
199954ee55bSGerd Hoffmann }
200954ee55bSGerd Hoffmann 
2012a6505b0SSven Schnelle int ps2_queue_empty(PS2State *s)
2022a6505b0SSven Schnelle {
2032a6505b0SSven Schnelle     return s->queue.count == 0;
2042a6505b0SSven Schnelle }
2052a6505b0SSven Schnelle 
2067abe7eb2SGeoffrey McRae void ps2_queue_noirq(PS2State *s, int b)
2070e43e99cSbellard {
2080e43e99cSbellard     PS2Queue *q = &s->queue;
2090e43e99cSbellard 
2109e24b2ddSVolker Rümelin     if (q->count >= PS2_QUEUE_SIZE) {
2110e43e99cSbellard         return;
2127abe7eb2SGeoffrey McRae     }
2137abe7eb2SGeoffrey McRae 
2140e43e99cSbellard     q->data[q->wptr] = b;
21547db2432SVolker Rümelin     if (++q->wptr == PS2_BUFFER_SIZE) {
2160e43e99cSbellard         q->wptr = 0;
21747db2432SVolker Rümelin     }
2180e43e99cSbellard     q->count++;
2197abe7eb2SGeoffrey McRae }
2207abe7eb2SGeoffrey McRae 
2217abe7eb2SGeoffrey McRae void ps2_raise_irq(PS2State *s)
2227abe7eb2SGeoffrey McRae {
2237abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2247abe7eb2SGeoffrey McRae }
2257abe7eb2SGeoffrey McRae 
2267abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
2277abe7eb2SGeoffrey McRae {
2287704bb02SVolker Rümelin     if (PS2_QUEUE_SIZE - s->queue.count < 1) {
2297704bb02SVolker Rümelin         return;
2307704bb02SVolker Rümelin     }
2317704bb02SVolker Rümelin 
2327abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
23396376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2347abe7eb2SGeoffrey McRae }
2357abe7eb2SGeoffrey McRae 
2367abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
2377abe7eb2SGeoffrey McRae {
2387abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
2397abe7eb2SGeoffrey McRae         return;
2407abe7eb2SGeoffrey McRae     }
2417abe7eb2SGeoffrey McRae 
2427abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2437abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
24496376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2457abe7eb2SGeoffrey McRae }
2467abe7eb2SGeoffrey McRae 
2477abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2487abe7eb2SGeoffrey McRae {
2497abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2507abe7eb2SGeoffrey McRae         return;
2517abe7eb2SGeoffrey McRae     }
2527abe7eb2SGeoffrey McRae 
2537abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2547abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2557abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
25696376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2577abe7eb2SGeoffrey McRae }
2587abe7eb2SGeoffrey McRae 
2597abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2607abe7eb2SGeoffrey McRae {
2617abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2627abe7eb2SGeoffrey McRae         return;
2637abe7eb2SGeoffrey McRae     }
2647abe7eb2SGeoffrey McRae 
2657abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2667abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2677abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2687abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
26996376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2700e43e99cSbellard }
2710e43e99cSbellard 
2729e24b2ddSVolker Rümelin static void ps2_cqueue_data(PS2Queue *q, int b)
2739e24b2ddSVolker Rümelin {
2749e24b2ddSVolker Rümelin     q->data[q->cwptr] = b;
2759e24b2ddSVolker Rümelin     if (++q->cwptr >= PS2_BUFFER_SIZE) {
2769e24b2ddSVolker Rümelin         q->cwptr = 0;
2779e24b2ddSVolker Rümelin     }
2789e24b2ddSVolker Rümelin     q->count++;
2799e24b2ddSVolker Rümelin }
2809e24b2ddSVolker Rümelin 
2819e24b2ddSVolker Rümelin static void ps2_cqueue_1(PS2State *s, int b1)
2829e24b2ddSVolker Rümelin {
2839e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2849e24b2ddSVolker Rümelin 
2859e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 1) & (PS2_BUFFER_SIZE - 1);
2869e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2879e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2889e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2899e24b2ddSVolker Rümelin }
2909e24b2ddSVolker Rümelin 
2919e24b2ddSVolker Rümelin static void ps2_cqueue_2(PS2State *s, int b1, int b2)
2929e24b2ddSVolker Rümelin {
2939e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2949e24b2ddSVolker Rümelin 
2959e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 2) & (PS2_BUFFER_SIZE - 1);
2969e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2979e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2989e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
2999e24b2ddSVolker Rümelin     ps2_raise_irq(s);
3009e24b2ddSVolker Rümelin }
3019e24b2ddSVolker Rümelin 
3029e24b2ddSVolker Rümelin static void ps2_cqueue_3(PS2State *s, int b1, int b2, int b3)
3039e24b2ddSVolker Rümelin {
3049e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
3059e24b2ddSVolker Rümelin 
3069e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 3) & (PS2_BUFFER_SIZE - 1);
3079e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
3089e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
3099e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
3109e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b3);
3119e24b2ddSVolker Rümelin     ps2_raise_irq(s);
3129e24b2ddSVolker Rümelin }
3139e24b2ddSVolker Rümelin 
3149e24b2ddSVolker Rümelin static void ps2_cqueue_reset(PS2State *s)
3159e24b2ddSVolker Rümelin {
3169e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
3179e24b2ddSVolker Rümelin     int ccount;
3189e24b2ddSVolker Rümelin 
3199e24b2ddSVolker Rümelin     if (q->cwptr == -1) {
3209e24b2ddSVolker Rümelin         return;
3219e24b2ddSVolker Rümelin     }
3229e24b2ddSVolker Rümelin 
3239e24b2ddSVolker Rümelin     ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
3249e24b2ddSVolker Rümelin     q->count -= ccount;
3259e24b2ddSVolker Rümelin     q->rptr = q->cwptr;
3269e24b2ddSVolker Rümelin     q->cwptr = -1;
3279e24b2ddSVolker Rümelin }
3289e24b2ddSVolker Rümelin 
32957d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
3300e43e99cSbellard static void ps2_put_keycode(void *opaque, int keycode)
3310e43e99cSbellard {
332f94f5d71Spbrook     PS2KbdState *s = opaque;
333e7d93956Saurel32 
3345edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
335fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
33657d5c005SHervé Poussineau 
33757d5c005SHervé Poussineau     if (s->translate) {
33857d5c005SHervé Poussineau         if (keycode == 0xf0) {
33957d5c005SHervé Poussineau             s->need_high_bit = true;
34057d5c005SHervé Poussineau         } else if (s->need_high_bit) {
34157d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode] | 0x80);
34257d5c005SHervé Poussineau             s->need_high_bit = false;
34357d5c005SHervé Poussineau         } else {
34457d5c005SHervé Poussineau             ps2_queue(&s->common, translate_table[keycode]);
3457096a96dSRoy Tam         }
34657d5c005SHervé Poussineau     } else {
3470e43e99cSbellard         ps2_queue(&s->common, keycode);
3480e43e99cSbellard     }
34957d5c005SHervé Poussineau }
3500e43e99cSbellard 
35166e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
35266e6536eSGerd Hoffmann                                InputEvent *evt)
35366e6536eSGerd Hoffmann {
35466e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
35532bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
3568c10e0baSHervé Poussineau     int qcode;
357ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
358620775d1SDaniel P. Berrange     int mod;
35966e6536eSGerd Hoffmann 
360143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
361143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
362143c04c7SGeoffrey McRae         return;
363143c04c7SGeoffrey McRae     }
364143c04c7SGeoffrey McRae 
365fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
3668c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
3678c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
36857d5c005SHervé Poussineau 
369620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
370644f66bfSDaniel P. Berrangé     trace_ps2_keyboard_event(s, qcode, key->down, mod,
371644f66bfSDaniel P. Berrangé                              s->modifiers, s->scancode_set, s->translate);
372620775d1SDaniel P. Berrange     if (key->down) {
373620775d1SDaniel P. Berrange         s->modifiers |= mod;
374620775d1SDaniel P. Berrange     } else {
375620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
376620775d1SDaniel P. Berrange     }
377620775d1SDaniel P. Berrange 
3788c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3798c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
38029fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
38129fd23a5SDaniel P. Berrange                 if (key->down) {
38229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
38329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
38429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
38529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
38629fd23a5SDaniel P. Berrange                 }
38729fd23a5SDaniel P. Berrange             } else {
3888c10e0baSHervé Poussineau                 if (key->down) {
3898c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3908c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3918c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
392927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3938c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
3948c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
3958c10e0baSHervé Poussineau                 }
39629fd23a5SDaniel P. Berrange             }
3978c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
398620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
399620775d1SDaniel P. Berrange                 if (key->down) {
400620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
401620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
402620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
403620775d1SDaniel P. Berrange                 } else {
404620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
405620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
406620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
407620775d1SDaniel P. Berrange                 }
408620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
409620775d1SDaniel P. Berrange                 if (key->down) {
410620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
411620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
412620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
413620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
414620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
415620775d1SDaniel P. Berrange                 } else {
416620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
417620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
418620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
419620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
420620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
421620775d1SDaniel P. Berrange                 }
4228f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4238f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4248f63458fSDaniel P. Berrange                 if (key->down) {
4258f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4268f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
4278f63458fSDaniel P. Berrange                 } else {
4288f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4298f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
4308f63458fSDaniel P. Berrange                 }
431620775d1SDaniel P. Berrange             } else {
4328c10e0baSHervé Poussineau                 if (key->down) {
4338c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4348c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
4358c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4368c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
4378c10e0baSHervé Poussineau                 } else {
4388c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4398c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
4408c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4418c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
4428c10e0baSHervé Poussineau                 }
443620775d1SDaniel P. Berrange             }
4448c10e0baSHervé Poussineau         } else {
445545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset1_len) {
446ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
447545e5cf8SMark Cave-Ayland             }
4488c10e0baSHervé Poussineau             if (keycode) {
4498c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4508c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4518c10e0baSHervé Poussineau                 }
4528c10e0baSHervé Poussineau                 if (!key->down) {
4538c10e0baSHervé Poussineau                     keycode |= 0x80;
4548c10e0baSHervé Poussineau                 }
4558c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
4568c10e0baSHervé Poussineau             } else {
457ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
458ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
4598c10e0baSHervé Poussineau             }
4608c10e0baSHervé Poussineau         }
4618c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
4628c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
46329fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
46429fd23a5SDaniel P. Berrange                 if (key->down) {
46529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
46629fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
46729fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
46829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
46929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
47029fd23a5SDaniel P. Berrange                 }
47129fd23a5SDaniel P. Berrange             } else {
4728c10e0baSHervé Poussineau                 if (key->down) {
4738c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4748c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4758c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4768c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4778c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4788c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4798c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4808c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4818c10e0baSHervé Poussineau                 }
48229fd23a5SDaniel P. Berrange             }
4838c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
484620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
485620775d1SDaniel P. Berrange                 if (key->down) {
486620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
487620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
488620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
489620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
490620775d1SDaniel P. Berrange                 } else {
491620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
492620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
493620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
494620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
495620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
496620775d1SDaniel P. Berrange                 }
497620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
498620775d1SDaniel P. Berrange                 if (key->down) {
499620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
500620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
501620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
502620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
503620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
504620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
505620775d1SDaniel P. Berrange                 } else {
506620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
507620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
508620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
509620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
510620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
511620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
512620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
513620775d1SDaniel P. Berrange                 }
5148f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
5158f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
5168f63458fSDaniel P. Berrange                 if (key->down) {
5178f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
5188f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
5198f63458fSDaniel P. Berrange                 } else {
5208f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
5218f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
5228f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
5238f63458fSDaniel P. Berrange                 }
524620775d1SDaniel P. Berrange             } else {
5258c10e0baSHervé Poussineau                 if (key->down) {
5268c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5278c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
5288c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5298c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
5308c10e0baSHervé Poussineau                 } else {
5318c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5328c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5338c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
5348c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5358c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5368c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
5378c10e0baSHervé Poussineau                 }
538620775d1SDaniel P. Berrange             }
5398c10e0baSHervé Poussineau         } else {
540545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset2_len) {
541ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
542545e5cf8SMark Cave-Ayland             }
5438c10e0baSHervé Poussineau             if (keycode) {
5448c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
5458c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
5468c10e0baSHervé Poussineau                 }
5478c10e0baSHervé Poussineau                 if (!key->down) {
5488c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5498c10e0baSHervé Poussineau                 }
5508c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
55157d5c005SHervé Poussineau             } else {
552ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
553ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
55457d5c005SHervé Poussineau             }
55557d5c005SHervé Poussineau         }
55657d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
557545e5cf8SMark Cave-Ayland         if (qcode < qemu_input_map_qcode_to_atset3_len) {
558ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
559545e5cf8SMark Cave-Ayland         }
5608c10e0baSHervé Poussineau         if (keycode) {
5618c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
5628c10e0baSHervé Poussineau             if (!key->down) {
5638c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
56457d5c005SHervé Poussineau             }
56557d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
5668c10e0baSHervé Poussineau         } else {
567ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
568ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
5698c10e0baSHervé Poussineau         }
57066e6536eSGerd Hoffmann     }
57166e6536eSGerd Hoffmann }
57266e6536eSGerd Hoffmann 
5738498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
5740e43e99cSbellard {
5750e43e99cSbellard     PS2Queue *q;
5760e43e99cSbellard     int val, index;
5770e43e99cSbellard 
5788498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
5790e43e99cSbellard     q = &s->queue;
5800e43e99cSbellard     if (q->count == 0) {
581545e5cf8SMark Cave-Ayland         /*
582545e5cf8SMark Cave-Ayland          * NOTE: if no data left, we return the last keyboard one
583545e5cf8SMark Cave-Ayland          * (needed for EMM386)
584545e5cf8SMark Cave-Ayland          */
5850e43e99cSbellard         /* XXX: need a timer to do things correctly */
5860e43e99cSbellard         index = q->rptr - 1;
58747db2432SVolker Rümelin         if (index < 0) {
58847db2432SVolker Rümelin             index = PS2_BUFFER_SIZE - 1;
58947db2432SVolker Rümelin         }
5900e43e99cSbellard         val = q->data[index];
5910e43e99cSbellard     } else {
5920e43e99cSbellard         val = q->data[q->rptr];
59347db2432SVolker Rümelin         if (++q->rptr == PS2_BUFFER_SIZE) {
5940e43e99cSbellard             q->rptr = 0;
59547db2432SVolker Rümelin         }
5960e43e99cSbellard         q->count--;
5979e24b2ddSVolker Rümelin         if (q->rptr == q->cwptr) {
5989e24b2ddSVolker Rümelin             /* command reply queue is empty */
5999e24b2ddSVolker Rümelin             q->cwptr = -1;
6009e24b2ddSVolker Rümelin         }
6010e43e99cSbellard         /* reading deasserts IRQ */
6020e43e99cSbellard         s->update_irq(s->update_arg, 0);
6030e43e99cSbellard         /* reassert IRQs if data left */
604cec32524SVolker Rümelin         if (q->count) {
605cec32524SVolker Rümelin             s->update_irq(s->update_arg, 1);
606cec32524SVolker Rümelin         }
6070e43e99cSbellard     }
6080e43e99cSbellard     return val;
6090e43e99cSbellard }
6100e43e99cSbellard 
6117f540ab5SChristophe Fergeau static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
6127f540ab5SChristophe Fergeau {
6135edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
6147f540ab5SChristophe Fergeau     s->ledstate = ledstate;
6157f540ab5SChristophe Fergeau     kbd_put_ledstate(ledstate);
6167f540ab5SChristophe Fergeau }
6177f540ab5SChristophe Fergeau 
6180e43e99cSbellard static void ps2_reset_keyboard(PS2KbdState *s)
6190e43e99cSbellard {
6205edab03dSDon Koch     trace_ps2_reset_keyboard(s);
6210e43e99cSbellard     s->scan_enabled = 1;
622e7d93956Saurel32     s->scancode_set = 2;
6236e24ee0cSGerd Hoffmann     ps2_reset_queue(&s->common);
6247f540ab5SChristophe Fergeau     ps2_set_ledstate(s, 0);
6250e43e99cSbellard }
6260e43e99cSbellard 
6270e43e99cSbellard void ps2_write_keyboard(void *opaque, int val)
6280e43e99cSbellard {
6290e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
6300e43e99cSbellard 
6315edab03dSDon Koch     trace_ps2_write_keyboard(opaque, val);
6329e24b2ddSVolker Rümelin     ps2_cqueue_reset(&s->common);
6330e43e99cSbellard     switch (s->common.write_cmd) {
6340e43e99cSbellard     default:
6350e43e99cSbellard     case -1:
6360e43e99cSbellard         switch (val) {
6370e43e99cSbellard         case 0x00:
6389e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6390e43e99cSbellard             break;
6400e43e99cSbellard         case 0x05:
6419e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_RESEND);
6420e43e99cSbellard             break;
6430e43e99cSbellard         case KBD_CMD_GET_ID:
644e7d93956Saurel32             /* We emulate a MF2 AT keyboard here */
6459e24b2ddSVolker Rümelin             ps2_cqueue_3(&s->common, KBD_REPLY_ACK, KBD_REPLY_ID,
6469e24b2ddSVolker Rümelin                          s->translate ? 0x41 : 0x83);
6470e43e99cSbellard             break;
6480e43e99cSbellard         case KBD_CMD_ECHO:
6499e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_CMD_ECHO);
6500e43e99cSbellard             break;
6510e43e99cSbellard         case KBD_CMD_ENABLE:
6520e43e99cSbellard             s->scan_enabled = 1;
6539e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6540e43e99cSbellard             break;
655e7d93956Saurel32         case KBD_CMD_SCANCODE:
6560e43e99cSbellard         case KBD_CMD_SET_LEDS:
6570e43e99cSbellard         case KBD_CMD_SET_RATE:
658c56b6209SSven Schnelle         case KBD_CMD_SET_MAKE_BREAK:
6590e43e99cSbellard             s->common.write_cmd = val;
6609e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6610e43e99cSbellard             break;
6620e43e99cSbellard         case KBD_CMD_RESET_DISABLE:
6630e43e99cSbellard             ps2_reset_keyboard(s);
6640e43e99cSbellard             s->scan_enabled = 0;
6659e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6660e43e99cSbellard             break;
6670e43e99cSbellard         case KBD_CMD_RESET_ENABLE:
6680e43e99cSbellard             ps2_reset_keyboard(s);
6690e43e99cSbellard             s->scan_enabled = 1;
6709e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6710e43e99cSbellard             break;
6720e43e99cSbellard         case KBD_CMD_RESET:
6730e43e99cSbellard             ps2_reset_keyboard(s);
6749e24b2ddSVolker Rümelin             ps2_cqueue_2(&s->common,
6757abe7eb2SGeoffrey McRae                          KBD_REPLY_ACK,
6767abe7eb2SGeoffrey McRae                          KBD_REPLY_POR);
6770e43e99cSbellard             break;
678c56b6209SSven Schnelle         case KBD_CMD_SET_TYPEMATIC:
6799e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
680c56b6209SSven Schnelle             break;
6810e43e99cSbellard         default:
6829e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_RESEND);
6830e43e99cSbellard             break;
6840e43e99cSbellard         }
6850e43e99cSbellard         break;
686c56b6209SSven Schnelle     case KBD_CMD_SET_MAKE_BREAK:
6879e24b2ddSVolker Rümelin         ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
688c56b6209SSven Schnelle         s->common.write_cmd = -1;
689c56b6209SSven Schnelle         break;
690e7d93956Saurel32     case KBD_CMD_SCANCODE:
691e7d93956Saurel32         if (val == 0) {
6929e24b2ddSVolker Rümelin             ps2_cqueue_2(&s->common, KBD_REPLY_ACK, s->translate ?
6939e24b2ddSVolker Rümelin                 translate_table[s->scancode_set] : s->scancode_set);
6944df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
695e7d93956Saurel32             s->scancode_set = val;
6969e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
6974df23b64SHervé Poussineau         } else {
6989e24b2ddSVolker Rümelin             ps2_cqueue_1(&s->common, KBD_REPLY_RESEND);
699e7d93956Saurel32         }
700e7d93956Saurel32         s->common.write_cmd = -1;
701e7d93956Saurel32         break;
7020e43e99cSbellard     case KBD_CMD_SET_LEDS:
7037f540ab5SChristophe Fergeau         ps2_set_ledstate(s, val);
7049e24b2ddSVolker Rümelin         ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
7050e43e99cSbellard         s->common.write_cmd = -1;
7060e43e99cSbellard         break;
7070e43e99cSbellard     case KBD_CMD_SET_RATE:
7089e24b2ddSVolker Rümelin         ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
7090e43e99cSbellard         s->common.write_cmd = -1;
7100e43e99cSbellard         break;
7110e43e99cSbellard     }
7120e43e99cSbellard }
7130e43e99cSbellard 
714545e5cf8SMark Cave-Ayland /*
715545e5cf8SMark Cave-Ayland  * Set the scancode translation mode.
716545e5cf8SMark Cave-Ayland  * 0 = raw scancodes.
717545e5cf8SMark Cave-Ayland  * 1 = translated scancodes (used by qemu internally).
718545e5cf8SMark Cave-Ayland  */
719f94f5d71Spbrook 
720f94f5d71Spbrook void ps2_keyboard_set_translation(void *opaque, int mode)
721f94f5d71Spbrook {
722f94f5d71Spbrook     PS2KbdState *s = (PS2KbdState *)opaque;
7235edab03dSDon Koch     trace_ps2_keyboard_set_translation(opaque, mode);
724f94f5d71Spbrook     s->translate = mode;
725f94f5d71Spbrook }
726f94f5d71Spbrook 
7277abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
7280e43e99cSbellard {
72976968101SVolker Rümelin     /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
73076968101SVolker Rümelin     const int needed = s->mouse_type ? 4 : 3;
7310e43e99cSbellard     unsigned int b;
73264ebbb7dSDmitry Petrov     int dx1, dy1, dz1, dw1;
7330e43e99cSbellard 
7347abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->common.queue.count < needed) {
7357abe7eb2SGeoffrey McRae         return 0;
7367abe7eb2SGeoffrey McRae     }
7377abe7eb2SGeoffrey McRae 
7380e43e99cSbellard     dx1 = s->mouse_dx;
7390e43e99cSbellard     dy1 = s->mouse_dy;
7400e43e99cSbellard     dz1 = s->mouse_dz;
74164ebbb7dSDmitry Petrov     dw1 = s->mouse_dw;
7420e43e99cSbellard     /* XXX: increase range to 8 bits ? */
743545e5cf8SMark Cave-Ayland     if (dx1 > 127) {
7440e43e99cSbellard         dx1 = 127;
745545e5cf8SMark Cave-Ayland     } else if (dx1 < -127) {
7460e43e99cSbellard         dx1 = -127;
747545e5cf8SMark Cave-Ayland     }
748545e5cf8SMark Cave-Ayland     if (dy1 > 127) {
7490e43e99cSbellard         dy1 = 127;
750545e5cf8SMark Cave-Ayland     } else if (dy1 < -127) {
7510e43e99cSbellard         dy1 = -127;
752545e5cf8SMark Cave-Ayland     }
7530e43e99cSbellard     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
7547abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, b);
7557abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dx1 & 0xff);
7567abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dy1 & 0xff);
7570e43e99cSbellard     /* extra byte for IMPS/2 or IMEX */
7580e43e99cSbellard     switch (s->mouse_type) {
7590e43e99cSbellard     default:
76064ebbb7dSDmitry Petrov         /* Just ignore the wheels if not supported */
76164ebbb7dSDmitry Petrov         s->mouse_dz = 0;
76264ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7630e43e99cSbellard         break;
7640e43e99cSbellard     case 3:
765545e5cf8SMark Cave-Ayland         if (dz1 > 127) {
7660e43e99cSbellard             dz1 = 127;
767545e5cf8SMark Cave-Ayland         } else if (dz1 < -127) {
7680e43e99cSbellard             dz1 = -127;
769545e5cf8SMark Cave-Ayland         }
7707abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, dz1 & 0xff);
77164ebbb7dSDmitry Petrov         s->mouse_dz -= dz1;
77264ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7730e43e99cSbellard         break;
7740e43e99cSbellard     case 4:
77564ebbb7dSDmitry Petrov         /*
77664ebbb7dSDmitry Petrov          * This matches what the Linux kernel expects for exps/2 in
77764ebbb7dSDmitry Petrov          * drivers/input/mouse/psmouse-base.c. Note, if you happen to
77864ebbb7dSDmitry Petrov          * press/release the 4th or 5th buttons at the same moment as a
77964ebbb7dSDmitry Petrov          * horizontal wheel scroll, those button presses will get lost. I'm not
78064ebbb7dSDmitry Petrov          * sure what to do about that, since by this point we don't know
78164ebbb7dSDmitry Petrov          * whether those buttons actually changed state.
78264ebbb7dSDmitry Petrov          */
78364ebbb7dSDmitry Petrov         if (dw1 != 0) {
78464ebbb7dSDmitry Petrov             if (dw1 > 31) {
78564ebbb7dSDmitry Petrov                 dw1 = 31;
78664ebbb7dSDmitry Petrov             } else if (dw1 < -31) {
78764ebbb7dSDmitry Petrov                 dw1 = -31;
78864ebbb7dSDmitry Petrov             }
78964ebbb7dSDmitry Petrov 
79064ebbb7dSDmitry Petrov             /*
79164ebbb7dSDmitry Petrov              * linux kernel expects first 6 bits to represent the value
79264ebbb7dSDmitry Petrov              * for horizontal scroll
79364ebbb7dSDmitry Petrov              */
79464ebbb7dSDmitry Petrov             b = (dw1 & 0x3f) | 0x40;
79564ebbb7dSDmitry Petrov             s->mouse_dw -= dw1;
79664ebbb7dSDmitry Petrov         } else {
79764ebbb7dSDmitry Petrov             if (dz1 > 7) {
7980e43e99cSbellard                 dz1 = 7;
79964ebbb7dSDmitry Petrov             } else if (dz1 < -7) {
8000e43e99cSbellard                 dz1 = -7;
80164ebbb7dSDmitry Petrov             }
80264ebbb7dSDmitry Petrov 
8030e43e99cSbellard             b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
80464ebbb7dSDmitry Petrov             s->mouse_dz -= dz1;
80564ebbb7dSDmitry Petrov         }
8067abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, b);
8070e43e99cSbellard         break;
8080e43e99cSbellard     }
8090e43e99cSbellard 
8107abe7eb2SGeoffrey McRae     ps2_raise_irq(&s->common);
8117abe7eb2SGeoffrey McRae 
8125edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
8130e43e99cSbellard     /* update deltas */
8140e43e99cSbellard     s->mouse_dx -= dx1;
8150e43e99cSbellard     s->mouse_dy -= dy1;
8167abe7eb2SGeoffrey McRae 
8177abe7eb2SGeoffrey McRae     return 1;
8180e43e99cSbellard }
8190e43e99cSbellard 
8202a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
8212a766d29SGerd Hoffmann                             InputEvent *evt)
8220e43e99cSbellard {
8237fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
8248b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
8258b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
8268b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
8278b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
8288b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
8292a766d29SGerd Hoffmann     };
8302a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
831b5a1b443SEric Blake     InputMoveEvent *move;
832b5a1b443SEric Blake     InputBtnEvent *btn;
8330e43e99cSbellard 
8340e43e99cSbellard     /* check if deltas are recorded when disabled */
835545e5cf8SMark Cave-Ayland     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
8360e43e99cSbellard         return;
837545e5cf8SMark Cave-Ayland     }
8380e43e99cSbellard 
839568c73a4SEric Blake     switch (evt->type) {
8402a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
84132bafa8fSEric Blake         move = evt->u.rel.data;
842b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
843b5a1b443SEric Blake             s->mouse_dx += move->value;
844b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
845b5a1b443SEric Blake             s->mouse_dy -= move->value;
8462a766d29SGerd Hoffmann         }
8472a766d29SGerd Hoffmann         break;
8480e43e99cSbellard 
8492a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
85032bafa8fSEric Blake         btn = evt->u.btn.data;
851b5a1b443SEric Blake         if (btn->down) {
852b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
853b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
8542a766d29SGerd Hoffmann                 s->mouse_dz--;
855b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
8562a766d29SGerd Hoffmann                 s->mouse_dz++;
8572a766d29SGerd Hoffmann             }
85864ebbb7dSDmitry Petrov 
85964ebbb7dSDmitry Petrov             if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
86064ebbb7dSDmitry Petrov                 s->mouse_dw--;
86164ebbb7dSDmitry Petrov             } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
86264ebbb7dSDmitry Petrov                 s->mouse_dw++;
86364ebbb7dSDmitry Petrov             }
8642a766d29SGerd Hoffmann         } else {
865b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
8662a766d29SGerd Hoffmann         }
8672a766d29SGerd Hoffmann         break;
8682a766d29SGerd Hoffmann 
8692a766d29SGerd Hoffmann     default:
8702a766d29SGerd Hoffmann         /* keep gcc happy */
8712a766d29SGerd Hoffmann         break;
8722a766d29SGerd Hoffmann     }
873fd214d18SGerd Hoffmann }
874fd214d18SGerd Hoffmann 
8752a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
8762a766d29SGerd Hoffmann {
8772a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
8782a766d29SGerd Hoffmann 
879143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
880143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
881143c04c7SGeoffrey McRae         return;
882143c04c7SGeoffrey McRae     }
883143c04c7SGeoffrey McRae 
8842a766d29SGerd Hoffmann     if (s->mouse_buttons) {
885fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
8862a766d29SGerd Hoffmann     }
8872858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
888545e5cf8SMark Cave-Ayland         /*
889545e5cf8SMark Cave-Ayland          * if not remote, send event. Multiple events are sent if
890545e5cf8SMark Cave-Ayland          * too big deltas
891545e5cf8SMark Cave-Ayland          */
8927abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
89364ebbb7dSDmitry Petrov             if (s->mouse_dx == 0 && s->mouse_dy == 0
89464ebbb7dSDmitry Petrov                     && s->mouse_dz == 0 && s->mouse_dw == 0) {
8950e43e99cSbellard                 break;
8960e43e99cSbellard             }
8970e43e99cSbellard         }
8980e43e99cSbellard     }
89964ebbb7dSDmitry Petrov }
9000e43e99cSbellard 
901548df2acSths void ps2_mouse_fake_event(void *opaque)
902548df2acSths {
9032a766d29SGerd Hoffmann     PS2MouseState *s = opaque;
9045edab03dSDon Koch     trace_ps2_mouse_fake_event(opaque);
9052a766d29SGerd Hoffmann     s->mouse_dx++;
9062a766d29SGerd Hoffmann     ps2_mouse_sync(opaque);
907548df2acSths }
908548df2acSths 
9090e43e99cSbellard void ps2_write_mouse(void *opaque, int val)
9100e43e99cSbellard {
9110e43e99cSbellard     PS2MouseState *s = (PS2MouseState *)opaque;
9125edab03dSDon Koch 
9135edab03dSDon Koch     trace_ps2_write_mouse(opaque, val);
9140e43e99cSbellard     switch (s->common.write_cmd) {
9150e43e99cSbellard     default:
9160e43e99cSbellard     case -1:
9170e43e99cSbellard         /* mouse command */
9180e43e99cSbellard         if (s->mouse_wrap) {
9190e43e99cSbellard             if (val == AUX_RESET_WRAP) {
9200e43e99cSbellard                 s->mouse_wrap = 0;
9210e43e99cSbellard                 ps2_queue(&s->common, AUX_ACK);
9220e43e99cSbellard                 return;
9230e43e99cSbellard             } else if (val != AUX_RESET) {
9240e43e99cSbellard                 ps2_queue(&s->common, val);
9250e43e99cSbellard                 return;
9260e43e99cSbellard             }
9270e43e99cSbellard         }
9280e43e99cSbellard         switch (val) {
9290e43e99cSbellard         case AUX_SET_SCALE11:
9300e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
9310e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9320e43e99cSbellard             break;
9330e43e99cSbellard         case AUX_SET_SCALE21:
9340e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_SCALE21;
9350e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9360e43e99cSbellard             break;
9370e43e99cSbellard         case AUX_SET_STREAM:
9380e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
9390e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9400e43e99cSbellard             break;
9410e43e99cSbellard         case AUX_SET_WRAP:
9420e43e99cSbellard             s->mouse_wrap = 1;
9430e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9440e43e99cSbellard             break;
9450e43e99cSbellard         case AUX_SET_REMOTE:
9460e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_REMOTE;
9470e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9480e43e99cSbellard             break;
9490e43e99cSbellard         case AUX_GET_TYPE:
9507abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
9517abe7eb2SGeoffrey McRae                 AUX_ACK,
9527abe7eb2SGeoffrey McRae                 s->mouse_type);
9530e43e99cSbellard             break;
9540e43e99cSbellard         case AUX_SET_RES:
9550e43e99cSbellard         case AUX_SET_SAMPLE:
9560e43e99cSbellard             s->common.write_cmd = val;
9570e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9580e43e99cSbellard             break;
9590e43e99cSbellard         case AUX_GET_SCALE:
9607abe7eb2SGeoffrey McRae             ps2_queue_4(&s->common,
9617abe7eb2SGeoffrey McRae                 AUX_ACK,
9627abe7eb2SGeoffrey McRae                 s->mouse_status,
9637abe7eb2SGeoffrey McRae                 s->mouse_resolution,
9647abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
9650e43e99cSbellard             break;
9660e43e99cSbellard         case AUX_POLL:
9670e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9680e43e99cSbellard             ps2_mouse_send_packet(s);
9690e43e99cSbellard             break;
9700e43e99cSbellard         case AUX_ENABLE_DEV:
9710e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_ENABLED;
9720e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9730e43e99cSbellard             break;
9740e43e99cSbellard         case AUX_DISABLE_DEV:
9750e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
9760e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9770e43e99cSbellard             break;
9780e43e99cSbellard         case AUX_SET_DEFAULT:
9790e43e99cSbellard             s->mouse_sample_rate = 100;
9800e43e99cSbellard             s->mouse_resolution = 2;
9810e43e99cSbellard             s->mouse_status = 0;
9820e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9830e43e99cSbellard             break;
9840e43e99cSbellard         case AUX_RESET:
9850e43e99cSbellard             s->mouse_sample_rate = 100;
9860e43e99cSbellard             s->mouse_resolution = 2;
9870e43e99cSbellard             s->mouse_status = 0;
9880e43e99cSbellard             s->mouse_type = 0;
989143c04c7SGeoffrey McRae             ps2_reset_queue(&s->common);
9907abe7eb2SGeoffrey McRae             ps2_queue_3(&s->common,
9917abe7eb2SGeoffrey McRae                 AUX_ACK,
9927abe7eb2SGeoffrey McRae                 0xaa,
9937abe7eb2SGeoffrey McRae                 s->mouse_type);
9940e43e99cSbellard             break;
9950e43e99cSbellard         default:
9960e43e99cSbellard             break;
9970e43e99cSbellard         }
9980e43e99cSbellard         break;
9990e43e99cSbellard     case AUX_SET_SAMPLE:
10000e43e99cSbellard         s->mouse_sample_rate = val;
10010e43e99cSbellard         /* detect IMPS/2 or IMEX */
10020e43e99cSbellard         switch (s->mouse_detect_state) {
10030e43e99cSbellard         default:
10040e43e99cSbellard         case 0:
1005545e5cf8SMark Cave-Ayland             if (val == 200) {
10060e43e99cSbellard                 s->mouse_detect_state = 1;
1007545e5cf8SMark Cave-Ayland             }
10080e43e99cSbellard             break;
10090e43e99cSbellard         case 1:
1010545e5cf8SMark Cave-Ayland             if (val == 100) {
10110e43e99cSbellard                 s->mouse_detect_state = 2;
1012545e5cf8SMark Cave-Ayland             } else if (val == 200) {
10130e43e99cSbellard                 s->mouse_detect_state = 3;
1014545e5cf8SMark Cave-Ayland             } else {
10150e43e99cSbellard                 s->mouse_detect_state = 0;
1016545e5cf8SMark Cave-Ayland             }
10170e43e99cSbellard             break;
10180e43e99cSbellard         case 2:
1019545e5cf8SMark Cave-Ayland             if (val == 80) {
10200e43e99cSbellard                 s->mouse_type = 3; /* IMPS/2 */
1021545e5cf8SMark Cave-Ayland             }
10220e43e99cSbellard             s->mouse_detect_state = 0;
10230e43e99cSbellard             break;
10240e43e99cSbellard         case 3:
1025545e5cf8SMark Cave-Ayland             if (val == 80) {
10260e43e99cSbellard                 s->mouse_type = 4; /* IMEX */
1027545e5cf8SMark Cave-Ayland             }
10280e43e99cSbellard             s->mouse_detect_state = 0;
10290e43e99cSbellard             break;
10300e43e99cSbellard         }
10310e43e99cSbellard         ps2_queue(&s->common, AUX_ACK);
10320e43e99cSbellard         s->common.write_cmd = -1;
10330e43e99cSbellard         break;
10340e43e99cSbellard     case AUX_SET_RES:
10350e43e99cSbellard         s->mouse_resolution = val;
10360e43e99cSbellard         ps2_queue(&s->common, AUX_ACK);
10370e43e99cSbellard         s->common.write_cmd = -1;
10380e43e99cSbellard         break;
10390e43e99cSbellard     }
10400e43e99cSbellard }
10410e43e99cSbellard 
1042ef74679aSDinesh Subhraveti static void ps2_common_reset(PS2State *s)
10430e43e99cSbellard {
10440e43e99cSbellard     s->write_cmd = -1;
1045954ee55bSGerd Hoffmann     ps2_reset_queue(s);
1046deeccef3Saliguori     s->update_irq(s->update_arg, 0);
10470e43e99cSbellard }
10480e43e99cSbellard 
10492858ab09SGonglei static void ps2_common_post_load(PS2State *s)
10502858ab09SGonglei {
10512858ab09SGonglei     PS2Queue *q = &s->queue;
10524e9bddcbSVolker Rümelin     int ccount = 0;
10532858ab09SGonglei 
10544e9bddcbSVolker Rümelin     /* limit the number of queued command replies to PS2_QUEUE_HEADROOM */
10554e9bddcbSVolker Rümelin     if (q->cwptr != -1) {
10564e9bddcbSVolker Rümelin         ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
10574e9bddcbSVolker Rümelin         if (ccount > PS2_QUEUE_HEADROOM) {
10584e9bddcbSVolker Rümelin             ccount = PS2_QUEUE_HEADROOM;
10594e9bddcbSVolker Rümelin         }
1060a1f2ed2aSPavel Dovgalyuk     }
10612858ab09SGonglei 
10624e9bddcbSVolker Rümelin     /* limit the scancode queue size to PS2_QUEUE_SIZE */
10634e9bddcbSVolker Rümelin     if (q->count < ccount) {
10644e9bddcbSVolker Rümelin         q->count = ccount;
10654e9bddcbSVolker Rümelin     } else if (q->count > ccount + PS2_QUEUE_SIZE) {
10664e9bddcbSVolker Rümelin         q->count = ccount + PS2_QUEUE_SIZE;
10674e9bddcbSVolker Rümelin     }
10684e9bddcbSVolker Rümelin 
10694e9bddcbSVolker Rümelin     /* sanitize rptr and recalculate wptr and cwptr */
107047db2432SVolker Rümelin     q->rptr = q->rptr & (PS2_BUFFER_SIZE - 1);
107147db2432SVolker Rümelin     q->wptr = (q->rptr + q->count) & (PS2_BUFFER_SIZE - 1);
10724e9bddcbSVolker Rümelin     q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1;
10732858ab09SGonglei }
10742858ab09SGonglei 
1075ef74679aSDinesh Subhraveti static void ps2_kbd_reset(void *opaque)
1076ef74679aSDinesh Subhraveti {
1077ef74679aSDinesh Subhraveti     PS2KbdState *s = (PS2KbdState *) opaque;
1078ef74679aSDinesh Subhraveti 
10795edab03dSDon Koch     trace_ps2_kbd_reset(opaque);
1080ef74679aSDinesh Subhraveti     ps2_common_reset(&s->common);
1081d2e550a8SHervé Poussineau     s->scan_enabled = 1;
1082ef74679aSDinesh Subhraveti     s->translate = 0;
1083089adafdSHervé Poussineau     s->scancode_set = 2;
1084620775d1SDaniel P. Berrange     s->modifiers = 0;
1085ef74679aSDinesh Subhraveti }
1086ef74679aSDinesh Subhraveti 
1087ef74679aSDinesh Subhraveti static void ps2_mouse_reset(void *opaque)
1088ef74679aSDinesh Subhraveti {
1089ef74679aSDinesh Subhraveti     PS2MouseState *s = (PS2MouseState *) opaque;
1090ef74679aSDinesh Subhraveti 
10915edab03dSDon Koch     trace_ps2_mouse_reset(opaque);
1092ef74679aSDinesh Subhraveti     ps2_common_reset(&s->common);
1093ef74679aSDinesh Subhraveti     s->mouse_status = 0;
1094ef74679aSDinesh Subhraveti     s->mouse_resolution = 0;
1095ef74679aSDinesh Subhraveti     s->mouse_sample_rate = 0;
1096ef74679aSDinesh Subhraveti     s->mouse_wrap = 0;
1097ef74679aSDinesh Subhraveti     s->mouse_type = 0;
1098ef74679aSDinesh Subhraveti     s->mouse_detect_state = 0;
1099ef74679aSDinesh Subhraveti     s->mouse_dx = 0;
1100ef74679aSDinesh Subhraveti     s->mouse_dy = 0;
1101ef74679aSDinesh Subhraveti     s->mouse_dz = 0;
110264ebbb7dSDmitry Petrov     s->mouse_dw = 0;
1103ef74679aSDinesh Subhraveti     s->mouse_buttons = 0;
1104ef74679aSDinesh Subhraveti }
1105ef74679aSDinesh Subhraveti 
1106b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_common = {
1107b31442c3SJuan Quintela     .name = "PS2 Common State",
1108b31442c3SJuan Quintela     .version_id = 3,
1109b31442c3SJuan Quintela     .minimum_version_id = 2,
1110b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1111b31442c3SJuan Quintela         VMSTATE_INT32(write_cmd, PS2State),
1112b31442c3SJuan Quintela         VMSTATE_INT32(queue.rptr, PS2State),
1113b31442c3SJuan Quintela         VMSTATE_INT32(queue.wptr, PS2State),
1114b31442c3SJuan Quintela         VMSTATE_INT32(queue.count, PS2State),
1115b31442c3SJuan Quintela         VMSTATE_BUFFER(queue.data, PS2State),
1116b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
11177783e9f0Spbrook     }
1118b31442c3SJuan Quintela };
11197783e9f0Spbrook 
11207f540ab5SChristophe Fergeau static bool ps2_keyboard_ledstate_needed(void *opaque)
11217f540ab5SChristophe Fergeau {
11227f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
11237f540ab5SChristophe Fergeau 
11247f540ab5SChristophe Fergeau     return s->ledstate != 0; /* 0 is default state */
11257f540ab5SChristophe Fergeau }
11267f540ab5SChristophe Fergeau 
11277f540ab5SChristophe Fergeau static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
11287f540ab5SChristophe Fergeau {
11297f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
11307f540ab5SChristophe Fergeau 
11317f540ab5SChristophe Fergeau     kbd_put_ledstate(s->ledstate);
11327f540ab5SChristophe Fergeau     return 0;
11337f540ab5SChristophe Fergeau }
11347f540ab5SChristophe Fergeau 
11357f540ab5SChristophe Fergeau static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
11367f540ab5SChristophe Fergeau     .name = "ps2kbd/ledstate",
11377f540ab5SChristophe Fergeau     .version_id = 3,
11387f540ab5SChristophe Fergeau     .minimum_version_id = 2,
11397f540ab5SChristophe Fergeau     .post_load = ps2_kbd_ledstate_post_load,
11405cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
11417f540ab5SChristophe Fergeau     .fields = (VMStateField[]) {
11427f540ab5SChristophe Fergeau         VMSTATE_INT32(ledstate, PS2KbdState),
11437f540ab5SChristophe Fergeau         VMSTATE_END_OF_LIST()
11447f540ab5SChristophe Fergeau     }
11457f540ab5SChristophe Fergeau };
11467f540ab5SChristophe Fergeau 
114757d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
114857d5c005SHervé Poussineau {
114957d5c005SHervé Poussineau     PS2KbdState *s = opaque;
115057d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
115157d5c005SHervé Poussineau }
115257d5c005SHervé Poussineau 
115357d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
115457d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
115557d5c005SHervé Poussineau     .version_id = 1,
115657d5c005SHervé Poussineau     .minimum_version_id = 1,
115757d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
115857d5c005SHervé Poussineau     .fields = (VMStateField[]) {
115957d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
116057d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
116157d5c005SHervé Poussineau     }
116257d5c005SHervé Poussineau };
116357d5c005SHervé Poussineau 
11644e9bddcbSVolker Rümelin static bool ps2_keyboard_cqueue_needed(void *opaque)
11654e9bddcbSVolker Rümelin {
11664e9bddcbSVolker Rümelin     PS2KbdState *s = opaque;
11674e9bddcbSVolker Rümelin 
11684e9bddcbSVolker Rümelin     return s->common.queue.cwptr != -1; /* the queue is mostly empty */
11694e9bddcbSVolker Rümelin }
11704e9bddcbSVolker Rümelin 
11714e9bddcbSVolker Rümelin static const VMStateDescription vmstate_ps2_keyboard_cqueue = {
11724e9bddcbSVolker Rümelin     .name = "ps2kbd/command_reply_queue",
11734e9bddcbSVolker Rümelin     .needed = ps2_keyboard_cqueue_needed,
11744e9bddcbSVolker Rümelin     .fields = (VMStateField[]) {
11754e9bddcbSVolker Rümelin         VMSTATE_INT32(common.queue.cwptr, PS2KbdState),
11764e9bddcbSVolker Rümelin         VMSTATE_END_OF_LIST()
11774e9bddcbSVolker Rümelin     }
11784e9bddcbSVolker Rümelin };
11794e9bddcbSVolker Rümelin 
1180db596c53SJuan Quintela static int ps2_kbd_post_load(void *opaque, int version_id)
11810e43e99cSbellard {
11820e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
11832858ab09SGonglei     PS2State *ps2 = &s->common;
11840e43e99cSbellard 
1185545e5cf8SMark Cave-Ayland     if (version_id == 2) {
1186e7d93956Saurel32         s->scancode_set = 2;
1187545e5cf8SMark Cave-Ayland     }
11882858ab09SGonglei 
11892858ab09SGonglei     ps2_common_post_load(ps2);
11902858ab09SGonglei 
11910e43e99cSbellard     return 0;
11920e43e99cSbellard }
11930e43e99cSbellard 
1194b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_keyboard = {
1195b31442c3SJuan Quintela     .name = "ps2kbd",
1196b31442c3SJuan Quintela     .version_id = 3,
1197db596c53SJuan Quintela     .minimum_version_id = 2,
1198db596c53SJuan Quintela     .post_load = ps2_kbd_post_load,
1199b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1200b31442c3SJuan Quintela         VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
1201b31442c3SJuan Quintela         VMSTATE_INT32(scan_enabled, PS2KbdState),
1202b31442c3SJuan Quintela         VMSTATE_INT32(translate, PS2KbdState),
1203b31442c3SJuan Quintela         VMSTATE_INT32_V(scancode_set, PS2KbdState, 3),
1204b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
12057f540ab5SChristophe Fergeau     },
12065cd8cadaSJuan Quintela     .subsections = (const VMStateDescription * []) {
12075cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
120857d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
12094e9bddcbSVolker Rümelin         &vmstate_ps2_keyboard_cqueue,
12105cd8cadaSJuan Quintela         NULL
12110e43e99cSbellard     }
1212b31442c3SJuan Quintela };
1213b31442c3SJuan Quintela 
12142858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
12152858ab09SGonglei {
12162858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
12172858ab09SGonglei     PS2State *ps2 = &s->common;
12182858ab09SGonglei 
12192858ab09SGonglei     ps2_common_post_load(ps2);
12202858ab09SGonglei 
12212858ab09SGonglei     return 0;
12222858ab09SGonglei }
12232858ab09SGonglei 
1224b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_mouse = {
1225b31442c3SJuan Quintela     .name = "ps2mouse",
1226b31442c3SJuan Quintela     .version_id = 2,
1227b31442c3SJuan Quintela     .minimum_version_id = 2,
12282858ab09SGonglei     .post_load = ps2_mouse_post_load,
1229b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1230b31442c3SJuan Quintela         VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
1231b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_status, PS2MouseState),
1232b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
1233b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
1234b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
1235b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_type, PS2MouseState),
1236b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
1237b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dx, PS2MouseState),
1238b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dy, PS2MouseState),
1239b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dz, PS2MouseState),
1240b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
1241b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
1242b31442c3SJuan Quintela     }
1243b31442c3SJuan Quintela };
12440e43e99cSbellard 
124566e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
124666e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
124766e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
124866e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
124966e6536eSGerd Hoffmann };
125066e6536eSGerd Hoffmann 
12510e43e99cSbellard void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
12520e43e99cSbellard {
1253b21e2380SMarkus Armbruster     PS2KbdState *s = g_new0(PS2KbdState, 1);
12540e43e99cSbellard 
12555edab03dSDon Koch     trace_ps2_kbd_init(s);
12560e43e99cSbellard     s->common.update_irq = update_irq;
12570e43e99cSbellard     s->common.update_arg = update_arg;
1258e7d93956Saurel32     s->scancode_set = 2;
12590be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
126066e6536eSGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
126166e6536eSGerd Hoffmann                                 &ps2_keyboard_handler);
1262ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_kbd_reset, s);
12630e43e99cSbellard     return s;
12640e43e99cSbellard }
12650e43e99cSbellard 
12662a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
12672a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
12682a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
12692a766d29SGerd Hoffmann     .event = ps2_mouse_event,
12702a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
12712a766d29SGerd Hoffmann };
12722a766d29SGerd Hoffmann 
12730e43e99cSbellard void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
12740e43e99cSbellard {
1275b21e2380SMarkus Armbruster     PS2MouseState *s = g_new0(PS2MouseState, 1);
12760e43e99cSbellard 
12775edab03dSDon Koch     trace_ps2_mouse_init(s);
12780e43e99cSbellard     s->common.update_irq = update_irq;
12790e43e99cSbellard     s->common.update_arg = update_arg;
12800be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
12812a766d29SGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
12822a766d29SGerd Hoffmann                                 &ps2_mouse_handler);
1283ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_mouse_reset, s);
12840e43e99cSbellard     return s;
12850e43e99cSbellard }
1286*64bbdd13SMark Cave-Ayland 
1287*64bbdd13SMark Cave-Ayland static void ps2_class_init(ObjectClass *klass, void *data)
1288*64bbdd13SMark Cave-Ayland {
1289*64bbdd13SMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
1290*64bbdd13SMark Cave-Ayland 
1291*64bbdd13SMark Cave-Ayland     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
1292*64bbdd13SMark Cave-Ayland }
1293*64bbdd13SMark Cave-Ayland 
1294*64bbdd13SMark Cave-Ayland static const TypeInfo ps2_info = {
1295*64bbdd13SMark Cave-Ayland     .name          = TYPE_PS2_DEVICE,
1296*64bbdd13SMark Cave-Ayland     .parent        = TYPE_SYS_BUS_DEVICE,
1297*64bbdd13SMark Cave-Ayland     .instance_size = sizeof(PS2State),
1298*64bbdd13SMark Cave-Ayland     .class_init    = ps2_class_init,
1299*64bbdd13SMark Cave-Ayland     .abstract      = true
1300*64bbdd13SMark Cave-Ayland };
1301*64bbdd13SMark Cave-Ayland 
1302*64bbdd13SMark Cave-Ayland static void ps2_register_types(void)
1303*64bbdd13SMark Cave-Ayland {
1304*64bbdd13SMark Cave-Ayland     type_register_static(&ps2_info);
1305*64bbdd13SMark Cave-Ayland }
1306*64bbdd13SMark Cave-Ayland 
1307*64bbdd13SMark Cave-Ayland type_init(ps2_register_types)
1308