xref: /qemu/hw/input/ps2.c (revision 8f84e53cd0067a1d4be4f6b2deb7d24bd5f469c6)
10e43e99cSbellard /*
20e43e99cSbellard  * QEMU PS/2 keyboard/mouse emulation
30e43e99cSbellard  *
40e43e99cSbellard  * Copyright (c) 2003 Fabrice Bellard
50e43e99cSbellard  *
60e43e99cSbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
70e43e99cSbellard  * of this software and associated documentation files (the "Software"), to deal
80e43e99cSbellard  * in the Software without restriction, including without limitation the rights
90e43e99cSbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
100e43e99cSbellard  * copies of the Software, and to permit persons to whom the Software is
110e43e99cSbellard  * furnished to do so, subject to the following conditions:
120e43e99cSbellard  *
130e43e99cSbellard  * The above copyright notice and this permission notice shall be included in
140e43e99cSbellard  * all copies or substantial portions of the Software.
150e43e99cSbellard  *
160e43e99cSbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
170e43e99cSbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
180e43e99cSbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
190e43e99cSbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
200e43e99cSbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
210e43e99cSbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
220e43e99cSbellard  * THE SOFTWARE.
230e43e99cSbellard  */
2471e8a915SMarkus Armbruster 
250430891cSPeter Maydell #include "qemu/osdep.h"
26ec044a80SHervé Poussineau #include "qemu/log.h"
2764bbdd13SMark Cave-Ayland #include "hw/sysbus.h"
280d09e41aSPaolo Bonzini #include "hw/input/ps2.h"
29d6454270SMarkus Armbruster #include "migration/vmstate.h"
3028ecbaeeSPaolo Bonzini #include "ui/console.h"
3166e6536eSGerd Hoffmann #include "ui/input.h"
3271e8a915SMarkus Armbruster #include "sysemu/reset.h"
3354d31236SMarkus Armbruster #include "sysemu/runstate.h"
34*8f84e53cSMark Cave-Ayland #include "qapi/error.h"
350e43e99cSbellard 
365edab03dSDon Koch #include "trace.h"
375edab03dSDon Koch 
380e43e99cSbellard /* Keyboard Commands */
390e43e99cSbellard #define KBD_CMD_SET_LEDS        0xED    /* Set keyboard leds */
400e43e99cSbellard #define KBD_CMD_ECHO            0xEE
41e7d93956Saurel32 #define KBD_CMD_SCANCODE        0xF0    /* Get/set scancode set */
420e43e99cSbellard #define KBD_CMD_GET_ID          0xF2    /* get keyboard ID */
430e43e99cSbellard #define KBD_CMD_SET_RATE        0xF3    /* Set typematic rate */
440e43e99cSbellard #define KBD_CMD_ENABLE          0xF4    /* Enable scanning */
450e43e99cSbellard #define KBD_CMD_RESET_DISABLE   0xF5    /* reset and disable scanning */
460e43e99cSbellard #define KBD_CMD_RESET_ENABLE    0xF6    /* reset and enable scanning */
470e43e99cSbellard #define KBD_CMD_RESET           0xFF    /* Reset */
48c56b6209SSven Schnelle #define KBD_CMD_SET_MAKE_BREAK  0xFC    /* Set Make and Break mode */
49c56b6209SSven Schnelle #define KBD_CMD_SET_TYPEMATIC   0xFA    /* Set Typematic Make and Break mode */
500e43e99cSbellard 
510e43e99cSbellard /* Keyboard Replies */
520e43e99cSbellard #define KBD_REPLY_POR       0xAA    /* Power on reset */
5335c4d671Saurel32 #define KBD_REPLY_ID        0xAB    /* Keyboard ID */
540e43e99cSbellard #define KBD_REPLY_ACK       0xFA    /* Command ACK */
550e43e99cSbellard #define KBD_REPLY_RESEND    0xFE    /* Command NACK, send the cmd again */
560e43e99cSbellard 
570e43e99cSbellard /* Mouse Commands */
580e43e99cSbellard #define AUX_SET_SCALE11     0xE6    /* Set 1:1 scaling */
590e43e99cSbellard #define AUX_SET_SCALE21     0xE7    /* Set 2:1 scaling */
600e43e99cSbellard #define AUX_SET_RES         0xE8    /* Set resolution */
610e43e99cSbellard #define AUX_GET_SCALE       0xE9    /* Get scaling factor */
620e43e99cSbellard #define AUX_SET_STREAM      0xEA    /* Set stream mode */
630e43e99cSbellard #define AUX_POLL            0xEB    /* Poll */
640e43e99cSbellard #define AUX_RESET_WRAP      0xEC    /* Reset wrap mode */
650e43e99cSbellard #define AUX_SET_WRAP        0xEE    /* Set wrap mode */
660e43e99cSbellard #define AUX_SET_REMOTE      0xF0    /* Set remote mode */
670e43e99cSbellard #define AUX_GET_TYPE        0xF2    /* Get type */
680e43e99cSbellard #define AUX_SET_SAMPLE      0xF3    /* Set sample rate */
690e43e99cSbellard #define AUX_ENABLE_DEV      0xF4    /* Enable aux device */
700e43e99cSbellard #define AUX_DISABLE_DEV     0xF5    /* Disable aux device */
710e43e99cSbellard #define AUX_SET_DEFAULT     0xF6
720e43e99cSbellard #define AUX_RESET           0xFF    /* Reset aux device */
730e43e99cSbellard #define AUX_ACK             0xFA    /* Command byte ACK. */
740e43e99cSbellard 
750e43e99cSbellard #define MOUSE_STATUS_REMOTE     0x40
760e43e99cSbellard #define MOUSE_STATUS_ENABLED    0x20
770e43e99cSbellard #define MOUSE_STATUS_SCALE21    0x10
780e43e99cSbellard 
7947db2432SVolker Rümelin /*
8047db2432SVolker Rümelin  * PS/2 buffer size. Keep 256 bytes for compatibility with
8147db2432SVolker Rümelin  * older QEMU versions.
8247db2432SVolker Rümelin  */
8347db2432SVolker Rümelin #define PS2_BUFFER_SIZE     256
8447db2432SVolker Rümelin #define PS2_QUEUE_SIZE      16  /* Queue size required by PS/2 protocol */
854e9bddcbSVolker Rümelin #define PS2_QUEUE_HEADROOM  8   /* Queue size for keyboard command replies */
860e43e99cSbellard 
87620775d1SDaniel P. Berrange /* Bits for 'modifiers' field in PS2KbdState */
88620775d1SDaniel P. Berrange #define MOD_CTRL_L  (1 << 0)
89620775d1SDaniel P. Berrange #define MOD_SHIFT_L (1 << 1)
90620775d1SDaniel P. Berrange #define MOD_ALT_L   (1 << 2)
91620775d1SDaniel P. Berrange #define MOD_CTRL_R  (1 << 3)
92620775d1SDaniel P. Berrange #define MOD_SHIFT_R (1 << 4)
93620775d1SDaniel P. Berrange #define MOD_ALT_R   (1 << 5)
94620775d1SDaniel P. Berrange 
950e43e99cSbellard typedef struct {
9647db2432SVolker Rümelin     uint8_t data[PS2_BUFFER_SIZE];
979e24b2ddSVolker Rümelin     int rptr, wptr, cwptr, count;
980e43e99cSbellard } PS2Queue;
990e43e99cSbellard 
1008498bb8dSGerd Hoffmann struct PS2State {
10164bbdd13SMark Cave-Ayland     SysBusDevice parent_obj;
10264bbdd13SMark Cave-Ayland 
1030e43e99cSbellard     PS2Queue queue;
1040e43e99cSbellard     int32_t write_cmd;
1050e43e99cSbellard     void (*update_irq)(void *, int);
1060e43e99cSbellard     void *update_arg;
1078498bb8dSGerd Hoffmann };
1080e43e99cSbellard 
10964bbdd13SMark Cave-Ayland #define TYPE_PS2_DEVICE "ps2-device"
11064bbdd13SMark Cave-Ayland OBJECT_DECLARE_SIMPLE_TYPE(PS2State, PS2_DEVICE)
11164bbdd13SMark Cave-Ayland 
112*8f84e53cSMark Cave-Ayland struct PS2KbdState {
113*8f84e53cSMark Cave-Ayland     PS2State parent_obj;
114*8f84e53cSMark Cave-Ayland 
1150e43e99cSbellard     int scan_enabled;
116f94f5d71Spbrook     int translate;
117e7d93956Saurel32     int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
1187f540ab5SChristophe Fergeau     int ledstate;
11957d5c005SHervé Poussineau     bool need_high_bit;
120620775d1SDaniel P. Berrange     unsigned int modifiers; /* bitmask of MOD_* constants above */
121*8f84e53cSMark Cave-Ayland };
122*8f84e53cSMark Cave-Ayland 
123*8f84e53cSMark Cave-Ayland #define TYPE_PS2_KBD_DEVICE "ps2-kbd"
124*8f84e53cSMark Cave-Ayland OBJECT_DECLARE_SIMPLE_TYPE(PS2KbdState, PS2_KBD_DEVICE)
1250e43e99cSbellard 
1260e43e99cSbellard typedef struct {
1270e43e99cSbellard     PS2State common;
1280e43e99cSbellard     uint8_t mouse_status;
1290e43e99cSbellard     uint8_t mouse_resolution;
1300e43e99cSbellard     uint8_t mouse_sample_rate;
1310e43e99cSbellard     uint8_t mouse_wrap;
1320e43e99cSbellard     uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
1330e43e99cSbellard     uint8_t mouse_detect_state;
1340e43e99cSbellard     int mouse_dx; /* current values, needed for 'poll' mode */
1350e43e99cSbellard     int mouse_dy;
1360e43e99cSbellard     int mouse_dz;
13764ebbb7dSDmitry Petrov     int mouse_dw;
1380e43e99cSbellard     uint8_t mouse_buttons;
1390e43e99cSbellard } PS2MouseState;
1400e43e99cSbellard 
14157d5c005SHervé Poussineau static uint8_t translate_table[256] = {
14257d5c005SHervé Poussineau     0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
14357d5c005SHervé Poussineau     0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
14457d5c005SHervé Poussineau     0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
14557d5c005SHervé Poussineau     0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
14657d5c005SHervé Poussineau     0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
14757d5c005SHervé Poussineau     0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
14857d5c005SHervé Poussineau     0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
14957d5c005SHervé Poussineau     0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
15057d5c005SHervé Poussineau     0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
15157d5c005SHervé Poussineau     0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
15257d5c005SHervé Poussineau     0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
15357d5c005SHervé Poussineau     0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
15457d5c005SHervé Poussineau     0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
15557d5c005SHervé Poussineau     0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
15657d5c005SHervé Poussineau     0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
15757d5c005SHervé Poussineau     0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
15857d5c005SHervé Poussineau     0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
15957d5c005SHervé Poussineau     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
16057d5c005SHervé Poussineau     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
16157d5c005SHervé Poussineau     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
16257d5c005SHervé Poussineau     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
16357d5c005SHervé Poussineau     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
16457d5c005SHervé Poussineau     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
16557d5c005SHervé Poussineau     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
16657d5c005SHervé Poussineau     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
16757d5c005SHervé Poussineau     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
16857d5c005SHervé Poussineau     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
16957d5c005SHervé Poussineau     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
17057d5c005SHervé Poussineau     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
17157d5c005SHervé Poussineau     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
17257d5c005SHervé Poussineau     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
17357d5c005SHervé Poussineau     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
17457d5c005SHervé Poussineau };
17557d5c005SHervé Poussineau 
176620775d1SDaniel P. Berrange static unsigned int ps2_modifier_bit(QKeyCode key)
177620775d1SDaniel P. Berrange {
178620775d1SDaniel P. Berrange     switch (key) {
179620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL:
180620775d1SDaniel P. Berrange         return MOD_CTRL_L;
181620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL_R:
182620775d1SDaniel P. Berrange         return MOD_CTRL_R;
183620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT:
184620775d1SDaniel P. Berrange         return MOD_SHIFT_L;
185620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT_R:
186620775d1SDaniel P. Berrange         return MOD_SHIFT_R;
187620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT:
188620775d1SDaniel P. Berrange         return MOD_ALT_L;
189620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT_R:
190620775d1SDaniel P. Berrange         return MOD_ALT_R;
191620775d1SDaniel P. Berrange     default:
192620775d1SDaniel P. Berrange         return 0;
193620775d1SDaniel P. Berrange     }
194620775d1SDaniel P. Berrange }
195620775d1SDaniel P. Berrange 
196954ee55bSGerd Hoffmann static void ps2_reset_queue(PS2State *s)
197954ee55bSGerd Hoffmann {
198954ee55bSGerd Hoffmann     PS2Queue *q = &s->queue;
199954ee55bSGerd Hoffmann 
200954ee55bSGerd Hoffmann     q->rptr = 0;
201954ee55bSGerd Hoffmann     q->wptr = 0;
2029e24b2ddSVolker Rümelin     q->cwptr = -1;
203954ee55bSGerd Hoffmann     q->count = 0;
204954ee55bSGerd Hoffmann }
205954ee55bSGerd Hoffmann 
2062a6505b0SSven Schnelle int ps2_queue_empty(PS2State *s)
2072a6505b0SSven Schnelle {
2082a6505b0SSven Schnelle     return s->queue.count == 0;
2092a6505b0SSven Schnelle }
2102a6505b0SSven Schnelle 
2117abe7eb2SGeoffrey McRae void ps2_queue_noirq(PS2State *s, int b)
2120e43e99cSbellard {
2130e43e99cSbellard     PS2Queue *q = &s->queue;
2140e43e99cSbellard 
2159e24b2ddSVolker Rümelin     if (q->count >= PS2_QUEUE_SIZE) {
2160e43e99cSbellard         return;
2177abe7eb2SGeoffrey McRae     }
2187abe7eb2SGeoffrey McRae 
2190e43e99cSbellard     q->data[q->wptr] = b;
22047db2432SVolker Rümelin     if (++q->wptr == PS2_BUFFER_SIZE) {
2210e43e99cSbellard         q->wptr = 0;
22247db2432SVolker Rümelin     }
2230e43e99cSbellard     q->count++;
2247abe7eb2SGeoffrey McRae }
2257abe7eb2SGeoffrey McRae 
2267abe7eb2SGeoffrey McRae void ps2_raise_irq(PS2State *s)
2277abe7eb2SGeoffrey McRae {
2287abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2297abe7eb2SGeoffrey McRae }
2307abe7eb2SGeoffrey McRae 
2317abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
2327abe7eb2SGeoffrey McRae {
2337704bb02SVolker Rümelin     if (PS2_QUEUE_SIZE - s->queue.count < 1) {
2347704bb02SVolker Rümelin         return;
2357704bb02SVolker Rümelin     }
2367704bb02SVolker Rümelin 
2377abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
23896376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2397abe7eb2SGeoffrey McRae }
2407abe7eb2SGeoffrey McRae 
2417abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
2427abe7eb2SGeoffrey McRae {
2437abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
2447abe7eb2SGeoffrey McRae         return;
2457abe7eb2SGeoffrey McRae     }
2467abe7eb2SGeoffrey McRae 
2477abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2487abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
24996376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2507abe7eb2SGeoffrey McRae }
2517abe7eb2SGeoffrey McRae 
2527abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2537abe7eb2SGeoffrey McRae {
2547abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2557abe7eb2SGeoffrey McRae         return;
2567abe7eb2SGeoffrey McRae     }
2577abe7eb2SGeoffrey McRae 
2587abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2597abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2607abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
26196376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2627abe7eb2SGeoffrey McRae }
2637abe7eb2SGeoffrey McRae 
2647abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2657abe7eb2SGeoffrey McRae {
2667abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2677abe7eb2SGeoffrey McRae         return;
2687abe7eb2SGeoffrey McRae     }
2697abe7eb2SGeoffrey McRae 
2707abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2717abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2727abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2737abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
27496376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2750e43e99cSbellard }
2760e43e99cSbellard 
2779e24b2ddSVolker Rümelin static void ps2_cqueue_data(PS2Queue *q, int b)
2789e24b2ddSVolker Rümelin {
2799e24b2ddSVolker Rümelin     q->data[q->cwptr] = b;
2809e24b2ddSVolker Rümelin     if (++q->cwptr >= PS2_BUFFER_SIZE) {
2819e24b2ddSVolker Rümelin         q->cwptr = 0;
2829e24b2ddSVolker Rümelin     }
2839e24b2ddSVolker Rümelin     q->count++;
2849e24b2ddSVolker Rümelin }
2859e24b2ddSVolker Rümelin 
2869e24b2ddSVolker Rümelin static void ps2_cqueue_1(PS2State *s, int b1)
2879e24b2ddSVolker Rümelin {
2889e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2899e24b2ddSVolker Rümelin 
2909e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 1) & (PS2_BUFFER_SIZE - 1);
2919e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2929e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2939e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2949e24b2ddSVolker Rümelin }
2959e24b2ddSVolker Rümelin 
2969e24b2ddSVolker Rümelin static void ps2_cqueue_2(PS2State *s, int b1, int b2)
2979e24b2ddSVolker Rümelin {
2989e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2999e24b2ddSVolker Rümelin 
3009e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 2) & (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_raise_irq(s);
3059e24b2ddSVolker Rümelin }
3069e24b2ddSVolker Rümelin 
3079e24b2ddSVolker Rümelin static void ps2_cqueue_3(PS2State *s, int b1, int b2, int b3)
3089e24b2ddSVolker Rümelin {
3099e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
3109e24b2ddSVolker Rümelin 
3119e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 3) & (PS2_BUFFER_SIZE - 1);
3129e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
3139e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
3149e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
3159e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b3);
3169e24b2ddSVolker Rümelin     ps2_raise_irq(s);
3179e24b2ddSVolker Rümelin }
3189e24b2ddSVolker Rümelin 
3199e24b2ddSVolker Rümelin static void ps2_cqueue_reset(PS2State *s)
3209e24b2ddSVolker Rümelin {
3219e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
3229e24b2ddSVolker Rümelin     int ccount;
3239e24b2ddSVolker Rümelin 
3249e24b2ddSVolker Rümelin     if (q->cwptr == -1) {
3259e24b2ddSVolker Rümelin         return;
3269e24b2ddSVolker Rümelin     }
3279e24b2ddSVolker Rümelin 
3289e24b2ddSVolker Rümelin     ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
3299e24b2ddSVolker Rümelin     q->count -= ccount;
3309e24b2ddSVolker Rümelin     q->rptr = q->cwptr;
3319e24b2ddSVolker Rümelin     q->cwptr = -1;
3329e24b2ddSVolker Rümelin }
3339e24b2ddSVolker Rümelin 
33457d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
3350e43e99cSbellard static void ps2_put_keycode(void *opaque, int keycode)
3360e43e99cSbellard {
337f94f5d71Spbrook     PS2KbdState *s = opaque;
338*8f84e53cSMark Cave-Ayland     PS2State *ps = PS2_DEVICE(s);
339e7d93956Saurel32 
3405edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
341fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
34257d5c005SHervé Poussineau 
34357d5c005SHervé Poussineau     if (s->translate) {
34457d5c005SHervé Poussineau         if (keycode == 0xf0) {
34557d5c005SHervé Poussineau             s->need_high_bit = true;
34657d5c005SHervé Poussineau         } else if (s->need_high_bit) {
347*8f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode] | 0x80);
34857d5c005SHervé Poussineau             s->need_high_bit = false;
34957d5c005SHervé Poussineau         } else {
350*8f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode]);
3517096a96dSRoy Tam         }
35257d5c005SHervé Poussineau     } else {
353*8f84e53cSMark Cave-Ayland         ps2_queue(ps, keycode);
3540e43e99cSbellard     }
35557d5c005SHervé Poussineau }
3560e43e99cSbellard 
35766e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
35866e6536eSGerd Hoffmann                                InputEvent *evt)
35966e6536eSGerd Hoffmann {
36066e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
36132bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
3628c10e0baSHervé Poussineau     int qcode;
363ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
364620775d1SDaniel P. Berrange     int mod;
36566e6536eSGerd Hoffmann 
366143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
367143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
368143c04c7SGeoffrey McRae         return;
369143c04c7SGeoffrey McRae     }
370143c04c7SGeoffrey McRae 
371fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
3728c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
3738c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
37457d5c005SHervé Poussineau 
375620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
376644f66bfSDaniel P. Berrangé     trace_ps2_keyboard_event(s, qcode, key->down, mod,
377644f66bfSDaniel P. Berrangé                              s->modifiers, s->scancode_set, s->translate);
378620775d1SDaniel P. Berrange     if (key->down) {
379620775d1SDaniel P. Berrange         s->modifiers |= mod;
380620775d1SDaniel P. Berrange     } else {
381620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
382620775d1SDaniel P. Berrange     }
383620775d1SDaniel P. Berrange 
3848c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3858c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
38629fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
38729fd23a5SDaniel P. Berrange                 if (key->down) {
38829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
38929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
39029fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
39129fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
39229fd23a5SDaniel P. Berrange                 }
39329fd23a5SDaniel P. Berrange             } else {
3948c10e0baSHervé Poussineau                 if (key->down) {
3958c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
3968c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
3978c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
398927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
3998c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
4008c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
4018c10e0baSHervé Poussineau                 }
40229fd23a5SDaniel P. Berrange             }
4038c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
404620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
405620775d1SDaniel P. Berrange                 if (key->down) {
406620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
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, 0xb8);
412620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
413620775d1SDaniel P. Berrange                 }
414620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
415620775d1SDaniel P. Berrange                 if (key->down) {
416620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
417620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
418620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
419620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
420620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
421620775d1SDaniel P. Berrange                 } else {
422620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
423620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
424620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
425620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
426620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
427620775d1SDaniel P. Berrange                 }
4288f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4298f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4308f63458fSDaniel P. Berrange                 if (key->down) {
4318f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4328f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
4338f63458fSDaniel P. Berrange                 } else {
4348f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4358f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
4368f63458fSDaniel P. Berrange                 }
437620775d1SDaniel P. Berrange             } else {
4388c10e0baSHervé Poussineau                 if (key->down) {
4398c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4408c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
4418c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4428c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
4438c10e0baSHervé Poussineau                 } else {
4448c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4458c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
4468c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4478c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
4488c10e0baSHervé Poussineau                 }
449620775d1SDaniel P. Berrange             }
4508c10e0baSHervé Poussineau         } else {
451545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset1_len) {
452ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
453545e5cf8SMark Cave-Ayland             }
4548c10e0baSHervé Poussineau             if (keycode) {
4558c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4568c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4578c10e0baSHervé Poussineau                 }
4588c10e0baSHervé Poussineau                 if (!key->down) {
4598c10e0baSHervé Poussineau                     keycode |= 0x80;
4608c10e0baSHervé Poussineau                 }
4618c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
4628c10e0baSHervé Poussineau             } else {
463ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
464ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
4658c10e0baSHervé Poussineau             }
4668c10e0baSHervé Poussineau         }
4678c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
4688c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
46929fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
47029fd23a5SDaniel P. Berrange                 if (key->down) {
47129fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
47229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
47329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
47429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
47529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
47629fd23a5SDaniel P. Berrange                 }
47729fd23a5SDaniel P. Berrange             } else {
4788c10e0baSHervé Poussineau                 if (key->down) {
4798c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4808c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4818c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4828c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4838c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4848c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4858c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4868c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4878c10e0baSHervé Poussineau                 }
48829fd23a5SDaniel P. Berrange             }
4898c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
490620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
491620775d1SDaniel P. Berrange                 if (key->down) {
492620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
493620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
494620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
495620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
496620775d1SDaniel P. Berrange                 } else {
497620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
498620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
499620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
500620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
501620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
502620775d1SDaniel P. Berrange                 }
503620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
504620775d1SDaniel P. Berrange                 if (key->down) {
505620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
506620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
507620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
508620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
509620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
510620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
511620775d1SDaniel P. Berrange                 } else {
512620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
513620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
514620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
515620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
516620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
517620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
518620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
519620775d1SDaniel P. Berrange                 }
5208f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
5218f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
5228f63458fSDaniel P. Berrange                 if (key->down) {
5238f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
5248f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
5258f63458fSDaniel P. Berrange                 } else {
5268f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
5278f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
5288f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
5298f63458fSDaniel P. Berrange                 }
530620775d1SDaniel P. Berrange             } else {
5318c10e0baSHervé Poussineau                 if (key->down) {
5328c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5338c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
5348c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5358c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
5368c10e0baSHervé Poussineau                 } else {
5378c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5388c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5398c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
5408c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5418c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5428c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
5438c10e0baSHervé Poussineau                 }
544620775d1SDaniel P. Berrange             }
5458c10e0baSHervé Poussineau         } else {
546545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset2_len) {
547ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
548545e5cf8SMark Cave-Ayland             }
5498c10e0baSHervé Poussineau             if (keycode) {
5508c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
5518c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
5528c10e0baSHervé Poussineau                 }
5538c10e0baSHervé Poussineau                 if (!key->down) {
5548c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5558c10e0baSHervé Poussineau                 }
5568c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
55757d5c005SHervé Poussineau             } else {
558ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
559ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
56057d5c005SHervé Poussineau             }
56157d5c005SHervé Poussineau         }
56257d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
563545e5cf8SMark Cave-Ayland         if (qcode < qemu_input_map_qcode_to_atset3_len) {
564ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
565545e5cf8SMark Cave-Ayland         }
5668c10e0baSHervé Poussineau         if (keycode) {
5678c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
5688c10e0baSHervé Poussineau             if (!key->down) {
5698c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
57057d5c005SHervé Poussineau             }
57157d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
5728c10e0baSHervé Poussineau         } else {
573ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
574ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
5758c10e0baSHervé Poussineau         }
57666e6536eSGerd Hoffmann     }
57766e6536eSGerd Hoffmann }
57866e6536eSGerd Hoffmann 
5798498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
5800e43e99cSbellard {
5810e43e99cSbellard     PS2Queue *q;
5820e43e99cSbellard     int val, index;
5830e43e99cSbellard 
5848498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
5850e43e99cSbellard     q = &s->queue;
5860e43e99cSbellard     if (q->count == 0) {
587545e5cf8SMark Cave-Ayland         /*
588545e5cf8SMark Cave-Ayland          * NOTE: if no data left, we return the last keyboard one
589545e5cf8SMark Cave-Ayland          * (needed for EMM386)
590545e5cf8SMark Cave-Ayland          */
5910e43e99cSbellard         /* XXX: need a timer to do things correctly */
5920e43e99cSbellard         index = q->rptr - 1;
59347db2432SVolker Rümelin         if (index < 0) {
59447db2432SVolker Rümelin             index = PS2_BUFFER_SIZE - 1;
59547db2432SVolker Rümelin         }
5960e43e99cSbellard         val = q->data[index];
5970e43e99cSbellard     } else {
5980e43e99cSbellard         val = q->data[q->rptr];
59947db2432SVolker Rümelin         if (++q->rptr == PS2_BUFFER_SIZE) {
6000e43e99cSbellard             q->rptr = 0;
60147db2432SVolker Rümelin         }
6020e43e99cSbellard         q->count--;
6039e24b2ddSVolker Rümelin         if (q->rptr == q->cwptr) {
6049e24b2ddSVolker Rümelin             /* command reply queue is empty */
6059e24b2ddSVolker Rümelin             q->cwptr = -1;
6069e24b2ddSVolker Rümelin         }
6070e43e99cSbellard         /* reading deasserts IRQ */
6080e43e99cSbellard         s->update_irq(s->update_arg, 0);
6090e43e99cSbellard         /* reassert IRQs if data left */
610cec32524SVolker Rümelin         if (q->count) {
611cec32524SVolker Rümelin             s->update_irq(s->update_arg, 1);
612cec32524SVolker Rümelin         }
6130e43e99cSbellard     }
6140e43e99cSbellard     return val;
6150e43e99cSbellard }
6160e43e99cSbellard 
6177f540ab5SChristophe Fergeau static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
6187f540ab5SChristophe Fergeau {
6195edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
6207f540ab5SChristophe Fergeau     s->ledstate = ledstate;
6217f540ab5SChristophe Fergeau     kbd_put_ledstate(ledstate);
6227f540ab5SChristophe Fergeau }
6237f540ab5SChristophe Fergeau 
6240e43e99cSbellard static void ps2_reset_keyboard(PS2KbdState *s)
6250e43e99cSbellard {
626*8f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
627*8f84e53cSMark Cave-Ayland 
6285edab03dSDon Koch     trace_ps2_reset_keyboard(s);
6290e43e99cSbellard     s->scan_enabled = 1;
630e7d93956Saurel32     s->scancode_set = 2;
631*8f84e53cSMark Cave-Ayland     ps2_reset_queue(ps2);
6327f540ab5SChristophe Fergeau     ps2_set_ledstate(s, 0);
6330e43e99cSbellard }
6340e43e99cSbellard 
6350e43e99cSbellard void ps2_write_keyboard(void *opaque, int val)
6360e43e99cSbellard {
6370e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
638*8f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
6390e43e99cSbellard 
6405edab03dSDon Koch     trace_ps2_write_keyboard(opaque, val);
641*8f84e53cSMark Cave-Ayland     ps2_cqueue_reset(ps2);
642*8f84e53cSMark Cave-Ayland     switch (ps2->write_cmd) {
6430e43e99cSbellard     default:
6440e43e99cSbellard     case -1:
6450e43e99cSbellard         switch (val) {
6460e43e99cSbellard         case 0x00:
647*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6480e43e99cSbellard             break;
6490e43e99cSbellard         case 0x05:
650*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
6510e43e99cSbellard             break;
6520e43e99cSbellard         case KBD_CMD_GET_ID:
653e7d93956Saurel32             /* We emulate a MF2 AT keyboard here */
654*8f84e53cSMark Cave-Ayland             ps2_cqueue_3(ps2, KBD_REPLY_ACK, KBD_REPLY_ID,
6559e24b2ddSVolker Rümelin                          s->translate ? 0x41 : 0x83);
6560e43e99cSbellard             break;
6570e43e99cSbellard         case KBD_CMD_ECHO:
658*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_CMD_ECHO);
6590e43e99cSbellard             break;
6600e43e99cSbellard         case KBD_CMD_ENABLE:
6610e43e99cSbellard             s->scan_enabled = 1;
662*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6630e43e99cSbellard             break;
664e7d93956Saurel32         case KBD_CMD_SCANCODE:
6650e43e99cSbellard         case KBD_CMD_SET_LEDS:
6660e43e99cSbellard         case KBD_CMD_SET_RATE:
667c56b6209SSven Schnelle         case KBD_CMD_SET_MAKE_BREAK:
668*8f84e53cSMark Cave-Ayland             ps2->write_cmd = val;
669*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6700e43e99cSbellard             break;
6710e43e99cSbellard         case KBD_CMD_RESET_DISABLE:
6720e43e99cSbellard             ps2_reset_keyboard(s);
6730e43e99cSbellard             s->scan_enabled = 0;
674*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6750e43e99cSbellard             break;
6760e43e99cSbellard         case KBD_CMD_RESET_ENABLE:
6770e43e99cSbellard             ps2_reset_keyboard(s);
6780e43e99cSbellard             s->scan_enabled = 1;
679*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6800e43e99cSbellard             break;
6810e43e99cSbellard         case KBD_CMD_RESET:
6820e43e99cSbellard             ps2_reset_keyboard(s);
683*8f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2,
6847abe7eb2SGeoffrey McRae                          KBD_REPLY_ACK,
6857abe7eb2SGeoffrey McRae                          KBD_REPLY_POR);
6860e43e99cSbellard             break;
687c56b6209SSven Schnelle         case KBD_CMD_SET_TYPEMATIC:
688*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
689c56b6209SSven Schnelle             break;
6900e43e99cSbellard         default:
691*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
6920e43e99cSbellard             break;
6930e43e99cSbellard         }
6940e43e99cSbellard         break;
695c56b6209SSven Schnelle     case KBD_CMD_SET_MAKE_BREAK:
696*8f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
697*8f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
698c56b6209SSven Schnelle         break;
699e7d93956Saurel32     case KBD_CMD_SCANCODE:
700e7d93956Saurel32         if (val == 0) {
701*8f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2, KBD_REPLY_ACK, s->translate ?
7029e24b2ddSVolker Rümelin                 translate_table[s->scancode_set] : s->scancode_set);
7034df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
704e7d93956Saurel32             s->scancode_set = val;
705*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
7064df23b64SHervé Poussineau         } else {
707*8f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
708e7d93956Saurel32         }
709*8f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
710e7d93956Saurel32         break;
7110e43e99cSbellard     case KBD_CMD_SET_LEDS:
7127f540ab5SChristophe Fergeau         ps2_set_ledstate(s, val);
713*8f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
714*8f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
7150e43e99cSbellard         break;
7160e43e99cSbellard     case KBD_CMD_SET_RATE:
717*8f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
718*8f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
7190e43e99cSbellard         break;
7200e43e99cSbellard     }
7210e43e99cSbellard }
7220e43e99cSbellard 
723545e5cf8SMark Cave-Ayland /*
724545e5cf8SMark Cave-Ayland  * Set the scancode translation mode.
725545e5cf8SMark Cave-Ayland  * 0 = raw scancodes.
726545e5cf8SMark Cave-Ayland  * 1 = translated scancodes (used by qemu internally).
727545e5cf8SMark Cave-Ayland  */
728f94f5d71Spbrook 
729f94f5d71Spbrook void ps2_keyboard_set_translation(void *opaque, int mode)
730f94f5d71Spbrook {
731f94f5d71Spbrook     PS2KbdState *s = (PS2KbdState *)opaque;
7325edab03dSDon Koch     trace_ps2_keyboard_set_translation(opaque, mode);
733f94f5d71Spbrook     s->translate = mode;
734f94f5d71Spbrook }
735f94f5d71Spbrook 
7367abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
7370e43e99cSbellard {
73876968101SVolker Rümelin     /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
73976968101SVolker Rümelin     const int needed = s->mouse_type ? 4 : 3;
7400e43e99cSbellard     unsigned int b;
74164ebbb7dSDmitry Petrov     int dx1, dy1, dz1, dw1;
7420e43e99cSbellard 
7437abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->common.queue.count < needed) {
7447abe7eb2SGeoffrey McRae         return 0;
7457abe7eb2SGeoffrey McRae     }
7467abe7eb2SGeoffrey McRae 
7470e43e99cSbellard     dx1 = s->mouse_dx;
7480e43e99cSbellard     dy1 = s->mouse_dy;
7490e43e99cSbellard     dz1 = s->mouse_dz;
75064ebbb7dSDmitry Petrov     dw1 = s->mouse_dw;
7510e43e99cSbellard     /* XXX: increase range to 8 bits ? */
752545e5cf8SMark Cave-Ayland     if (dx1 > 127) {
7530e43e99cSbellard         dx1 = 127;
754545e5cf8SMark Cave-Ayland     } else if (dx1 < -127) {
7550e43e99cSbellard         dx1 = -127;
756545e5cf8SMark Cave-Ayland     }
757545e5cf8SMark Cave-Ayland     if (dy1 > 127) {
7580e43e99cSbellard         dy1 = 127;
759545e5cf8SMark Cave-Ayland     } else if (dy1 < -127) {
7600e43e99cSbellard         dy1 = -127;
761545e5cf8SMark Cave-Ayland     }
7620e43e99cSbellard     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
7637abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, b);
7647abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dx1 & 0xff);
7657abe7eb2SGeoffrey McRae     ps2_queue_noirq(&s->common, dy1 & 0xff);
7660e43e99cSbellard     /* extra byte for IMPS/2 or IMEX */
7670e43e99cSbellard     switch (s->mouse_type) {
7680e43e99cSbellard     default:
76964ebbb7dSDmitry Petrov         /* Just ignore the wheels if not supported */
77064ebbb7dSDmitry Petrov         s->mouse_dz = 0;
77164ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7720e43e99cSbellard         break;
7730e43e99cSbellard     case 3:
774545e5cf8SMark Cave-Ayland         if (dz1 > 127) {
7750e43e99cSbellard             dz1 = 127;
776545e5cf8SMark Cave-Ayland         } else if (dz1 < -127) {
7770e43e99cSbellard             dz1 = -127;
778545e5cf8SMark Cave-Ayland         }
7797abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, dz1 & 0xff);
78064ebbb7dSDmitry Petrov         s->mouse_dz -= dz1;
78164ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7820e43e99cSbellard         break;
7830e43e99cSbellard     case 4:
78464ebbb7dSDmitry Petrov         /*
78564ebbb7dSDmitry Petrov          * This matches what the Linux kernel expects for exps/2 in
78664ebbb7dSDmitry Petrov          * drivers/input/mouse/psmouse-base.c. Note, if you happen to
78764ebbb7dSDmitry Petrov          * press/release the 4th or 5th buttons at the same moment as a
78864ebbb7dSDmitry Petrov          * horizontal wheel scroll, those button presses will get lost. I'm not
78964ebbb7dSDmitry Petrov          * sure what to do about that, since by this point we don't know
79064ebbb7dSDmitry Petrov          * whether those buttons actually changed state.
79164ebbb7dSDmitry Petrov          */
79264ebbb7dSDmitry Petrov         if (dw1 != 0) {
79364ebbb7dSDmitry Petrov             if (dw1 > 31) {
79464ebbb7dSDmitry Petrov                 dw1 = 31;
79564ebbb7dSDmitry Petrov             } else if (dw1 < -31) {
79664ebbb7dSDmitry Petrov                 dw1 = -31;
79764ebbb7dSDmitry Petrov             }
79864ebbb7dSDmitry Petrov 
79964ebbb7dSDmitry Petrov             /*
80064ebbb7dSDmitry Petrov              * linux kernel expects first 6 bits to represent the value
80164ebbb7dSDmitry Petrov              * for horizontal scroll
80264ebbb7dSDmitry Petrov              */
80364ebbb7dSDmitry Petrov             b = (dw1 & 0x3f) | 0x40;
80464ebbb7dSDmitry Petrov             s->mouse_dw -= dw1;
80564ebbb7dSDmitry Petrov         } else {
80664ebbb7dSDmitry Petrov             if (dz1 > 7) {
8070e43e99cSbellard                 dz1 = 7;
80864ebbb7dSDmitry Petrov             } else if (dz1 < -7) {
8090e43e99cSbellard                 dz1 = -7;
81064ebbb7dSDmitry Petrov             }
81164ebbb7dSDmitry Petrov 
8120e43e99cSbellard             b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
81364ebbb7dSDmitry Petrov             s->mouse_dz -= dz1;
81464ebbb7dSDmitry Petrov         }
8157abe7eb2SGeoffrey McRae         ps2_queue_noirq(&s->common, b);
8160e43e99cSbellard         break;
8170e43e99cSbellard     }
8180e43e99cSbellard 
8197abe7eb2SGeoffrey McRae     ps2_raise_irq(&s->common);
8207abe7eb2SGeoffrey McRae 
8215edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
8220e43e99cSbellard     /* update deltas */
8230e43e99cSbellard     s->mouse_dx -= dx1;
8240e43e99cSbellard     s->mouse_dy -= dy1;
8257abe7eb2SGeoffrey McRae 
8267abe7eb2SGeoffrey McRae     return 1;
8270e43e99cSbellard }
8280e43e99cSbellard 
8292a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
8302a766d29SGerd Hoffmann                             InputEvent *evt)
8310e43e99cSbellard {
8327fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
8338b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
8348b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
8358b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
8368b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
8378b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
8382a766d29SGerd Hoffmann     };
8392a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
840b5a1b443SEric Blake     InputMoveEvent *move;
841b5a1b443SEric Blake     InputBtnEvent *btn;
8420e43e99cSbellard 
8430e43e99cSbellard     /* check if deltas are recorded when disabled */
844545e5cf8SMark Cave-Ayland     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
8450e43e99cSbellard         return;
846545e5cf8SMark Cave-Ayland     }
8470e43e99cSbellard 
848568c73a4SEric Blake     switch (evt->type) {
8492a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
85032bafa8fSEric Blake         move = evt->u.rel.data;
851b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
852b5a1b443SEric Blake             s->mouse_dx += move->value;
853b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
854b5a1b443SEric Blake             s->mouse_dy -= move->value;
8552a766d29SGerd Hoffmann         }
8562a766d29SGerd Hoffmann         break;
8570e43e99cSbellard 
8582a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
85932bafa8fSEric Blake         btn = evt->u.btn.data;
860b5a1b443SEric Blake         if (btn->down) {
861b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
862b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
8632a766d29SGerd Hoffmann                 s->mouse_dz--;
864b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
8652a766d29SGerd Hoffmann                 s->mouse_dz++;
8662a766d29SGerd Hoffmann             }
86764ebbb7dSDmitry Petrov 
86864ebbb7dSDmitry Petrov             if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
86964ebbb7dSDmitry Petrov                 s->mouse_dw--;
87064ebbb7dSDmitry Petrov             } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
87164ebbb7dSDmitry Petrov                 s->mouse_dw++;
87264ebbb7dSDmitry Petrov             }
8732a766d29SGerd Hoffmann         } else {
874b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
8752a766d29SGerd Hoffmann         }
8762a766d29SGerd Hoffmann         break;
8772a766d29SGerd Hoffmann 
8782a766d29SGerd Hoffmann     default:
8792a766d29SGerd Hoffmann         /* keep gcc happy */
8802a766d29SGerd Hoffmann         break;
8812a766d29SGerd Hoffmann     }
882fd214d18SGerd Hoffmann }
883fd214d18SGerd Hoffmann 
8842a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
8852a766d29SGerd Hoffmann {
8862a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
8872a766d29SGerd Hoffmann 
888143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
889143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
890143c04c7SGeoffrey McRae         return;
891143c04c7SGeoffrey McRae     }
892143c04c7SGeoffrey McRae 
8932a766d29SGerd Hoffmann     if (s->mouse_buttons) {
894fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
8952a766d29SGerd Hoffmann     }
8962858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
897545e5cf8SMark Cave-Ayland         /*
898545e5cf8SMark Cave-Ayland          * if not remote, send event. Multiple events are sent if
899545e5cf8SMark Cave-Ayland          * too big deltas
900545e5cf8SMark Cave-Ayland          */
9017abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
90264ebbb7dSDmitry Petrov             if (s->mouse_dx == 0 && s->mouse_dy == 0
90364ebbb7dSDmitry Petrov                     && s->mouse_dz == 0 && s->mouse_dw == 0) {
9040e43e99cSbellard                 break;
9050e43e99cSbellard             }
9060e43e99cSbellard         }
9070e43e99cSbellard     }
90864ebbb7dSDmitry Petrov }
9090e43e99cSbellard 
910548df2acSths void ps2_mouse_fake_event(void *opaque)
911548df2acSths {
9122a766d29SGerd Hoffmann     PS2MouseState *s = opaque;
9135edab03dSDon Koch     trace_ps2_mouse_fake_event(opaque);
9142a766d29SGerd Hoffmann     s->mouse_dx++;
9152a766d29SGerd Hoffmann     ps2_mouse_sync(opaque);
916548df2acSths }
917548df2acSths 
9180e43e99cSbellard void ps2_write_mouse(void *opaque, int val)
9190e43e99cSbellard {
9200e43e99cSbellard     PS2MouseState *s = (PS2MouseState *)opaque;
9215edab03dSDon Koch 
9225edab03dSDon Koch     trace_ps2_write_mouse(opaque, val);
9230e43e99cSbellard     switch (s->common.write_cmd) {
9240e43e99cSbellard     default:
9250e43e99cSbellard     case -1:
9260e43e99cSbellard         /* mouse command */
9270e43e99cSbellard         if (s->mouse_wrap) {
9280e43e99cSbellard             if (val == AUX_RESET_WRAP) {
9290e43e99cSbellard                 s->mouse_wrap = 0;
9300e43e99cSbellard                 ps2_queue(&s->common, AUX_ACK);
9310e43e99cSbellard                 return;
9320e43e99cSbellard             } else if (val != AUX_RESET) {
9330e43e99cSbellard                 ps2_queue(&s->common, val);
9340e43e99cSbellard                 return;
9350e43e99cSbellard             }
9360e43e99cSbellard         }
9370e43e99cSbellard         switch (val) {
9380e43e99cSbellard         case AUX_SET_SCALE11:
9390e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
9400e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9410e43e99cSbellard             break;
9420e43e99cSbellard         case AUX_SET_SCALE21:
9430e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_SCALE21;
9440e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9450e43e99cSbellard             break;
9460e43e99cSbellard         case AUX_SET_STREAM:
9470e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
9480e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9490e43e99cSbellard             break;
9500e43e99cSbellard         case AUX_SET_WRAP:
9510e43e99cSbellard             s->mouse_wrap = 1;
9520e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9530e43e99cSbellard             break;
9540e43e99cSbellard         case AUX_SET_REMOTE:
9550e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_REMOTE;
9560e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9570e43e99cSbellard             break;
9580e43e99cSbellard         case AUX_GET_TYPE:
9597abe7eb2SGeoffrey McRae             ps2_queue_2(&s->common,
9607abe7eb2SGeoffrey McRae                 AUX_ACK,
9617abe7eb2SGeoffrey McRae                 s->mouse_type);
9620e43e99cSbellard             break;
9630e43e99cSbellard         case AUX_SET_RES:
9640e43e99cSbellard         case AUX_SET_SAMPLE:
9650e43e99cSbellard             s->common.write_cmd = val;
9660e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9670e43e99cSbellard             break;
9680e43e99cSbellard         case AUX_GET_SCALE:
9697abe7eb2SGeoffrey McRae             ps2_queue_4(&s->common,
9707abe7eb2SGeoffrey McRae                 AUX_ACK,
9717abe7eb2SGeoffrey McRae                 s->mouse_status,
9727abe7eb2SGeoffrey McRae                 s->mouse_resolution,
9737abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
9740e43e99cSbellard             break;
9750e43e99cSbellard         case AUX_POLL:
9760e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9770e43e99cSbellard             ps2_mouse_send_packet(s);
9780e43e99cSbellard             break;
9790e43e99cSbellard         case AUX_ENABLE_DEV:
9800e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_ENABLED;
9810e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9820e43e99cSbellard             break;
9830e43e99cSbellard         case AUX_DISABLE_DEV:
9840e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
9850e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9860e43e99cSbellard             break;
9870e43e99cSbellard         case AUX_SET_DEFAULT:
9880e43e99cSbellard             s->mouse_sample_rate = 100;
9890e43e99cSbellard             s->mouse_resolution = 2;
9900e43e99cSbellard             s->mouse_status = 0;
9910e43e99cSbellard             ps2_queue(&s->common, AUX_ACK);
9920e43e99cSbellard             break;
9930e43e99cSbellard         case AUX_RESET:
9940e43e99cSbellard             s->mouse_sample_rate = 100;
9950e43e99cSbellard             s->mouse_resolution = 2;
9960e43e99cSbellard             s->mouse_status = 0;
9970e43e99cSbellard             s->mouse_type = 0;
998143c04c7SGeoffrey McRae             ps2_reset_queue(&s->common);
9997abe7eb2SGeoffrey McRae             ps2_queue_3(&s->common,
10007abe7eb2SGeoffrey McRae                 AUX_ACK,
10017abe7eb2SGeoffrey McRae                 0xaa,
10027abe7eb2SGeoffrey McRae                 s->mouse_type);
10030e43e99cSbellard             break;
10040e43e99cSbellard         default:
10050e43e99cSbellard             break;
10060e43e99cSbellard         }
10070e43e99cSbellard         break;
10080e43e99cSbellard     case AUX_SET_SAMPLE:
10090e43e99cSbellard         s->mouse_sample_rate = val;
10100e43e99cSbellard         /* detect IMPS/2 or IMEX */
10110e43e99cSbellard         switch (s->mouse_detect_state) {
10120e43e99cSbellard         default:
10130e43e99cSbellard         case 0:
1014545e5cf8SMark Cave-Ayland             if (val == 200) {
10150e43e99cSbellard                 s->mouse_detect_state = 1;
1016545e5cf8SMark Cave-Ayland             }
10170e43e99cSbellard             break;
10180e43e99cSbellard         case 1:
1019545e5cf8SMark Cave-Ayland             if (val == 100) {
10200e43e99cSbellard                 s->mouse_detect_state = 2;
1021545e5cf8SMark Cave-Ayland             } else if (val == 200) {
10220e43e99cSbellard                 s->mouse_detect_state = 3;
1023545e5cf8SMark Cave-Ayland             } else {
10240e43e99cSbellard                 s->mouse_detect_state = 0;
1025545e5cf8SMark Cave-Ayland             }
10260e43e99cSbellard             break;
10270e43e99cSbellard         case 2:
1028545e5cf8SMark Cave-Ayland             if (val == 80) {
10290e43e99cSbellard                 s->mouse_type = 3; /* IMPS/2 */
1030545e5cf8SMark Cave-Ayland             }
10310e43e99cSbellard             s->mouse_detect_state = 0;
10320e43e99cSbellard             break;
10330e43e99cSbellard         case 3:
1034545e5cf8SMark Cave-Ayland             if (val == 80) {
10350e43e99cSbellard                 s->mouse_type = 4; /* IMEX */
1036545e5cf8SMark Cave-Ayland             }
10370e43e99cSbellard             s->mouse_detect_state = 0;
10380e43e99cSbellard             break;
10390e43e99cSbellard         }
10400e43e99cSbellard         ps2_queue(&s->common, AUX_ACK);
10410e43e99cSbellard         s->common.write_cmd = -1;
10420e43e99cSbellard         break;
10430e43e99cSbellard     case AUX_SET_RES:
10440e43e99cSbellard         s->mouse_resolution = val;
10450e43e99cSbellard         ps2_queue(&s->common, AUX_ACK);
10460e43e99cSbellard         s->common.write_cmd = -1;
10470e43e99cSbellard         break;
10480e43e99cSbellard     }
10490e43e99cSbellard }
10500e43e99cSbellard 
1051ef74679aSDinesh Subhraveti static void ps2_common_reset(PS2State *s)
10520e43e99cSbellard {
10530e43e99cSbellard     s->write_cmd = -1;
1054954ee55bSGerd Hoffmann     ps2_reset_queue(s);
1055deeccef3Saliguori     s->update_irq(s->update_arg, 0);
10560e43e99cSbellard }
10570e43e99cSbellard 
10582858ab09SGonglei static void ps2_common_post_load(PS2State *s)
10592858ab09SGonglei {
10602858ab09SGonglei     PS2Queue *q = &s->queue;
10614e9bddcbSVolker Rümelin     int ccount = 0;
10622858ab09SGonglei 
10634e9bddcbSVolker Rümelin     /* limit the number of queued command replies to PS2_QUEUE_HEADROOM */
10644e9bddcbSVolker Rümelin     if (q->cwptr != -1) {
10654e9bddcbSVolker Rümelin         ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
10664e9bddcbSVolker Rümelin         if (ccount > PS2_QUEUE_HEADROOM) {
10674e9bddcbSVolker Rümelin             ccount = PS2_QUEUE_HEADROOM;
10684e9bddcbSVolker Rümelin         }
1069a1f2ed2aSPavel Dovgalyuk     }
10702858ab09SGonglei 
10714e9bddcbSVolker Rümelin     /* limit the scancode queue size to PS2_QUEUE_SIZE */
10724e9bddcbSVolker Rümelin     if (q->count < ccount) {
10734e9bddcbSVolker Rümelin         q->count = ccount;
10744e9bddcbSVolker Rümelin     } else if (q->count > ccount + PS2_QUEUE_SIZE) {
10754e9bddcbSVolker Rümelin         q->count = ccount + PS2_QUEUE_SIZE;
10764e9bddcbSVolker Rümelin     }
10774e9bddcbSVolker Rümelin 
10784e9bddcbSVolker Rümelin     /* sanitize rptr and recalculate wptr and cwptr */
107947db2432SVolker Rümelin     q->rptr = q->rptr & (PS2_BUFFER_SIZE - 1);
108047db2432SVolker Rümelin     q->wptr = (q->rptr + q->count) & (PS2_BUFFER_SIZE - 1);
10814e9bddcbSVolker Rümelin     q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1;
10822858ab09SGonglei }
10832858ab09SGonglei 
1084ef74679aSDinesh Subhraveti static void ps2_kbd_reset(void *opaque)
1085ef74679aSDinesh Subhraveti {
1086ef74679aSDinesh Subhraveti     PS2KbdState *s = (PS2KbdState *) opaque;
1087*8f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
1088ef74679aSDinesh Subhraveti 
10895edab03dSDon Koch     trace_ps2_kbd_reset(opaque);
1090*8f84e53cSMark Cave-Ayland     ps2_common_reset(ps2);
1091d2e550a8SHervé Poussineau     s->scan_enabled = 1;
1092ef74679aSDinesh Subhraveti     s->translate = 0;
1093089adafdSHervé Poussineau     s->scancode_set = 2;
1094620775d1SDaniel P. Berrange     s->modifiers = 0;
1095ef74679aSDinesh Subhraveti }
1096ef74679aSDinesh Subhraveti 
1097ef74679aSDinesh Subhraveti static void ps2_mouse_reset(void *opaque)
1098ef74679aSDinesh Subhraveti {
1099ef74679aSDinesh Subhraveti     PS2MouseState *s = (PS2MouseState *) opaque;
1100ef74679aSDinesh Subhraveti 
11015edab03dSDon Koch     trace_ps2_mouse_reset(opaque);
1102ef74679aSDinesh Subhraveti     ps2_common_reset(&s->common);
1103ef74679aSDinesh Subhraveti     s->mouse_status = 0;
1104ef74679aSDinesh Subhraveti     s->mouse_resolution = 0;
1105ef74679aSDinesh Subhraveti     s->mouse_sample_rate = 0;
1106ef74679aSDinesh Subhraveti     s->mouse_wrap = 0;
1107ef74679aSDinesh Subhraveti     s->mouse_type = 0;
1108ef74679aSDinesh Subhraveti     s->mouse_detect_state = 0;
1109ef74679aSDinesh Subhraveti     s->mouse_dx = 0;
1110ef74679aSDinesh Subhraveti     s->mouse_dy = 0;
1111ef74679aSDinesh Subhraveti     s->mouse_dz = 0;
111264ebbb7dSDmitry Petrov     s->mouse_dw = 0;
1113ef74679aSDinesh Subhraveti     s->mouse_buttons = 0;
1114ef74679aSDinesh Subhraveti }
1115ef74679aSDinesh Subhraveti 
1116b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_common = {
1117b31442c3SJuan Quintela     .name = "PS2 Common State",
1118b31442c3SJuan Quintela     .version_id = 3,
1119b31442c3SJuan Quintela     .minimum_version_id = 2,
1120b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1121b31442c3SJuan Quintela         VMSTATE_INT32(write_cmd, PS2State),
1122b31442c3SJuan Quintela         VMSTATE_INT32(queue.rptr, PS2State),
1123b31442c3SJuan Quintela         VMSTATE_INT32(queue.wptr, PS2State),
1124b31442c3SJuan Quintela         VMSTATE_INT32(queue.count, PS2State),
1125b31442c3SJuan Quintela         VMSTATE_BUFFER(queue.data, PS2State),
1126b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
11277783e9f0Spbrook     }
1128b31442c3SJuan Quintela };
11297783e9f0Spbrook 
11307f540ab5SChristophe Fergeau static bool ps2_keyboard_ledstate_needed(void *opaque)
11317f540ab5SChristophe Fergeau {
11327f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
11337f540ab5SChristophe Fergeau 
11347f540ab5SChristophe Fergeau     return s->ledstate != 0; /* 0 is default state */
11357f540ab5SChristophe Fergeau }
11367f540ab5SChristophe Fergeau 
11377f540ab5SChristophe Fergeau static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
11387f540ab5SChristophe Fergeau {
11397f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
11407f540ab5SChristophe Fergeau 
11417f540ab5SChristophe Fergeau     kbd_put_ledstate(s->ledstate);
11427f540ab5SChristophe Fergeau     return 0;
11437f540ab5SChristophe Fergeau }
11447f540ab5SChristophe Fergeau 
11457f540ab5SChristophe Fergeau static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
11467f540ab5SChristophe Fergeau     .name = "ps2kbd/ledstate",
11477f540ab5SChristophe Fergeau     .version_id = 3,
11487f540ab5SChristophe Fergeau     .minimum_version_id = 2,
11497f540ab5SChristophe Fergeau     .post_load = ps2_kbd_ledstate_post_load,
11505cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
11517f540ab5SChristophe Fergeau     .fields = (VMStateField[]) {
11527f540ab5SChristophe Fergeau         VMSTATE_INT32(ledstate, PS2KbdState),
11537f540ab5SChristophe Fergeau         VMSTATE_END_OF_LIST()
11547f540ab5SChristophe Fergeau     }
11557f540ab5SChristophe Fergeau };
11567f540ab5SChristophe Fergeau 
115757d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
115857d5c005SHervé Poussineau {
115957d5c005SHervé Poussineau     PS2KbdState *s = opaque;
116057d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
116157d5c005SHervé Poussineau }
116257d5c005SHervé Poussineau 
116357d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
116457d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
116557d5c005SHervé Poussineau     .version_id = 1,
116657d5c005SHervé Poussineau     .minimum_version_id = 1,
116757d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
116857d5c005SHervé Poussineau     .fields = (VMStateField[]) {
116957d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
117057d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
117157d5c005SHervé Poussineau     }
117257d5c005SHervé Poussineau };
117357d5c005SHervé Poussineau 
11744e9bddcbSVolker Rümelin static bool ps2_keyboard_cqueue_needed(void *opaque)
11754e9bddcbSVolker Rümelin {
11764e9bddcbSVolker Rümelin     PS2KbdState *s = opaque;
1177*8f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11784e9bddcbSVolker Rümelin 
1179*8f84e53cSMark Cave-Ayland     return ps2->queue.cwptr != -1; /* the queue is mostly empty */
11804e9bddcbSVolker Rümelin }
11814e9bddcbSVolker Rümelin 
11824e9bddcbSVolker Rümelin static const VMStateDescription vmstate_ps2_keyboard_cqueue = {
11834e9bddcbSVolker Rümelin     .name = "ps2kbd/command_reply_queue",
11844e9bddcbSVolker Rümelin     .needed = ps2_keyboard_cqueue_needed,
11854e9bddcbSVolker Rümelin     .fields = (VMStateField[]) {
1186*8f84e53cSMark Cave-Ayland         VMSTATE_INT32(parent_obj.queue.cwptr, PS2KbdState),
11874e9bddcbSVolker Rümelin         VMSTATE_END_OF_LIST()
11884e9bddcbSVolker Rümelin     }
11894e9bddcbSVolker Rümelin };
11904e9bddcbSVolker Rümelin 
1191db596c53SJuan Quintela static int ps2_kbd_post_load(void *opaque, int version_id)
11920e43e99cSbellard {
11930e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
1194*8f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11950e43e99cSbellard 
1196545e5cf8SMark Cave-Ayland     if (version_id == 2) {
1197e7d93956Saurel32         s->scancode_set = 2;
1198545e5cf8SMark Cave-Ayland     }
11992858ab09SGonglei 
12002858ab09SGonglei     ps2_common_post_load(ps2);
12012858ab09SGonglei 
12020e43e99cSbellard     return 0;
12030e43e99cSbellard }
12040e43e99cSbellard 
1205b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_keyboard = {
1206b31442c3SJuan Quintela     .name = "ps2kbd",
1207b31442c3SJuan Quintela     .version_id = 3,
1208db596c53SJuan Quintela     .minimum_version_id = 2,
1209db596c53SJuan Quintela     .post_load = ps2_kbd_post_load,
1210b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1211*8f84e53cSMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2KbdState, 0, vmstate_ps2_common,
1212*8f84e53cSMark Cave-Ayland                        PS2State),
1213b31442c3SJuan Quintela         VMSTATE_INT32(scan_enabled, PS2KbdState),
1214b31442c3SJuan Quintela         VMSTATE_INT32(translate, PS2KbdState),
1215b31442c3SJuan Quintela         VMSTATE_INT32_V(scancode_set, PS2KbdState, 3),
1216b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
12177f540ab5SChristophe Fergeau     },
12185cd8cadaSJuan Quintela     .subsections = (const VMStateDescription * []) {
12195cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
122057d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
12214e9bddcbSVolker Rümelin         &vmstate_ps2_keyboard_cqueue,
12225cd8cadaSJuan Quintela         NULL
12230e43e99cSbellard     }
1224b31442c3SJuan Quintela };
1225b31442c3SJuan Quintela 
12262858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
12272858ab09SGonglei {
12282858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
12292858ab09SGonglei     PS2State *ps2 = &s->common;
12302858ab09SGonglei 
12312858ab09SGonglei     ps2_common_post_load(ps2);
12322858ab09SGonglei 
12332858ab09SGonglei     return 0;
12342858ab09SGonglei }
12352858ab09SGonglei 
1236b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_mouse = {
1237b31442c3SJuan Quintela     .name = "ps2mouse",
1238b31442c3SJuan Quintela     .version_id = 2,
1239b31442c3SJuan Quintela     .minimum_version_id = 2,
12402858ab09SGonglei     .post_load = ps2_mouse_post_load,
1241b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1242b31442c3SJuan Quintela         VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
1243b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_status, PS2MouseState),
1244b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
1245b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
1246b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
1247b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_type, PS2MouseState),
1248b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
1249b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dx, PS2MouseState),
1250b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dy, PS2MouseState),
1251b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dz, PS2MouseState),
1252b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
1253b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
1254b31442c3SJuan Quintela     }
1255b31442c3SJuan Quintela };
12560e43e99cSbellard 
125766e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
125866e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
125966e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
126066e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
126166e6536eSGerd Hoffmann };
126266e6536eSGerd Hoffmann 
12630e43e99cSbellard void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
12640e43e99cSbellard {
1265*8f84e53cSMark Cave-Ayland     DeviceState *dev;
1266*8f84e53cSMark Cave-Ayland     PS2KbdState *s;
1267*8f84e53cSMark Cave-Ayland     PS2State *ps2;
1268*8f84e53cSMark Cave-Ayland 
1269*8f84e53cSMark Cave-Ayland     dev = qdev_new(TYPE_PS2_KBD_DEVICE);
1270*8f84e53cSMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
1271*8f84e53cSMark Cave-Ayland     s = PS2_KBD_DEVICE(dev);
1272*8f84e53cSMark Cave-Ayland     ps2 = PS2_DEVICE(s);
12730e43e99cSbellard 
12745edab03dSDon Koch     trace_ps2_kbd_init(s);
1275*8f84e53cSMark Cave-Ayland     ps2->update_irq = update_irq;
1276*8f84e53cSMark Cave-Ayland     ps2->update_arg = update_arg;
1277e7d93956Saurel32     s->scancode_set = 2;
12780be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
127966e6536eSGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
128066e6536eSGerd Hoffmann                                 &ps2_keyboard_handler);
1281ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_kbd_reset, s);
12820e43e99cSbellard     return s;
12830e43e99cSbellard }
12840e43e99cSbellard 
12852a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
12862a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
12872a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
12882a766d29SGerd Hoffmann     .event = ps2_mouse_event,
12892a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
12902a766d29SGerd Hoffmann };
12912a766d29SGerd Hoffmann 
12920e43e99cSbellard void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
12930e43e99cSbellard {
1294b21e2380SMarkus Armbruster     PS2MouseState *s = g_new0(PS2MouseState, 1);
12950e43e99cSbellard 
12965edab03dSDon Koch     trace_ps2_mouse_init(s);
12970e43e99cSbellard     s->common.update_irq = update_irq;
12980e43e99cSbellard     s->common.update_arg = update_arg;
12990be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
13002a766d29SGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
13012a766d29SGerd Hoffmann                                 &ps2_mouse_handler);
1302ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_mouse_reset, s);
13030e43e99cSbellard     return s;
13040e43e99cSbellard }
130564bbdd13SMark Cave-Ayland 
1306*8f84e53cSMark Cave-Ayland static const TypeInfo ps2_kbd_info = {
1307*8f84e53cSMark Cave-Ayland     .name          = TYPE_PS2_KBD_DEVICE,
1308*8f84e53cSMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
1309*8f84e53cSMark Cave-Ayland     .instance_size = sizeof(PS2KbdState),
1310*8f84e53cSMark Cave-Ayland };
1311*8f84e53cSMark Cave-Ayland 
131264bbdd13SMark Cave-Ayland static void ps2_class_init(ObjectClass *klass, void *data)
131364bbdd13SMark Cave-Ayland {
131464bbdd13SMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
131564bbdd13SMark Cave-Ayland 
131664bbdd13SMark Cave-Ayland     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
131764bbdd13SMark Cave-Ayland }
131864bbdd13SMark Cave-Ayland 
131964bbdd13SMark Cave-Ayland static const TypeInfo ps2_info = {
132064bbdd13SMark Cave-Ayland     .name          = TYPE_PS2_DEVICE,
132164bbdd13SMark Cave-Ayland     .parent        = TYPE_SYS_BUS_DEVICE,
132264bbdd13SMark Cave-Ayland     .instance_size = sizeof(PS2State),
132364bbdd13SMark Cave-Ayland     .class_init    = ps2_class_init,
132464bbdd13SMark Cave-Ayland     .abstract      = true
132564bbdd13SMark Cave-Ayland };
132664bbdd13SMark Cave-Ayland 
132764bbdd13SMark Cave-Ayland static void ps2_register_types(void)
132864bbdd13SMark Cave-Ayland {
132964bbdd13SMark Cave-Ayland     type_register_static(&ps2_info);
1330*8f84e53cSMark Cave-Ayland     type_register_static(&ps2_kbd_info);
133164bbdd13SMark Cave-Ayland }
133264bbdd13SMark Cave-Ayland 
133364bbdd13SMark Cave-Ayland type_init(ps2_register_types)
1334