|
|
1.1 ! root 1: /* ! 2: * QEMU PS/2 keyboard/mouse emulation ! 3: * ! 4: * Copyright (c) 2003 Fabrice Bellard ! 5: * ! 6: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 7: * of this software and associated documentation files (the "Software"), to deal ! 8: * in the Software without restriction, including without limitation the rights ! 9: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 10: * copies of the Software, and to permit persons to whom the Software is ! 11: * furnished to do so, subject to the following conditions: ! 12: * ! 13: * The above copyright notice and this permission notice shall be included in ! 14: * all copies or substantial portions of the Software. ! 15: * ! 16: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 17: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 19: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 20: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 21: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 22: * THE SOFTWARE. ! 23: */ ! 24: #include "vl.h" ! 25: ! 26: /* debug PC keyboard */ ! 27: //#define DEBUG_KBD ! 28: ! 29: /* debug PC keyboard : only mouse */ ! 30: //#define DEBUG_MOUSE ! 31: ! 32: /* Keyboard Commands */ ! 33: #define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ ! 34: #define KBD_CMD_ECHO 0xEE ! 35: #define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */ ! 36: #define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ ! 37: #define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ ! 38: #define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */ ! 39: #define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */ ! 40: #define KBD_CMD_RESET 0xFF /* Reset */ ! 41: ! 42: /* Keyboard Replies */ ! 43: #define KBD_REPLY_POR 0xAA /* Power on reset */ ! 44: #define KBD_REPLY_ACK 0xFA /* Command ACK */ ! 45: #define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ ! 46: ! 47: /* Mouse Commands */ ! 48: #define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ ! 49: #define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ ! 50: #define AUX_SET_RES 0xE8 /* Set resolution */ ! 51: #define AUX_GET_SCALE 0xE9 /* Get scaling factor */ ! 52: #define AUX_SET_STREAM 0xEA /* Set stream mode */ ! 53: #define AUX_POLL 0xEB /* Poll */ ! 54: #define AUX_RESET_WRAP 0xEC /* Reset wrap mode */ ! 55: #define AUX_SET_WRAP 0xEE /* Set wrap mode */ ! 56: #define AUX_SET_REMOTE 0xF0 /* Set remote mode */ ! 57: #define AUX_GET_TYPE 0xF2 /* Get type */ ! 58: #define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ ! 59: #define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ ! 60: #define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ ! 61: #define AUX_SET_DEFAULT 0xF6 ! 62: #define AUX_RESET 0xFF /* Reset aux device */ ! 63: #define AUX_ACK 0xFA /* Command byte ACK. */ ! 64: ! 65: #define MOUSE_STATUS_REMOTE 0x40 ! 66: #define MOUSE_STATUS_ENABLED 0x20 ! 67: #define MOUSE_STATUS_SCALE21 0x10 ! 68: ! 69: #define PS2_QUEUE_SIZE 256 ! 70: ! 71: typedef struct { ! 72: uint8_t data[PS2_QUEUE_SIZE]; ! 73: int rptr, wptr, count; ! 74: } PS2Queue; ! 75: ! 76: typedef struct { ! 77: PS2Queue queue; ! 78: int32_t write_cmd; ! 79: void (*update_irq)(void *, int); ! 80: void *update_arg; ! 81: } PS2State; ! 82: ! 83: typedef struct { ! 84: PS2State common; ! 85: int scan_enabled; ! 86: } PS2KbdState; ! 87: ! 88: typedef struct { ! 89: PS2State common; ! 90: uint8_t mouse_status; ! 91: uint8_t mouse_resolution; ! 92: uint8_t mouse_sample_rate; ! 93: uint8_t mouse_wrap; ! 94: uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */ ! 95: uint8_t mouse_detect_state; ! 96: int mouse_dx; /* current values, needed for 'poll' mode */ ! 97: int mouse_dy; ! 98: int mouse_dz; ! 99: uint8_t mouse_buttons; ! 100: } PS2MouseState; ! 101: ! 102: void ps2_queue(void *opaque, int b) ! 103: { ! 104: PS2State *s = (PS2State *)opaque; ! 105: PS2Queue *q = &s->queue; ! 106: ! 107: if (q->count >= PS2_QUEUE_SIZE) ! 108: return; ! 109: q->data[q->wptr] = b; ! 110: if (++q->wptr == PS2_QUEUE_SIZE) ! 111: q->wptr = 0; ! 112: q->count++; ! 113: s->update_irq(s->update_arg, 1); ! 114: } ! 115: ! 116: static void ps2_put_keycode(void *opaque, int keycode) ! 117: { ! 118: PS2MouseState *s = opaque; ! 119: ps2_queue(&s->common, keycode); ! 120: } ! 121: ! 122: uint32_t ps2_read_data(void *opaque) ! 123: { ! 124: PS2State *s = (PS2State *)opaque; ! 125: PS2Queue *q; ! 126: int val, index; ! 127: ! 128: q = &s->queue; ! 129: if (q->count == 0) { ! 130: /* NOTE: if no data left, we return the last keyboard one ! 131: (needed for EMM386) */ ! 132: /* XXX: need a timer to do things correctly */ ! 133: index = q->rptr - 1; ! 134: if (index < 0) ! 135: index = PS2_QUEUE_SIZE - 1; ! 136: val = q->data[index]; ! 137: } else { ! 138: val = q->data[q->rptr]; ! 139: if (++q->rptr == PS2_QUEUE_SIZE) ! 140: q->rptr = 0; ! 141: q->count--; ! 142: /* reading deasserts IRQ */ ! 143: s->update_irq(s->update_arg, 0); ! 144: /* reassert IRQs if data left */ ! 145: s->update_irq(s->update_arg, q->count != 0); ! 146: } ! 147: return val; ! 148: } ! 149: ! 150: static void ps2_reset_keyboard(PS2KbdState *s) ! 151: { ! 152: s->scan_enabled = 1; ! 153: } ! 154: ! 155: void ps2_write_keyboard(void *opaque, int val) ! 156: { ! 157: PS2KbdState *s = (PS2KbdState *)opaque; ! 158: ! 159: switch(s->common.write_cmd) { ! 160: default: ! 161: case -1: ! 162: switch(val) { ! 163: case 0x00: ! 164: ps2_queue(&s->common, KBD_REPLY_ACK); ! 165: break; ! 166: case 0x05: ! 167: ps2_queue(&s->common, KBD_REPLY_RESEND); ! 168: break; ! 169: case KBD_CMD_GET_ID: ! 170: ps2_queue(&s->common, KBD_REPLY_ACK); ! 171: ps2_queue(&s->common, 0xab); ! 172: ps2_queue(&s->common, 0x83); ! 173: break; ! 174: case KBD_CMD_ECHO: ! 175: ps2_queue(&s->common, KBD_CMD_ECHO); ! 176: break; ! 177: case KBD_CMD_ENABLE: ! 178: s->scan_enabled = 1; ! 179: ps2_queue(&s->common, KBD_REPLY_ACK); ! 180: break; ! 181: case KBD_CMD_SET_LEDS: ! 182: case KBD_CMD_SET_RATE: ! 183: s->common.write_cmd = val; ! 184: ps2_queue(&s->common, KBD_REPLY_ACK); ! 185: break; ! 186: case KBD_CMD_RESET_DISABLE: ! 187: ps2_reset_keyboard(s); ! 188: s->scan_enabled = 0; ! 189: ps2_queue(&s->common, KBD_REPLY_ACK); ! 190: break; ! 191: case KBD_CMD_RESET_ENABLE: ! 192: ps2_reset_keyboard(s); ! 193: s->scan_enabled = 1; ! 194: ps2_queue(&s->common, KBD_REPLY_ACK); ! 195: break; ! 196: case KBD_CMD_RESET: ! 197: ps2_reset_keyboard(s); ! 198: ps2_queue(&s->common, KBD_REPLY_ACK); ! 199: ps2_queue(&s->common, KBD_REPLY_POR); ! 200: break; ! 201: default: ! 202: ps2_queue(&s->common, KBD_REPLY_ACK); ! 203: break; ! 204: } ! 205: break; ! 206: case KBD_CMD_SET_LEDS: ! 207: ps2_queue(&s->common, KBD_REPLY_ACK); ! 208: s->common.write_cmd = -1; ! 209: break; ! 210: case KBD_CMD_SET_RATE: ! 211: ps2_queue(&s->common, KBD_REPLY_ACK); ! 212: s->common.write_cmd = -1; ! 213: break; ! 214: } ! 215: } ! 216: ! 217: static void ps2_mouse_send_packet(PS2MouseState *s) ! 218: { ! 219: unsigned int b; ! 220: int dx1, dy1, dz1; ! 221: ! 222: dx1 = s->mouse_dx; ! 223: dy1 = s->mouse_dy; ! 224: dz1 = s->mouse_dz; ! 225: /* XXX: increase range to 8 bits ? */ ! 226: if (dx1 > 127) ! 227: dx1 = 127; ! 228: else if (dx1 < -127) ! 229: dx1 = -127; ! 230: if (dy1 > 127) ! 231: dy1 = 127; ! 232: else if (dy1 < -127) ! 233: dy1 = -127; ! 234: b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07); ! 235: ps2_queue(&s->common, b); ! 236: ps2_queue(&s->common, dx1 & 0xff); ! 237: ps2_queue(&s->common, dy1 & 0xff); ! 238: /* extra byte for IMPS/2 or IMEX */ ! 239: switch(s->mouse_type) { ! 240: default: ! 241: break; ! 242: case 3: ! 243: if (dz1 > 127) ! 244: dz1 = 127; ! 245: else if (dz1 < -127) ! 246: dz1 = -127; ! 247: ps2_queue(&s->common, dz1 & 0xff); ! 248: break; ! 249: case 4: ! 250: if (dz1 > 7) ! 251: dz1 = 7; ! 252: else if (dz1 < -7) ! 253: dz1 = -7; ! 254: b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); ! 255: ps2_queue(&s->common, b); ! 256: break; ! 257: } ! 258: ! 259: /* update deltas */ ! 260: s->mouse_dx -= dx1; ! 261: s->mouse_dy -= dy1; ! 262: s->mouse_dz -= dz1; ! 263: } ! 264: ! 265: static void ps2_mouse_event(void *opaque, ! 266: int dx, int dy, int dz, int buttons_state) ! 267: { ! 268: PS2MouseState *s = opaque; ! 269: ! 270: /* check if deltas are recorded when disabled */ ! 271: if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) ! 272: return; ! 273: ! 274: s->mouse_dx += dx; ! 275: s->mouse_dy -= dy; ! 276: s->mouse_dz += dz; ! 277: /* XXX: SDL sometimes generates nul events: we delete them */ ! 278: if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 && ! 279: s->mouse_buttons == buttons_state) ! 280: return; ! 281: s->mouse_buttons = buttons_state; ! 282: ! 283: if (!(s->mouse_status & MOUSE_STATUS_REMOTE) && ! 284: (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) { ! 285: for(;;) { ! 286: /* if not remote, send event. Multiple events are sent if ! 287: too big deltas */ ! 288: ps2_mouse_send_packet(s); ! 289: if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0) ! 290: break; ! 291: } ! 292: } ! 293: } ! 294: ! 295: void ps2_write_mouse(void *opaque, int val) ! 296: { ! 297: PS2MouseState *s = (PS2MouseState *)opaque; ! 298: #ifdef DEBUG_MOUSE ! 299: printf("kbd: write mouse 0x%02x\n", val); ! 300: #endif ! 301: switch(s->common.write_cmd) { ! 302: default: ! 303: case -1: ! 304: /* mouse command */ ! 305: if (s->mouse_wrap) { ! 306: if (val == AUX_RESET_WRAP) { ! 307: s->mouse_wrap = 0; ! 308: ps2_queue(&s->common, AUX_ACK); ! 309: return; ! 310: } else if (val != AUX_RESET) { ! 311: ps2_queue(&s->common, val); ! 312: return; ! 313: } ! 314: } ! 315: switch(val) { ! 316: case AUX_SET_SCALE11: ! 317: s->mouse_status &= ~MOUSE_STATUS_SCALE21; ! 318: ps2_queue(&s->common, AUX_ACK); ! 319: break; ! 320: case AUX_SET_SCALE21: ! 321: s->mouse_status |= MOUSE_STATUS_SCALE21; ! 322: ps2_queue(&s->common, AUX_ACK); ! 323: break; ! 324: case AUX_SET_STREAM: ! 325: s->mouse_status &= ~MOUSE_STATUS_REMOTE; ! 326: ps2_queue(&s->common, AUX_ACK); ! 327: break; ! 328: case AUX_SET_WRAP: ! 329: s->mouse_wrap = 1; ! 330: ps2_queue(&s->common, AUX_ACK); ! 331: break; ! 332: case AUX_SET_REMOTE: ! 333: s->mouse_status |= MOUSE_STATUS_REMOTE; ! 334: ps2_queue(&s->common, AUX_ACK); ! 335: break; ! 336: case AUX_GET_TYPE: ! 337: ps2_queue(&s->common, AUX_ACK); ! 338: ps2_queue(&s->common, s->mouse_type); ! 339: break; ! 340: case AUX_SET_RES: ! 341: case AUX_SET_SAMPLE: ! 342: s->common.write_cmd = val; ! 343: ps2_queue(&s->common, AUX_ACK); ! 344: break; ! 345: case AUX_GET_SCALE: ! 346: ps2_queue(&s->common, AUX_ACK); ! 347: ps2_queue(&s->common, s->mouse_status); ! 348: ps2_queue(&s->common, s->mouse_resolution); ! 349: ps2_queue(&s->common, s->mouse_sample_rate); ! 350: break; ! 351: case AUX_POLL: ! 352: ps2_queue(&s->common, AUX_ACK); ! 353: ps2_mouse_send_packet(s); ! 354: break; ! 355: case AUX_ENABLE_DEV: ! 356: s->mouse_status |= MOUSE_STATUS_ENABLED; ! 357: ps2_queue(&s->common, AUX_ACK); ! 358: break; ! 359: case AUX_DISABLE_DEV: ! 360: s->mouse_status &= ~MOUSE_STATUS_ENABLED; ! 361: ps2_queue(&s->common, AUX_ACK); ! 362: break; ! 363: case AUX_SET_DEFAULT: ! 364: s->mouse_sample_rate = 100; ! 365: s->mouse_resolution = 2; ! 366: s->mouse_status = 0; ! 367: ps2_queue(&s->common, AUX_ACK); ! 368: break; ! 369: case AUX_RESET: ! 370: s->mouse_sample_rate = 100; ! 371: s->mouse_resolution = 2; ! 372: s->mouse_status = 0; ! 373: s->mouse_type = 0; ! 374: ps2_queue(&s->common, AUX_ACK); ! 375: ps2_queue(&s->common, 0xaa); ! 376: ps2_queue(&s->common, s->mouse_type); ! 377: break; ! 378: default: ! 379: break; ! 380: } ! 381: break; ! 382: case AUX_SET_SAMPLE: ! 383: s->mouse_sample_rate = val; ! 384: /* detect IMPS/2 or IMEX */ ! 385: switch(s->mouse_detect_state) { ! 386: default: ! 387: case 0: ! 388: if (val == 200) ! 389: s->mouse_detect_state = 1; ! 390: break; ! 391: case 1: ! 392: if (val == 100) ! 393: s->mouse_detect_state = 2; ! 394: else if (val == 200) ! 395: s->mouse_detect_state = 3; ! 396: else ! 397: s->mouse_detect_state = 0; ! 398: break; ! 399: case 2: ! 400: if (val == 80) ! 401: s->mouse_type = 3; /* IMPS/2 */ ! 402: s->mouse_detect_state = 0; ! 403: break; ! 404: case 3: ! 405: if (val == 80) ! 406: s->mouse_type = 4; /* IMEX */ ! 407: s->mouse_detect_state = 0; ! 408: break; ! 409: } ! 410: ps2_queue(&s->common, AUX_ACK); ! 411: s->common.write_cmd = -1; ! 412: break; ! 413: case AUX_SET_RES: ! 414: s->mouse_resolution = val; ! 415: ps2_queue(&s->common, AUX_ACK); ! 416: s->common.write_cmd = -1; ! 417: break; ! 418: } ! 419: } ! 420: ! 421: static void ps2_reset(void *opaque) ! 422: { ! 423: PS2State *s = (PS2State *)opaque; ! 424: PS2Queue *q; ! 425: s->write_cmd = -1; ! 426: q = &s->queue; ! 427: q->rptr = 0; ! 428: q->wptr = 0; ! 429: q->count = 0; ! 430: } ! 431: ! 432: static void ps2_kbd_save(QEMUFile* f, void* opaque) ! 433: { ! 434: PS2KbdState *s = (PS2KbdState*)opaque; ! 435: ! 436: qemu_put_be32s(f, &s->common.write_cmd); ! 437: qemu_put_be32s(f, &s->scan_enabled); ! 438: } ! 439: ! 440: static void ps2_mouse_save(QEMUFile* f, void* opaque) ! 441: { ! 442: PS2MouseState *s = (PS2MouseState*)opaque; ! 443: ! 444: qemu_put_be32s(f, &s->common.write_cmd); ! 445: qemu_put_8s(f, &s->mouse_status); ! 446: qemu_put_8s(f, &s->mouse_resolution); ! 447: qemu_put_8s(f, &s->mouse_sample_rate); ! 448: qemu_put_8s(f, &s->mouse_wrap); ! 449: qemu_put_8s(f, &s->mouse_type); ! 450: qemu_put_8s(f, &s->mouse_detect_state); ! 451: qemu_put_be32s(f, &s->mouse_dx); ! 452: qemu_put_be32s(f, &s->mouse_dy); ! 453: qemu_put_be32s(f, &s->mouse_dz); ! 454: qemu_put_8s(f, &s->mouse_buttons); ! 455: } ! 456: ! 457: static int ps2_kbd_load(QEMUFile* f, void* opaque, int version_id) ! 458: { ! 459: PS2KbdState *s = (PS2KbdState*)opaque; ! 460: ! 461: if (version_id != 1) ! 462: return -EINVAL; ! 463: qemu_get_be32s(f, &s->common.write_cmd); ! 464: qemu_get_be32s(f, &s->scan_enabled); ! 465: return 0; ! 466: } ! 467: ! 468: static int ps2_mouse_load(QEMUFile* f, void* opaque, int version_id) ! 469: { ! 470: PS2MouseState *s = (PS2MouseState*)opaque; ! 471: ! 472: if (version_id != 1) ! 473: return -EINVAL; ! 474: qemu_get_be32s(f, &s->common.write_cmd); ! 475: qemu_get_8s(f, &s->mouse_status); ! 476: qemu_get_8s(f, &s->mouse_resolution); ! 477: qemu_get_8s(f, &s->mouse_sample_rate); ! 478: qemu_get_8s(f, &s->mouse_wrap); ! 479: qemu_get_8s(f, &s->mouse_type); ! 480: qemu_get_8s(f, &s->mouse_detect_state); ! 481: qemu_get_be32s(f, &s->mouse_dx); ! 482: qemu_get_be32s(f, &s->mouse_dy); ! 483: qemu_get_be32s(f, &s->mouse_dz); ! 484: qemu_get_8s(f, &s->mouse_buttons); ! 485: return 0; ! 486: } ! 487: ! 488: void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg) ! 489: { ! 490: PS2KbdState *s = (PS2KbdState *)qemu_mallocz(sizeof(PS2KbdState)); ! 491: ! 492: s->common.update_irq = update_irq; ! 493: s->common.update_arg = update_arg; ! 494: ps2_reset(&s->common); ! 495: register_savevm("ps2kbd", 0, 1, ps2_kbd_save, ps2_kbd_load, s); ! 496: qemu_add_kbd_event_handler(ps2_put_keycode, s); ! 497: qemu_register_reset(ps2_reset, &s->common); ! 498: return s; ! 499: } ! 500: ! 501: void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg) ! 502: { ! 503: PS2MouseState *s = (PS2MouseState *)qemu_mallocz(sizeof(PS2MouseState)); ! 504: ! 505: s->common.update_irq = update_irq; ! 506: s->common.update_arg = update_arg; ! 507: ps2_reset(&s->common); ! 508: register_savevm("ps2mouse", 0, 1, ps2_mouse_save, ps2_mouse_load, s); ! 509: qemu_add_mouse_event_handler(ps2_mouse_event, s); ! 510: qemu_register_reset(ps2_reset, &s->common); ! 511: return s; ! 512: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.