io port API change
[qemu] / hw / pckbd.c
1 /*
2  * QEMU PC keyboard 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 <stdlib.h>
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <getopt.h>
29 #include <inttypes.h>
30 #include <unistd.h>
31 #include <sys/mman.h>
32 #include <fcntl.h>
33 #include <signal.h>
34 #include <time.h>
35 #include <sys/time.h>
36 #include <malloc.h>
37 #include <termios.h>
38 #include <sys/poll.h>
39 #include <errno.h>
40 #include <sys/wait.h>
41 #include <netinet/in.h>
42
43 #include "cpu.h"
44 #include "vl.h"
45
46 /* debug PC keyboard */
47 //#define DEBUG_KBD
48
49 /* debug PC keyboard : only mouse */
50 //#define DEBUG_MOUSE
51
52 /*      Keyboard Controller Commands */
53 #define KBD_CCMD_READ_MODE      0x20    /* Read mode bits */
54 #define KBD_CCMD_WRITE_MODE     0x60    /* Write mode bits */
55 #define KBD_CCMD_GET_VERSION    0xA1    /* Get controller version */
56 #define KBD_CCMD_MOUSE_DISABLE  0xA7    /* Disable mouse interface */
57 #define KBD_CCMD_MOUSE_ENABLE   0xA8    /* Enable mouse interface */
58 #define KBD_CCMD_TEST_MOUSE     0xA9    /* Mouse interface test */
59 #define KBD_CCMD_SELF_TEST      0xAA    /* Controller self test */
60 #define KBD_CCMD_KBD_TEST       0xAB    /* Keyboard interface test */
61 #define KBD_CCMD_KBD_DISABLE    0xAD    /* Keyboard interface disable */
62 #define KBD_CCMD_KBD_ENABLE     0xAE    /* Keyboard interface enable */
63 #define KBD_CCMD_READ_INPORT    0xC0    /* read input port */
64 #define KBD_CCMD_READ_OUTPORT   0xD0    /* read output port */
65 #define KBD_CCMD_WRITE_OUTPORT  0xD1    /* write output port */
66 #define KBD_CCMD_WRITE_OBUF     0xD2
67 #define KBD_CCMD_WRITE_AUX_OBUF 0xD3    /* Write to output buffer as if
68                                            initiated by the auxiliary device */
69 #define KBD_CCMD_WRITE_MOUSE    0xD4    /* Write the following byte to the mouse */
70 #define KBD_CCMD_DISABLE_A20    0xDD    /* HP vectra only ? */
71 #define KBD_CCMD_ENABLE_A20     0xDF    /* HP vectra only ? */
72 #define KBD_CCMD_RESET          0xFE
73
74 /* Keyboard Commands */
75 #define KBD_CMD_SET_LEDS        0xED    /* Set keyboard leds */
76 #define KBD_CMD_ECHO            0xEE
77 #define KBD_CMD_GET_ID          0xF2    /* get keyboard ID */
78 #define KBD_CMD_SET_RATE        0xF3    /* Set typematic rate */
79 #define KBD_CMD_ENABLE          0xF4    /* Enable scanning */
80 #define KBD_CMD_RESET_DISABLE   0xF5    /* reset and disable scanning */
81 #define KBD_CMD_RESET_ENABLE    0xF6    /* reset and enable scanning */
82 #define KBD_CMD_RESET           0xFF    /* Reset */
83
84 /* Keyboard Replies */
85 #define KBD_REPLY_POR           0xAA    /* Power on reset */
86 #define KBD_REPLY_ACK           0xFA    /* Command ACK */
87 #define KBD_REPLY_RESEND        0xFE    /* Command NACK, send the cmd again */
88
89 /* Status Register Bits */
90 #define KBD_STAT_OBF            0x01    /* Keyboard output buffer full */
91 #define KBD_STAT_IBF            0x02    /* Keyboard input buffer full */
92 #define KBD_STAT_SELFTEST       0x04    /* Self test successful */
93 #define KBD_STAT_CMD            0x08    /* Last write was a command write (0=data) */
94 #define KBD_STAT_UNLOCKED       0x10    /* Zero if keyboard locked */
95 #define KBD_STAT_MOUSE_OBF      0x20    /* Mouse output buffer full */
96 #define KBD_STAT_GTO            0x40    /* General receive/xmit timeout */
97 #define KBD_STAT_PERR           0x80    /* Parity error */
98
99 /* Controller Mode Register Bits */
100 #define KBD_MODE_KBD_INT        0x01    /* Keyboard data generate IRQ1 */
101 #define KBD_MODE_MOUSE_INT      0x02    /* Mouse data generate IRQ12 */
102 #define KBD_MODE_SYS            0x04    /* The system flag (?) */
103 #define KBD_MODE_NO_KEYLOCK     0x08    /* The keylock doesn't affect the keyboard if set */
104 #define KBD_MODE_DISABLE_KBD    0x10    /* Disable keyboard interface */
105 #define KBD_MODE_DISABLE_MOUSE  0x20    /* Disable mouse interface */
106 #define KBD_MODE_KCC            0x40    /* Scan code conversion to PC format */
107 #define KBD_MODE_RFU            0x80
108
109 /* Mouse Commands */
110 #define AUX_SET_SCALE11         0xE6    /* Set 1:1 scaling */
111 #define AUX_SET_SCALE21         0xE7    /* Set 2:1 scaling */
112 #define AUX_SET_RES             0xE8    /* Set resolution */
113 #define AUX_GET_SCALE           0xE9    /* Get scaling factor */
114 #define AUX_SET_STREAM          0xEA    /* Set stream mode */
115 #define AUX_POLL                0xEB    /* Poll */
116 #define AUX_RESET_WRAP          0xEC    /* Reset wrap mode */
117 #define AUX_SET_WRAP            0xEE    /* Set wrap mode */
118 #define AUX_SET_REMOTE          0xF0    /* Set remote mode */
119 #define AUX_GET_TYPE            0xF2    /* Get type */
120 #define AUX_SET_SAMPLE          0xF3    /* Set sample rate */
121 #define AUX_ENABLE_DEV          0xF4    /* Enable aux device */
122 #define AUX_DISABLE_DEV         0xF5    /* Disable aux device */
123 #define AUX_SET_DEFAULT         0xF6
124 #define AUX_RESET               0xFF    /* Reset aux device */
125 #define AUX_ACK                 0xFA    /* Command byte ACK. */
126
127 #define MOUSE_STATUS_REMOTE     0x40
128 #define MOUSE_STATUS_ENABLED    0x20
129 #define MOUSE_STATUS_SCALE21    0x10
130
131 #define KBD_QUEUE_SIZE 256
132
133 typedef struct {
134     uint8_t data[KBD_QUEUE_SIZE];
135     int rptr, wptr, count;
136 } KBDQueue;
137
138 typedef struct KBDState {
139     KBDQueue queues[2];
140     uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
141     uint8_t status;
142     uint8_t mode;
143     /* keyboard state */
144     int kbd_write_cmd;
145     int scan_enabled;
146     /* mouse state */
147     int mouse_write_cmd;
148     uint8_t mouse_status;
149     uint8_t mouse_resolution;
150     uint8_t mouse_sample_rate;
151     uint8_t mouse_wrap;
152     uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
153     uint8_t mouse_detect_state;
154     int mouse_dx; /* current values, needed for 'poll' mode */
155     int mouse_dy;
156     int mouse_dz;
157     uint8_t mouse_buttons;
158 } KBDState;
159
160 KBDState kbd_state;
161 int reset_requested;
162
163 /* update irq and KBD_STAT_[MOUSE_]OBF */
164 /* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
165    incorrect, but it avoids having to simulate exact delays */
166 static void kbd_update_irq(KBDState *s)
167 {
168     int irq12_level, irq1_level;
169
170     irq1_level = 0;    
171     irq12_level = 0;    
172     s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
173     if (s->queues[0].count != 0 ||
174         s->queues[1].count != 0) {
175         s->status |= KBD_STAT_OBF;
176         if (s->queues[1].count != 0) {
177             s->status |= KBD_STAT_MOUSE_OBF;
178             if (s->mode & KBD_MODE_MOUSE_INT)
179                 irq12_level = 1;
180         } else {
181             if ((s->mode & KBD_MODE_KBD_INT) && 
182                 !(s->mode & KBD_MODE_DISABLE_KBD))
183                 irq1_level = 1;
184         }
185     }
186     pic_set_irq(1, irq1_level);
187     pic_set_irq(12, irq12_level);
188 }
189
190 static void kbd_queue(KBDState *s, int b, int aux)
191 {
192     KBDQueue *q = &s->queues[aux];
193
194 #if defined(DEBUG_MOUSE) || defined(DEBUG_KBD)
195     if (aux)
196         printf("mouse event: 0x%02x\n", b);
197 #ifdef DEBUG_KBD
198     else
199         printf("kbd event: 0x%02x\n", b);
200 #endif
201 #endif
202     if (q->count >= KBD_QUEUE_SIZE)
203         return;
204     q->data[q->wptr] = b;
205     if (++q->wptr == KBD_QUEUE_SIZE)
206         q->wptr = 0;
207     q->count++;
208     kbd_update_irq(s);
209 }
210
211 void kbd_put_keycode(int keycode)
212 {
213     KBDState *s = &kbd_state;
214     kbd_queue(s, keycode, 0);
215 }
216
217 static uint32_t kbd_read_status(void *opaque, uint32_t addr)
218 {
219     KBDState *s = opaque;
220     int val;
221     val = s->status;
222 #if defined(DEBUG_KBD)
223     printf("kbd: read status=0x%02x\n", val);
224 #endif
225     return val;
226 }
227
228 static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
229 {
230     KBDState *s = opaque;
231
232 #ifdef DEBUG_KBD
233     printf("kbd: write cmd=0x%02x\n", val);
234 #endif
235     switch(val) {
236     case KBD_CCMD_READ_MODE:
237         kbd_queue(s, s->mode, 0);
238         break;
239     case KBD_CCMD_WRITE_MODE:
240     case KBD_CCMD_WRITE_OBUF:
241     case KBD_CCMD_WRITE_AUX_OBUF:
242     case KBD_CCMD_WRITE_MOUSE:
243     case KBD_CCMD_WRITE_OUTPORT:
244         s->write_cmd = val;
245         break;
246     case KBD_CCMD_MOUSE_DISABLE:
247         s->mode |= KBD_MODE_DISABLE_MOUSE;
248         break;
249     case KBD_CCMD_MOUSE_ENABLE:
250         s->mode &= ~KBD_MODE_DISABLE_MOUSE;
251         break;
252     case KBD_CCMD_TEST_MOUSE:
253         kbd_queue(s, 0x00, 0);
254         break;
255     case KBD_CCMD_SELF_TEST:
256         s->status |= KBD_STAT_SELFTEST;
257         kbd_queue(s, 0x55, 0);
258         break;
259     case KBD_CCMD_KBD_TEST:
260         kbd_queue(s, 0x00, 0);
261         break;
262     case KBD_CCMD_KBD_DISABLE:
263         s->mode |= KBD_MODE_DISABLE_KBD;
264         kbd_update_irq(s);
265         break;
266     case KBD_CCMD_KBD_ENABLE:
267         s->mode &= ~KBD_MODE_DISABLE_KBD;
268         kbd_update_irq(s);
269         break;
270     case KBD_CCMD_READ_INPORT:
271         kbd_queue(s, 0x00, 0);
272         break;
273     case KBD_CCMD_READ_OUTPORT:
274         /* XXX: check that */
275 #ifdef TARGET_I386
276         val = 0x01 | (((cpu_single_env->a20_mask >> 20) & 1) << 1);
277 #else
278         val = 0x01;
279 #endif
280         if (s->status & KBD_STAT_OBF)
281             val |= 0x10;
282         if (s->status & KBD_STAT_MOUSE_OBF)
283             val |= 0x20;
284         kbd_queue(s, val, 0);
285         break;
286 #ifdef TARGET_I386
287     case KBD_CCMD_ENABLE_A20:
288         cpu_x86_set_a20(cpu_single_env, 1);
289         break;
290     case KBD_CCMD_DISABLE_A20:
291         cpu_x86_set_a20(cpu_single_env, 0);
292         break;
293 #endif
294     case KBD_CCMD_RESET:
295         reset_requested = 1;
296         cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
297         break;
298     case 0xff:
299         /* ignore that - I don't know what is its use */
300         break;
301     default:
302         fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val);
303         break;
304     }
305 }
306
307 static uint32_t kbd_read_data(void *opaque, uint32_t addr)
308 {
309     KBDState *s = opaque;
310     KBDQueue *q;
311     int val, index;
312     
313     q = &s->queues[0]; /* first check KBD data */
314     if (q->count == 0)
315         q = &s->queues[1]; /* then check AUX data */
316     if (q->count == 0) {
317         /* NOTE: if no data left, we return the last keyboard one
318            (needed for EMM386) */
319         /* XXX: need a timer to do things correctly */
320         q = &s->queues[0];
321         index = q->rptr - 1;
322         if (index < 0)
323             index = KBD_QUEUE_SIZE - 1;
324         val = q->data[index];
325     } else {
326         val = q->data[q->rptr];
327         if (++q->rptr == KBD_QUEUE_SIZE)
328             q->rptr = 0;
329         q->count--;
330         /* reading deasserts IRQ */
331         if (q == &s->queues[0])
332             pic_set_irq(1, 0);
333         else
334             pic_set_irq(12, 0);
335     }
336     /* reassert IRQs if data left */
337     kbd_update_irq(s);
338 #ifdef DEBUG_KBD
339     printf("kbd: read data=0x%02x\n", val);
340 #endif
341     return val;
342 }
343
344 static void kbd_reset_keyboard(KBDState *s)
345 {
346     s->scan_enabled = 1;
347 }
348
349 static void kbd_write_keyboard(KBDState *s, int val)
350 {
351     switch(s->kbd_write_cmd) {
352     default:
353     case -1:
354         switch(val) {
355         case 0x00:
356             kbd_queue(s, KBD_REPLY_ACK, 0);
357             break;
358         case 0x05:
359             kbd_queue(s, KBD_REPLY_RESEND, 0);
360             break;
361         case KBD_CMD_GET_ID:
362             kbd_queue(s, KBD_REPLY_ACK, 0);
363             kbd_queue(s, 0xab, 0);
364             kbd_queue(s, 0x83, 0);
365             break;
366         case KBD_CMD_ECHO:
367             kbd_queue(s, KBD_CMD_ECHO, 0);
368             break;
369         case KBD_CMD_ENABLE:
370             s->scan_enabled = 1;
371             kbd_queue(s, KBD_REPLY_ACK, 0);
372             break;
373         case KBD_CMD_SET_LEDS:
374         case KBD_CMD_SET_RATE:
375             s->kbd_write_cmd = val;
376             kbd_queue(s, KBD_REPLY_ACK, 0);
377             break;
378         case KBD_CMD_RESET_DISABLE:
379             kbd_reset_keyboard(s);
380             s->scan_enabled = 0;
381             kbd_queue(s, KBD_REPLY_ACK, 0);
382             break;
383         case KBD_CMD_RESET_ENABLE:
384             kbd_reset_keyboard(s);
385             s->scan_enabled = 1;
386             kbd_queue(s, KBD_REPLY_ACK, 0);
387             break;
388         case KBD_CMD_RESET:
389             kbd_reset_keyboard(s);
390             kbd_queue(s, KBD_REPLY_ACK, 0);
391             kbd_queue(s, KBD_REPLY_POR, 0);
392             break;
393         default:
394             kbd_queue(s, KBD_REPLY_ACK, 0);
395             break;
396         }
397         break;
398     case KBD_CMD_SET_LEDS:
399         kbd_queue(s, KBD_REPLY_ACK, 0);
400         s->kbd_write_cmd = -1;
401         break;
402     case KBD_CMD_SET_RATE:
403         kbd_queue(s, KBD_REPLY_ACK, 0);
404         s->kbd_write_cmd = -1;
405         break;
406     }
407 }
408
409 static void kbd_mouse_send_packet(KBDState *s)
410 {
411     unsigned int b;
412     int dx1, dy1, dz1;
413
414     dx1 = s->mouse_dx;
415     dy1 = s->mouse_dy;
416     dz1 = s->mouse_dz;
417     /* XXX: increase range to 8 bits ? */
418     if (dx1 > 127)
419         dx1 = 127;
420     else if (dx1 < -127)
421         dx1 = -127;
422     if (dy1 > 127)
423         dy1 = 127;
424     else if (dy1 < -127)
425         dy1 = -127;
426     b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
427     kbd_queue(s, b, 1);
428     kbd_queue(s, dx1 & 0xff, 1);
429     kbd_queue(s, dy1 & 0xff, 1);
430     /* extra byte for IMPS/2 or IMEX */
431     switch(s->mouse_type) {
432     default:
433         break;
434     case 3:
435         if (dz1 > 127)
436             dz1 = 127;
437         else if (dz1 < -127)
438                 dz1 = -127;
439         kbd_queue(s, dz1 & 0xff, 1);
440         break;
441     case 4:
442         if (dz1 > 7)
443             dz1 = 7;
444         else if (dz1 < -7)
445             dz1 = -7;
446         b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
447         kbd_queue(s, b, 1);
448         break;
449     }
450
451     /* update deltas */
452     s->mouse_dx -= dx1;
453     s->mouse_dy -= dy1;
454     s->mouse_dz -= dz1;
455 }
456
457 void kbd_mouse_event(int dx, int dy, int dz, int buttons_state)
458 {
459     KBDState *s = &kbd_state;
460
461     /* check if deltas are recorded when disabled */
462     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
463         return;
464
465     s->mouse_dx += dx;
466     s->mouse_dy -= dy;
467     s->mouse_dz += dz;
468     s->mouse_buttons = buttons_state;
469     
470     if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
471         (s->queues[1].count < (KBD_QUEUE_SIZE - 16))) {
472         for(;;) {
473             /* if not remote, send event. Multiple events are sent if
474                too big deltas */
475             kbd_mouse_send_packet(s);
476             if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
477                 break;
478         }
479     }
480 }
481
482 static void kbd_write_mouse(KBDState *s, int val)
483 {
484 #ifdef DEBUG_MOUSE
485     printf("kbd: write mouse 0x%02x\n", val);
486 #endif
487     switch(s->mouse_write_cmd) {
488     default:
489     case -1:
490         /* mouse command */
491         if (s->mouse_wrap) {
492             if (val == AUX_RESET_WRAP) {
493                 s->mouse_wrap = 0;
494                 kbd_queue(s, AUX_ACK, 1);
495                 return;
496             } else if (val != AUX_RESET) {
497                 kbd_queue(s, val, 1);
498                 return;
499             }
500         }
501         switch(val) {
502         case AUX_SET_SCALE11:
503             s->mouse_status &= ~MOUSE_STATUS_SCALE21;
504             kbd_queue(s, AUX_ACK, 1);
505             break;
506         case AUX_SET_SCALE21:
507             s->mouse_status |= MOUSE_STATUS_SCALE21;
508             kbd_queue(s, AUX_ACK, 1);
509             break;
510         case AUX_SET_STREAM:
511             s->mouse_status &= ~MOUSE_STATUS_REMOTE;
512             kbd_queue(s, AUX_ACK, 1);
513             break;
514         case AUX_SET_WRAP:
515             s->mouse_wrap = 1;
516             kbd_queue(s, AUX_ACK, 1);
517             break;
518         case AUX_SET_REMOTE:
519             s->mouse_status |= MOUSE_STATUS_REMOTE;
520             kbd_queue(s, AUX_ACK, 1);
521             break;
522         case AUX_GET_TYPE:
523             kbd_queue(s, AUX_ACK, 1);
524             kbd_queue(s, s->mouse_type, 1);
525             break;
526         case AUX_SET_RES:
527         case AUX_SET_SAMPLE:
528             s->mouse_write_cmd = val;
529             kbd_queue(s, AUX_ACK, 1);
530             break;
531         case AUX_GET_SCALE:
532             kbd_queue(s, AUX_ACK, 1);
533             kbd_queue(s, s->mouse_status, 1);
534             kbd_queue(s, s->mouse_resolution, 1);
535             kbd_queue(s, s->mouse_sample_rate, 1);
536             break;
537         case AUX_POLL:
538             kbd_queue(s, AUX_ACK, 1);
539             kbd_mouse_send_packet(s);
540             break;
541         case AUX_ENABLE_DEV:
542             s->mouse_status |= MOUSE_STATUS_ENABLED;
543             kbd_queue(s, AUX_ACK, 1);
544             break;
545         case AUX_DISABLE_DEV:
546             s->mouse_status &= ~MOUSE_STATUS_ENABLED;
547             kbd_queue(s, AUX_ACK, 1);
548             break;
549         case AUX_SET_DEFAULT:
550             s->mouse_sample_rate = 100;
551             s->mouse_resolution = 2;
552             s->mouse_status = 0;
553             kbd_queue(s, AUX_ACK, 1);
554             break;
555         case AUX_RESET:
556             s->mouse_sample_rate = 100;
557             s->mouse_resolution = 2;
558             s->mouse_status = 0;
559             kbd_queue(s, AUX_ACK, 1);
560             kbd_queue(s, 0xaa, 1);
561             kbd_queue(s, s->mouse_type, 1);
562             break;
563         default:
564             break;
565         }
566         break;
567     case AUX_SET_SAMPLE:
568         s->mouse_sample_rate = val;
569 #if 0
570         /* detect IMPS/2 or IMEX */
571         switch(s->mouse_detect_state) {
572         default:
573         case 0:
574             if (val == 200)
575                 s->mouse_detect_state = 1;
576             break;
577         case 1:
578             if (val == 100)
579                 s->mouse_detect_state = 2;
580             else if (val == 200)
581                 s->mouse_detect_state = 3;
582             else
583                 s->mouse_detect_state = 0;
584             break;
585         case 2:
586             if (val == 80) 
587                 s->mouse_type = 3; /* IMPS/2 */
588             s->mouse_detect_state = 0;
589             break;
590         case 3:
591             if (val == 80) 
592                 s->mouse_type = 4; /* IMEX */
593             s->mouse_detect_state = 0;
594             break;
595         }
596 #endif
597         kbd_queue(s, AUX_ACK, 1);
598         s->mouse_write_cmd = -1;
599         break;
600     case AUX_SET_RES:
601         s->mouse_resolution = val;
602         kbd_queue(s, AUX_ACK, 1);
603         s->mouse_write_cmd = -1;
604         break;
605     }
606 }
607
608 void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
609 {
610     KBDState *s = opaque;
611
612 #ifdef DEBUG_KBD
613     printf("kbd: write data=0x%02x\n", val);
614 #endif
615
616     switch(s->write_cmd) {
617     case 0:
618         kbd_write_keyboard(s, val);
619         break;
620     case KBD_CCMD_WRITE_MODE:
621         s->mode = val;
622         kbd_update_irq(s);
623         break;
624     case KBD_CCMD_WRITE_OBUF:
625         kbd_queue(s, val, 0);
626         break;
627     case KBD_CCMD_WRITE_AUX_OBUF:
628         kbd_queue(s, val, 1);
629         break;
630     case KBD_CCMD_WRITE_OUTPORT:
631 #ifdef TARGET_I386
632         cpu_x86_set_a20(cpu_single_env, (val >> 1) & 1);
633 #endif
634         if (!(val & 1)) {
635             reset_requested = 1;
636             cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
637         }
638         break;
639     case KBD_CCMD_WRITE_MOUSE:
640         kbd_write_mouse(s, val);
641         break;
642     default:
643         break;
644     }
645     s->write_cmd = 0;
646 }
647
648 void kbd_reset(KBDState *s)
649 {
650     KBDQueue *q;
651     int i;
652
653     s->kbd_write_cmd = -1;
654     s->mouse_write_cmd = -1;
655     s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
656     s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
657     for(i = 0; i < 2; i++) {
658         q = &s->queues[i];
659         q->rptr = 0;
660         q->wptr = 0;
661         q->count = 0;
662     }
663 }
664
665 void kbd_init(void)
666 {
667     KBDState *s = &kbd_state;
668     
669     kbd_reset(s);
670     register_ioport_read(0x60, 1, 1, kbd_read_data, s);
671     register_ioport_write(0x60, 1, 1, kbd_write_data, s);
672     register_ioport_read(0x64, 1, 1, kbd_read_status, s);
673     register_ioport_write(0x64, 1, 1, kbd_write_command, s);
674 }