xref: /qemu/hw/input/ps2.c (revision 2d135409e642bb31cff032dc533bf7ce6b0d0051)
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"
348f84e53cSMark 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 
1128f84e53cSMark Cave-Ayland struct PS2KbdState {
1138f84e53cSMark Cave-Ayland     PS2State parent_obj;
1148f84e53cSMark 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 */
1218f84e53cSMark Cave-Ayland };
1228f84e53cSMark Cave-Ayland 
1238f84e53cSMark Cave-Ayland #define TYPE_PS2_KBD_DEVICE "ps2-kbd"
1248f84e53cSMark Cave-Ayland OBJECT_DECLARE_SIMPLE_TYPE(PS2KbdState, PS2_KBD_DEVICE)
1250e43e99cSbellard 
126*2d135409SMark Cave-Ayland struct PS2MouseState {
127*2d135409SMark Cave-Ayland     PS2State parent_obj;
128*2d135409SMark Cave-Ayland 
1290e43e99cSbellard     uint8_t mouse_status;
1300e43e99cSbellard     uint8_t mouse_resolution;
1310e43e99cSbellard     uint8_t mouse_sample_rate;
1320e43e99cSbellard     uint8_t mouse_wrap;
1330e43e99cSbellard     uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
1340e43e99cSbellard     uint8_t mouse_detect_state;
1350e43e99cSbellard     int mouse_dx; /* current values, needed for 'poll' mode */
1360e43e99cSbellard     int mouse_dy;
1370e43e99cSbellard     int mouse_dz;
13864ebbb7dSDmitry Petrov     int mouse_dw;
1390e43e99cSbellard     uint8_t mouse_buttons;
140*2d135409SMark Cave-Ayland };
141*2d135409SMark Cave-Ayland 
142*2d135409SMark Cave-Ayland #define TYPE_PS2_MOUSE_DEVICE "ps2-mouse"
143*2d135409SMark Cave-Ayland OBJECT_DECLARE_SIMPLE_TYPE(PS2MouseState, PS2_MOUSE_DEVICE)
1440e43e99cSbellard 
14557d5c005SHervé Poussineau static uint8_t translate_table[256] = {
14657d5c005SHervé Poussineau     0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
14757d5c005SHervé Poussineau     0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
14857d5c005SHervé Poussineau     0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
14957d5c005SHervé Poussineau     0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
15057d5c005SHervé Poussineau     0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
15157d5c005SHervé Poussineau     0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
15257d5c005SHervé Poussineau     0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
15357d5c005SHervé Poussineau     0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
15457d5c005SHervé Poussineau     0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
15557d5c005SHervé Poussineau     0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
15657d5c005SHervé Poussineau     0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
15757d5c005SHervé Poussineau     0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
15857d5c005SHervé Poussineau     0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
15957d5c005SHervé Poussineau     0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
16057d5c005SHervé Poussineau     0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
16157d5c005SHervé Poussineau     0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
16257d5c005SHervé Poussineau     0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
16357d5c005SHervé Poussineau     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
16457d5c005SHervé Poussineau     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
16557d5c005SHervé Poussineau     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
16657d5c005SHervé Poussineau     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
16757d5c005SHervé Poussineau     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
16857d5c005SHervé Poussineau     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
16957d5c005SHervé Poussineau     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
17057d5c005SHervé Poussineau     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
17157d5c005SHervé Poussineau     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
17257d5c005SHervé Poussineau     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
17357d5c005SHervé Poussineau     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
17457d5c005SHervé Poussineau     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
17557d5c005SHervé Poussineau     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
17657d5c005SHervé Poussineau     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
17757d5c005SHervé Poussineau     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
17857d5c005SHervé Poussineau };
17957d5c005SHervé Poussineau 
180620775d1SDaniel P. Berrange static unsigned int ps2_modifier_bit(QKeyCode key)
181620775d1SDaniel P. Berrange {
182620775d1SDaniel P. Berrange     switch (key) {
183620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL:
184620775d1SDaniel P. Berrange         return MOD_CTRL_L;
185620775d1SDaniel P. Berrange     case Q_KEY_CODE_CTRL_R:
186620775d1SDaniel P. Berrange         return MOD_CTRL_R;
187620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT:
188620775d1SDaniel P. Berrange         return MOD_SHIFT_L;
189620775d1SDaniel P. Berrange     case Q_KEY_CODE_SHIFT_R:
190620775d1SDaniel P. Berrange         return MOD_SHIFT_R;
191620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT:
192620775d1SDaniel P. Berrange         return MOD_ALT_L;
193620775d1SDaniel P. Berrange     case Q_KEY_CODE_ALT_R:
194620775d1SDaniel P. Berrange         return MOD_ALT_R;
195620775d1SDaniel P. Berrange     default:
196620775d1SDaniel P. Berrange         return 0;
197620775d1SDaniel P. Berrange     }
198620775d1SDaniel P. Berrange }
199620775d1SDaniel P. Berrange 
200954ee55bSGerd Hoffmann static void ps2_reset_queue(PS2State *s)
201954ee55bSGerd Hoffmann {
202954ee55bSGerd Hoffmann     PS2Queue *q = &s->queue;
203954ee55bSGerd Hoffmann 
204954ee55bSGerd Hoffmann     q->rptr = 0;
205954ee55bSGerd Hoffmann     q->wptr = 0;
2069e24b2ddSVolker Rümelin     q->cwptr = -1;
207954ee55bSGerd Hoffmann     q->count = 0;
208954ee55bSGerd Hoffmann }
209954ee55bSGerd Hoffmann 
2102a6505b0SSven Schnelle int ps2_queue_empty(PS2State *s)
2112a6505b0SSven Schnelle {
2122a6505b0SSven Schnelle     return s->queue.count == 0;
2132a6505b0SSven Schnelle }
2142a6505b0SSven Schnelle 
2157abe7eb2SGeoffrey McRae void ps2_queue_noirq(PS2State *s, int b)
2160e43e99cSbellard {
2170e43e99cSbellard     PS2Queue *q = &s->queue;
2180e43e99cSbellard 
2199e24b2ddSVolker Rümelin     if (q->count >= PS2_QUEUE_SIZE) {
2200e43e99cSbellard         return;
2217abe7eb2SGeoffrey McRae     }
2227abe7eb2SGeoffrey McRae 
2230e43e99cSbellard     q->data[q->wptr] = b;
22447db2432SVolker Rümelin     if (++q->wptr == PS2_BUFFER_SIZE) {
2250e43e99cSbellard         q->wptr = 0;
22647db2432SVolker Rümelin     }
2270e43e99cSbellard     q->count++;
2287abe7eb2SGeoffrey McRae }
2297abe7eb2SGeoffrey McRae 
2307abe7eb2SGeoffrey McRae void ps2_raise_irq(PS2State *s)
2317abe7eb2SGeoffrey McRae {
2327abe7eb2SGeoffrey McRae     s->update_irq(s->update_arg, 1);
2337abe7eb2SGeoffrey McRae }
2347abe7eb2SGeoffrey McRae 
2357abe7eb2SGeoffrey McRae void ps2_queue(PS2State *s, int b)
2367abe7eb2SGeoffrey McRae {
2377704bb02SVolker Rümelin     if (PS2_QUEUE_SIZE - s->queue.count < 1) {
2387704bb02SVolker Rümelin         return;
2397704bb02SVolker Rümelin     }
2407704bb02SVolker Rümelin 
2417abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b);
24296376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2437abe7eb2SGeoffrey McRae }
2447abe7eb2SGeoffrey McRae 
2457abe7eb2SGeoffrey McRae void ps2_queue_2(PS2State *s, int b1, int b2)
2467abe7eb2SGeoffrey McRae {
2477abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 2) {
2487abe7eb2SGeoffrey McRae         return;
2497abe7eb2SGeoffrey McRae     }
2507abe7eb2SGeoffrey McRae 
2517abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2527abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
25396376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2547abe7eb2SGeoffrey McRae }
2557abe7eb2SGeoffrey McRae 
2567abe7eb2SGeoffrey McRae void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
2577abe7eb2SGeoffrey McRae {
2587abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 3) {
2597abe7eb2SGeoffrey McRae         return;
2607abe7eb2SGeoffrey McRae     }
2617abe7eb2SGeoffrey McRae 
2627abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2637abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2647abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
26596376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2667abe7eb2SGeoffrey McRae }
2677abe7eb2SGeoffrey McRae 
2687abe7eb2SGeoffrey McRae void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
2697abe7eb2SGeoffrey McRae {
2707abe7eb2SGeoffrey McRae     if (PS2_QUEUE_SIZE - s->queue.count < 4) {
2717abe7eb2SGeoffrey McRae         return;
2727abe7eb2SGeoffrey McRae     }
2737abe7eb2SGeoffrey McRae 
2747abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b1);
2757abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b2);
2767abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b3);
2777abe7eb2SGeoffrey McRae     ps2_queue_noirq(s, b4);
27896376ab1SPhilippe Mathieu-Daudé     ps2_raise_irq(s);
2790e43e99cSbellard }
2800e43e99cSbellard 
2819e24b2ddSVolker Rümelin static void ps2_cqueue_data(PS2Queue *q, int b)
2829e24b2ddSVolker Rümelin {
2839e24b2ddSVolker Rümelin     q->data[q->cwptr] = b;
2849e24b2ddSVolker Rümelin     if (++q->cwptr >= PS2_BUFFER_SIZE) {
2859e24b2ddSVolker Rümelin         q->cwptr = 0;
2869e24b2ddSVolker Rümelin     }
2879e24b2ddSVolker Rümelin     q->count++;
2889e24b2ddSVolker Rümelin }
2899e24b2ddSVolker Rümelin 
2909e24b2ddSVolker Rümelin static void ps2_cqueue_1(PS2State *s, int b1)
2919e24b2ddSVolker Rümelin {
2929e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
2939e24b2ddSVolker Rümelin 
2949e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 1) & (PS2_BUFFER_SIZE - 1);
2959e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
2969e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
2979e24b2ddSVolker Rümelin     ps2_raise_irq(s);
2989e24b2ddSVolker Rümelin }
2999e24b2ddSVolker Rümelin 
3009e24b2ddSVolker Rümelin static void ps2_cqueue_2(PS2State *s, int b1, int b2)
3019e24b2ddSVolker Rümelin {
3029e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
3039e24b2ddSVolker Rümelin 
3049e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 2) & (PS2_BUFFER_SIZE - 1);
3059e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
3069e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
3079e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
3089e24b2ddSVolker Rümelin     ps2_raise_irq(s);
3099e24b2ddSVolker Rümelin }
3109e24b2ddSVolker Rümelin 
3119e24b2ddSVolker Rümelin static void ps2_cqueue_3(PS2State *s, int b1, int b2, int b3)
3129e24b2ddSVolker Rümelin {
3139e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
3149e24b2ddSVolker Rümelin 
3159e24b2ddSVolker Rümelin     q->rptr = (q->rptr - 3) & (PS2_BUFFER_SIZE - 1);
3169e24b2ddSVolker Rümelin     q->cwptr = q->rptr;
3179e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b1);
3189e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b2);
3199e24b2ddSVolker Rümelin     ps2_cqueue_data(q, b3);
3209e24b2ddSVolker Rümelin     ps2_raise_irq(s);
3219e24b2ddSVolker Rümelin }
3229e24b2ddSVolker Rümelin 
3239e24b2ddSVolker Rümelin static void ps2_cqueue_reset(PS2State *s)
3249e24b2ddSVolker Rümelin {
3259e24b2ddSVolker Rümelin     PS2Queue *q = &s->queue;
3269e24b2ddSVolker Rümelin     int ccount;
3279e24b2ddSVolker Rümelin 
3289e24b2ddSVolker Rümelin     if (q->cwptr == -1) {
3299e24b2ddSVolker Rümelin         return;
3309e24b2ddSVolker Rümelin     }
3319e24b2ddSVolker Rümelin 
3329e24b2ddSVolker Rümelin     ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
3339e24b2ddSVolker Rümelin     q->count -= ccount;
3349e24b2ddSVolker Rümelin     q->rptr = q->cwptr;
3359e24b2ddSVolker Rümelin     q->cwptr = -1;
3369e24b2ddSVolker Rümelin }
3379e24b2ddSVolker Rümelin 
33857d5c005SHervé Poussineau /* keycode is the untranslated scancode in the current scancode set. */
3390e43e99cSbellard static void ps2_put_keycode(void *opaque, int keycode)
3400e43e99cSbellard {
341f94f5d71Spbrook     PS2KbdState *s = opaque;
3428f84e53cSMark Cave-Ayland     PS2State *ps = PS2_DEVICE(s);
343e7d93956Saurel32 
3445edab03dSDon Koch     trace_ps2_put_keycode(opaque, keycode);
345fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
34657d5c005SHervé Poussineau 
34757d5c005SHervé Poussineau     if (s->translate) {
34857d5c005SHervé Poussineau         if (keycode == 0xf0) {
34957d5c005SHervé Poussineau             s->need_high_bit = true;
35057d5c005SHervé Poussineau         } else if (s->need_high_bit) {
3518f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode] | 0x80);
35257d5c005SHervé Poussineau             s->need_high_bit = false;
35357d5c005SHervé Poussineau         } else {
3548f84e53cSMark Cave-Ayland             ps2_queue(ps, translate_table[keycode]);
3557096a96dSRoy Tam         }
35657d5c005SHervé Poussineau     } else {
3578f84e53cSMark Cave-Ayland         ps2_queue(ps, keycode);
3580e43e99cSbellard     }
35957d5c005SHervé Poussineau }
3600e43e99cSbellard 
36166e6536eSGerd Hoffmann static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
36266e6536eSGerd Hoffmann                                InputEvent *evt)
36366e6536eSGerd Hoffmann {
36466e6536eSGerd Hoffmann     PS2KbdState *s = (PS2KbdState *)dev;
36532bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
3668c10e0baSHervé Poussineau     int qcode;
367ab8f9d49SDaniel P. Berrange     uint16_t keycode = 0;
368620775d1SDaniel P. Berrange     int mod;
36966e6536eSGerd Hoffmann 
370143c04c7SGeoffrey McRae     /* do not process events while disabled to prevent stream corruption */
371143c04c7SGeoffrey McRae     if (!s->scan_enabled) {
372143c04c7SGeoffrey McRae         return;
373143c04c7SGeoffrey McRae     }
374143c04c7SGeoffrey McRae 
375fb064112SDaniel Henrique Barboza     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
3768c10e0baSHervé Poussineau     assert(evt->type == INPUT_EVENT_KIND_KEY);
3778c10e0baSHervé Poussineau     qcode = qemu_input_key_value_to_qcode(key->key);
37857d5c005SHervé Poussineau 
379620775d1SDaniel P. Berrange     mod = ps2_modifier_bit(qcode);
380644f66bfSDaniel P. Berrangé     trace_ps2_keyboard_event(s, qcode, key->down, mod,
381644f66bfSDaniel P. Berrangé                              s->modifiers, s->scancode_set, s->translate);
382620775d1SDaniel P. Berrange     if (key->down) {
383620775d1SDaniel P. Berrange         s->modifiers |= mod;
384620775d1SDaniel P. Berrange     } else {
385620775d1SDaniel P. Berrange         s->modifiers &= ~mod;
386620775d1SDaniel P. Berrange     }
387620775d1SDaniel P. Berrange 
3888c10e0baSHervé Poussineau     if (s->scancode_set == 1) {
3898c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
39029fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
39129fd23a5SDaniel P. Berrange                 if (key->down) {
39229fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
39329fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x46);
39429fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
39529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xc6);
39629fd23a5SDaniel P. Berrange                 }
39729fd23a5SDaniel P. Berrange             } else {
3988c10e0baSHervé Poussineau                 if (key->down) {
3998c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4008c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x1d);
4018c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x45);
402927f0425SDaniel P. Berrange                     ps2_put_keycode(s, 0xe1);
4038c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x9d);
4048c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xc5);
4058c10e0baSHervé Poussineau                 }
40629fd23a5SDaniel P. Berrange             }
4078c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
408620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
409620775d1SDaniel P. Berrange                 if (key->down) {
410620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
411620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
412620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
413620775d1SDaniel P. Berrange                 } else {
414620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
415620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
416620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
417620775d1SDaniel P. Berrange                 }
418620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
419620775d1SDaniel P. Berrange                 if (key->down) {
420620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
421620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
422620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
423620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
424620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x54);
425620775d1SDaniel P. Berrange                 } else {
426620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xd4);
427620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
428620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xb8);
429620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
430620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x38);
431620775d1SDaniel P. Berrange                 }
4328f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
4338f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
4348f63458fSDaniel P. Berrange                 if (key->down) {
4358f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4368f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x37);
4378f63458fSDaniel P. Berrange                 } else {
4388f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
4398f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xb7);
4408f63458fSDaniel P. Berrange                 }
441620775d1SDaniel P. Berrange             } else {
4428c10e0baSHervé Poussineau                 if (key->down) {
4438c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4448c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x2a);
4458c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4468c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x37);
4478c10e0baSHervé Poussineau                 } else {
4488c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4498c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xb7);
4508c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
4518c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xaa);
4528c10e0baSHervé Poussineau                 }
453620775d1SDaniel P. Berrange             }
4548c10e0baSHervé Poussineau         } else {
455545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset1_len) {
456ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset1[qcode];
457545e5cf8SMark Cave-Ayland             }
4588c10e0baSHervé Poussineau             if (keycode) {
4598c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
4608c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
4618c10e0baSHervé Poussineau                 }
4628c10e0baSHervé Poussineau                 if (!key->down) {
4638c10e0baSHervé Poussineau                     keycode |= 0x80;
4648c10e0baSHervé Poussineau                 }
4658c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
4668c10e0baSHervé Poussineau             } else {
467ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
468ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
4698c10e0baSHervé Poussineau             }
4708c10e0baSHervé Poussineau         }
4718c10e0baSHervé Poussineau     } else if (s->scancode_set == 2) {
4728c10e0baSHervé Poussineau         if (qcode == Q_KEY_CODE_PAUSE) {
47329fd23a5SDaniel P. Berrange             if (s->modifiers & (MOD_CTRL_L | MOD_CTRL_R)) {
47429fd23a5SDaniel P. Berrange                 if (key->down) {
47529fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
47629fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
47729fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
47829fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
47929fd23a5SDaniel P. Berrange                     ps2_put_keycode(s, 0x7e);
48029fd23a5SDaniel P. Berrange                 }
48129fd23a5SDaniel P. Berrange             } else {
4828c10e0baSHervé Poussineau                 if (key->down) {
4838c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4848c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4858c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4868c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe1);
4878c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4888c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x14);
4898c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
4908c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x77);
4918c10e0baSHervé Poussineau                 }
49229fd23a5SDaniel P. Berrange             }
4938c10e0baSHervé Poussineau         } else if (qcode == Q_KEY_CODE_PRINT) {
494620775d1SDaniel P. Berrange             if (s->modifiers & MOD_ALT_L) {
495620775d1SDaniel P. Berrange                 if (key->down) {
496620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
497620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
498620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
499620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
500620775d1SDaniel P. Berrange                 } else {
501620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
502620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
503620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
504620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
505620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
506620775d1SDaniel P. Berrange                 }
507620775d1SDaniel P. Berrange             } else if (s->modifiers & MOD_ALT_R) {
508620775d1SDaniel P. Berrange                 if (key->down) {
509620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
510620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
511620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
512620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
513620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
514620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
515620775d1SDaniel P. Berrange                 } else {
516620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
517620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x84);
518620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
519620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
520620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
521620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
522620775d1SDaniel P. Berrange                     ps2_put_keycode(s, 0x11);
523620775d1SDaniel P. Berrange                 }
5248f63458fSDaniel P. Berrange             } else if (s->modifiers & (MOD_SHIFT_L | MOD_CTRL_L |
5258f63458fSDaniel P. Berrange                                        MOD_SHIFT_R | MOD_CTRL_R)) {
5268f63458fSDaniel P. Berrange                 if (key->down) {
5278f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
5288f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
5298f63458fSDaniel P. Berrange                 } else {
5308f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xe0);
5318f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0xf0);
5328f63458fSDaniel P. Berrange                     ps2_put_keycode(s, 0x7c);
5338f63458fSDaniel P. Berrange                 }
534620775d1SDaniel P. Berrange             } else {
5358c10e0baSHervé Poussineau                 if (key->down) {
5368c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5378c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
5388c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5398c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
5408c10e0baSHervé Poussineau                 } else {
5418c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5428c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5438c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x7c);
5448c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xe0);
5458c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5468c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0x12);
5478c10e0baSHervé Poussineau                 }
548620775d1SDaniel P. Berrange             }
5498c10e0baSHervé Poussineau         } else {
550545e5cf8SMark Cave-Ayland             if (qcode < qemu_input_map_qcode_to_atset2_len) {
551ab8f9d49SDaniel P. Berrange                 keycode = qemu_input_map_qcode_to_atset2[qcode];
552545e5cf8SMark Cave-Ayland             }
5538c10e0baSHervé Poussineau             if (keycode) {
5548c10e0baSHervé Poussineau                 if (keycode & 0xff00) {
5558c10e0baSHervé Poussineau                     ps2_put_keycode(s, keycode >> 8);
5568c10e0baSHervé Poussineau                 }
5578c10e0baSHervé Poussineau                 if (!key->down) {
5588c10e0baSHervé Poussineau                     ps2_put_keycode(s, 0xf0);
5598c10e0baSHervé Poussineau                 }
5608c10e0baSHervé Poussineau                 ps2_put_keycode(s, keycode & 0xff);
56157d5c005SHervé Poussineau             } else {
562ec044a80SHervé Poussineau                 qemu_log_mask(LOG_UNIMP,
563ec044a80SHervé Poussineau                               "ps2: ignoring key with qcode %d\n", qcode);
56457d5c005SHervé Poussineau             }
56557d5c005SHervé Poussineau         }
56657d5c005SHervé Poussineau     } else if (s->scancode_set == 3) {
567545e5cf8SMark Cave-Ayland         if (qcode < qemu_input_map_qcode_to_atset3_len) {
568ab8f9d49SDaniel P. Berrange             keycode = qemu_input_map_qcode_to_atset3[qcode];
569545e5cf8SMark Cave-Ayland         }
5708c10e0baSHervé Poussineau         if (keycode) {
5718c10e0baSHervé Poussineau             /* FIXME: break code should be configured on a key by key basis */
5728c10e0baSHervé Poussineau             if (!key->down) {
5738c10e0baSHervé Poussineau                 ps2_put_keycode(s, 0xf0);
57457d5c005SHervé Poussineau             }
57557d5c005SHervé Poussineau             ps2_put_keycode(s, keycode);
5768c10e0baSHervé Poussineau         } else {
577ec044a80SHervé Poussineau             qemu_log_mask(LOG_UNIMP,
578ec044a80SHervé Poussineau                           "ps2: ignoring key with qcode %d\n", qcode);
5798c10e0baSHervé Poussineau         }
58066e6536eSGerd Hoffmann     }
58166e6536eSGerd Hoffmann }
58266e6536eSGerd Hoffmann 
5838498bb8dSGerd Hoffmann uint32_t ps2_read_data(PS2State *s)
5840e43e99cSbellard {
5850e43e99cSbellard     PS2Queue *q;
5860e43e99cSbellard     int val, index;
5870e43e99cSbellard 
5888498bb8dSGerd Hoffmann     trace_ps2_read_data(s);
5890e43e99cSbellard     q = &s->queue;
5900e43e99cSbellard     if (q->count == 0) {
591545e5cf8SMark Cave-Ayland         /*
592545e5cf8SMark Cave-Ayland          * NOTE: if no data left, we return the last keyboard one
593545e5cf8SMark Cave-Ayland          * (needed for EMM386)
594545e5cf8SMark Cave-Ayland          */
5950e43e99cSbellard         /* XXX: need a timer to do things correctly */
5960e43e99cSbellard         index = q->rptr - 1;
59747db2432SVolker Rümelin         if (index < 0) {
59847db2432SVolker Rümelin             index = PS2_BUFFER_SIZE - 1;
59947db2432SVolker Rümelin         }
6000e43e99cSbellard         val = q->data[index];
6010e43e99cSbellard     } else {
6020e43e99cSbellard         val = q->data[q->rptr];
60347db2432SVolker Rümelin         if (++q->rptr == PS2_BUFFER_SIZE) {
6040e43e99cSbellard             q->rptr = 0;
60547db2432SVolker Rümelin         }
6060e43e99cSbellard         q->count--;
6079e24b2ddSVolker Rümelin         if (q->rptr == q->cwptr) {
6089e24b2ddSVolker Rümelin             /* command reply queue is empty */
6099e24b2ddSVolker Rümelin             q->cwptr = -1;
6109e24b2ddSVolker Rümelin         }
6110e43e99cSbellard         /* reading deasserts IRQ */
6120e43e99cSbellard         s->update_irq(s->update_arg, 0);
6130e43e99cSbellard         /* reassert IRQs if data left */
614cec32524SVolker Rümelin         if (q->count) {
615cec32524SVolker Rümelin             s->update_irq(s->update_arg, 1);
616cec32524SVolker Rümelin         }
6170e43e99cSbellard     }
6180e43e99cSbellard     return val;
6190e43e99cSbellard }
6200e43e99cSbellard 
6217f540ab5SChristophe Fergeau static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
6227f540ab5SChristophe Fergeau {
6235edab03dSDon Koch     trace_ps2_set_ledstate(s, ledstate);
6247f540ab5SChristophe Fergeau     s->ledstate = ledstate;
6257f540ab5SChristophe Fergeau     kbd_put_ledstate(ledstate);
6267f540ab5SChristophe Fergeau }
6277f540ab5SChristophe Fergeau 
6280e43e99cSbellard static void ps2_reset_keyboard(PS2KbdState *s)
6290e43e99cSbellard {
6308f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
6318f84e53cSMark Cave-Ayland 
6325edab03dSDon Koch     trace_ps2_reset_keyboard(s);
6330e43e99cSbellard     s->scan_enabled = 1;
634e7d93956Saurel32     s->scancode_set = 2;
6358f84e53cSMark Cave-Ayland     ps2_reset_queue(ps2);
6367f540ab5SChristophe Fergeau     ps2_set_ledstate(s, 0);
6370e43e99cSbellard }
6380e43e99cSbellard 
6390e43e99cSbellard void ps2_write_keyboard(void *opaque, int val)
6400e43e99cSbellard {
6410e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
6428f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
6430e43e99cSbellard 
6445edab03dSDon Koch     trace_ps2_write_keyboard(opaque, val);
6458f84e53cSMark Cave-Ayland     ps2_cqueue_reset(ps2);
6468f84e53cSMark Cave-Ayland     switch (ps2->write_cmd) {
6470e43e99cSbellard     default:
6480e43e99cSbellard     case -1:
6490e43e99cSbellard         switch (val) {
6500e43e99cSbellard         case 0x00:
6518f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6520e43e99cSbellard             break;
6530e43e99cSbellard         case 0x05:
6548f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
6550e43e99cSbellard             break;
6560e43e99cSbellard         case KBD_CMD_GET_ID:
657e7d93956Saurel32             /* We emulate a MF2 AT keyboard here */
6588f84e53cSMark Cave-Ayland             ps2_cqueue_3(ps2, KBD_REPLY_ACK, KBD_REPLY_ID,
6599e24b2ddSVolker Rümelin                          s->translate ? 0x41 : 0x83);
6600e43e99cSbellard             break;
6610e43e99cSbellard         case KBD_CMD_ECHO:
6628f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_CMD_ECHO);
6630e43e99cSbellard             break;
6640e43e99cSbellard         case KBD_CMD_ENABLE:
6650e43e99cSbellard             s->scan_enabled = 1;
6668f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6670e43e99cSbellard             break;
668e7d93956Saurel32         case KBD_CMD_SCANCODE:
6690e43e99cSbellard         case KBD_CMD_SET_LEDS:
6700e43e99cSbellard         case KBD_CMD_SET_RATE:
671c56b6209SSven Schnelle         case KBD_CMD_SET_MAKE_BREAK:
6728f84e53cSMark Cave-Ayland             ps2->write_cmd = val;
6738f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6740e43e99cSbellard             break;
6750e43e99cSbellard         case KBD_CMD_RESET_DISABLE:
6760e43e99cSbellard             ps2_reset_keyboard(s);
6770e43e99cSbellard             s->scan_enabled = 0;
6788f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6790e43e99cSbellard             break;
6800e43e99cSbellard         case KBD_CMD_RESET_ENABLE:
6810e43e99cSbellard             ps2_reset_keyboard(s);
6820e43e99cSbellard             s->scan_enabled = 1;
6838f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
6840e43e99cSbellard             break;
6850e43e99cSbellard         case KBD_CMD_RESET:
6860e43e99cSbellard             ps2_reset_keyboard(s);
6878f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2,
6887abe7eb2SGeoffrey McRae                          KBD_REPLY_ACK,
6897abe7eb2SGeoffrey McRae                          KBD_REPLY_POR);
6900e43e99cSbellard             break;
691c56b6209SSven Schnelle         case KBD_CMD_SET_TYPEMATIC:
6928f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
693c56b6209SSven Schnelle             break;
6940e43e99cSbellard         default:
6958f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
6960e43e99cSbellard             break;
6970e43e99cSbellard         }
6980e43e99cSbellard         break;
699c56b6209SSven Schnelle     case KBD_CMD_SET_MAKE_BREAK:
7008f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
7018f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
702c56b6209SSven Schnelle         break;
703e7d93956Saurel32     case KBD_CMD_SCANCODE:
704e7d93956Saurel32         if (val == 0) {
7058f84e53cSMark Cave-Ayland             ps2_cqueue_2(ps2, KBD_REPLY_ACK, s->translate ?
7069e24b2ddSVolker Rümelin                 translate_table[s->scancode_set] : s->scancode_set);
7074df23b64SHervé Poussineau         } else if (val >= 1 && val <= 3) {
708e7d93956Saurel32             s->scancode_set = val;
7098f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_ACK);
7104df23b64SHervé Poussineau         } else {
7118f84e53cSMark Cave-Ayland             ps2_cqueue_1(ps2, KBD_REPLY_RESEND);
712e7d93956Saurel32         }
7138f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
714e7d93956Saurel32         break;
7150e43e99cSbellard     case KBD_CMD_SET_LEDS:
7167f540ab5SChristophe Fergeau         ps2_set_ledstate(s, val);
7178f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
7188f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
7190e43e99cSbellard         break;
7200e43e99cSbellard     case KBD_CMD_SET_RATE:
7218f84e53cSMark Cave-Ayland         ps2_cqueue_1(ps2, KBD_REPLY_ACK);
7228f84e53cSMark Cave-Ayland         ps2->write_cmd = -1;
7230e43e99cSbellard         break;
7240e43e99cSbellard     }
7250e43e99cSbellard }
7260e43e99cSbellard 
727545e5cf8SMark Cave-Ayland /*
728545e5cf8SMark Cave-Ayland  * Set the scancode translation mode.
729545e5cf8SMark Cave-Ayland  * 0 = raw scancodes.
730545e5cf8SMark Cave-Ayland  * 1 = translated scancodes (used by qemu internally).
731545e5cf8SMark Cave-Ayland  */
732f94f5d71Spbrook 
733f94f5d71Spbrook void ps2_keyboard_set_translation(void *opaque, int mode)
734f94f5d71Spbrook {
735f94f5d71Spbrook     PS2KbdState *s = (PS2KbdState *)opaque;
7365edab03dSDon Koch     trace_ps2_keyboard_set_translation(opaque, mode);
737f94f5d71Spbrook     s->translate = mode;
738f94f5d71Spbrook }
739f94f5d71Spbrook 
7407abe7eb2SGeoffrey McRae static int ps2_mouse_send_packet(PS2MouseState *s)
7410e43e99cSbellard {
742*2d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
74376968101SVolker Rümelin     /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
74476968101SVolker Rümelin     const int needed = s->mouse_type ? 4 : 3;
7450e43e99cSbellard     unsigned int b;
74664ebbb7dSDmitry Petrov     int dx1, dy1, dz1, dw1;
7470e43e99cSbellard 
748*2d135409SMark Cave-Ayland     if (PS2_QUEUE_SIZE - ps2->queue.count < needed) {
7497abe7eb2SGeoffrey McRae         return 0;
7507abe7eb2SGeoffrey McRae     }
7517abe7eb2SGeoffrey McRae 
7520e43e99cSbellard     dx1 = s->mouse_dx;
7530e43e99cSbellard     dy1 = s->mouse_dy;
7540e43e99cSbellard     dz1 = s->mouse_dz;
75564ebbb7dSDmitry Petrov     dw1 = s->mouse_dw;
7560e43e99cSbellard     /* XXX: increase range to 8 bits ? */
757545e5cf8SMark Cave-Ayland     if (dx1 > 127) {
7580e43e99cSbellard         dx1 = 127;
759545e5cf8SMark Cave-Ayland     } else if (dx1 < -127) {
7600e43e99cSbellard         dx1 = -127;
761545e5cf8SMark Cave-Ayland     }
762545e5cf8SMark Cave-Ayland     if (dy1 > 127) {
7630e43e99cSbellard         dy1 = 127;
764545e5cf8SMark Cave-Ayland     } else if (dy1 < -127) {
7650e43e99cSbellard         dy1 = -127;
766545e5cf8SMark Cave-Ayland     }
7670e43e99cSbellard     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
768*2d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, b);
769*2d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, dx1 & 0xff);
770*2d135409SMark Cave-Ayland     ps2_queue_noirq(ps2, dy1 & 0xff);
7710e43e99cSbellard     /* extra byte for IMPS/2 or IMEX */
7720e43e99cSbellard     switch (s->mouse_type) {
7730e43e99cSbellard     default:
77464ebbb7dSDmitry Petrov         /* Just ignore the wheels if not supported */
77564ebbb7dSDmitry Petrov         s->mouse_dz = 0;
77664ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7770e43e99cSbellard         break;
7780e43e99cSbellard     case 3:
779545e5cf8SMark Cave-Ayland         if (dz1 > 127) {
7800e43e99cSbellard             dz1 = 127;
781545e5cf8SMark Cave-Ayland         } else if (dz1 < -127) {
7820e43e99cSbellard             dz1 = -127;
783545e5cf8SMark Cave-Ayland         }
784*2d135409SMark Cave-Ayland         ps2_queue_noirq(ps2, dz1 & 0xff);
78564ebbb7dSDmitry Petrov         s->mouse_dz -= dz1;
78664ebbb7dSDmitry Petrov         s->mouse_dw = 0;
7870e43e99cSbellard         break;
7880e43e99cSbellard     case 4:
78964ebbb7dSDmitry Petrov         /*
79064ebbb7dSDmitry Petrov          * This matches what the Linux kernel expects for exps/2 in
79164ebbb7dSDmitry Petrov          * drivers/input/mouse/psmouse-base.c. Note, if you happen to
79264ebbb7dSDmitry Petrov          * press/release the 4th or 5th buttons at the same moment as a
79364ebbb7dSDmitry Petrov          * horizontal wheel scroll, those button presses will get lost. I'm not
79464ebbb7dSDmitry Petrov          * sure what to do about that, since by this point we don't know
79564ebbb7dSDmitry Petrov          * whether those buttons actually changed state.
79664ebbb7dSDmitry Petrov          */
79764ebbb7dSDmitry Petrov         if (dw1 != 0) {
79864ebbb7dSDmitry Petrov             if (dw1 > 31) {
79964ebbb7dSDmitry Petrov                 dw1 = 31;
80064ebbb7dSDmitry Petrov             } else if (dw1 < -31) {
80164ebbb7dSDmitry Petrov                 dw1 = -31;
80264ebbb7dSDmitry Petrov             }
80364ebbb7dSDmitry Petrov 
80464ebbb7dSDmitry Petrov             /*
80564ebbb7dSDmitry Petrov              * linux kernel expects first 6 bits to represent the value
80664ebbb7dSDmitry Petrov              * for horizontal scroll
80764ebbb7dSDmitry Petrov              */
80864ebbb7dSDmitry Petrov             b = (dw1 & 0x3f) | 0x40;
80964ebbb7dSDmitry Petrov             s->mouse_dw -= dw1;
81064ebbb7dSDmitry Petrov         } else {
81164ebbb7dSDmitry Petrov             if (dz1 > 7) {
8120e43e99cSbellard                 dz1 = 7;
81364ebbb7dSDmitry Petrov             } else if (dz1 < -7) {
8140e43e99cSbellard                 dz1 = -7;
81564ebbb7dSDmitry Petrov             }
81664ebbb7dSDmitry Petrov 
8170e43e99cSbellard             b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
81864ebbb7dSDmitry Petrov             s->mouse_dz -= dz1;
81964ebbb7dSDmitry Petrov         }
820*2d135409SMark Cave-Ayland         ps2_queue_noirq(ps2, b);
8210e43e99cSbellard         break;
8220e43e99cSbellard     }
8230e43e99cSbellard 
824*2d135409SMark Cave-Ayland     ps2_raise_irq(ps2);
8257abe7eb2SGeoffrey McRae 
8265edab03dSDon Koch     trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
8270e43e99cSbellard     /* update deltas */
8280e43e99cSbellard     s->mouse_dx -= dx1;
8290e43e99cSbellard     s->mouse_dy -= dy1;
8307abe7eb2SGeoffrey McRae 
8317abe7eb2SGeoffrey McRae     return 1;
8320e43e99cSbellard }
8330e43e99cSbellard 
8342a766d29SGerd Hoffmann static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
8352a766d29SGerd Hoffmann                             InputEvent *evt)
8360e43e99cSbellard {
8377fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
8388b0caab0SFabian Lesniak         [INPUT_BUTTON_LEFT]   = PS2_MOUSE_BUTTON_LEFT,
8398b0caab0SFabian Lesniak         [INPUT_BUTTON_MIDDLE] = PS2_MOUSE_BUTTON_MIDDLE,
8408b0caab0SFabian Lesniak         [INPUT_BUTTON_RIGHT]  = PS2_MOUSE_BUTTON_RIGHT,
8418b0caab0SFabian Lesniak         [INPUT_BUTTON_SIDE]   = PS2_MOUSE_BUTTON_SIDE,
8428b0caab0SFabian Lesniak         [INPUT_BUTTON_EXTRA]  = PS2_MOUSE_BUTTON_EXTRA,
8432a766d29SGerd Hoffmann     };
8442a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
845b5a1b443SEric Blake     InputMoveEvent *move;
846b5a1b443SEric Blake     InputBtnEvent *btn;
8470e43e99cSbellard 
8480e43e99cSbellard     /* check if deltas are recorded when disabled */
849545e5cf8SMark Cave-Ayland     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
8500e43e99cSbellard         return;
851545e5cf8SMark Cave-Ayland     }
8520e43e99cSbellard 
853568c73a4SEric Blake     switch (evt->type) {
8542a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_REL:
85532bafa8fSEric Blake         move = evt->u.rel.data;
856b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
857b5a1b443SEric Blake             s->mouse_dx += move->value;
858b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
859b5a1b443SEric Blake             s->mouse_dy -= move->value;
8602a766d29SGerd Hoffmann         }
8612a766d29SGerd Hoffmann         break;
8620e43e99cSbellard 
8632a766d29SGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
86432bafa8fSEric Blake         btn = evt->u.btn.data;
865b5a1b443SEric Blake         if (btn->down) {
866b5a1b443SEric Blake             s->mouse_buttons |= bmap[btn->button];
867b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
8682a766d29SGerd Hoffmann                 s->mouse_dz--;
869b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
8702a766d29SGerd Hoffmann                 s->mouse_dz++;
8712a766d29SGerd Hoffmann             }
87264ebbb7dSDmitry Petrov 
87364ebbb7dSDmitry Petrov             if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
87464ebbb7dSDmitry Petrov                 s->mouse_dw--;
87564ebbb7dSDmitry Petrov             } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
87664ebbb7dSDmitry Petrov                 s->mouse_dw++;
87764ebbb7dSDmitry Petrov             }
8782a766d29SGerd Hoffmann         } else {
879b5a1b443SEric Blake             s->mouse_buttons &= ~bmap[btn->button];
8802a766d29SGerd Hoffmann         }
8812a766d29SGerd Hoffmann         break;
8822a766d29SGerd Hoffmann 
8832a766d29SGerd Hoffmann     default:
8842a766d29SGerd Hoffmann         /* keep gcc happy */
8852a766d29SGerd Hoffmann         break;
8862a766d29SGerd Hoffmann     }
887fd214d18SGerd Hoffmann }
888fd214d18SGerd Hoffmann 
8892a766d29SGerd Hoffmann static void ps2_mouse_sync(DeviceState *dev)
8902a766d29SGerd Hoffmann {
8912a766d29SGerd Hoffmann     PS2MouseState *s = (PS2MouseState *)dev;
8922a766d29SGerd Hoffmann 
893143c04c7SGeoffrey McRae     /* do not sync while disabled to prevent stream corruption */
894143c04c7SGeoffrey McRae     if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
895143c04c7SGeoffrey McRae         return;
896143c04c7SGeoffrey McRae     }
897143c04c7SGeoffrey McRae 
8982a766d29SGerd Hoffmann     if (s->mouse_buttons) {
899fb064112SDaniel Henrique Barboza         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
9002a766d29SGerd Hoffmann     }
9012858ab09SGonglei     if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
902545e5cf8SMark Cave-Ayland         /*
903545e5cf8SMark Cave-Ayland          * if not remote, send event. Multiple events are sent if
904545e5cf8SMark Cave-Ayland          * too big deltas
905545e5cf8SMark Cave-Ayland          */
9067abe7eb2SGeoffrey McRae         while (ps2_mouse_send_packet(s)) {
90764ebbb7dSDmitry Petrov             if (s->mouse_dx == 0 && s->mouse_dy == 0
90864ebbb7dSDmitry Petrov                     && s->mouse_dz == 0 && s->mouse_dw == 0) {
9090e43e99cSbellard                 break;
9100e43e99cSbellard             }
9110e43e99cSbellard         }
9120e43e99cSbellard     }
91364ebbb7dSDmitry Petrov }
9140e43e99cSbellard 
915548df2acSths void ps2_mouse_fake_event(void *opaque)
916548df2acSths {
9172a766d29SGerd Hoffmann     PS2MouseState *s = opaque;
9185edab03dSDon Koch     trace_ps2_mouse_fake_event(opaque);
9192a766d29SGerd Hoffmann     s->mouse_dx++;
9202a766d29SGerd Hoffmann     ps2_mouse_sync(opaque);
921548df2acSths }
922548df2acSths 
9230e43e99cSbellard void ps2_write_mouse(void *opaque, int val)
9240e43e99cSbellard {
9250e43e99cSbellard     PS2MouseState *s = (PS2MouseState *)opaque;
926*2d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
9275edab03dSDon Koch 
9285edab03dSDon Koch     trace_ps2_write_mouse(opaque, val);
929*2d135409SMark Cave-Ayland     switch (ps2->write_cmd) {
9300e43e99cSbellard     default:
9310e43e99cSbellard     case -1:
9320e43e99cSbellard         /* mouse command */
9330e43e99cSbellard         if (s->mouse_wrap) {
9340e43e99cSbellard             if (val == AUX_RESET_WRAP) {
9350e43e99cSbellard                 s->mouse_wrap = 0;
936*2d135409SMark Cave-Ayland                 ps2_queue(ps2, AUX_ACK);
9370e43e99cSbellard                 return;
9380e43e99cSbellard             } else if (val != AUX_RESET) {
939*2d135409SMark Cave-Ayland                 ps2_queue(ps2, val);
9400e43e99cSbellard                 return;
9410e43e99cSbellard             }
9420e43e99cSbellard         }
9430e43e99cSbellard         switch (val) {
9440e43e99cSbellard         case AUX_SET_SCALE11:
9450e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
946*2d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9470e43e99cSbellard             break;
9480e43e99cSbellard         case AUX_SET_SCALE21:
9490e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_SCALE21;
950*2d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9510e43e99cSbellard             break;
9520e43e99cSbellard         case AUX_SET_STREAM:
9530e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
954*2d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9550e43e99cSbellard             break;
9560e43e99cSbellard         case AUX_SET_WRAP:
9570e43e99cSbellard             s->mouse_wrap = 1;
958*2d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9590e43e99cSbellard             break;
9600e43e99cSbellard         case AUX_SET_REMOTE:
9610e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_REMOTE;
962*2d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9630e43e99cSbellard             break;
9640e43e99cSbellard         case AUX_GET_TYPE:
965*2d135409SMark Cave-Ayland             ps2_queue_2(ps2,
9667abe7eb2SGeoffrey McRae                 AUX_ACK,
9677abe7eb2SGeoffrey McRae                 s->mouse_type);
9680e43e99cSbellard             break;
9690e43e99cSbellard         case AUX_SET_RES:
9700e43e99cSbellard         case AUX_SET_SAMPLE:
971*2d135409SMark Cave-Ayland             ps2->write_cmd = val;
972*2d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9730e43e99cSbellard             break;
9740e43e99cSbellard         case AUX_GET_SCALE:
975*2d135409SMark Cave-Ayland             ps2_queue_4(ps2,
9767abe7eb2SGeoffrey McRae                 AUX_ACK,
9777abe7eb2SGeoffrey McRae                 s->mouse_status,
9787abe7eb2SGeoffrey McRae                 s->mouse_resolution,
9797abe7eb2SGeoffrey McRae                 s->mouse_sample_rate);
9800e43e99cSbellard             break;
9810e43e99cSbellard         case AUX_POLL:
982*2d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9830e43e99cSbellard             ps2_mouse_send_packet(s);
9840e43e99cSbellard             break;
9850e43e99cSbellard         case AUX_ENABLE_DEV:
9860e43e99cSbellard             s->mouse_status |= MOUSE_STATUS_ENABLED;
987*2d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9880e43e99cSbellard             break;
9890e43e99cSbellard         case AUX_DISABLE_DEV:
9900e43e99cSbellard             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
991*2d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9920e43e99cSbellard             break;
9930e43e99cSbellard         case AUX_SET_DEFAULT:
9940e43e99cSbellard             s->mouse_sample_rate = 100;
9950e43e99cSbellard             s->mouse_resolution = 2;
9960e43e99cSbellard             s->mouse_status = 0;
997*2d135409SMark Cave-Ayland             ps2_queue(ps2, AUX_ACK);
9980e43e99cSbellard             break;
9990e43e99cSbellard         case AUX_RESET:
10000e43e99cSbellard             s->mouse_sample_rate = 100;
10010e43e99cSbellard             s->mouse_resolution = 2;
10020e43e99cSbellard             s->mouse_status = 0;
10030e43e99cSbellard             s->mouse_type = 0;
1004*2d135409SMark Cave-Ayland             ps2_reset_queue(ps2);
1005*2d135409SMark Cave-Ayland             ps2_queue_3(ps2,
10067abe7eb2SGeoffrey McRae                 AUX_ACK,
10077abe7eb2SGeoffrey McRae                 0xaa,
10087abe7eb2SGeoffrey McRae                 s->mouse_type);
10090e43e99cSbellard             break;
10100e43e99cSbellard         default:
10110e43e99cSbellard             break;
10120e43e99cSbellard         }
10130e43e99cSbellard         break;
10140e43e99cSbellard     case AUX_SET_SAMPLE:
10150e43e99cSbellard         s->mouse_sample_rate = val;
10160e43e99cSbellard         /* detect IMPS/2 or IMEX */
10170e43e99cSbellard         switch (s->mouse_detect_state) {
10180e43e99cSbellard         default:
10190e43e99cSbellard         case 0:
1020545e5cf8SMark Cave-Ayland             if (val == 200) {
10210e43e99cSbellard                 s->mouse_detect_state = 1;
1022545e5cf8SMark Cave-Ayland             }
10230e43e99cSbellard             break;
10240e43e99cSbellard         case 1:
1025545e5cf8SMark Cave-Ayland             if (val == 100) {
10260e43e99cSbellard                 s->mouse_detect_state = 2;
1027545e5cf8SMark Cave-Ayland             } else if (val == 200) {
10280e43e99cSbellard                 s->mouse_detect_state = 3;
1029545e5cf8SMark Cave-Ayland             } else {
10300e43e99cSbellard                 s->mouse_detect_state = 0;
1031545e5cf8SMark Cave-Ayland             }
10320e43e99cSbellard             break;
10330e43e99cSbellard         case 2:
1034545e5cf8SMark Cave-Ayland             if (val == 80) {
10350e43e99cSbellard                 s->mouse_type = 3; /* IMPS/2 */
1036545e5cf8SMark Cave-Ayland             }
10370e43e99cSbellard             s->mouse_detect_state = 0;
10380e43e99cSbellard             break;
10390e43e99cSbellard         case 3:
1040545e5cf8SMark Cave-Ayland             if (val == 80) {
10410e43e99cSbellard                 s->mouse_type = 4; /* IMEX */
1042545e5cf8SMark Cave-Ayland             }
10430e43e99cSbellard             s->mouse_detect_state = 0;
10440e43e99cSbellard             break;
10450e43e99cSbellard         }
1046*2d135409SMark Cave-Ayland         ps2_queue(ps2, AUX_ACK);
1047*2d135409SMark Cave-Ayland         ps2->write_cmd = -1;
10480e43e99cSbellard         break;
10490e43e99cSbellard     case AUX_SET_RES:
10500e43e99cSbellard         s->mouse_resolution = val;
1051*2d135409SMark Cave-Ayland         ps2_queue(ps2, AUX_ACK);
1052*2d135409SMark Cave-Ayland         ps2->write_cmd = -1;
10530e43e99cSbellard         break;
10540e43e99cSbellard     }
10550e43e99cSbellard }
10560e43e99cSbellard 
1057ef74679aSDinesh Subhraveti static void ps2_common_reset(PS2State *s)
10580e43e99cSbellard {
10590e43e99cSbellard     s->write_cmd = -1;
1060954ee55bSGerd Hoffmann     ps2_reset_queue(s);
1061deeccef3Saliguori     s->update_irq(s->update_arg, 0);
10620e43e99cSbellard }
10630e43e99cSbellard 
10642858ab09SGonglei static void ps2_common_post_load(PS2State *s)
10652858ab09SGonglei {
10662858ab09SGonglei     PS2Queue *q = &s->queue;
10674e9bddcbSVolker Rümelin     int ccount = 0;
10682858ab09SGonglei 
10694e9bddcbSVolker Rümelin     /* limit the number of queued command replies to PS2_QUEUE_HEADROOM */
10704e9bddcbSVolker Rümelin     if (q->cwptr != -1) {
10714e9bddcbSVolker Rümelin         ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
10724e9bddcbSVolker Rümelin         if (ccount > PS2_QUEUE_HEADROOM) {
10734e9bddcbSVolker Rümelin             ccount = PS2_QUEUE_HEADROOM;
10744e9bddcbSVolker Rümelin         }
1075a1f2ed2aSPavel Dovgalyuk     }
10762858ab09SGonglei 
10774e9bddcbSVolker Rümelin     /* limit the scancode queue size to PS2_QUEUE_SIZE */
10784e9bddcbSVolker Rümelin     if (q->count < ccount) {
10794e9bddcbSVolker Rümelin         q->count = ccount;
10804e9bddcbSVolker Rümelin     } else if (q->count > ccount + PS2_QUEUE_SIZE) {
10814e9bddcbSVolker Rümelin         q->count = ccount + PS2_QUEUE_SIZE;
10824e9bddcbSVolker Rümelin     }
10834e9bddcbSVolker Rümelin 
10844e9bddcbSVolker Rümelin     /* sanitize rptr and recalculate wptr and cwptr */
108547db2432SVolker Rümelin     q->rptr = q->rptr & (PS2_BUFFER_SIZE - 1);
108647db2432SVolker Rümelin     q->wptr = (q->rptr + q->count) & (PS2_BUFFER_SIZE - 1);
10874e9bddcbSVolker Rümelin     q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1;
10882858ab09SGonglei }
10892858ab09SGonglei 
1090ef74679aSDinesh Subhraveti static void ps2_kbd_reset(void *opaque)
1091ef74679aSDinesh Subhraveti {
1092ef74679aSDinesh Subhraveti     PS2KbdState *s = (PS2KbdState *) opaque;
10938f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
1094ef74679aSDinesh Subhraveti 
10955edab03dSDon Koch     trace_ps2_kbd_reset(opaque);
10968f84e53cSMark Cave-Ayland     ps2_common_reset(ps2);
1097d2e550a8SHervé Poussineau     s->scan_enabled = 1;
1098ef74679aSDinesh Subhraveti     s->translate = 0;
1099089adafdSHervé Poussineau     s->scancode_set = 2;
1100620775d1SDaniel P. Berrange     s->modifiers = 0;
1101ef74679aSDinesh Subhraveti }
1102ef74679aSDinesh Subhraveti 
1103ef74679aSDinesh Subhraveti static void ps2_mouse_reset(void *opaque)
1104ef74679aSDinesh Subhraveti {
1105ef74679aSDinesh Subhraveti     PS2MouseState *s = (PS2MouseState *) opaque;
1106*2d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
1107ef74679aSDinesh Subhraveti 
11085edab03dSDon Koch     trace_ps2_mouse_reset(opaque);
1109*2d135409SMark Cave-Ayland     ps2_common_reset(ps2);
1110ef74679aSDinesh Subhraveti     s->mouse_status = 0;
1111ef74679aSDinesh Subhraveti     s->mouse_resolution = 0;
1112ef74679aSDinesh Subhraveti     s->mouse_sample_rate = 0;
1113ef74679aSDinesh Subhraveti     s->mouse_wrap = 0;
1114ef74679aSDinesh Subhraveti     s->mouse_type = 0;
1115ef74679aSDinesh Subhraveti     s->mouse_detect_state = 0;
1116ef74679aSDinesh Subhraveti     s->mouse_dx = 0;
1117ef74679aSDinesh Subhraveti     s->mouse_dy = 0;
1118ef74679aSDinesh Subhraveti     s->mouse_dz = 0;
111964ebbb7dSDmitry Petrov     s->mouse_dw = 0;
1120ef74679aSDinesh Subhraveti     s->mouse_buttons = 0;
1121ef74679aSDinesh Subhraveti }
1122ef74679aSDinesh Subhraveti 
1123b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_common = {
1124b31442c3SJuan Quintela     .name = "PS2 Common State",
1125b31442c3SJuan Quintela     .version_id = 3,
1126b31442c3SJuan Quintela     .minimum_version_id = 2,
1127b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1128b31442c3SJuan Quintela         VMSTATE_INT32(write_cmd, PS2State),
1129b31442c3SJuan Quintela         VMSTATE_INT32(queue.rptr, PS2State),
1130b31442c3SJuan Quintela         VMSTATE_INT32(queue.wptr, PS2State),
1131b31442c3SJuan Quintela         VMSTATE_INT32(queue.count, PS2State),
1132b31442c3SJuan Quintela         VMSTATE_BUFFER(queue.data, PS2State),
1133b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
11347783e9f0Spbrook     }
1135b31442c3SJuan Quintela };
11367783e9f0Spbrook 
11377f540ab5SChristophe Fergeau static bool ps2_keyboard_ledstate_needed(void *opaque)
11387f540ab5SChristophe Fergeau {
11397f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
11407f540ab5SChristophe Fergeau 
11417f540ab5SChristophe Fergeau     return s->ledstate != 0; /* 0 is default state */
11427f540ab5SChristophe Fergeau }
11437f540ab5SChristophe Fergeau 
11447f540ab5SChristophe Fergeau static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
11457f540ab5SChristophe Fergeau {
11467f540ab5SChristophe Fergeau     PS2KbdState *s = opaque;
11477f540ab5SChristophe Fergeau 
11487f540ab5SChristophe Fergeau     kbd_put_ledstate(s->ledstate);
11497f540ab5SChristophe Fergeau     return 0;
11507f540ab5SChristophe Fergeau }
11517f540ab5SChristophe Fergeau 
11527f540ab5SChristophe Fergeau static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
11537f540ab5SChristophe Fergeau     .name = "ps2kbd/ledstate",
11547f540ab5SChristophe Fergeau     .version_id = 3,
11557f540ab5SChristophe Fergeau     .minimum_version_id = 2,
11567f540ab5SChristophe Fergeau     .post_load = ps2_kbd_ledstate_post_load,
11575cd8cadaSJuan Quintela     .needed = ps2_keyboard_ledstate_needed,
11587f540ab5SChristophe Fergeau     .fields = (VMStateField[]) {
11597f540ab5SChristophe Fergeau         VMSTATE_INT32(ledstate, PS2KbdState),
11607f540ab5SChristophe Fergeau         VMSTATE_END_OF_LIST()
11617f540ab5SChristophe Fergeau     }
11627f540ab5SChristophe Fergeau };
11637f540ab5SChristophe Fergeau 
116457d5c005SHervé Poussineau static bool ps2_keyboard_need_high_bit_needed(void *opaque)
116557d5c005SHervé Poussineau {
116657d5c005SHervé Poussineau     PS2KbdState *s = opaque;
116757d5c005SHervé Poussineau     return s->need_high_bit != 0; /* 0 is the usual state */
116857d5c005SHervé Poussineau }
116957d5c005SHervé Poussineau 
117057d5c005SHervé Poussineau static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = {
117157d5c005SHervé Poussineau     .name = "ps2kbd/need_high_bit",
117257d5c005SHervé Poussineau     .version_id = 1,
117357d5c005SHervé Poussineau     .minimum_version_id = 1,
117457d5c005SHervé Poussineau     .needed = ps2_keyboard_need_high_bit_needed,
117557d5c005SHervé Poussineau     .fields = (VMStateField[]) {
117657d5c005SHervé Poussineau         VMSTATE_BOOL(need_high_bit, PS2KbdState),
117757d5c005SHervé Poussineau         VMSTATE_END_OF_LIST()
117857d5c005SHervé Poussineau     }
117957d5c005SHervé Poussineau };
118057d5c005SHervé Poussineau 
11814e9bddcbSVolker Rümelin static bool ps2_keyboard_cqueue_needed(void *opaque)
11824e9bddcbSVolker Rümelin {
11834e9bddcbSVolker Rümelin     PS2KbdState *s = opaque;
11848f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
11854e9bddcbSVolker Rümelin 
11868f84e53cSMark Cave-Ayland     return ps2->queue.cwptr != -1; /* the queue is mostly empty */
11874e9bddcbSVolker Rümelin }
11884e9bddcbSVolker Rümelin 
11894e9bddcbSVolker Rümelin static const VMStateDescription vmstate_ps2_keyboard_cqueue = {
11904e9bddcbSVolker Rümelin     .name = "ps2kbd/command_reply_queue",
11914e9bddcbSVolker Rümelin     .needed = ps2_keyboard_cqueue_needed,
11924e9bddcbSVolker Rümelin     .fields = (VMStateField[]) {
11938f84e53cSMark Cave-Ayland         VMSTATE_INT32(parent_obj.queue.cwptr, PS2KbdState),
11944e9bddcbSVolker Rümelin         VMSTATE_END_OF_LIST()
11954e9bddcbSVolker Rümelin     }
11964e9bddcbSVolker Rümelin };
11974e9bddcbSVolker Rümelin 
1198db596c53SJuan Quintela static int ps2_kbd_post_load(void *opaque, int version_id)
11990e43e99cSbellard {
12000e43e99cSbellard     PS2KbdState *s = (PS2KbdState *)opaque;
12018f84e53cSMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
12020e43e99cSbellard 
1203545e5cf8SMark Cave-Ayland     if (version_id == 2) {
1204e7d93956Saurel32         s->scancode_set = 2;
1205545e5cf8SMark Cave-Ayland     }
12062858ab09SGonglei 
12072858ab09SGonglei     ps2_common_post_load(ps2);
12082858ab09SGonglei 
12090e43e99cSbellard     return 0;
12100e43e99cSbellard }
12110e43e99cSbellard 
1212b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_keyboard = {
1213b31442c3SJuan Quintela     .name = "ps2kbd",
1214b31442c3SJuan Quintela     .version_id = 3,
1215db596c53SJuan Quintela     .minimum_version_id = 2,
1216db596c53SJuan Quintela     .post_load = ps2_kbd_post_load,
1217b31442c3SJuan Quintela     .fields = (VMStateField[]) {
12188f84e53cSMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2KbdState, 0, vmstate_ps2_common,
12198f84e53cSMark Cave-Ayland                        PS2State),
1220b31442c3SJuan Quintela         VMSTATE_INT32(scan_enabled, PS2KbdState),
1221b31442c3SJuan Quintela         VMSTATE_INT32(translate, PS2KbdState),
1222b31442c3SJuan Quintela         VMSTATE_INT32_V(scancode_set, PS2KbdState, 3),
1223b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
12247f540ab5SChristophe Fergeau     },
12255cd8cadaSJuan Quintela     .subsections = (const VMStateDescription * []) {
12265cd8cadaSJuan Quintela         &vmstate_ps2_keyboard_ledstate,
122757d5c005SHervé Poussineau         &vmstate_ps2_keyboard_need_high_bit,
12284e9bddcbSVolker Rümelin         &vmstate_ps2_keyboard_cqueue,
12295cd8cadaSJuan Quintela         NULL
12300e43e99cSbellard     }
1231b31442c3SJuan Quintela };
1232b31442c3SJuan Quintela 
12332858ab09SGonglei static int ps2_mouse_post_load(void *opaque, int version_id)
12342858ab09SGonglei {
12352858ab09SGonglei     PS2MouseState *s = (PS2MouseState *)opaque;
1236*2d135409SMark Cave-Ayland     PS2State *ps2 = PS2_DEVICE(s);
12372858ab09SGonglei 
12382858ab09SGonglei     ps2_common_post_load(ps2);
12392858ab09SGonglei 
12402858ab09SGonglei     return 0;
12412858ab09SGonglei }
12422858ab09SGonglei 
1243b31442c3SJuan Quintela static const VMStateDescription vmstate_ps2_mouse = {
1244b31442c3SJuan Quintela     .name = "ps2mouse",
1245b31442c3SJuan Quintela     .version_id = 2,
1246b31442c3SJuan Quintela     .minimum_version_id = 2,
12472858ab09SGonglei     .post_load = ps2_mouse_post_load,
1248b31442c3SJuan Quintela     .fields = (VMStateField[]) {
1249*2d135409SMark Cave-Ayland         VMSTATE_STRUCT(parent_obj, PS2MouseState, 0, vmstate_ps2_common,
1250*2d135409SMark Cave-Ayland                        PS2State),
1251b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_status, PS2MouseState),
1252b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_resolution, PS2MouseState),
1253b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
1254b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_wrap, PS2MouseState),
1255b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_type, PS2MouseState),
1256b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
1257b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dx, PS2MouseState),
1258b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dy, PS2MouseState),
1259b31442c3SJuan Quintela         VMSTATE_INT32(mouse_dz, PS2MouseState),
1260b31442c3SJuan Quintela         VMSTATE_UINT8(mouse_buttons, PS2MouseState),
1261b31442c3SJuan Quintela         VMSTATE_END_OF_LIST()
1262b31442c3SJuan Quintela     }
1263b31442c3SJuan Quintela };
12640e43e99cSbellard 
126566e6536eSGerd Hoffmann static QemuInputHandler ps2_keyboard_handler = {
126666e6536eSGerd Hoffmann     .name  = "QEMU PS/2 Keyboard",
126766e6536eSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
126866e6536eSGerd Hoffmann     .event = ps2_keyboard_event,
126966e6536eSGerd Hoffmann };
127066e6536eSGerd Hoffmann 
12710e43e99cSbellard void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
12720e43e99cSbellard {
12738f84e53cSMark Cave-Ayland     DeviceState *dev;
12748f84e53cSMark Cave-Ayland     PS2KbdState *s;
12758f84e53cSMark Cave-Ayland     PS2State *ps2;
12768f84e53cSMark Cave-Ayland 
12778f84e53cSMark Cave-Ayland     dev = qdev_new(TYPE_PS2_KBD_DEVICE);
12788f84e53cSMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
12798f84e53cSMark Cave-Ayland     s = PS2_KBD_DEVICE(dev);
12808f84e53cSMark Cave-Ayland     ps2 = PS2_DEVICE(s);
12810e43e99cSbellard 
12825edab03dSDon Koch     trace_ps2_kbd_init(s);
12838f84e53cSMark Cave-Ayland     ps2->update_irq = update_irq;
12848f84e53cSMark Cave-Ayland     ps2->update_arg = update_arg;
1285e7d93956Saurel32     s->scancode_set = 2;
12860be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
128766e6536eSGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
128866e6536eSGerd Hoffmann                                 &ps2_keyboard_handler);
1289ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_kbd_reset, s);
12900e43e99cSbellard     return s;
12910e43e99cSbellard }
12920e43e99cSbellard 
12932a766d29SGerd Hoffmann static QemuInputHandler ps2_mouse_handler = {
12942a766d29SGerd Hoffmann     .name  = "QEMU PS/2 Mouse",
12952a766d29SGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
12962a766d29SGerd Hoffmann     .event = ps2_mouse_event,
12972a766d29SGerd Hoffmann     .sync  = ps2_mouse_sync,
12982a766d29SGerd Hoffmann };
12992a766d29SGerd Hoffmann 
13000e43e99cSbellard void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
13010e43e99cSbellard {
1302*2d135409SMark Cave-Ayland     DeviceState *dev;
1303*2d135409SMark Cave-Ayland     PS2MouseState *s;
1304*2d135409SMark Cave-Ayland     PS2State *ps2;
1305*2d135409SMark Cave-Ayland 
1306*2d135409SMark Cave-Ayland     dev = qdev_new(TYPE_PS2_MOUSE_DEVICE);
1307*2d135409SMark Cave-Ayland     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
1308*2d135409SMark Cave-Ayland     s = PS2_MOUSE_DEVICE(dev);
1309*2d135409SMark Cave-Ayland     ps2 = PS2_DEVICE(s);
13100e43e99cSbellard 
13115edab03dSDon Koch     trace_ps2_mouse_init(s);
1312*2d135409SMark Cave-Ayland     ps2->update_irq = update_irq;
1313*2d135409SMark Cave-Ayland     ps2->update_arg = update_arg;
13140be71e32SAlex Williamson     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
13152a766d29SGerd Hoffmann     qemu_input_handler_register((DeviceState *)s,
13162a766d29SGerd Hoffmann                                 &ps2_mouse_handler);
1317ef74679aSDinesh Subhraveti     qemu_register_reset(ps2_mouse_reset, s);
13180e43e99cSbellard     return s;
13190e43e99cSbellard }
132064bbdd13SMark Cave-Ayland 
13218f84e53cSMark Cave-Ayland static const TypeInfo ps2_kbd_info = {
13228f84e53cSMark Cave-Ayland     .name          = TYPE_PS2_KBD_DEVICE,
13238f84e53cSMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
13248f84e53cSMark Cave-Ayland     .instance_size = sizeof(PS2KbdState),
13258f84e53cSMark Cave-Ayland };
13268f84e53cSMark Cave-Ayland 
1327*2d135409SMark Cave-Ayland static const TypeInfo ps2_mouse_info = {
1328*2d135409SMark Cave-Ayland     .name          = TYPE_PS2_MOUSE_DEVICE,
1329*2d135409SMark Cave-Ayland     .parent        = TYPE_PS2_DEVICE,
1330*2d135409SMark Cave-Ayland     .instance_size = sizeof(PS2MouseState),
1331*2d135409SMark Cave-Ayland };
1332*2d135409SMark Cave-Ayland 
133364bbdd13SMark Cave-Ayland static void ps2_class_init(ObjectClass *klass, void *data)
133464bbdd13SMark Cave-Ayland {
133564bbdd13SMark Cave-Ayland     DeviceClass *dc = DEVICE_CLASS(klass);
133664bbdd13SMark Cave-Ayland 
133764bbdd13SMark Cave-Ayland     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
133864bbdd13SMark Cave-Ayland }
133964bbdd13SMark Cave-Ayland 
134064bbdd13SMark Cave-Ayland static const TypeInfo ps2_info = {
134164bbdd13SMark Cave-Ayland     .name          = TYPE_PS2_DEVICE,
134264bbdd13SMark Cave-Ayland     .parent        = TYPE_SYS_BUS_DEVICE,
134364bbdd13SMark Cave-Ayland     .instance_size = sizeof(PS2State),
134464bbdd13SMark Cave-Ayland     .class_init    = ps2_class_init,
134564bbdd13SMark Cave-Ayland     .abstract      = true
134664bbdd13SMark Cave-Ayland };
134764bbdd13SMark Cave-Ayland 
134864bbdd13SMark Cave-Ayland static void ps2_register_types(void)
134964bbdd13SMark Cave-Ayland {
135064bbdd13SMark Cave-Ayland     type_register_static(&ps2_info);
13518f84e53cSMark Cave-Ayland     type_register_static(&ps2_kbd_info);
1352*2d135409SMark Cave-Ayland     type_register_static(&ps2_mouse_info);
135364bbdd13SMark Cave-Ayland }
135464bbdd13SMark Cave-Ayland 
135564bbdd13SMark Cave-Ayland type_init(ps2_register_types)
1356