linux/arch/ppc/boot/lib/kbd.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);
}