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