1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include "qemu/osdep.h"
25#include "hw/hw.h"
26#include "hw/isa/isa.h"
27#include "hw/i386/pc.h"
28#include "hw/input/ps2.h"
29#include "hw/input/i8042.h"
30#include "sysemu/sysemu.h"
31
32
33
34#ifdef DEBUG_KBD
35#define DPRINTF(fmt, ...) \
36 do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0)
37#else
38#define DPRINTF(fmt, ...)
39#endif
40
41
42#define KBD_CCMD_READ_MODE 0x20
43#define KBD_CCMD_WRITE_MODE 0x60
44#define KBD_CCMD_GET_VERSION 0xA1
45#define KBD_CCMD_MOUSE_DISABLE 0xA7
46#define KBD_CCMD_MOUSE_ENABLE 0xA8
47#define KBD_CCMD_TEST_MOUSE 0xA9
48#define KBD_CCMD_SELF_TEST 0xAA
49#define KBD_CCMD_KBD_TEST 0xAB
50#define KBD_CCMD_KBD_DISABLE 0xAD
51#define KBD_CCMD_KBD_ENABLE 0xAE
52#define KBD_CCMD_READ_INPORT 0xC0
53#define KBD_CCMD_READ_OUTPORT 0xD0
54#define KBD_CCMD_WRITE_OUTPORT 0xD1
55#define KBD_CCMD_WRITE_OBUF 0xD2
56#define KBD_CCMD_WRITE_AUX_OBUF 0xD3
57
58#define KBD_CCMD_WRITE_MOUSE 0xD4
59#define KBD_CCMD_DISABLE_A20 0xDD
60#define KBD_CCMD_ENABLE_A20 0xDF
61#define KBD_CCMD_PULSE_BITS_3_0 0xF0
62#define KBD_CCMD_RESET 0xFE
63#define KBD_CCMD_NO_OP 0xFF
64
65
66#define KBD_CMD_SET_LEDS 0xED
67#define KBD_CMD_ECHO 0xEE
68#define KBD_CMD_GET_ID 0xF2
69#define KBD_CMD_SET_RATE 0xF3
70#define KBD_CMD_ENABLE 0xF4
71#define KBD_CMD_RESET_DISABLE 0xF5
72#define KBD_CMD_RESET_ENABLE 0xF6
73#define KBD_CMD_RESET 0xFF
74
75
76#define KBD_REPLY_POR 0xAA
77#define KBD_REPLY_ACK 0xFA
78#define KBD_REPLY_RESEND 0xFE
79
80
81#define KBD_STAT_OBF 0x01
82#define KBD_STAT_IBF 0x02
83#define KBD_STAT_SELFTEST 0x04
84#define KBD_STAT_CMD 0x08
85#define KBD_STAT_UNLOCKED 0x10
86#define KBD_STAT_MOUSE_OBF 0x20
87#define KBD_STAT_GTO 0x40
88#define KBD_STAT_PERR 0x80
89
90
91#define KBD_MODE_KBD_INT 0x01
92#define KBD_MODE_MOUSE_INT 0x02
93#define KBD_MODE_SYS 0x04
94#define KBD_MODE_NO_KEYLOCK 0x08
95#define KBD_MODE_DISABLE_KBD 0x10
96#define KBD_MODE_DISABLE_MOUSE 0x20
97#define KBD_MODE_KCC 0x40
98#define KBD_MODE_RFU 0x80
99
100
101#define KBD_OUT_RESET 0x01
102#define KBD_OUT_A20 0x02
103#define KBD_OUT_OBF 0x10
104#define KBD_OUT_MOUSE_OBF 0x20
105
106
107
108
109
110#define KBD_OUT_ONES 0xcc
111
112
113#define AUX_SET_SCALE11 0xE6
114#define AUX_SET_SCALE21 0xE7
115#define AUX_SET_RES 0xE8
116#define AUX_GET_SCALE 0xE9
117#define AUX_SET_STREAM 0xEA
118#define AUX_POLL 0xEB
119#define AUX_RESET_WRAP 0xEC
120#define AUX_SET_WRAP 0xEE
121#define AUX_SET_REMOTE 0xF0
122#define AUX_GET_TYPE 0xF2
123#define AUX_SET_SAMPLE 0xF3
124#define AUX_ENABLE_DEV 0xF4
125#define AUX_DISABLE_DEV 0xF5
126#define AUX_SET_DEFAULT 0xF6
127#define AUX_RESET 0xFF
128#define AUX_ACK 0xFA
129
130#define MOUSE_STATUS_REMOTE 0x40
131#define MOUSE_STATUS_ENABLED 0x20
132#define MOUSE_STATUS_SCALE21 0x10
133
134#define KBD_PENDING_KBD 1
135#define KBD_PENDING_AUX 2
136
137typedef struct KBDState {
138 uint8_t write_cmd;
139 uint8_t status;
140 uint8_t mode;
141 uint8_t outport;
142 bool outport_present;
143
144 uint8_t pending;
145 void *kbd;
146 void *mouse;
147
148 qemu_irq irq_kbd;
149 qemu_irq irq_mouse;
150 qemu_irq a20_out;
151 hwaddr mask;
152} KBDState;
153
154
155
156
157static void kbd_update_irq(KBDState *s)
158{
159 int irq_kbd_level, irq_mouse_level;
160
161 irq_kbd_level = 0;
162 irq_mouse_level = 0;
163 s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
164 s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF);
165 if (s->pending) {
166 s->status |= KBD_STAT_OBF;
167 s->outport |= KBD_OUT_OBF;
168
169 if (s->pending == KBD_PENDING_AUX) {
170 s->status |= KBD_STAT_MOUSE_OBF;
171 s->outport |= KBD_OUT_MOUSE_OBF;
172 if (s->mode & KBD_MODE_MOUSE_INT)
173 irq_mouse_level = 1;
174 } else {
175 if ((s->mode & KBD_MODE_KBD_INT) &&
176 !(s->mode & KBD_MODE_DISABLE_KBD))
177 irq_kbd_level = 1;
178 }
179 }
180 qemu_set_irq(s->irq_kbd, irq_kbd_level);
181 qemu_set_irq(s->irq_mouse, irq_mouse_level);
182}
183
184static void kbd_update_kbd_irq(void *opaque, int level)
185{
186 KBDState *s = (KBDState *)opaque;
187
188 if (level)
189 s->pending |= KBD_PENDING_KBD;
190 else
191 s->pending &= ~KBD_PENDING_KBD;
192 kbd_update_irq(s);
193}
194
195static void kbd_update_aux_irq(void *opaque, int level)
196{
197 KBDState *s = (KBDState *)opaque;
198
199 if (level)
200 s->pending |= KBD_PENDING_AUX;
201 else
202 s->pending &= ~KBD_PENDING_AUX;
203 kbd_update_irq(s);
204}
205
206static uint64_t kbd_read_status(void *opaque, hwaddr addr,
207 unsigned size)
208{
209 KBDState *s = opaque;
210 int val;
211 val = s->status;
212 DPRINTF("kbd: read status=0x%02x\n", val);
213 return val;
214}
215
216static void kbd_queue(KBDState *s, int b, int aux)
217{
218 if (aux)
219 ps2_queue(s->mouse, b);
220 else
221 ps2_queue(s->kbd, b);
222}
223
224static void outport_write(KBDState *s, uint32_t val)
225{
226 DPRINTF("kbd: write outport=0x%02x\n", val);
227 s->outport = val;
228 qemu_set_irq(s->a20_out, (val >> 1) & 1);
229 if (!(val & 1)) {
230 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
231 }
232}
233
234static void kbd_write_command(void *opaque, hwaddr addr,
235 uint64_t val, unsigned size)
236{
237 KBDState *s = opaque;
238
239 DPRINTF("kbd: write cmd=0x%02" PRIx64 "\n", val);
240
241
242
243
244
245
246
247
248 if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) {
249 if(!(val & 1))
250 val = KBD_CCMD_RESET;
251 else
252 val = KBD_CCMD_NO_OP;
253 }
254
255 switch(val) {
256 case KBD_CCMD_READ_MODE:
257 kbd_queue(s, s->mode, 0);
258 break;
259 case KBD_CCMD_WRITE_MODE:
260 case KBD_CCMD_WRITE_OBUF:
261 case KBD_CCMD_WRITE_AUX_OBUF:
262 case KBD_CCMD_WRITE_MOUSE:
263 case KBD_CCMD_WRITE_OUTPORT:
264 s->write_cmd = val;
265 break;
266 case KBD_CCMD_MOUSE_DISABLE:
267 s->mode |= KBD_MODE_DISABLE_MOUSE;
268 break;
269 case KBD_CCMD_MOUSE_ENABLE:
270 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
271 break;
272 case KBD_CCMD_TEST_MOUSE:
273 kbd_queue(s, 0x00, 0);
274 break;
275 case KBD_CCMD_SELF_TEST:
276 s->status |= KBD_STAT_SELFTEST;
277 kbd_queue(s, 0x55, 0);
278 break;
279 case KBD_CCMD_KBD_TEST:
280 kbd_queue(s, 0x00, 0);
281 break;
282 case KBD_CCMD_KBD_DISABLE:
283 s->mode |= KBD_MODE_DISABLE_KBD;
284 kbd_update_irq(s);
285 break;
286 case KBD_CCMD_KBD_ENABLE:
287 s->mode &= ~KBD_MODE_DISABLE_KBD;
288 kbd_update_irq(s);
289 break;
290 case KBD_CCMD_READ_INPORT:
291 kbd_queue(s, 0x80, 0);
292 break;
293 case KBD_CCMD_READ_OUTPORT:
294 kbd_queue(s, s->outport, 0);
295 break;
296 case KBD_CCMD_ENABLE_A20:
297 qemu_irq_raise(s->a20_out);
298 s->outport |= KBD_OUT_A20;
299 break;
300 case KBD_CCMD_DISABLE_A20:
301 qemu_irq_lower(s->a20_out);
302 s->outport &= ~KBD_OUT_A20;
303 break;
304 case KBD_CCMD_RESET:
305 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
306 break;
307 case KBD_CCMD_NO_OP:
308
309 break;
310 default:
311 fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", (int)val);
312 break;
313 }
314}
315
316static uint64_t kbd_read_data(void *opaque, hwaddr addr,
317 unsigned size)
318{
319 KBDState *s = opaque;
320 uint32_t val;
321
322 if (s->pending == KBD_PENDING_AUX)
323 val = ps2_read_data(s->mouse);
324 else
325 val = ps2_read_data(s->kbd);
326
327 DPRINTF("kbd: read data=0x%02x\n", val);
328 return val;
329}
330
331static void kbd_write_data(void *opaque, hwaddr addr,
332 uint64_t val, unsigned size)
333{
334 KBDState *s = opaque;
335
336 DPRINTF("kbd: write data=0x%02" PRIx64 "\n", val);
337
338 switch(s->write_cmd) {
339 case 0:
340 ps2_write_keyboard(s->kbd, val);
341 break;
342 case KBD_CCMD_WRITE_MODE:
343 s->mode = val;
344 ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
345
346 kbd_update_irq(s);
347 break;
348 case KBD_CCMD_WRITE_OBUF:
349 kbd_queue(s, val, 0);
350 break;
351 case KBD_CCMD_WRITE_AUX_OBUF:
352 kbd_queue(s, val, 1);
353 break;
354 case KBD_CCMD_WRITE_OUTPORT:
355 outport_write(s, val);
356 break;
357 case KBD_CCMD_WRITE_MOUSE:
358 ps2_write_mouse(s->mouse, val);
359 break;
360 default:
361 break;
362 }
363 s->write_cmd = 0;
364}
365
366static void kbd_reset(void *opaque)
367{
368 KBDState *s = opaque;
369
370 s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
371 s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
372 s->outport = KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES;
373 s->outport_present = false;
374}
375
376static uint8_t kbd_outport_default(KBDState *s)
377{
378 return KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES
379 | (s->status & KBD_STAT_OBF ? KBD_OUT_OBF : 0)
380 | (s->status & KBD_STAT_MOUSE_OBF ? KBD_OUT_MOUSE_OBF : 0);
381}
382
383static int kbd_outport_post_load(void *opaque, int version_id)
384{
385 KBDState *s = opaque;
386 s->outport_present = true;
387 return 0;
388}
389
390static bool kbd_outport_needed(void *opaque)
391{
392 KBDState *s = opaque;
393 return s->outport != kbd_outport_default(s);
394}
395
396static const VMStateDescription vmstate_kbd_outport = {
397 .name = "pckbd_outport",
398 .version_id = 1,
399 .minimum_version_id = 1,
400 .post_load = kbd_outport_post_load,
401 .needed = kbd_outport_needed,
402 .fields = (VMStateField[]) {
403 VMSTATE_UINT8(outport, KBDState),
404 VMSTATE_END_OF_LIST()
405 }
406};
407
408static int kbd_post_load(void *opaque, int version_id)
409{
410 KBDState *s = opaque;
411 if (!s->outport_present) {
412 s->outport = kbd_outport_default(s);
413 }
414 s->outport_present = false;
415 return 0;
416}
417
418static const VMStateDescription vmstate_kbd = {
419 .name = "pckbd",
420 .version_id = 3,
421 .minimum_version_id = 3,
422 .post_load = kbd_post_load,
423 .fields = (VMStateField[]) {
424 VMSTATE_UINT8(write_cmd, KBDState),
425 VMSTATE_UINT8(status, KBDState),
426 VMSTATE_UINT8(mode, KBDState),
427 VMSTATE_UINT8(pending, KBDState),
428 VMSTATE_END_OF_LIST()
429 },
430 .subsections = (const VMStateDescription*[]) {
431 &vmstate_kbd_outport,
432 NULL
433 }
434};
435
436
437static uint32_t kbd_mm_readb (void *opaque, hwaddr addr)
438{
439 KBDState *s = opaque;
440
441 if (addr & s->mask)
442 return kbd_read_status(s, 0, 1) & 0xff;
443 else
444 return kbd_read_data(s, 0, 1) & 0xff;
445}
446
447static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value)
448{
449 KBDState *s = opaque;
450
451 if (addr & s->mask)
452 kbd_write_command(s, 0, value & 0xff, 1);
453 else
454 kbd_write_data(s, 0, value & 0xff, 1);
455}
456
457static const MemoryRegionOps i8042_mmio_ops = {
458 .endianness = DEVICE_NATIVE_ENDIAN,
459 .old_mmio = {
460 .read = { kbd_mm_readb, kbd_mm_readb, kbd_mm_readb },
461 .write = { kbd_mm_writeb, kbd_mm_writeb, kbd_mm_writeb },
462 },
463};
464
465void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
466 MemoryRegion *region, ram_addr_t size,
467 hwaddr mask)
468{
469 KBDState *s = g_malloc0(sizeof(KBDState));
470
471 s->irq_kbd = kbd_irq;
472 s->irq_mouse = mouse_irq;
473 s->mask = mask;
474
475 vmstate_register(NULL, 0, &vmstate_kbd, s);
476
477 memory_region_init_io(region, NULL, &i8042_mmio_ops, s, "i8042", size);
478
479 s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
480 s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
481 qemu_register_reset(kbd_reset, s);
482}
483
484#define I8042(obj) OBJECT_CHECK(ISAKBDState, (obj), TYPE_I8042)
485
486typedef struct ISAKBDState {
487 ISADevice parent_obj;
488
489 KBDState kbd;
490 MemoryRegion io[2];
491} ISAKBDState;
492
493void i8042_isa_mouse_fake_event(void *opaque)
494{
495 ISADevice *dev = opaque;
496 ISAKBDState *isa = I8042(dev);
497 KBDState *s = &isa->kbd;
498
499 ps2_mouse_fake_event(s->mouse);
500}
501
502void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out)
503{
504 qdev_connect_gpio_out_named(DEVICE(dev), I8042_A20_LINE, 0, a20_out);
505}
506
507static const VMStateDescription vmstate_kbd_isa = {
508 .name = "pckbd",
509 .version_id = 3,
510 .minimum_version_id = 3,
511 .fields = (VMStateField[]) {
512 VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState),
513 VMSTATE_END_OF_LIST()
514 }
515};
516
517static const MemoryRegionOps i8042_data_ops = {
518 .read = kbd_read_data,
519 .write = kbd_write_data,
520 .impl = {
521 .min_access_size = 1,
522 .max_access_size = 1,
523 },
524 .endianness = DEVICE_LITTLE_ENDIAN,
525};
526
527static const MemoryRegionOps i8042_cmd_ops = {
528 .read = kbd_read_status,
529 .write = kbd_write_command,
530 .impl = {
531 .min_access_size = 1,
532 .max_access_size = 1,
533 },
534 .endianness = DEVICE_LITTLE_ENDIAN,
535};
536
537static void i8042_initfn(Object *obj)
538{
539 ISAKBDState *isa_s = I8042(obj);
540 KBDState *s = &isa_s->kbd;
541
542 memory_region_init_io(isa_s->io + 0, obj, &i8042_data_ops, s,
543 "i8042-data", 1);
544 memory_region_init_io(isa_s->io + 1, obj, &i8042_cmd_ops, s,
545 "i8042-cmd", 1);
546
547 qdev_init_gpio_out_named(DEVICE(obj), &s->a20_out, I8042_A20_LINE, 1);
548}
549
550static void i8042_realizefn(DeviceState *dev, Error **errp)
551{
552 ISADevice *isadev = ISA_DEVICE(dev);
553 ISAKBDState *isa_s = I8042(dev);
554 KBDState *s = &isa_s->kbd;
555
556 isa_init_irq(isadev, &s->irq_kbd, 1);
557 isa_init_irq(isadev, &s->irq_mouse, 12);
558
559 isa_register_ioport(isadev, isa_s->io + 0, 0x60);
560 isa_register_ioport(isadev, isa_s->io + 1, 0x64);
561
562 s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
563 s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
564 qemu_register_reset(kbd_reset, s);
565}
566
567static void i8042_class_initfn(ObjectClass *klass, void *data)
568{
569 DeviceClass *dc = DEVICE_CLASS(klass);
570
571 dc->realize = i8042_realizefn;
572 dc->vmsd = &vmstate_kbd_isa;
573}
574
575static const TypeInfo i8042_info = {
576 .name = TYPE_I8042,
577 .parent = TYPE_ISA_DEVICE,
578 .instance_size = sizeof(ISAKBDState),
579 .instance_init = i8042_initfn,
580 .class_init = i8042_class_initfn,
581};
582
583static void i8042_register_types(void)
584{
585 type_register_static(&i8042_info);
586}
587
588type_init(i8042_register_types)
589