249 lines
5.0 KiB
C
249 lines
5.0 KiB
C
#include <linux/keyboard.h>
|
|
|
|
#include "defkeymap.c" /* yeah I know it's bad -- Cort */
|
|
|
|
|
|
unsigned char shfts, ctls, alts, caps;
|
|
|
|
#define KBDATAP 0x60 /* kbd data port */
|
|
#define KBSTATUSPORT 0x61 /* kbd status */
|
|
#define KBSTATP 0x64 /* kbd status port */
|
|
#define KBINRDY 0x01
|
|
#define KBOUTRDY 0x02
|
|
|
|
extern unsigned char inb(int port);
|
|
extern void outb(int port, char val);
|
|
extern void puts(const char *);
|
|
extern void puthex(unsigned long val);
|
|
extern void udelay(long x);
|
|
|
|
static int kbd(int noblock)
|
|
{
|
|
unsigned char dt, brk, val;
|
|
unsigned code;
|
|
loop:
|
|
if (noblock) {
|
|
if ((inb(KBSTATP) & KBINRDY) == 0)
|
|
return (-1);
|
|
} else while((inb(KBSTATP) & KBINRDY) == 0) ;
|
|
|
|
dt = inb(KBDATAP);
|
|
|
|
brk = dt & 0x80; /* brk == 1 on key release */
|
|
dt = dt & 0x7f; /* keycode */
|
|
|
|
if (shfts)
|
|
code = shift_map[dt];
|
|
else if (ctls)
|
|
code = ctrl_map[dt];
|
|
else
|
|
code = plain_map[dt];
|
|
|
|
val = KVAL(code);
|
|
switch (KTYP(code) & 0x0f) {
|
|
case KT_LATIN:
|
|
if (brk)
|
|
break;
|
|
if (alts)
|
|
val |= 0x80;
|
|
if (val == 0x7f) /* map delete to backspace */
|
|
val = '\b';
|
|
return val;
|
|
|
|
case KT_LETTER:
|
|
if (brk)
|
|
break;
|
|
if (caps)
|
|
val -= 'a'-'A';
|
|
return val;
|
|
|
|
case KT_SPEC:
|
|
if (brk)
|
|
break;
|
|
if (val == KVAL(K_CAPS))
|
|
caps = !caps;
|
|
else if (val == KVAL(K_ENTER)) {
|
|
enter: /* Wait for key up */
|
|
while (1) {
|
|
while((inb(KBSTATP) & KBINRDY) == 0) ;
|
|
dt = inb(KBDATAP);
|
|
if (dt & 0x80) /* key up */ break;
|
|
}
|
|
return 10;
|
|
}
|
|
break;
|
|
|
|
case KT_PAD:
|
|
if (brk)
|
|
break;
|
|
if (val < 10)
|
|
return val;
|
|
if (val == KVAL(K_PENTER))
|
|
goto enter;
|
|
break;
|
|
|
|
case KT_SHIFT:
|
|
switch (val) {
|
|
case KG_SHIFT:
|
|
case KG_SHIFTL:
|
|
case KG_SHIFTR:
|
|
shfts = brk ? 0 : 1;
|
|
break;
|
|
case KG_ALT:
|
|
case KG_ALTGR:
|
|
alts = brk ? 0 : 1;
|
|
break;
|
|
case KG_CTRL:
|
|
case KG_CTRLL:
|
|
case KG_CTRLR:
|
|
ctls = brk ? 0 : 1;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case KT_LOCK:
|
|
switch (val) {
|
|
case KG_SHIFT:
|
|
case KG_SHIFTL:
|
|
case KG_SHIFTR:
|
|
if (brk)
|
|
shfts = !shfts;
|
|
break;
|
|
case KG_ALT:
|
|
case KG_ALTGR:
|
|
if (brk)
|
|
alts = !alts;
|
|
break;
|
|
case KG_CTRL:
|
|
case KG_CTRLL:
|
|
case KG_CTRLR:
|
|
if (brk)
|
|
ctls = !ctls;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (brk) return (-1); /* Ignore initial 'key up' codes */
|
|
goto loop;
|
|
}
|
|
|
|
static int __kbdreset(void)
|
|
{
|
|
unsigned char c;
|
|
int i, t;
|
|
|
|
/* flush input queue */
|
|
t = 2000;
|
|
while ((inb(KBSTATP) & KBINRDY))
|
|
{
|
|
(void)inb(KBDATAP);
|
|
if (--t == 0)
|
|
return 1;
|
|
}
|
|
/* Send self-test */
|
|
t = 20000;
|
|
while (inb(KBSTATP) & KBOUTRDY)
|
|
if (--t == 0)
|
|
return 2;
|
|
outb(KBSTATP,0xAA);
|
|
t = 200000;
|
|
while ((inb(KBSTATP) & KBINRDY) == 0) /* wait input ready */
|
|
if (--t == 0)
|
|
return 3;
|
|
if ((c = inb(KBDATAP)) != 0x55)
|
|
{
|
|
puts("Keyboard self test failed - result:");
|
|
puthex(c);
|
|
puts("\n");
|
|
}
|
|
/* Enable interrupts and keyboard controller */
|
|
t = 20000;
|
|
while (inb(KBSTATP) & KBOUTRDY)
|
|
if (--t == 0) return 4;
|
|
outb(KBSTATP,0x60);
|
|
t = 20000;
|
|
while (inb(KBSTATP) & KBOUTRDY)
|
|
if (--t == 0) return 5;
|
|
outb(KBDATAP,0x45);
|
|
for (i = 0; i < 10000; i++) udelay(1);
|
|
|
|
t = 20000;
|
|
while (inb(KBSTATP) & KBOUTRDY)
|
|
if (--t == 0) return 6;
|
|
outb(KBSTATP,0x20);
|
|
t = 200000;
|
|
while ((inb(KBSTATP) & KBINRDY) == 0) /* wait input ready */
|
|
if (--t == 0) return 7;
|
|
if (! (inb(KBDATAP) & 0x40)) {
|
|
/*
|
|
* Quote from PS/2 System Reference Manual:
|
|
*
|
|
* "Address hex 0060 and address hex 0064 should be
|
|
* written only when the input-buffer-full bit and
|
|
* output-buffer-full bit in the Controller Status
|
|
* register are set 0." (KBINRDY and KBOUTRDY)
|
|
*/
|
|
t = 200000;
|
|
while (inb(KBSTATP) & (KBINRDY | KBOUTRDY))
|
|
if (--t == 0) return 8;
|
|
outb(KBDATAP,0xF0);
|
|
t = 200000;
|
|
while (inb(KBSTATP) & (KBINRDY | KBOUTRDY))
|
|
if (--t == 0) return 9;
|
|
outb(KBDATAP,0x01);
|
|
}
|
|
t = 20000;
|
|
while (inb(KBSTATP) & KBOUTRDY)
|
|
if (--t == 0) return 10;
|
|
outb(KBSTATP,0xAE);
|
|
return 0;
|
|
}
|
|
|
|
static void kbdreset(void)
|
|
{
|
|
int ret = __kbdreset();
|
|
|
|
if (ret) {
|
|
puts("__kbdreset failed: ");
|
|
puthex(ret);
|
|
puts("\n");
|
|
}
|
|
}
|
|
|
|
/* We have to actually read the keyboard when CRT_tstc is called,
|
|
* since the pending data might be a key release code, and therefore
|
|
* not valid data. In this case, kbd() will return -1, even though there's
|
|
* data to be read. Of course, we might actually read a valid key press,
|
|
* in which case it gets queued into key_pending for use by CRT_getc.
|
|
*/
|
|
|
|
static int kbd_reset = 0;
|
|
|
|
static int key_pending = -1;
|
|
|
|
int CRT_getc(void)
|
|
{
|
|
int c;
|
|
if (!kbd_reset) {kbdreset(); kbd_reset++; }
|
|
|
|
if (key_pending != -1) {
|
|
c = key_pending;
|
|
key_pending = -1;
|
|
return c;
|
|
} else {
|
|
while ((c = kbd(0)) == 0) ;
|
|
return c;
|
|
}
|
|
}
|
|
|
|
int CRT_tstc(void)
|
|
{
|
|
if (!kbd_reset) {kbdreset(); kbd_reset++; }
|
|
|
|
while (key_pending == -1 && ((inb(KBSTATP) & KBINRDY) != 0)) {
|
|
key_pending = kbd(1);
|
|
}
|
|
|
|
return (key_pending != -1);
|
|
}
|