1 |
/* |
/* |
2 |
* Copyright (C) 2003-2005 Anders Gavare. All rights reserved. |
* Copyright (C) 2003-2006 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.51 2005/09/27 23:55:44 debug Exp $ |
* $Id: dev_pckbc.c,v 1.69 2006/07/25 18:58:02 debug Exp $ |
29 |
* |
* |
30 |
* Standard 8042 PC keyboard controller (and a 8242WB PS2 keyboard/mouse |
* Standard 8042 PC keyboard controller (and a 8242WB PS2 keyboard/mouse |
31 |
* controller), including the 8048 keyboard chip. |
* controller), including the 8048 keyboard chip. |
32 |
* |
* |
33 |
|
* Quick source of good info: http://my.execpc.com/~geezer/osd/kbd/kbd.txt |
34 |
* |
* |
35 |
* TODO: Finish the rewrite for 8242. |
* |
36 |
|
* TODOs: |
37 |
|
* Finish the rewrite for 8242. |
38 |
*/ |
*/ |
39 |
|
|
40 |
#include <stdio.h> |
#include <stdio.h> |
101 |
#define STATE_LDCMDBYTE 1 |
#define STATE_LDCMDBYTE 1 |
102 |
#define STATE_RDCMDBYTE 2 |
#define STATE_RDCMDBYTE 2 |
103 |
#define STATE_WAITING_FOR_TRANSLTABLE 3 |
#define STATE_WAITING_FOR_TRANSLTABLE 3 |
104 |
#define STATE_WAITING_FOR_F3 4 |
#define STATE_WAITING_FOR_RATE 4 |
105 |
#define STATE_WAITING_FOR_FC 5 |
#define STATE_WAITING_FOR_ONEKEY_MB 5 |
106 |
#define STATE_LDOUTPUT 6 |
#define STATE_WAITING_FOR_AUX 6 |
107 |
#define STATE_RDOUTPUT 7 |
#define STATE_WAITING_FOR_AUX_OUT 7 |
108 |
|
#define STATE_LDOUTPUT 8 |
109 |
|
#define STATE_RDOUTPUT 9 |
110 |
|
|
111 |
|
|
112 |
/* |
/* |
274 |
|
|
275 |
|
|
276 |
/* |
/* |
277 |
* ascii_to_scancodes(): |
* ascii_to_scancodes_type2(): |
278 |
* |
* |
279 |
* Conversion from ASCII codes to default (US) keyboard scancodes. |
* Conversion from ASCII codes to default (US) keyboard scancodes. |
280 |
* (See http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html) |
* (See http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html) |
281 |
|
* |
282 |
|
* NOTE/TODO: This seems to be type 2, not type 1. |
283 |
*/ |
*/ |
284 |
static void ascii_to_pc_scancodes(int a, struct pckbc_data *d) |
static void ascii_to_pc_scancodes_type2(int a, struct pckbc_data *d) |
285 |
{ |
{ |
286 |
int old_head; |
int old_head; |
287 |
int p = 0; /* port */ |
int p = 0; /* port */ |
292 |
return; |
return; |
293 |
} |
} |
294 |
|
|
295 |
if (d->translation_table != 1) { |
if (d->translation_table != 2) { |
296 |
fatal("[ ascii_to_pc_scancodes: unimplemented type! ]\n"); |
fatal("[ ascii_to_pc_scancodes: unimplemented type! ]\n"); |
297 |
return; |
return; |
298 |
} |
} |
321 |
if (a=='<') { a = ','; shift = 1; } |
if (a=='<') { a = ','; shift = 1; } |
322 |
if (a=='>') { a = '.'; shift = 1; } |
if (a=='>') { a = '.'; shift = 1; } |
323 |
if (a=='?') { a = '/'; shift = 1; } |
if (a=='?') { a = '/'; shift = 1; } |
324 |
|
if (a=='~') { a = '`'; shift = 1; } |
325 |
|
|
326 |
if (shift) |
if (shift) |
327 |
pckbc_add_code(d, 0x2a, p); |
pckbc_add_code(d, 0x2a, p); |
407 |
|
|
408 |
if (a==';') pckbc_add_code(d, 0x27, p); |
if (a==';') pckbc_add_code(d, 0x27, p); |
409 |
if (a=='\'') pckbc_add_code(d, 0x28, p); |
if (a=='\'') pckbc_add_code(d, 0x28, p); |
410 |
if (a=='~') pckbc_add_code(d, 0x29, p); |
if (a=='`') pckbc_add_code(d, 0x29, p); |
411 |
if (a=='\\') pckbc_add_code(d, 0x2b, p); |
if (a=='\\') pckbc_add_code(d, 0x2b, p); |
412 |
|
|
413 |
if (a=='z') pckbc_add_code(d, 0x2c, p); |
if (a=='z') pckbc_add_code(d, 0x2c, p); |
447 |
if (d->in_use && console_charavail(d->console_handle)) { |
if (d->in_use && console_charavail(d->console_handle)) { |
448 |
ch = console_readchar(d->console_handle); |
ch = console_readchar(d->console_handle); |
449 |
if (ch >= 0) |
if (ch >= 0) |
450 |
ascii_to_pc_scancodes(ch, d); |
ascii_to_pc_scancodes_type2(ch, d); |
451 |
} |
} |
452 |
|
|
453 |
ints_enabled = d->rx_int_enable; |
ints_enabled = d->rx_int_enable; |
460 |
for (port_nr=0; port_nr<2; port_nr++) { |
for (port_nr=0; port_nr<2; port_nr++) { |
461 |
/* Cause receive interrupt, if there's something in the |
/* Cause receive interrupt, if there's something in the |
462 |
receive buffer: (Otherwise deassert the interrupt.) */ |
receive buffer: (Otherwise deassert the interrupt.) */ |
463 |
|
int irq = port_nr==0? d->keyboard_irqnr : d->mouse_irqnr; |
464 |
|
|
465 |
if (d->head[port_nr] != d->tail[port_nr] && ints_enabled) { |
if (d->head[port_nr] != d->tail[port_nr] && ints_enabled) { |
466 |
debug("[ pckbc: interrupt port %i ]\n", port_nr); |
debug("[ pckbc: interrupt port %i ]\n", port_nr); |
467 |
cpu_interrupt(cpu, port_nr==0? d->keyboard_irqnr |
cpu_interrupt(cpu, irq); |
|
: d->mouse_irqnr); |
|
468 |
d->currently_asserted[port_nr] = 1; |
d->currently_asserted[port_nr] = 1; |
469 |
} else { |
} else { |
470 |
if (d->currently_asserted[port_nr]) |
if (d->currently_asserted[port_nr]) |
471 |
cpu_interrupt_ack(cpu, port_nr==0? |
cpu_interrupt_ack(cpu, irq); |
|
d->keyboard_irqnr : d->mouse_irqnr); |
|
472 |
d->currently_asserted[port_nr] = 0; |
d->currently_asserted[port_nr] = 0; |
473 |
} |
} |
474 |
} |
} |
491 |
debug("[ pckbc: (port %i) switching to translation table " |
debug("[ pckbc: (port %i) switching to translation table " |
492 |
"0x%02x ]\n", port_nr, cmd); |
"0x%02x ]\n", port_nr, cmd); |
493 |
switch (cmd) { |
switch (cmd) { |
494 |
case 1: |
case 2: |
495 |
case 3: d->translation_table = cmd; |
case 3: d->translation_table = cmd; |
496 |
break; |
break; |
497 |
default:fatal("[ pckbc: (port %i) translation table " |
default:fatal("[ pckbc: (port %i) translation table " |
498 |
"0x%02x is NOT YET IMPLEMENTED ]\n", port_nr, cmd); |
"0x%02x is NOT YET IMPLEMENTED ]\n", |
499 |
|
port_nr, cmd); |
500 |
} |
} |
501 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
502 |
d->state = STATE_NORMAL; |
d->state = STATE_NORMAL; |
503 |
return; |
return; |
504 |
} |
} |
505 |
|
|
506 |
if (d->state == STATE_WAITING_FOR_F3) { |
if (d->state == STATE_WAITING_FOR_RATE) { |
507 |
debug("[ pckbc: (port %i) received '0xf3' data: " |
debug("[ pckbc: (port %i) received Typematic Rate data: " |
508 |
"0x%02x ]\n", port_nr, cmd); |
"0x%02x ]\n", port_nr, cmd); |
509 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
510 |
d->state = STATE_NORMAL; |
d->state = STATE_NORMAL; |
511 |
return; |
return; |
512 |
} |
} |
513 |
|
|
514 |
if (d->state == STATE_WAITING_FOR_FC) { |
if (d->state == STATE_WAITING_FOR_ONEKEY_MB) { |
515 |
debug("[ pckbc: (port %i) received '0xfc' data: " |
debug("[ pckbc: (port %i) received One-key make/break data: " |
516 |
"0x%02x ]\n", port_nr, cmd); |
"0x%02x ]\n", port_nr, cmd); |
517 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
518 |
d->state = STATE_NORMAL; |
d->state = STATE_NORMAL; |
519 |
return; |
return; |
520 |
} |
} |
521 |
|
|
522 |
|
if (d->state == STATE_WAITING_FOR_AUX) { |
523 |
|
debug("[ pckbc: (port %i) received aux data: " |
524 |
|
"0x%02x ]\n", port_nr, cmd); |
525 |
|
/* Echo back. */ |
526 |
|
pckbc_add_code(d, cmd, port_nr); |
527 |
|
d->state = STATE_NORMAL; |
528 |
|
return; |
529 |
|
} |
530 |
|
|
531 |
|
if (d->state == STATE_WAITING_FOR_AUX_OUT) { |
532 |
|
debug("[ pckbc: (port %i) received aux out data: " |
533 |
|
"0x%02x ]\n", port_nr, cmd); |
534 |
|
/* Echo back. */ |
535 |
|
pckbc_add_code(d, cmd, port_nr); |
536 |
|
d->state = STATE_NORMAL; |
537 |
|
return; |
538 |
|
} |
539 |
|
|
540 |
switch (cmd) { |
switch (cmd) { |
541 |
|
|
542 |
case 0x00: |
case 0x00: |
543 |
|
/* |
544 |
|
* TODO: What does this do? This is possibly due to an |
545 |
|
* error in the handling of some other command code. |
546 |
|
*/ |
547 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
548 |
break; |
break; |
549 |
|
|
550 |
case KBC_MODEIND: /* Set LEDs */ |
case KBC_MODEIND: /* Set LEDs */ |
551 |
/* Just ACK, no LEDs are actually set. */ |
/* Just ACK, no LEDs are actually set. */ |
552 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
553 |
break; |
break; |
554 |
|
|
555 |
case KBC_SETTABLE: |
case KBC_SETTABLE: |
556 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
557 |
d->state = STATE_WAITING_FOR_TRANSLTABLE; |
d->state = STATE_WAITING_FOR_TRANSLTABLE; |
558 |
break; |
break; |
559 |
|
|
560 |
case KBC_ENABLE: |
case KBC_ENABLE: |
561 |
d->keyscanning_enabled = 1; |
d->keyscanning_enabled = 1; |
562 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
563 |
break; |
break; |
564 |
|
|
565 |
case KBC_DISABLE: |
case KBC_DISABLE: |
566 |
d->keyscanning_enabled = 0; |
d->keyscanning_enabled = 0; |
567 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
568 |
break; |
break; |
569 |
|
|
570 |
case KBC_SETDEFAULT: |
case KBC_SETDEFAULT: |
571 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
572 |
break; |
break; |
573 |
case 0xf3: |
|
574 |
|
case KBC_GETID: |
575 |
|
/* Get keyboard ID. NOTE/TODO: Ugly hardcoded answer. */ |
576 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
577 |
d->state = STATE_WAITING_FOR_F3; |
pckbc_add_code(d, 0xab, port_nr); |
578 |
|
pckbc_add_code(d, 0x41, port_nr); |
579 |
break; |
break; |
580 |
case 0xfa: /* Just ack? */ |
|
581 |
|
case KBC_TYPEMATIC: |
582 |
|
/* Set typematic (auto-repeat) delay/speed: */ |
583 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
584 |
|
d->state = STATE_WAITING_FOR_RATE; |
585 |
break; |
break; |
586 |
case 0xfc: |
|
587 |
|
case KBC_ALLKEYS_TMB: |
588 |
|
/* "Make all keys typematic/make/break" */ |
589 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
|
d->state = STATE_WAITING_FOR_FC; |
|
590 |
break; |
break; |
591 |
|
|
592 |
|
case KBC_ONEKEY_MB: |
593 |
|
/* "Make one key typematic/make/break" */ |
594 |
|
pckbc_add_code(d, KBR_ACK, port_nr); |
595 |
|
d->state = STATE_WAITING_FOR_ONEKEY_MB; |
596 |
|
break; |
597 |
|
|
598 |
case KBC_RESET: |
case KBC_RESET: |
599 |
pckbc_add_code(d, KBR_ACK, port_nr); |
pckbc_add_code(d, KBR_ACK, port_nr); |
600 |
pckbc_add_code(d, KBR_RSTDONE, port_nr); |
pckbc_add_code(d, KBR_RSTDONE, port_nr); |
601 |
|
/* |
602 |
|
* Disable interrupts during reset, or Linux 2.6 |
603 |
|
* prints warnings about spurious interrupts. |
604 |
|
*/ |
605 |
|
d->rx_int_enable = 0; |
606 |
break; |
break; |
607 |
|
|
608 |
default: |
default: |
609 |
fatal("[ pckbc: (port %i) UNIMPLEMENTED 8048 command" |
fatal("[ pckbc: (port %i) UNIMPLEMENTED 8048 command" |
610 |
" 0x%02x ]\n", port_nr, cmd); |
" 0x%02x ]\n", port_nr, cmd); |
611 |
|
exit(1); |
612 |
} |
} |
613 |
} |
} |
614 |
|
|
615 |
|
|
616 |
/* |
DEVICE_ACCESS(pckbc) |
|
* dev_pckbc_access(): |
|
|
*/ |
|
|
int dev_pckbc_access(struct cpu *cpu, struct memory *mem, |
|
|
uint64_t relative_addr, unsigned char *data, size_t len, |
|
|
int writeflag, void *extra) |
|
617 |
{ |
{ |
618 |
uint64_t idata = 0, odata = 0; |
uint64_t idata = 0, odata = 0; |
619 |
int i, port_nr = 0; |
int port_nr = 0; |
620 |
|
size_t i; |
621 |
struct pckbc_data *d = extra; |
struct pckbc_data *d = extra; |
622 |
|
|
623 |
idata = memory_readmax64(cpu, data, len); |
if (writeflag == MEM_WRITE) |
624 |
|
idata = memory_readmax64(cpu, data, len); |
625 |
|
|
626 |
#ifdef PCKBC_DEBUG |
#ifdef PCKBC_DEBUG |
627 |
if (writeflag == MEM_WRITE) |
if (writeflag == MEM_WRITE) |
694 |
} |
} |
695 |
} |
} |
696 |
/* debug("[ pckbc: read from DATA: 0x%02x ]\n", |
/* debug("[ pckbc: read from DATA: 0x%02x ]\n", |
697 |
odata); */ |
(int)odata); */ |
698 |
} else { |
} else { |
699 |
debug("[ pckbc: write to DATA:"); |
debug("[ pckbc: write to DATA:"); |
700 |
for (i=0; i<len; i++) |
for (i=0; i<len; i++) |
743 |
d->reg[relative_addr] = idata; |
d->reg[relative_addr] = idata; |
744 |
|
|
745 |
switch (idata) { |
switch (idata) { |
746 |
|
case 0x10: |
747 |
|
case 0x11: |
748 |
|
/* TODO: For now, don't print warnings about |
749 |
|
these. NetBSD sends these. */ |
750 |
|
break; |
751 |
case K_RDCMDBYTE: |
case K_RDCMDBYTE: |
752 |
d->state = STATE_RDCMDBYTE; |
d->state = STATE_RDCMDBYTE; |
753 |
break; |
break; |
766 |
case 0xaa: /* keyboard self-test */ |
case 0xaa: /* keyboard self-test */ |
767 |
pckbc_add_code(d, 0x55, port_nr); |
pckbc_add_code(d, 0x55, port_nr); |
768 |
break; |
break; |
769 |
|
case 0xab: /* keyboard interface self-test */ |
770 |
|
pckbc_add_code(d, 0x00, port_nr); |
771 |
|
break; |
772 |
case 0xad: |
case 0xad: |
773 |
d->cmdbyte |= KC8_KDISABLE; |
d->cmdbyte |= KC8_KDISABLE; |
774 |
break; |
break; |
781 |
case 0xd1: |
case 0xd1: |
782 |
d->state = STATE_LDOUTPUT; |
d->state = STATE_LDOUTPUT; |
783 |
break; |
break; |
784 |
|
case 0xd3: /* write to auxiliary device |
785 |
|
output buffer */ |
786 |
|
debug("[ pckbc: CONTROL 0xd3, TODO ]\n"); |
787 |
|
d->state = STATE_WAITING_FOR_AUX_OUT; |
788 |
|
break; |
789 |
case 0xd4: /* write to auxiliary port */ |
case 0xd4: /* write to auxiliary port */ |
790 |
debug("[ pckbc: CONTROL 0xd4, TODO ]\n"); |
debug("[ pckbc: CONTROL 0xd4, TODO ]\n"); |
791 |
|
d->state = STATE_WAITING_FOR_AUX; |
792 |
break; |
break; |
793 |
default: |
default: |
794 |
fatal("[ pckbc: unknown CONTROL 0x%x ]\n", |
fatal("[ pckbc: unknown CONTROL 0x%x ]\n", |
795 |
idata); |
(int)idata); |
796 |
d->state = STATE_NORMAL; |
d->state = STATE_NORMAL; |
797 |
} |
} |
798 |
} |
} |
878 |
} |
} |
879 |
} |
} |
880 |
|
|
881 |
/* SGI? */ |
/* SGI? TODO: fix */ |
882 |
if (len == 8) |
if (len == 8) |
883 |
odata |= (odata << 8) | (odata << 16) | (odata << 24) | |
odata |= (odata << 8) | (odata << 16) | (odata << 24) | |
884 |
(odata << 32) | (odata << 40) | (odata << 48) | |
(odata << 32) | (odata << 40) | (odata << 48) | |
913 |
memset(d, 0, sizeof(struct pckbc_data)); |
memset(d, 0, sizeof(struct pckbc_data)); |
914 |
|
|
915 |
if (type == PCKBC_8242) |
if (type == PCKBC_8242) |
916 |
len = 0x40; |
len = 0x18; |
917 |
|
|
918 |
if (type == PCKBC_JAZZ) { |
if (type == PCKBC_JAZZ) { |
919 |
type = PCKBC_8042; |
type = PCKBC_8042; |
925 |
d->mouse_irqnr = mouse_irqnr; |
d->mouse_irqnr = mouse_irqnr; |
926 |
d->in_use = in_use; |
d->in_use = in_use; |
927 |
d->pc_style_flag = pc_style_flag; |
d->pc_style_flag = pc_style_flag; |
928 |
d->console_handle = console_start_slave_inputonly(machine, "pckbc"); |
d->translation_table = 2; |
|
d->translation_table = 1; |
|
929 |
d->rx_int_enable = 1; |
d->rx_int_enable = 1; |
930 |
d->output_byte = 0x02; /* A20 enable on PCs */ |
d->output_byte = 0x02; /* A20 enable on PCs */ |
931 |
|
|
932 |
|
d->console_handle = console_start_slave_inputonly( |
933 |
|
machine, "pckbc", d->in_use); |
934 |
|
|
935 |
memory_device_register(mem, "pckbc", baseaddr, |
memory_device_register(mem, "pckbc", baseaddr, |
936 |
len, dev_pckbc_access, d, MEM_DEFAULT, NULL); |
len, dev_pckbc_access, d, DM_DEFAULT, NULL); |
937 |
machine_add_tickfunction(machine, dev_pckbc_tick, d, PCKBC_TICKSHIFT); |
machine_add_tickfunction(machine, dev_pckbc_tick, d, |
938 |
|
PCKBC_TICKSHIFT, 0.0); |
939 |
|
|
940 |
return d->console_handle; |
return d->console_handle; |
941 |
} |
} |