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 if (s->a20_out) {
228 qemu_set_irq(*s->a20_out, (val >> 1) & 1);
229 }
230 if (!(val & 1)) {
231 qemu_system_reset_request();
232 }
233}
234
235static void kbd_write_command(void *opaque, hwaddr addr,
236 uint64_t val, unsigned size)
237{
238 KBDState *s = opaque;
239
240 DPRINTF("kbd: write cmd=0x%02" PRIx64 "\n", val);
241
242
243
244
245
246
247
248
249 if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) {
250 if(!(val & 1))
251 val = KBD_CCMD_RESET;
252 else
253 val = KBD_CCMD_NO_OP;
254 }
255
256 switch(val) {
257 case KBD_CCMD_READ_MODE:
258 kbd_queue(s, s->mode, 0);
259 break;
260 case KBD_CCMD_WRITE_MODE:
261 case KBD_CCMD_WRITE_OBUF:
262 case KBD_CCMD_WRITE_AUX_OBUF:
263 case KBD_CCMD_WRITE_MOUSE:
264 case KBD_CCMD_WRITE_OUTPORT:
265 s->write_cmd = val;
266 break;
267 case KBD_CCMD_MOUSE_DISABLE:
268 s->mode |= KBD_MODE_DISABLE_MOUSE;
269 break;
270 case KBD_CCMD_MOUSE_ENABLE:
271 s->mode &= ~KBD_MODE_DISABLE_MOUSE;
272 break;
273 case KBD_CCMD_TEST_MOUSE:
274 kbd_queue(s, 0x00, 0);
275 break;
276 case KBD_CCMD_SELF_TEST:
277 s->status |= KBD_STAT_SELFTEST;
278 kbd_queue(s, 0x55, 0);
279 break;
280 case KBD_CCMD_KBD_TEST:
281 kbd_queue(s, 0x00, 0);
282 break;
283 case KBD_CCMD_KBD_DISABLE:
284 s->mode |= KBD_MODE_DISABLE_KBD;
285 kbd_update_irq(s);
286 break;
287 case KBD_CCMD_KBD_ENABLE:
288 s->mode &= ~KBD_MODE_DISABLE_KBD;
289 kbd_update_irq(s);
290 break;
291 case KBD_CCMD_READ_INPORT:
292 kbd_queue(s, 0x80, 0);
293 break;
294 case KBD_CCMD_READ_OUTPORT:
295 kbd_queue(s, s->outport, 0);
296 break;
297 case KBD_CCMD_ENABLE_A20:
298 if (s->a20_out) {
299 qemu_irq_raise(*s->a20_out);
300 }
301 s->outport |= KBD_OUT_A20;
302 break;
303 case KBD_CCMD_DISABLE_A20:
304 if (s->a20_out) {
305 qemu_irq_lower(*s->a20_out);
306 }
307 s->outport &= ~KBD_OUT_A20;
308 break;
309 case KBD_CCMD_RESET:
310 qemu_system_reset_request();
311 break;
312 case KBD_CCMD_NO_OP:
313
314 break;
315 default:
316 fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", (int)val);
317 break;
318 }
319}
320
321static uint64_t kbd_read_data(void *opaque, hwaddr addr,
322 unsigned size)
323{
324 KBDState *s = opaque;
325 uint32_t val;
326
327 if (s->pending == KBD_PENDING_AUX)
328 val = ps2_read_data(s->mouse);
329 else
330 val = ps2_read_data(s->kbd);
331
332 DPRINTF("kbd: read data=0x%02x\n", val);
333 return val;
334}
335
336static void kbd_write_data(void *opaque, hwaddr addr,
337 uint64_t val, unsigned size)
338{
339 KBDState *s = opaque;
340
341 DPRINTF("kbd: write data=0x%02" PRIx64 "\n", val);
342
343 switch(s->write_cmd) {
344 case 0:
345 ps2_write_keyboard(s->kbd, val);
346 break;
347 case KBD_CCMD_WRITE_MODE:
348 s->mode = val;
349 ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
350
351 kbd_update_irq(s);
352 break;
353 case KBD_CCMD_WRITE_OBUF:
354 kbd_queue(s, val, 0);
355 break;
356 case KBD_CCMD_WRITE_AUX_OBUF:
357 kbd_queue(s, val, 1);
358 break;
359 case KBD_CCMD_WRITE_OUTPORT:
360 outport_write(s, val);
361 break;
362 case KBD_CCMD_WRITE_MOUSE:
363 ps2_write_mouse(s->mouse, val);
364 break;
365 default:
366 break;
367 }
368 s->write_cmd = 0;
369}
370
371static void kbd_reset(void *opaque)
372{
373 KBDState *s = opaque;
374
375 s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
376 s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
377 s->outport = KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES;
378 s->outport_present = false;
379}
380
381static uint8_t kbd_outport_default(KBDState *s)
382{
383 return KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES
384 | (s->status & KBD_STAT_OBF ? KBD_OUT_OBF : 0)
385 | (s->status & KBD_STAT_MOUSE_OBF ? KBD_OUT_MOUSE_OBF : 0);
386}
387
388static int kbd_outport_post_load(void *opaque, int version_id)
389{
390 KBDState *s = opaque;
391 s->outport_present = true;
392 return 0;
393}
394
395static bool kbd_outport_needed(void *opaque)
396{
397 KBDState *s = opaque;
398 return s->outport != kbd_outport_default(s);
399}
400
401static const VMStateDescription vmstate_kbd_outport = {
402 .name = "pckbd_outport",
403 .version_id = 1,
404 .minimum_version_id = 1,
405 .post_load = kbd_outport_post_load,
406 .needed = kbd_outport_needed,
407 .fields = (VMStateField[]) {
408 VMSTATE_UINT8(outport, KBDState),
409 VMSTATE_END_OF_LIST()
410 }
411};
412
413static int kbd_post_load(void *opaque, int version_id)
414{
415 KBDState *s = opaque;
416 if (!s->outport_present) {
417 s->outport = kbd_outport_default(s);
418 }
419 s->outport_present = false;
420 return 0;
421}
422
423static const VMStateDescription vmstate_kbd = {
424 .name = "pckbd",
425 .version_id = 3,
426 .minimum_version_id = 3,
427 .post_load = kbd_post_load,
428 .fields = (VMStateField[]) {
429 VMSTATE_UINT8(write_cmd, KBDState),
430 VMSTATE_UINT8(status, KBDState),
431 VMSTATE_UINT8(mode, KBDState),
432 VMSTATE_UINT8(pending, KBDState),
433 VMSTATE_END_OF_LIST()
434 },
435 .subsections = (const VMStateDescription*[]) {
436 &vmstate_kbd_outport,
437 NULL
438 }
439};
440
441
442static uint32_t kbd_mm_readb (void *opaque, hwaddr addr)
443{
444 KBDState *s = opaque;
445
446 if (addr & s->mask)
447 return kbd_read_status(s, 0, 1) & 0xff;
448 else
449 return kbd_read_data(s, 0, 1) & 0xff;
450}
451
452static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value)
453{
454 KBDState *s = opaque;
455
456 if (addr & s->mask)
457 kbd_write_command(s, 0, value & 0xff, 1);
458 else
459 kbd_write_data(s, 0, value & 0xff, 1);
460}
461
462static const MemoryRegionOps i8042_mmio_ops = {
463 .endianness = DEVICE_NATIVE_ENDIAN,
464 .old_mmio = {
465 .read = { kbd_mm_readb, kbd_mm_readb, kbd_mm_readb },
466 .write = { kbd_mm_writeb, kbd_mm_writeb, kbd_mm_writeb },
467 },
468};
469
470void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
471 MemoryRegion *region, ram_addr_t size,
472 hwaddr mask)
473{
474 KBDState *s = g_malloc0(sizeof(KBDState));
475
476 s->irq_kbd = kbd_irq;
477 s->irq_mouse = mouse_irq;
478 s->mask = mask;
479
480 vmstate_register(NULL, 0, &vmstate_kbd, s);
481
482 memory_region_init_io(region, NULL, &i8042_mmio_ops, s, "i8042", size);
483
484 s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
485 s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
486 qemu_register_reset(kbd_reset, s);
487}
488
489#define TYPE_I8042 "i8042"
490#define I8042(obj) OBJECT_CHECK(ISAKBDState, (obj), TYPE_I8042)
491
492typedef struct ISAKBDState {
493 ISADevice parent_obj;
494
495 KBDState kbd;
496 MemoryRegion io[2];
497} ISAKBDState;
498
499void i8042_isa_mouse_fake_event(void *opaque)
500{
501 ISADevice *dev = opaque;
502 ISAKBDState *isa = I8042(dev);
503 KBDState *s = &isa->kbd;
504
505 ps2_mouse_fake_event(s->mouse);
506}
507
508void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out)
509{
510 ISAKBDState *isa = I8042(dev);
511 KBDState *s = &isa->kbd;
512
513 s->a20_out = a20_out;
514}
515
516static const VMStateDescription vmstate_kbd_isa = {
517 .name = "pckbd",
518 .version_id = 3,
519 .minimum_version_id = 3,
520 .fields = (VMStateField[]) {
521 VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState),
522 VMSTATE_END_OF_LIST()
523 }
524};
525
526static const MemoryRegionOps i8042_data_ops = {
527 .read = kbd_read_data,
528 .write = kbd_write_data,
529 .impl = {
530 .min_access_size = 1,
531 .max_access_size = 1,
532 },
533 .endianness = DEVICE_LITTLE_ENDIAN,
534};
535
536static const MemoryRegionOps i8042_cmd_ops = {
537 .read = kbd_read_status,
538 .write = kbd_write_command,
539 .impl = {
540 .min_access_size = 1,
541 .max_access_size = 1,
542 },
543 .endianness = DEVICE_LITTLE_ENDIAN,
544};
545
546static void i8042_initfn(Object *obj)
547{
548 ISAKBDState *isa_s = I8042(obj);
549 KBDState *s = &isa_s->kbd;
550
551 memory_region_init_io(isa_s->io + 0, obj, &i8042_data_ops, s,
552 "i8042-data", 1);
553 memory_region_init_io(isa_s->io + 1, obj, &i8042_cmd_ops, s,
554 "i8042-cmd", 1);
555}
556
557static void i8042_realizefn(DeviceState *dev, Error **errp)
558{
559 ISADevice *isadev = ISA_DEVICE(dev);
560 ISAKBDState *isa_s = I8042(dev);
561 KBDState *s = &isa_s->kbd;
562
563 isa_init_irq(isadev, &s->irq_kbd, 1);
564 isa_init_irq(isadev, &s->irq_mouse, 12);
565
566 isa_register_ioport(isadev, isa_s->io + 0, 0x60);
567 isa_register_ioport(isadev, isa_s->io + 1, 0x64);
568
569 s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
570 s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
571 qemu_register_reset(kbd_reset, s);
572}
573
574static void i8042_class_initfn(ObjectClass *klass, void *data)
575{
576 DeviceClass *dc = DEVICE_CLASS(klass);
577
578 dc->realize = i8042_realizefn;
579 dc->vmsd = &vmstate_kbd_isa;
580}
581
582static const TypeInfo i8042_info = {
583 .name = TYPE_I8042,
584 .parent = TYPE_ISA_DEVICE,
585 .instance_size = sizeof(ISAKBDState),
586 .instance_init = i8042_initfn,
587 .class_init = i8042_class_initfn,
588};
589
590static void i8042_register_types(void)
591{
592 type_register_static(&i8042_info);
593}
594
595type_init(i8042_register_types)
596