Commit 0e43e99c045eb22415a7e52e2f88dbdb8e2d96f5

Authored by bellard
1 parent 98699967

PS2 mouse and keyboard separation (Paul Brook)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1660 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 512 additions and 0 deletions
hw/ps2.c 0 → 100644
  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 +}
... ...