Statistics
| Branch: | Revision:

root / hw / ps2.c @ f94f5d71

History | View | Annotate | Download (16.3 kB)

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
    /* Qemu uses translated PC scancodes internally.  To avoid multiple
87
       conversions we do the translation (if any) in the PS/2 emulation
88
       not the keyboard controller.  */
89
    int translate;
90
} PS2KbdState;
91

    
92
typedef struct {
93
    PS2State common;
94
    uint8_t mouse_status;
95
    uint8_t mouse_resolution;
96
    uint8_t mouse_sample_rate;
97
    uint8_t mouse_wrap;
98
    uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
99
    uint8_t mouse_detect_state;
100
    int mouse_dx; /* current values, needed for 'poll' mode */
101
    int mouse_dy;
102
    int mouse_dz;
103
    uint8_t mouse_buttons;
104
} PS2MouseState;
105

    
106
/* Table to convert from PC scancodes to raw scancodes.  */
107
static const unsigned char ps2_raw_keycode[128] = {
108
          0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
109
         21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
110
         35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
111
         50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88,  5,  6,  4, 12,  3,
112
         11,  2, 10,  1,  9,119,126,108,117,125,123,107,115,116,121,105,
113
        114,122,112,113,127, 96, 97,120,  7, 15, 23, 31, 39, 47, 55, 63,
114
         71, 79, 86, 94,  8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
115
         19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
116
};
117

    
118
void ps2_queue(void *opaque, int b)
119
{
120
    PS2State *s = (PS2State *)opaque;
121
    PS2Queue *q = &s->queue;
122

    
123
    if (q->count >= PS2_QUEUE_SIZE)
124
        return;
125
    q->data[q->wptr] = b;
126
    if (++q->wptr == PS2_QUEUE_SIZE)
127
        q->wptr = 0;
128
    q->count++;
129
    s->update_irq(s->update_arg, 1);
130
}
131

    
132
static void ps2_put_keycode(void *opaque, int keycode)
133
{
134
    PS2KbdState *s = opaque;
135
    if (!s->translate && keycode < 0xe0)
136
      {
137
        if (keycode & 0x80)
138
            ps2_queue(&s->common, 0xf0);
139
        keycode = ps2_raw_keycode[keycode & 0x7f];
140
      }
141
    ps2_queue(&s->common, keycode);
142
}
143

    
144
uint32_t ps2_read_data(void *opaque)
145
{
146
    PS2State *s = (PS2State *)opaque;
147
    PS2Queue *q;
148
    int val, index;
149
    
150
    q = &s->queue;
151
    if (q->count == 0) {
152
        /* NOTE: if no data left, we return the last keyboard one
153
           (needed for EMM386) */
154
        /* XXX: need a timer to do things correctly */
155
        index = q->rptr - 1;
156
        if (index < 0)
157
            index = PS2_QUEUE_SIZE - 1;
158
        val = q->data[index];
159
    } else {
160
        val = q->data[q->rptr];
161
        if (++q->rptr == PS2_QUEUE_SIZE)
162
            q->rptr = 0;
163
        q->count--;
164
        /* reading deasserts IRQ */
165
        s->update_irq(s->update_arg, 0);
166
        /* reassert IRQs if data left */
167
        s->update_irq(s->update_arg, q->count != 0);
168
    }
169
    return val;
170
}
171

    
172
static void ps2_reset_keyboard(PS2KbdState *s)
173
{
174
    s->scan_enabled = 1;
175
}
176

    
177
void ps2_write_keyboard(void *opaque, int val)
178
{
179
    PS2KbdState *s = (PS2KbdState *)opaque;
180

    
181
    switch(s->common.write_cmd) {
182
    default:
183
    case -1:
184
        switch(val) {
185
        case 0x00:
186
            ps2_queue(&s->common, KBD_REPLY_ACK);
187
            break;
188
        case 0x05:
189
            ps2_queue(&s->common, KBD_REPLY_RESEND);
190
            break;
191
        case KBD_CMD_GET_ID:
192
            ps2_queue(&s->common, KBD_REPLY_ACK);
193
            ps2_queue(&s->common, 0xab);
194
            ps2_queue(&s->common, 0x83);
195
            break;
196
        case KBD_CMD_ECHO:
197
            ps2_queue(&s->common, KBD_CMD_ECHO);
198
            break;
199
        case KBD_CMD_ENABLE:
200
            s->scan_enabled = 1;
201
            ps2_queue(&s->common, KBD_REPLY_ACK);
202
            break;
203
        case KBD_CMD_SET_LEDS:
204
        case KBD_CMD_SET_RATE:
205
            s->common.write_cmd = val;
206
            ps2_queue(&s->common, KBD_REPLY_ACK);
207
            break;
208
        case KBD_CMD_RESET_DISABLE:
209
            ps2_reset_keyboard(s);
210
            s->scan_enabled = 0;
211
            ps2_queue(&s->common, KBD_REPLY_ACK);
212
            break;
213
        case KBD_CMD_RESET_ENABLE:
214
            ps2_reset_keyboard(s);
215
            s->scan_enabled = 1;
216
            ps2_queue(&s->common, KBD_REPLY_ACK);
217
            break;
218
        case KBD_CMD_RESET:
219
            ps2_reset_keyboard(s);
220
            ps2_queue(&s->common, KBD_REPLY_ACK);
221
            ps2_queue(&s->common, KBD_REPLY_POR);
222
            break;
223
        default:
224
            ps2_queue(&s->common, KBD_REPLY_ACK);
225
            break;
226
        }
227
        break;
228
    case KBD_CMD_SET_LEDS:
229
        ps2_queue(&s->common, KBD_REPLY_ACK);
230
        s->common.write_cmd = -1;
231
        break;
232
    case KBD_CMD_SET_RATE:
233
        ps2_queue(&s->common, KBD_REPLY_ACK);
234
        s->common.write_cmd = -1;
235
        break;
236
    }
237
}
238

    
239
/* Set the scancode translation mode.
240
   0 = raw scancodes.
241
   1 = translated scancodes (used by qemu internally).  */
242

    
243
void ps2_keyboard_set_translation(void *opaque, int mode)
244
{
245
    PS2KbdState *s = (PS2KbdState *)opaque;
246
    s->translate = mode;
247
}
248

    
249
static void ps2_mouse_send_packet(PS2MouseState *s)
250
{
251
    unsigned int b;
252
    int dx1, dy1, dz1;
253

    
254
    dx1 = s->mouse_dx;
255
    dy1 = s->mouse_dy;
256
    dz1 = s->mouse_dz;
257
    /* XXX: increase range to 8 bits ? */
258
    if (dx1 > 127)
259
        dx1 = 127;
260
    else if (dx1 < -127)
261
        dx1 = -127;
262
    if (dy1 > 127)
263
        dy1 = 127;
264
    else if (dy1 < -127)
265
        dy1 = -127;
266
    b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
267
    ps2_queue(&s->common, b);
268
    ps2_queue(&s->common, dx1 & 0xff);
269
    ps2_queue(&s->common, dy1 & 0xff);
270
    /* extra byte for IMPS/2 or IMEX */
271
    switch(s->mouse_type) {
272
    default:
273
        break;
274
    case 3:
275
        if (dz1 > 127)
276
            dz1 = 127;
277
        else if (dz1 < -127)
278
                dz1 = -127;
279
        ps2_queue(&s->common, dz1 & 0xff);
280
        break;
281
    case 4:
282
        if (dz1 > 7)
283
            dz1 = 7;
284
        else if (dz1 < -7)
285
            dz1 = -7;
286
        b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
287
        ps2_queue(&s->common, b);
288
        break;
289
    }
290

    
291
    /* update deltas */
292
    s->mouse_dx -= dx1;
293
    s->mouse_dy -= dy1;
294
    s->mouse_dz -= dz1;
295
}
296

    
297
static void ps2_mouse_event(void *opaque, 
298
                            int dx, int dy, int dz, int buttons_state)
299
{
300
    PS2MouseState *s = opaque;
301

    
302
    /* check if deltas are recorded when disabled */
303
    if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
304
        return;
305

    
306
    s->mouse_dx += dx;
307
    s->mouse_dy -= dy;
308
    s->mouse_dz += dz;
309
    /* XXX: SDL sometimes generates nul events: we delete them */
310
    if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
311
        s->mouse_buttons == buttons_state)
312
        return;
313
    s->mouse_buttons = buttons_state;
314
    
315
    if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
316
        (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
317
        for(;;) {
318
            /* if not remote, send event. Multiple events are sent if
319
               too big deltas */
320
            ps2_mouse_send_packet(s);
321
            if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
322
                break;
323
        }
324
    }
325
}
326

    
327
void ps2_write_mouse(void *opaque, int val)
328
{
329
    PS2MouseState *s = (PS2MouseState *)opaque;
330
#ifdef DEBUG_MOUSE
331
    printf("kbd: write mouse 0x%02x\n", val);
332
#endif
333
    switch(s->common.write_cmd) {
334
    default:
335
    case -1:
336
        /* mouse command */
337
        if (s->mouse_wrap) {
338
            if (val == AUX_RESET_WRAP) {
339
                s->mouse_wrap = 0;
340
                ps2_queue(&s->common, AUX_ACK);
341
                return;
342
            } else if (val != AUX_RESET) {
343
                ps2_queue(&s->common, val);
344
                return;
345
            }
346
        }
347
        switch(val) {
348
        case AUX_SET_SCALE11:
349
            s->mouse_status &= ~MOUSE_STATUS_SCALE21;
350
            ps2_queue(&s->common, AUX_ACK);
351
            break;
352
        case AUX_SET_SCALE21:
353
            s->mouse_status |= MOUSE_STATUS_SCALE21;
354
            ps2_queue(&s->common, AUX_ACK);
355
            break;
356
        case AUX_SET_STREAM:
357
            s->mouse_status &= ~MOUSE_STATUS_REMOTE;
358
            ps2_queue(&s->common, AUX_ACK);
359
            break;
360
        case AUX_SET_WRAP:
361
            s->mouse_wrap = 1;
362
            ps2_queue(&s->common, AUX_ACK);
363
            break;
364
        case AUX_SET_REMOTE:
365
            s->mouse_status |= MOUSE_STATUS_REMOTE;
366
            ps2_queue(&s->common, AUX_ACK);
367
            break;
368
        case AUX_GET_TYPE:
369
            ps2_queue(&s->common, AUX_ACK);
370
            ps2_queue(&s->common, s->mouse_type);
371
            break;
372
        case AUX_SET_RES:
373
        case AUX_SET_SAMPLE:
374
            s->common.write_cmd = val;
375
            ps2_queue(&s->common, AUX_ACK);
376
            break;
377
        case AUX_GET_SCALE:
378
            ps2_queue(&s->common, AUX_ACK);
379
            ps2_queue(&s->common, s->mouse_status);
380
            ps2_queue(&s->common, s->mouse_resolution);
381
            ps2_queue(&s->common, s->mouse_sample_rate);
382
            break;
383
        case AUX_POLL:
384
            ps2_queue(&s->common, AUX_ACK);
385
            ps2_mouse_send_packet(s);
386
            break;
387
        case AUX_ENABLE_DEV:
388
            s->mouse_status |= MOUSE_STATUS_ENABLED;
389
            ps2_queue(&s->common, AUX_ACK);
390
            break;
391
        case AUX_DISABLE_DEV:
392
            s->mouse_status &= ~MOUSE_STATUS_ENABLED;
393
            ps2_queue(&s->common, AUX_ACK);
394
            break;
395
        case AUX_SET_DEFAULT:
396
            s->mouse_sample_rate = 100;
397
            s->mouse_resolution = 2;
398
            s->mouse_status = 0;
399
            ps2_queue(&s->common, AUX_ACK);
400
            break;
401
        case AUX_RESET:
402
            s->mouse_sample_rate = 100;
403
            s->mouse_resolution = 2;
404
            s->mouse_status = 0;
405
            s->mouse_type = 0;
406
            ps2_queue(&s->common, AUX_ACK);
407
            ps2_queue(&s->common, 0xaa);
408
            ps2_queue(&s->common, s->mouse_type);
409
            break;
410
        default:
411
            break;
412
        }
413
        break;
414
    case AUX_SET_SAMPLE:
415
        s->mouse_sample_rate = val;
416
        /* detect IMPS/2 or IMEX */
417
        switch(s->mouse_detect_state) {
418
        default:
419
        case 0:
420
            if (val == 200)
421
                s->mouse_detect_state = 1;
422
            break;
423
        case 1:
424
            if (val == 100)
425
                s->mouse_detect_state = 2;
426
            else if (val == 200)
427
                s->mouse_detect_state = 3;
428
            else
429
                s->mouse_detect_state = 0;
430
            break;
431
        case 2:
432
            if (val == 80) 
433
                s->mouse_type = 3; /* IMPS/2 */
434
            s->mouse_detect_state = 0;
435
            break;
436
        case 3:
437
            if (val == 80) 
438
                s->mouse_type = 4; /* IMEX */
439
            s->mouse_detect_state = 0;
440
            break;
441
        }
442
        ps2_queue(&s->common, AUX_ACK);
443
        s->common.write_cmd = -1;
444
        break;
445
    case AUX_SET_RES:
446
        s->mouse_resolution = val;
447
        ps2_queue(&s->common, AUX_ACK);
448
        s->common.write_cmd = -1;
449
        break;
450
    }
451
}
452

    
453
static void ps2_reset(void *opaque)
454
{
455
    PS2State *s = (PS2State *)opaque;
456
    PS2Queue *q;
457
    s->write_cmd = -1;
458
    q = &s->queue;
459
    q->rptr = 0;
460
    q->wptr = 0;
461
    q->count = 0;
462
}
463

    
464
static void ps2_kbd_save(QEMUFile* f, void* opaque)
465
{
466
    PS2KbdState *s = (PS2KbdState*)opaque;
467
    
468
    qemu_put_be32s(f, &s->common.write_cmd);
469
    qemu_put_be32s(f, &s->scan_enabled);
470
}
471

    
472
static void ps2_mouse_save(QEMUFile* f, void* opaque)
473
{
474
    PS2MouseState *s = (PS2MouseState*)opaque;
475
    
476
    qemu_put_be32s(f, &s->common.write_cmd);
477
    qemu_put_8s(f, &s->mouse_status);
478
    qemu_put_8s(f, &s->mouse_resolution);
479
    qemu_put_8s(f, &s->mouse_sample_rate);
480
    qemu_put_8s(f, &s->mouse_wrap);
481
    qemu_put_8s(f, &s->mouse_type);
482
    qemu_put_8s(f, &s->mouse_detect_state);
483
    qemu_put_be32s(f, &s->mouse_dx);
484
    qemu_put_be32s(f, &s->mouse_dy);
485
    qemu_put_be32s(f, &s->mouse_dz);
486
    qemu_put_8s(f, &s->mouse_buttons);
487
}
488

    
489
static int ps2_kbd_load(QEMUFile* f, void* opaque, int version_id)
490
{
491
    PS2KbdState *s = (PS2KbdState*)opaque;
492
    
493
    if (version_id != 1)
494
        return -EINVAL;
495
    qemu_get_be32s(f, &s->common.write_cmd);
496
    qemu_get_be32s(f, &s->scan_enabled);
497
    return 0;
498
}
499

    
500
static int ps2_mouse_load(QEMUFile* f, void* opaque, int version_id)
501
{
502
    PS2MouseState *s = (PS2MouseState*)opaque;
503
    
504
    if (version_id != 1)
505
        return -EINVAL;
506
    qemu_get_be32s(f, &s->common.write_cmd);
507
    qemu_get_8s(f, &s->mouse_status);
508
    qemu_get_8s(f, &s->mouse_resolution);
509
    qemu_get_8s(f, &s->mouse_sample_rate);
510
    qemu_get_8s(f, &s->mouse_wrap);
511
    qemu_get_8s(f, &s->mouse_type);
512
    qemu_get_8s(f, &s->mouse_detect_state);
513
    qemu_get_be32s(f, &s->mouse_dx);
514
    qemu_get_be32s(f, &s->mouse_dy);
515
    qemu_get_be32s(f, &s->mouse_dz);
516
    qemu_get_8s(f, &s->mouse_buttons);
517
    return 0;
518
}
519

    
520
void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
521
{
522
    PS2KbdState *s = (PS2KbdState *)qemu_mallocz(sizeof(PS2KbdState));
523

    
524
    s->common.update_irq = update_irq;
525
    s->common.update_arg = update_arg;
526
    ps2_reset(&s->common);
527
    register_savevm("ps2kbd", 0, 1, ps2_kbd_save, ps2_kbd_load, s);
528
    qemu_add_kbd_event_handler(ps2_put_keycode, s);
529
    qemu_register_reset(ps2_reset, &s->common);
530
    return s;
531
}
532

    
533
void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
534
{
535
    PS2MouseState *s = (PS2MouseState *)qemu_mallocz(sizeof(PS2MouseState));
536

    
537
    s->common.update_irq = update_irq;
538
    s->common.update_arg = update_arg;
539
    ps2_reset(&s->common);
540
    register_savevm("ps2mouse", 0, 1, ps2_mouse_save, ps2_mouse_load, s);
541
    qemu_add_mouse_event_handler(ps2_mouse_event, s);
542
    qemu_register_reset(ps2_reset, &s->common);
543
    return s;
544
}