]> git.proxmox.com Git - mirror_qemu.git/blob - hw/ps2.c
PS2 mouse and keyboard separation (Paul Brook)
[mirror_qemu.git] / hw / ps2.c
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 }