#include <sys/types.h>
#include <sys/archsystm.h>
#include <sys/boot_console.h>
#include "boot_keyboard_table.h"
#if defined(_BOOT)
#include "dboot/dboot_asm.h"
#include "dboot/dboot_xboot.h"
#endif
#define BIOS_KB_FLAG 0x417
#define BIOS_RIGHT_SHIFT 0x01
#define BIOS_LEFT_SHIFT 0x02
#define BIOS_EITHER_SHIFT (BIOS_LEFT_SHIFT | BIOS_RIGHT_SHIFT)
#define BIOS_CTL_SHIFT 0x04
#define BIOS_ALT_SHIFT 0x08
#define BIOS_SCROLL_STATE 0x10
#define BIOS_NUM_STATE 0x20
#define BIOS_CAPS_STATE 0x40
#define BIOS_INS_STATE 0x80
#define BIOS_KB_FLAG_1 0x418
#define BIOS_SYS_SHIFT 0x04
#define BIOS_HOLD_STATE 0x08
#define BIOS_SCROLL_SHIFT 0x10
#define BIOS_NUM_SHIFT 0x20
#define BIOS_CAPS_SHIFT 0x40
#define BIOS_INS_SHIFT 0x80
#if defined(__xpv) && defined(_BOOT)
extern unsigned short *kb_status;
#define kb_flag ((unsigned char *)&kb_status[BIOS_KB_FLAG])
#define kb_flag_1 ((unsigned char *)&kb_status[BIOS_KB_FLAG_1])
#else
static unsigned char *kb_status = ((unsigned char *)BIOS_KB_FLAG);
#define kb_flag (&kb_status[0])
#define kb_flag_1 (&kb_status[1])
#endif
#define I8042_DATA 0x60
#define I8042_STAT 0x64
#define I8042_CMD 0x64
#define I8042_STAT_OUTBF 0x01
#define I8042_STAT_INBF 0x02
#define I8042_STAT_AUXBF 0x20
#define I8042_RCB 0x20
#define I8042_WCB 0x60
#define KB_SET_LED 0xED
#define KB_LED_SCROLL_LOCK 0x01
#define KB_LED_NUM_LOCK 0x02
#define KB_LED_CAPS_LOCK 0x04
#ifndef ASSERT
#define ASSERT(x)
#endif
#define peek8(p) (*(p))
#define poke8(p, val) (*(p) = (val))
static struct {
boolean_t initialized;
enum { KB_LED_IDLE, KB_LED_COMMAND_SENT, KB_LED_VALUE_SENT }
led_state;
int led_commanded;
int pending;
} kb = {
B_FALSE,
KB_LED_IDLE,
-1,
-1,
};
#define KTAB_STRLEN 3
static char keystringtab[KTAB_STRLEN] = {'\033', '[', ' '};
static int keystring = -1;
static int kb_translate(unsigned char code);
static void kb_send(unsigned char cmd);
static void kb_update_leds(void);
static uchar_t kb_calculate_leds(void);
int
kb_getchar(void)
{
int ret;
while (!kb_ischar())
;
if (keystring >= 0) {
ret = keystringtab[keystring++];
if (keystring == KTAB_STRLEN) {
keystring = -1;
kb.pending = -1;
}
return (ret);
}
ASSERT(kb.pending >= 0);
if (kb.pending & 0x100) {
kb.pending &= 0xff;
switch (kb.pending) {
case 'H':
keystringtab[2] = 'A';
keystring = 0;
return (kb_getchar());
case 'P':
keystringtab[2] = 'B';
keystring = 0;
return (kb_getchar());
case 'M':
keystringtab[2] = 'C';
keystring = 0;
return (kb_getchar());
case 'K':
keystringtab[2] = 'D';
keystring = 0;
return (kb_getchar());
default:
ret = 0;
}
} else {
ret = kb.pending;
kb.pending = -1;
}
return (ret);
}
int
kb_ischar(void)
{
unsigned char buffer_stat;
unsigned char code;
unsigned char leds;
if (!kb.initialized) {
kb_init();
kb.initialized = B_TRUE;
}
if (kb.pending >= 0)
return (1);
for (;;) {
buffer_stat = inb(I8042_STAT);
if (buffer_stat == 0xff)
return (0);
buffer_stat &= (I8042_STAT_OUTBF | I8042_STAT_AUXBF);
switch (buffer_stat) {
case 0:
case I8042_STAT_AUXBF:
return (0);
case (I8042_STAT_OUTBF | I8042_STAT_AUXBF):
(void) inb(I8042_DATA);
continue;
}
code = inb(I8042_DATA);
switch (code) {
case 0xFA:
switch (kb.led_state) {
case KB_LED_IDLE:
break;
case KB_LED_COMMAND_SENT:
leds = kb_calculate_leds();
kb_send(leds);
kb.led_commanded = leds;
kb.led_state = KB_LED_VALUE_SENT;
break;
case KB_LED_VALUE_SENT:
kb.led_state = KB_LED_IDLE;
kb_update_leds();
break;
}
continue;
case 0xE0:
case 0xE1:
continue;
default:
if (code & 0x80) {
code &= 0x7f;
switch (keyboard_translate[code].normal) {
case KBTYPE_SPEC_LSHIFT:
poke8(kb_flag, peek8(kb_flag) &
~BIOS_LEFT_SHIFT);
break;
case KBTYPE_SPEC_RSHIFT:
poke8(kb_flag, peek8(kb_flag) &
~BIOS_RIGHT_SHIFT);
break;
case KBTYPE_SPEC_CTRL:
poke8(kb_flag, peek8(kb_flag) &
~BIOS_CTL_SHIFT);
break;
case KBTYPE_SPEC_ALT:
poke8(kb_flag, peek8(kb_flag) &
~BIOS_ALT_SHIFT);
break;
case KBTYPE_SPEC_CAPS_LOCK:
poke8(kb_flag_1, peek8(kb_flag_1) &
~BIOS_CAPS_SHIFT);
break;
case KBTYPE_SPEC_NUM_LOCK:
poke8(kb_flag_1, peek8(kb_flag_1) &
~BIOS_NUM_SHIFT);
break;
case KBTYPE_SPEC_SCROLL_LOCK:
poke8(kb_flag_1, peek8(kb_flag_1) &
~BIOS_SCROLL_SHIFT);
break;
default:
break;
}
} else {
kb.pending = kb_translate(code);
if (kb.pending >= 0) {
return (1);
}
}
}
}
}
int
kb_translate(unsigned char code)
{
struct keyboard_translate *k;
unsigned short action;
boolean_t shifted;
k = keyboard_translate + code;
shifted = (peek8(kb_flag) & BIOS_EITHER_SHIFT) != 0;
switch (k->normal & 0xFF00) {
case KBTYPE_NUMPAD:
if (peek8(kb_flag) & BIOS_NUM_STATE)
shifted = !shifted;
break;
case KBTYPE_ALPHA:
if (peek8(kb_flag) & BIOS_CAPS_STATE)
shifted = !shifted;
break;
}
if (peek8(kb_flag) & BIOS_ALT_SHIFT)
action = k->alted;
else if (peek8(kb_flag) & BIOS_CTL_SHIFT)
action = k->ctrled;
else if (shifted)
action = k->shifted;
else
action = k->normal;
switch (action & 0xFF00) {
case KBTYPE_NORMAL:
case KBTYPE_ALPHA:
return (action & 0xFF);
case KBTYPE_NUMPAD:
case KBTYPE_FUNC:
return ((action & 0xFF) | 0x100);
case KBTYPE_SPEC:
break;
default:
ASSERT(0);
return (-1);
}
switch (action) {
case KBTYPE_SPEC_NOP:
case KBTYPE_SPEC_UNDEF:
break;
case KBTYPE_SPEC_LSHIFT:
poke8(kb_flag, peek8(kb_flag) | BIOS_LEFT_SHIFT);
break;
case KBTYPE_SPEC_RSHIFT:
poke8(kb_flag, peek8(kb_flag) | BIOS_RIGHT_SHIFT);
break;
case KBTYPE_SPEC_CTRL:
poke8(kb_flag, peek8(kb_flag) | BIOS_CTL_SHIFT);
break;
case KBTYPE_SPEC_ALT:
poke8(kb_flag, peek8(kb_flag) | BIOS_ALT_SHIFT);
break;
case KBTYPE_SPEC_CAPS_LOCK:
if (!(peek8(kb_flag_1) & BIOS_CAPS_SHIFT)) {
poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_CAPS_SHIFT);
poke8(kb_flag, peek8(kb_flag) ^ BIOS_CAPS_STATE);
}
break;
case KBTYPE_SPEC_NUM_LOCK:
if (!(peek8(kb_flag_1) & BIOS_NUM_SHIFT)) {
poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_NUM_SHIFT);
poke8(kb_flag, peek8(kb_flag) ^ BIOS_NUM_STATE);
}
break;
case KBTYPE_SPEC_SCROLL_LOCK:
if (!(peek8(kb_flag_1) & BIOS_SCROLL_SHIFT)) {
poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_SCROLL_SHIFT);
poke8(kb_flag, peek8(kb_flag) ^ BIOS_SCROLL_STATE);
}
break;
case KBTYPE_SPEC_MAYBE_REBOOT:
#if 0
if ((peek8(kb_flag) & (BIOS_CTL_SHIFT|BIOS_ALT_SHIFT)) ==
(BIOS_CTL_SHIFT|BIOS_ALT_SHIFT)) {
reset();
}
#endif
break;
default:
ASSERT(0);
break;
}
kb_update_leds();
return (-1);
}
void
kb_send(unsigned char cmd)
{
int retries;
for (retries = 0;
(inb(I8042_STAT) & I8042_STAT_INBF) != 0 && retries < 100000;
retries++)
;
outb(I8042_DATA, cmd);
}
void
kb_update_leds(void)
{
if (kb.led_state != KB_LED_IDLE) {
return;
}
if (kb_calculate_leds() == kb.led_commanded) {
kb.led_state = KB_LED_IDLE;
} else {
kb_send(KB_SET_LED);
kb.led_state = KB_LED_COMMAND_SENT;
}
}
#define MIMR_PORT 0x21
#define MIMR_KB 2
void
kb_init(void)
{
kb_update_leds();
}
unsigned char
kb_calculate_leds(void)
{
int res;
res = 0;
if (peek8(kb_flag) & BIOS_CAPS_STATE)
res |= KB_LED_CAPS_LOCK;
if (peek8(kb_flag) & BIOS_NUM_STATE)
res |= KB_LED_NUM_LOCK;
if (peek8(kb_flag) & BIOS_SCROLL_STATE)
res |= KB_LED_SCROLL_LOCK;
return ((char)res);
}