1 |
/* |
/* |
2 |
* Copyright (C) 2003-2006 Anders Gavare. All rights reserved. |
* Copyright (C) 2003-2007 Anders Gavare. All rights reserved. |
3 |
* |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
* modification, are permitted provided that the following conditions are met: |
25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: dev_pckbc.c,v 1.64 2006/01/01 13:17:16 debug Exp $ |
* $Id: dev_pckbc.c,v 1.74 2007/06/15 19:57:33 debug Exp $ |
29 |
* |
* |
30 |
* Standard 8042 PC keyboard controller (and a 8242WB PS2 keyboard/mouse |
* COMMENT: 8042 PC keyboard controller (+ 8242WB Keyboard/Mouse controller) |
|
* controller), including the 8048 keyboard chip. |
|
31 |
* |
* |
32 |
|
* This module includes emulation of the 8048 keyboard chip too. |
33 |
* |
* |
34 |
* TODO: Finish the rewrite for 8242. |
* Quick source of good info: http://my.execpc.com/~geezer/osd/kbd/kbd.txt |
35 |
|
* |
36 |
|
* |
37 |
|
* TODOs: |
38 |
|
* Finish the rewrite for 8242. |
39 |
*/ |
*/ |
40 |
|
|
41 |
#include <stdio.h> |
#include <stdio.h> |
76 |
int in_use; |
int in_use; |
77 |
|
|
78 |
int reg[DEV_PCKBC_LENGTH]; |
int reg[DEV_PCKBC_LENGTH]; |
79 |
int keyboard_irqnr; |
|
80 |
int mouse_irqnr; |
struct interrupt irq_keyboard; |
81 |
|
struct interrupt irq_mouse; |
82 |
int currently_asserted[2]; |
int currently_asserted[2]; |
83 |
int type; |
int type; |
84 |
int pc_style_flag; |
int pc_style_flag; |
103 |
#define STATE_LDCMDBYTE 1 |
#define STATE_LDCMDBYTE 1 |
104 |
#define STATE_RDCMDBYTE 2 |
#define STATE_RDCMDBYTE 2 |
105 |
#define STATE_WAITING_FOR_TRANSLTABLE 3 |
#define STATE_WAITING_FOR_TRANSLTABLE 3 |
106 |
#define STATE_WAITING_FOR_F3 4 |
#define STATE_WAITING_FOR_RATE 4 |
107 |
#define STATE_WAITING_FOR_FC 5 |
#define STATE_WAITING_FOR_ONEKEY_MB 5 |
108 |
#define STATE_WAITING_FOR_AUX 6 |
#define STATE_WAITING_FOR_AUX 6 |
109 |
#define STATE_WAITING_FOR_AUX_OUT 7 |
#define STATE_WAITING_FOR_AUX_OUT 7 |
110 |
#define STATE_LDOUTPUT 8 |
#define STATE_LDOUTPUT 8 |
438 |
} |
} |
439 |
|
|
440 |
|
|
441 |
/* |
DEVICE_TICK(pckbc) |
|
* dev_pckbc_tick(): |
|
|
*/ |
|
|
void dev_pckbc_tick(struct cpu *cpu, void *extra) |
|
442 |
{ |
{ |
443 |
struct pckbc_data *d = extra; |
struct pckbc_data *d = extra; |
444 |
int port_nr, ch, ints_enabled; |
int port_nr, ch, ints_enabled; |
459 |
for (port_nr=0; port_nr<2; port_nr++) { |
for (port_nr=0; port_nr<2; port_nr++) { |
460 |
/* Cause receive interrupt, if there's something in the |
/* Cause receive interrupt, if there's something in the |
461 |
receive buffer: (Otherwise deassert the interrupt.) */ |
receive buffer: (Otherwise deassert the interrupt.) */ |
462 |
|
|
463 |
if (d->head[port_nr] != d->tail[port_nr] && ints_enabled) { |
if (d->head[port_nr] != d->tail[port_nr] && ints_enabled) { |
464 |
debug("[ pckbc: interrupt port %i ]\n", port_nr); |
debug("[ pckbc: interrupt port %i ]\n", port_nr); |
465 |
cpu_interrupt(cpu, port_nr==0? d->keyboard_irqnr |
if (port_nr == 0) |
466 |
: d->mouse_irqnr); |
INTERRUPT_ASSERT(d->irq_keyboard); |
467 |
|
else |
468 |
|
INTERRUPT_ASSERT(d->irq_mouse); |
469 |
d->currently_asserted[port_nr] = 1; |
d->currently_asserted[port_nr] = 1; |
470 |
} else { |
} else { |
471 |
if (d->currently_asserted[port_nr]) |
if (d->currently_asserted[port_nr]) { |
472 |
cpu_interrupt_ack(cpu, port_nr==0? |
if (port_nr == 0) |
473 |
d->keyboard_irqnr : d->mouse_irqnr); |
INTERRUPT_DEASSERT(d->irq_keyboard); |
474 |
|
else |
475 |
|
INTERRUPT_DEASSERT(d->irq_mouse); |
476 |
|
} |
477 |
d->currently_asserted[port_nr] = 0; |
d->currently_asserted[port_nr] = 0; |
478 |
} |
} |
479 |
} |
} |
508 |
return; |
return; |
509 |
} |
} |
510 |
|
|
511 |
if (d->state == STATE_WAITING_FOR_F3) { |
if (d->state == STATE_WAITING_FOR_RATE) { |
512 |
debug("[ pckbc: (port %i) received '0xf3' data: " |
debug("[ pckbc: (port %i) received Typematic Rate data: " |
513 |
"0x%02x ]\n", port_nr, cmd); |
"0x%02x ]\n", port_nr, cmd); |
514 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
515 |
d->state = STATE_NORMAL; |
d->state = STATE_NORMAL; |
516 |
return; |
return; |
517 |
} |
} |
518 |
|
|
519 |
if (d->state == STATE_WAITING_FOR_FC) { |
if (d->state == STATE_WAITING_FOR_ONEKEY_MB) { |
520 |
debug("[ pckbc: (port %i) received '0xfc' data: " |
debug("[ pckbc: (port %i) received One-key make/break data: " |
521 |
"0x%02x ]\n", port_nr, cmd); |
"0x%02x ]\n", port_nr, cmd); |
522 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
523 |
d->state = STATE_NORMAL; |
d->state = STATE_NORMAL; |
543 |
} |
} |
544 |
|
|
545 |
switch (cmd) { |
switch (cmd) { |
546 |
|
|
547 |
case 0x00: |
case 0x00: |
548 |
|
/* |
549 |
|
* TODO: What does this do? This is possibly due to an |
550 |
|
* error in the handling of some other command code. |
551 |
|
*/ |
552 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
553 |
break; |
break; |
554 |
|
|
555 |
case KBC_MODEIND: /* Set LEDs */ |
case KBC_MODEIND: /* Set LEDs */ |
556 |
/* Just ACK, no LEDs are actually set. */ |
/* Just ACK, no LEDs are actually set. */ |
557 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
558 |
break; |
break; |
559 |
|
|
560 |
case KBC_SETTABLE: |
case KBC_SETTABLE: |
561 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
562 |
d->state = STATE_WAITING_FOR_TRANSLTABLE; |
d->state = STATE_WAITING_FOR_TRANSLTABLE; |
563 |
break; |
break; |
564 |
|
|
565 |
case KBC_ENABLE: |
case KBC_ENABLE: |
566 |
d->keyscanning_enabled = 1; |
d->keyscanning_enabled = 1; |
567 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
568 |
break; |
break; |
569 |
|
|
570 |
case KBC_DISABLE: |
case KBC_DISABLE: |
571 |
d->keyscanning_enabled = 0; |
d->keyscanning_enabled = 0; |
572 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
573 |
break; |
break; |
574 |
|
|
575 |
case KBC_SETDEFAULT: |
case KBC_SETDEFAULT: |
576 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
577 |
break; |
break; |
578 |
case 0xf3: |
|
579 |
|
case KBC_GETID: |
580 |
|
/* Get keyboard ID. NOTE/TODO: Ugly hardcoded answer. */ |
581 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
582 |
d->state = STATE_WAITING_FOR_F3; |
pckbc_add_code(d, 0xab, port_nr); |
583 |
|
pckbc_add_code(d, 0x41, port_nr); |
584 |
break; |
break; |
585 |
case 0xfa: /* Just ack? */ |
|
586 |
|
case KBC_TYPEMATIC: |
587 |
|
/* Set typematic (auto-repeat) delay/speed: */ |
588 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
589 |
|
d->state = STATE_WAITING_FOR_RATE; |
590 |
break; |
break; |
591 |
case 0xfc: |
|
592 |
|
case KBC_ALLKEYS_TMB: |
593 |
|
/* "Make all keys typematic/make/break" */ |
594 |
|
pckbc_add_code(d, KBR_ACK, port_nr); |
595 |
|
break; |
596 |
|
|
597 |
|
case KBC_ONEKEY_MB: |
598 |
|
/* "Make one key typematic/make/break" */ |
599 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
600 |
d->state = STATE_WAITING_FOR_FC; |
d->state = STATE_WAITING_FOR_ONEKEY_MB; |
601 |
break; |
break; |
602 |
|
|
603 |
case KBC_RESET: |
case KBC_RESET: |
604 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
605 |
pckbc_add_code(d, KBR_RSTDONE, port_nr); |
pckbc_add_code(d, KBR_RSTDONE, port_nr); |
606 |
|
/* |
607 |
|
* Disable interrupts during reset, or Linux 2.6 |
608 |
|
* prints warnings about spurious interrupts. |
609 |
|
*/ |
610 |
|
d->rx_int_enable = 0; |
611 |
break; |
break; |
612 |
|
|
613 |
default: |
default: |
614 |
fatal("[ pckbc: (port %i) UNIMPLEMENTED 8048 command" |
fatal("[ pckbc: (port %i) UNIMPLEMENTED 8048 command" |
615 |
" 0x%02x ]\n", port_nr, cmd); |
" 0x%02x ]\n", port_nr, cmd); |
616 |
|
exit(1); |
617 |
} |
} |
618 |
} |
} |
619 |
|
|
620 |
|
|
|
/* |
|
|
* dev_pckbc_access(): |
|
|
*/ |
|
621 |
DEVICE_ACCESS(pckbc) |
DEVICE_ACCESS(pckbc) |
622 |
{ |
{ |
623 |
|
struct pckbc_data *d = extra; |
624 |
uint64_t idata = 0, odata = 0; |
uint64_t idata = 0, odata = 0; |
625 |
int port_nr = 0; |
int port_nr = 0; |
626 |
size_t i; |
size_t i; |
|
struct pckbc_data *d = extra; |
|
627 |
|
|
628 |
if (writeflag == MEM_WRITE) |
if (writeflag == MEM_WRITE) |
629 |
idata = memory_readmax64(cpu, data, len); |
idata = memory_readmax64(cpu, data, len); |
663 |
} |
} |
664 |
if (writeflag == MEM_READ) |
if (writeflag == MEM_READ) |
665 |
memory_writemax64(cpu, data, len, odata); |
memory_writemax64(cpu, data, len, odata); |
666 |
return 0; |
return 1; |
667 |
} |
} |
668 |
if (relative_addr != 0) |
if (relative_addr != 0) |
669 |
relative_addr = 1; |
relative_addr = 1; |
904 |
* Type should be PCKBC_8042 or PCKBC_8242. |
* Type should be PCKBC_8042 or PCKBC_8242. |
905 |
*/ |
*/ |
906 |
int dev_pckbc_init(struct machine *machine, struct memory *mem, |
int dev_pckbc_init(struct machine *machine, struct memory *mem, |
907 |
uint64_t baseaddr, int type, int keyboard_irqnr, int mouse_irqnr, |
uint64_t baseaddr, int type, char *keyboard_irqpath, |
908 |
int in_use, int pc_style_flag) |
char *mouse_irqpath, int in_use, int pc_style_flag) |
909 |
{ |
{ |
910 |
struct pckbc_data *d; |
struct pckbc_data *d; |
911 |
int len = DEV_PCKBC_LENGTH; |
int len = DEV_PCKBC_LENGTH; |
912 |
|
|
913 |
d = malloc(sizeof(struct pckbc_data)); |
CHECK_ALLOCATION(d = malloc(sizeof(struct pckbc_data))); |
|
if (d == NULL) { |
|
|
fprintf(stderr, "out of memory\n"); |
|
|
exit(1); |
|
|
} |
|
914 |
memset(d, 0, sizeof(struct pckbc_data)); |
memset(d, 0, sizeof(struct pckbc_data)); |
915 |
|
|
916 |
if (type == PCKBC_8242) |
if (type == PCKBC_8242) |
921 |
len = DEV_PCKBC_LENGTH + 0x60; |
len = DEV_PCKBC_LENGTH + 0x60; |
922 |
} |
} |
923 |
|
|
924 |
|
INTERRUPT_CONNECT(keyboard_irqpath, d->irq_keyboard); |
925 |
|
INTERRUPT_CONNECT(mouse_irqpath, d->irq_mouse); |
926 |
|
|
927 |
d->type = type; |
d->type = type; |
|
d->keyboard_irqnr = keyboard_irqnr; |
|
|
d->mouse_irqnr = mouse_irqnr; |
|
928 |
d->in_use = in_use; |
d->in_use = in_use; |
929 |
d->pc_style_flag = pc_style_flag; |
d->pc_style_flag = pc_style_flag; |
930 |
d->translation_table = 2; |
d->translation_table = 2; |
936 |
|
|
937 |
memory_device_register(mem, "pckbc", baseaddr, |
memory_device_register(mem, "pckbc", baseaddr, |
938 |
len, dev_pckbc_access, d, DM_DEFAULT, NULL); |
len, dev_pckbc_access, d, DM_DEFAULT, NULL); |
939 |
machine_add_tickfunction(machine, dev_pckbc_tick, d, PCKBC_TICKSHIFT); |
machine_add_tickfunction(machine, dev_pckbc_tick, d, |
940 |
|
PCKBC_TICKSHIFT); |
941 |
|
|
942 |
return d->console_handle; |
return d->console_handle; |
943 |
} |
} |