keyboard.c revision 6ee4b8d7ef9a262e3f564e81b2b2f4d8e61ffe15
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Miniature keyboard driver for bootstrap. This allows keyboard
* support to continue after we take over interrupts and disable
* BIOS keyboard support.
*/
#include "chario.h"
#include "keyboard_table.h"
#include "console.h"
#include "util.h"
#include "debug.h"
/*
* Definitions for BIOS keyboard state. We use BIOS's variable to store
* state, ensuring that we stay in sync with it.
*/
#define BIOS_KB_FLAG 0x417
#define BIOS_RIGHT_SHIFT 0x01
#define BIOS_LEFT_SHIFT 0x02
#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
#define kb_flag ((unsigned char *)BIOS_KB_FLAG)
#define kb_flag_1 ((unsigned char *)BIOS_KB_FLAG_1)
/*
* Keyboard controller registers
*/
#define I8042_DATA 0x60
#define I8042_STAT 0x64
#define I8042_CMD 0x64
/*
* Keyboard controller status register bits
*/
#define I8042_STAT_OUTBF 0x01
#define I8042_STAT_INBF 0x02
#define I8042_STAT_AUXBF 0x20
/*
* Keyboard controller commands
*/
#define I8042_RCB 0x20
#define I8042_WCB 0x60
/*
* Keyboard commands
*/
#define KB_LED_NUM_LOCK 0x02
#define KB_LED_CAPS_LOCK 0x04
#ifndef ASSERT
#define ASSERT(x)
#endif
#define peek8(p) (*(p))
#define peeks(p) (*(p))
static struct {
int led_commanded;
/*
* Possible values:
*
* -1 Nothing pending
* 0x000-0x0ff Pending byte
* 0x100-0x1ff Needs leading zero, then low byte next.
*
* Others undefined.
*/
int pending;
} kb = {
B_FALSE, /* initialized? */
KB_LED_IDLE, /* LED command state */
-1, /* commanded LEDs - force refresh */
-1, /* pending */
};
static int kb_translate(unsigned char code);
static void kb_update_leds(void);
static uchar_t kb_calculate_leds(void);
int
kb_getchar(void)
{
int ret;
if (kb_debug)
printf(" getchar()");
while (!kb_ischar())
/* LOOP */;
/*
* kb_ischar() doesn't succeed without leaving kb.pending
* set.
*/
ret = 0;
} else {
}
if (kb_debug)
return (ret);
}
int
kb_ischar(void)
{
unsigned char buffer_stat;
unsigned char code;
unsigned char leds;
static int cnt = 0;
if (!kb.initialized) {
kb_init();
}
if (kb_debug)
return (1);
for (;;) {
if (buffer_stat == 0xff)
return (0);
switch (buffer_stat) {
case 0:
case I8042_STAT_AUXBF:
return (0);
case (I8042_STAT_OUTBF | I8042_STAT_AUXBF):
/*
* Discard unwanted mouse data.
*/
(void) inb(I8042_DATA);
continue;
}
if (kb_debug)
switch (code) {
/*
* case 0xAA:
*
* You might think that we should ignore 0xAA on the
* grounds that it is the BAT Complete response and will
* it is ambiguous - this is also the code for a break
* of the left shift key. Since it will be harmless for
* us to "spuriously" process a break of Left Shift,
* we just let the normal code handle it. Perhaps we
* should take a hint and refresh the LEDs, but I
* refuse to get very worried about hot-plug issues
* in this mini-driver.
*/
case 0xFA:
if (kb_debug)
printf("ack ");
case KB_LED_IDLE:
/*
* Spurious. Oh well, ignore it.
*/
break;
case KB_LED_COMMAND_SENT:
leds = kb_calculate_leds();
break;
case KB_LED_VALUE_SENT:
/*
* Check for changes made while we were
* working on the last change.
*/
break;
}
continue;
case 0xE0:
case 0xE1:
/*
* These are used to distinguish the keys added on
* the AT-101 keyboard from the original 84 keys.
* We don't care, and the codes are carefully arranged
* so that we don't have to.
*/
if (kb_debug)
printf("ignored ");
continue;
default:
if (code & 0x80) {
if (kb_debug)
printf("release->");
/* Release */
code &= 0x7f;
case KBTYPE_SPEC_LSHIFT:
if (kb_debug)
printf("lshift ");
break;
case KBTYPE_SPEC_RSHIFT:
if (kb_debug)
printf("rshift ");
break;
case KBTYPE_SPEC_CTRL:
if (kb_debug)
printf("ctrl ");
break;
case KBTYPE_SPEC_ALT:
if (kb_debug)
printf("alt ");
break;
case KBTYPE_SPEC_CAPS_LOCK:
if (kb_debug)
printf("caps ");
break;
case KBTYPE_SPEC_NUM_LOCK:
if (kb_debug)
printf("num ");
break;
case KBTYPE_SPEC_SCROLL_LOCK:
if (kb_debug)
printf("scroll ");
break;
default:
/*
* Ignore all other releases.
*/
if (kb_debug)
printf("ignored ");
break;
}
} else {
/* Press */
if (kb_debug)
printf("press->");
if (kb_debug)
return (1);
} else {
if (kb_debug)
printf("ignored ");
}
}
}
}
}
int
kb_translate(unsigned char code)
{
struct keyboard_translate *k;
unsigned short action;
k = keyboard_translate + code;
switch (k->normal & 0xFF00) {
case KBTYPE_NUMPAD:
break;
case KBTYPE_ALPHA:
break;
}
else if (shifted)
else
switch (action & 0xFF00) {
case KBTYPE_NORMAL:
case KBTYPE_ALPHA:
return (action & 0xFF);
case KBTYPE_NUMPAD:
case KBTYPE_FUNC:
case KBTYPE_SPEC:
break;
default:
/*
* Bad entry.
*/
ASSERT(0);
return (-1);
}
/*
* Handle special keys, mostly shifts.
*/
switch (action) {
case KBTYPE_SPEC_NOP:
case KBTYPE_SPEC_UNDEF:
break;
case KBTYPE_SPEC_LSHIFT:
break;
case KBTYPE_SPEC_RSHIFT:
break;
case KBTYPE_SPEC_CTRL:
break;
case KBTYPE_SPEC_ALT:
break;
case KBTYPE_SPEC_CAPS_LOCK:
}
break;
case KBTYPE_SPEC_NUM_LOCK:
}
break;
case KBTYPE_SPEC_SCROLL_LOCK:
}
break;
case KBTYPE_SPEC_MAYBE_REBOOT:
reset();
/* NOTREACHED */
}
break;
default:
/*
* Bad entry
*/
ASSERT(0);
break;
}
/*
* Consider updating the LEDs. This does nothing if nothing
* needs to be done.
*/
return (-1);
}
void
{
int retries;
for (retries = 0;
retries++)
/* LOOP */;
}
void
kb_update_leds(void)
{
/*
* The state machine will take care of any additional
* changes that are necessary.
*/
return;
}
} else {
}
}
void
kb_init(void)
{
unsigned char pic_mask;
int retries;
/*
* Write the command byte to turn off interrupts and
* disable the auxiliary port.
*
* 0x80: 0 = Reserved, must be zero.
* 0x40: 1 = Translate to XT codes.
* Solaris turns this off later, but we have a legacy
* of using XT codes.
* 0x20: 1 = Disable aux (mouse) port.
* 0x10: 0 = Enable main (keyboard) port.
* 0x08: 0 = Reserved, must be zero.
* 0x04: 1 = System flag, 1 means passed self-test.
* Caution: setting this bit to zero causes some
* systems (HP Kayak XA) to fail to reboot without
* a hard reset.
* 0x02: 0 = Disable aux interrupts.
* 0x01: 0 = Disable aux interrupts.
*/
for (retries = 0;
retries++)
/* LOOP */;
for (retries = 0;
retries++)
/* LOOP */;
/*
* If we're running on a system with an emulated 8042 (with
* USB and SMI emulation), the above command *might* not
* have turned off keyboard interrupts. If it didn't,
* we will lose keystrokes to the BIOS int handler every
* time someone hits a key while BIOS and STI are active..
* that is, every time we're in bootconf.exe, for example.
* Turn off ints at the PIC to prevent this from happening.
*
* Yes, this is yet another workaround for buggy BIOS
* emulation.
*/
}
unsigned char
kb_calculate_leds(void)
{
int res;
res = 0;
res |= KB_LED_CAPS_LOCK;
res |= KB_LED_NUM_LOCK;
return (res);
}