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 "sysemu/sysemu.h"
30
31
32
33#ifdef DEBUG_KBD
34#define DPRINTF(fmt, ...) \
35 do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0)
36#else
37#define DPRINTF(fmt, ...)
38#endif
39
40
41#define KBD_CCMD_READ_MODE 0x20
42#define KBD_CCMD_WRITE_MODE 0x60
43#define KBD_CCMD_GET_VERSION 0xA1
44#define KBD_CCMD_MOUSE_DISABLE 0xA7
45#define KBD_CCMD_MOUSE_ENABLE 0xA8
46#define KBD_CCMD_TEST_MOUSE 0xA9
47#define KBD_CCMD_SELF_TEST 0xAA
48#define KBD_CCMD_KBD_TEST 0xAB
49#define KBD_CCMD_KBD_DISABLE 0xAD
50#define KBD_CCMD_KBD_ENABLE 0xAE
51#define KBD_CCMD_READ_INPORT 0xC0
52#define KBD_CCMD_READ_OUTPORT 0xD0
53#define KBD_CCMD_WRITE_OUTPORT 0xD1
54#define KBD_CCMD_WRITE_OBUF 0xD2
55#define KBD_CCMD_WRITE_AUX_OBUF 0xD3
56
57#define KBD_CCMD_WRITE_MOUSE 0xD4
58#define KBD_CCMD_DISABLE_A20 0xDD
59#define KBD_CCMD_ENABLE_A20 0xDF
60#define KBD_CCMD_PULSE_BITS_3_0 0xF0
61#define KBD_CCMD_RESET 0xFE
62#define KBD_CCMD_NO_OP 0xFF
63
64
65#define KBD_CMD_SET_LEDS 0xED
66#define KBD_CMD_ECHO 0xEE
67#define KBD_CMD_GET_ID 0xF2
68#define KBD_CMD_SET_RATE 0xF3
69#define KBD_CMD_ENABLE 0xF4
70#define KBD_CMD_RESET_DISABLE 0xF5
71#define KBD_CMD_RESET_ENABLE 0xF6
72#define KBD_CMD_RESET 0xFF
73
74
75#define KBD_REPLY_POR 0xAA
76#define KBD_REPLY_ACK 0xFA
77#define KBD_REPLY_RESEND 0xFE
78
79
80#define KBD_STAT_OBF 0x01
81#define KBD_STAT_IBF 0x02
82#define KBD_STAT_SELFTEST 0x04
83#define KBD_STAT_CMD 0x08
84#define KBD_STAT_UNLOCKED 0x10
85#define KBD_STAT_MOUSE_OBF 0x20
86#define KBD_STAT_GTO 0x40
87#define KBD_STAT_PERR 0x80
88
89
90#define KBD_MODE_KBD_INT 0x01
91#define KBD_MODE_MOUSE_INT 0x02
92#define KBD_MODE_SYS 0x04
93#define KBD_MODE_NO_KEYLOCK 0x08
94#define KBD_MODE_DISABLE_KBD 0x10
95#define KBD_MODE_DISABLE_MOUSE 0x20
96#define KBD_MODE_KCC 0x40
97#define KBD_MODE_RFU 0x80
98
99
100#define KBD_OUT_RESET 0x01
101#define KBD_OUT_A20 0x02
102#define KBD_OUT_OBF 0x10
103#define KBD_OUT_MOUSE_OBF 0x20
104
105
106
107
108
109#define KBD_OUT_ONES 0xcc
110
111
112#define AUX_SET_SCALE11 0xE6
113#define AUX_SET_SCALE21 0xE7
114#define AUX_SET_RES 0xE8
115#define AUX_GET_SCALE 0xE9
116#define AUX_SET_STREAM 0xEA
117#define AUX_POLL 0xEB
118#define AUX_RESET_WRAP 0xEC
119#define AUX_SET_WRAP 0xEE
120#define AUX_SET_REMOTE 0xF0
121#define AUX_GET_TYPE 0xF2
122#define AUX_SET_SAMPLE 0xF3
123#define AUX_ENABLE_DEV 0xF4
124#define AUX_DISABLE_DEV 0xF5
125#define AUX_SET_DEFAULT 0xF6
126#define AUX_RESET 0xFF
127#define AUX_ACK 0xFA
128
129#define MOUSE_STATUS_REMOTE 0x40
130#define MOUSE_STATUS_ENABLED 0x20
131#define MOUSE_STATUS_SCALE21 0x10
132
133#define KBD_PENDING_KBD 1
134#define KBD_PENDING_AUX 2
135
136typedef struct KBDState {
137 uint8_t write_cmd;
138 uint8_t status;
139 uint8_t mode;
140 uint8_t outport;
141 bool outport_present;
142
143 uint8_t pending;
144 void *kbd;
145 void *mouse;
146
147 qemu_irq irq_kbd;
148 qemu_irq irq_mouse;
149 qemu_irq a20_out;
150 hwaddr mask;
151} KBDState;
152
153
154
155
156static void kbd_update_irq(KBDState *s)
157{
158 int irq_kbd_level, irq_mouse_level;
159
160 irq_kbd_level = 0;
161 irq_mouse_level = 0;
162 s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
163 s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF);
164 if (s->pending) {
165 s->status |= KBD_STAT_OBF;
166 s->outport |= KBD_OUT_OBF;
167
168 if (s->pending == KBD_PENDING_AUX) {
169 s->status |= KBD_STAT_MOUSE_OBF;
170 s->outport |= KBD_OUT_MOUSE_OBF;
171 if (s->mode & KBD_MODE_MOUSE_INT)
172 irq_mouse_level = 1;
173 } else {
174 if ((s->mode & KBD_MODE_KBD_INT) &&
175 !(s->mode & KBD_MODE_DISABLE_KBD))
176 irq_kbd_level = 1;
177 }
178 }
179 qemu_set_irq(s->irq_kbd, irq_kbd_level);
180 qemu_set_irq(s->irq_mouse, irq_mouse_level);
181}
182
183static void kbd_update_kbd_irq(void *opaque, int level)
184{
185 KBDState *s = (KBDState *)opaque;
186
187 if (level)
188 s->pending |= KBD_PENDING_KBD;
189 else
190 s->pending &= ~KBD_PENDING_KBD;
191 kbd_update_irq(s);
192}
193
194static void kbd_update_aux_irq(void *opaque, int level)
195{
196 KBDState *s = (KBDState *)opaque;
197
198 if (level)
199 s->pending |= KBD_PENDING_AUX;
200 else
201 s->pending &= ~KBD_PENDING_AUX;
202 kbd_update_irq(s);
203}
204
205static uint64_t kbd_read_status(void *opaque, hwaddr addr,
206 unsigned size)
207{
208 KBDState *s = opaque;
209 int val;
210 val = s->status;
211 DPRINTF("kbd: read status=0x%02x\n", val);
212 return val;
213}
214
215static void kbd_queue(KBDState *s, int b, int aux)
216{
217 if (aux)
218 ps2_queue(s->mouse, b);
219 else
220 ps2_queue(s->kbd, b);
221}
222
223static void outport_write(KBDState *s, uint32_t val)
224{
225 DPRINTF("kbd: write outport=0x%02x\n", val);
226 s->outport = val;
227 qemu_set_irq(s->a20_out, (val >> 1) & 1);
228 if (!(val & 1)) {
229 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
230 }
231}
232
233static void kbd_write_command(void *opaque, hwaddr addr,
234 uint64_t val, unsigned size)
235{
236 KBDState *s = opaque;
237
238 DPRINTF("kbd: write cmd=0x%02" PRIx64 "\n", val);
239
240
241
242
243
244
245
246
247 if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) {
248 if(!(val & 1))
249 val = KBD_CCMD_RESET;
250 else
251 val = KBD_CCMD_NO_OP;
252 }
253
254 switch(val) {
255 case KBD_CCMD_READ_MODE:
256 kbd_queue(s, s->mode, 0);
257 break;
258 case KBD_CCMD_WRITE_MODE:
259 case KBD_CCMD_WRITE_OBUF:
260 case KBD_CCMD_WRITE_AUX_OBUF:
261 case KBD_CCMD_WRITE_MOUSE:
262 case KBD_CCMD_WRITE_OUTPORT:
263 s->write_cmd = val;
264 break;
265 case KBD_CCMD_MOUSE_DISABLE:
266 s->mode |= KBD_MODE_DISABLE_MOUSE;
267 break;
268 case KBD_CCMD_MOUSE_ENABLE:
269 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
270 break;
271 case KBD_CCMD_TEST_MOUSE:
272 kbd_queue(s, 0x00, 0);
273 break;
274 case KBD_CCMD_SELF_TEST:
275 s->status |= KBD_STAT_SELFTEST;
276 kbd_queue(s, 0x55, 0);
277 break;
278 case KBD_CCMD_KBD_TEST:
279 kbd_queue(s, 0x00, 0);
280 break;
281 case KBD_CCMD_KBD_DISABLE:
282 s->mode |= KBD_MODE_DISABLE_KBD;
283 kbd_update_irq(s);
284 break;
285 case KBD_CCMD_KBD_ENABLE:
286 s->mode &= ~KBD_MODE_DISABLE_KBD;
287 kbd_update_irq(s);
288 break;
289 case KBD_CCMD_READ_INPORT:
290 kbd_queue(s, 0x80, 0);
291 break;
292 case KBD_CCMD_READ_OUTPORT:
293 kbd_queue(s, s->outport, 0);
294 break;
295 case KBD_CCMD_ENABLE_A20:
296 qemu_irq_raise(s->a20_out);
297 s->outport |= KBD_OUT_A20;
298 break;
299 case KBD_CCMD_DISABLE_A20:
300 qemu_irq_lower(s->a20_out);
301 s->outport &= ~KBD_OUT_A20;
302 break;
303 case KBD_CCMD_RESET:
304 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
305 break;
306 case KBD_CCMD_NO_OP:
307
308 break;
309 default:
310 fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", (int)val);
311 break;
312 }
313}
314
315static uint64_t kbd_read_data(void *opaque, hwaddr addr,
316 unsigned size)
317{
318 KBDState *s = opaque;
319 uint32_t val;
320
321 if (s->pending == KBD_PENDING_AUX)
322 val = ps2_read_data(s->mouse);
323 else
324 val = ps2_read_data(s->kbd);
325
326 DPRINTF("kbd: read data=0x%02x\n", val);
327 return val;
328}
329
330static void kbd_write_data(void *opaque, hwaddr addr,
331 uint64_t val, unsigned size)
332{
333 KBDState *s = opaque;
334
335 DPRINTF("kbd: write data=0x%02" PRIx64 "\n", val);
336
337 switch(s->write_cmd) {
338 case 0:
339 ps2_write_keyboard(s->kbd, val);
340 break;
341 case KBD_CCMD_WRITE_MODE:
342 s->mode = val;
343 ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
344
345 kbd_update_irq(s);
346 break;
347 case KBD_CCMD_WRITE_OBUF:
348 kbd_queue(s, val, 0);
349 break;
350 case KBD_CCMD_WRITE_AUX_OBUF:
351 kbd_queue(s, val, 1);
352 break;
353 case KBD_CCMD_WRITE_OUTPORT:
354 outport_write(s, val);
355 break;
356 case KBD_CCMD_WRITE_MOUSE:
357 ps2_write_mouse(s->mouse, val);
358 break;
359 default:
360 break;
361 }
362 s->write_cmd = 0;
363}
364
365static void kbd_reset(void *opaque)
366{
367 KBDState *s = opaque;
368
369 s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
370 s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
371 s->outport = KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES;
372 s->outport_present = false;
373}
374
375static uint8_t kbd_outport_default(KBDState *s)
376{
377 return KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES
378 | (s->status & KBD_STAT_OBF ? KBD_OUT_OBF : 0)
379 | (s->status & KBD_STAT_MOUSE_OBF ? KBD_OUT_MOUSE_OBF : 0);
380}
381
382static int kbd_outport_post_load(void *opaque, int version_id)
383{
384 KBDState *s = opaque;
385 s->outport_present = true;
386 return 0;
387}
388
389static bool kbd_outport_needed(void *opaque)
390{
391 KBDState *s = opaque;
392 return s->outport != kbd_outport_default(s);
393}
394
395static const VMStateDescription vmstate_kbd_outport = {
396 .name = "pckbd_outport",
397 .version_id = 1,
398 .minimum_version_id = 1,
399 .post_load = kbd_outport_post_load,
400 .needed = kbd_outport_needed,
401 .fields = (VMStateField[]) {
402 VMSTATE_UINT8(outport, KBDState),
403 VMSTATE_END_OF_LIST()
404 }
405};
406
407static int kbd_post_load(void *opaque, int version_id)
408{
409 KBDState *s = opaque;
410 if (!s->outport_present) {
411 s->outport = kbd_outport_default(s);
412 }
413 s->outport_present = false;
414 return 0;
415}
416
417static const VMStateDescription vmstate_kbd = {
418 .name = "pckbd",
419 .version_id = 3,
420 .minimum_version_id = 3,
421 .post_load = kbd_post_load,
422 .fields = (VMStateField[]) {
423 VMSTATE_UINT8(write_cmd, KBDState),
424 VMSTATE_UINT8(status, KBDState),
425 VMSTATE_UINT8(mode, KBDState),
426 VMSTATE_UINT8(pending, KBDState),
427 VMSTATE_END_OF_LIST()
428 },
429 .subsections = (const VMStateDescription*[]) {
430 &vmstate_kbd_outport,
431 NULL
432 }
433};
434
435
436static uint32_t kbd_mm_readb (void *opaque, hwaddr addr)
437{
438 KBDState *s = opaque;
439
440 if (addr & s->mask)
441 return kbd_read_status(s, 0, 1) & 0xff;
442 else
443 return kbd_read_data(s, 0, 1) & 0xff;
444}
445
446static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value)
447{
448 KBDState *s = opaque;
449
450 if (addr & s->mask)
451 kbd_write_command(s, 0, value & 0xff, 1);
452 else
453 kbd_write_data(s, 0, value & 0xff, 1);
454}
455
456static const MemoryRegionOps i8042_mmio_ops = {
457 .endianness = DEVICE_NATIVE_ENDIAN,
458 .old_mmio = {
459 .read = { kbd_mm_readb, kbd_mm_readb, kbd_mm_readb },
460 .write = { kbd_mm_writeb, kbd_mm_writeb, kbd_mm_writeb },
461 },
462};
463
464void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
465 MemoryRegion *region, ram_addr_t size,
466 hwaddr mask)
467{
468 KBDState *s = g_malloc0(sizeof(KBDState));
469
470 s->irq_kbd = kbd_irq;
471 s->irq_mouse = mouse_irq;
472 s->mask = mask;
473
474 vmstate_register(NULL, 0, &vmstate_kbd, s);
475
476 memory_region_init_io(region, NULL, &i8042_mmio_ops, s, "i8042", size);
477
478 s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
479 s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
480 qemu_register_reset(kbd_reset, s);
481}
482
483#define TYPE_I8042 "i8042"
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