PS2 mouse and keyboard separation (Paul Brook)
[qemu] / 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 }