13446Smrj /*
23446Smrj * CDDL HEADER START
33446Smrj *
43446Smrj * The contents of this file are subject to the terms of the
53446Smrj * Common Development and Distribution License (the "License").
63446Smrj * You may not use this file except in compliance with the License.
73446Smrj *
83446Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj * or http://www.opensolaris.org/os/licensing.
103446Smrj * See the License for the specific language governing permissions
113446Smrj * and limitations under the License.
123446Smrj *
133446Smrj * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj * If applicable, add the following below this CDDL HEADER, with the
163446Smrj * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj *
193446Smrj * CDDL HEADER END
203446Smrj */
213446Smrj /*
223446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
233446Smrj * Use is subject to license terms.
243446Smrj */
253446Smrj
263446Smrj #pragma ident "%Z%%M% %I% %E% SMI"
273446Smrj
283446Smrj /*
293446Smrj * Miniature keyboard driver for bootstrap. This allows keyboard
303446Smrj * support to continue after we take over interrupts and disable
313446Smrj * BIOS keyboard support.
323446Smrj */
333446Smrj
343446Smrj #include <sys/types.h>
353446Smrj #include <sys/archsystm.h>
363446Smrj #include <sys/boot_console.h>
373446Smrj #include "boot_keyboard_table.h"
383446Smrj
393446Smrj #if defined(_BOOT)
40*5084Sjohnlev #include "dboot/dboot_asm.h"
413446Smrj #include "dboot/dboot_xboot.h"
42*5084Sjohnlev #endif /* _BOOT */
433446Smrj
443446Smrj /*
453446Smrj * Definitions for BIOS keyboard state. We use BIOS's variable to store
463446Smrj * state, ensuring that we stay in sync with it.
473446Smrj */
483446Smrj #define BIOS_KB_FLAG 0x417
493446Smrj #define BIOS_RIGHT_SHIFT 0x01
503446Smrj #define BIOS_LEFT_SHIFT 0x02
513446Smrj #define BIOS_EITHER_SHIFT (BIOS_LEFT_SHIFT | BIOS_RIGHT_SHIFT)
523446Smrj #define BIOS_CTL_SHIFT 0x04
533446Smrj #define BIOS_ALT_SHIFT 0x08
543446Smrj #define BIOS_SCROLL_STATE 0x10
553446Smrj #define BIOS_NUM_STATE 0x20
563446Smrj #define BIOS_CAPS_STATE 0x40
573446Smrj #define BIOS_INS_STATE 0x80
583446Smrj
593446Smrj #define BIOS_KB_FLAG_1 0x418
603446Smrj #define BIOS_SYS_SHIFT 0x04
613446Smrj #define BIOS_HOLD_STATE 0x08
623446Smrj #define BIOS_SCROLL_SHIFT 0x10
633446Smrj #define BIOS_NUM_SHIFT 0x20
643446Smrj #define BIOS_CAPS_SHIFT 0x40
653446Smrj #define BIOS_INS_SHIFT 0x80
663446Smrj
67*5084Sjohnlev #if defined(__xpv) && defined(_BOOT)
68*5084Sjohnlev
69*5084Sjohnlev /*
70*5084Sjohnlev * Device memory addresses
71*5084Sjohnlev *
72*5084Sjohnlev * In dboot under the hypervisor we don't have any memory mappings
73*5084Sjohnlev * for the first meg of low memory so we can't access devices there.
74*5084Sjohnlev * Intead we've mapped the device memory that we need to access into
75*5084Sjohnlev * a local variable within dboot so we can access the device memory
76*5084Sjohnlev * there.
77*5084Sjohnlev */
78*5084Sjohnlev extern unsigned short *kb_status;
79*5084Sjohnlev #define kb_flag ((unsigned char *)&kb_status[BIOS_KB_FLAG])
80*5084Sjohnlev #define kb_flag_1 ((unsigned char *)&kb_status[BIOS_KB_FLAG_1])
81*5084Sjohnlev
82*5084Sjohnlev #else /* __xpv && _BOOT */
83*5084Sjohnlev
84*5084Sjohnlev /* Device memory addresses */
853446Smrj #define kb_flag ((unsigned char *)BIOS_KB_FLAG)
863446Smrj #define kb_flag_1 ((unsigned char *)BIOS_KB_FLAG_1)
873446Smrj
88*5084Sjohnlev #endif /* __xpv && _BOOT */
89*5084Sjohnlev
903446Smrj /*
913446Smrj * Keyboard controller registers
923446Smrj */
933446Smrj #define I8042_DATA 0x60
943446Smrj #define I8042_STAT 0x64
953446Smrj #define I8042_CMD 0x64
963446Smrj
973446Smrj /*
983446Smrj * Keyboard controller status register bits
993446Smrj */
1003446Smrj #define I8042_STAT_OUTBF 0x01
1013446Smrj #define I8042_STAT_INBF 0x02
1023446Smrj #define I8042_STAT_AUXBF 0x20
1033446Smrj
1043446Smrj /*
1053446Smrj * Keyboard controller commands
1063446Smrj */
1073446Smrj #define I8042_RCB 0x20
1083446Smrj #define I8042_WCB 0x60
1093446Smrj
1103446Smrj /*
1113446Smrj * Keyboard commands
1123446Smrj */
1133446Smrj #define KB_SET_LED 0xED /* LED byte follows... */
1143446Smrj #define KB_LED_SCROLL_LOCK 0x01 /* Bits for LED byte */
1153446Smrj #define KB_LED_NUM_LOCK 0x02
1163446Smrj #define KB_LED_CAPS_LOCK 0x04
1173446Smrj
1183446Smrj #ifndef ASSERT
1193446Smrj #define ASSERT(x)
1203446Smrj #endif
1213446Smrj
1223446Smrj #define peek8(p) (*(p))
1233446Smrj #define poke8(p, val) (*(p) = (val))
1243446Smrj
1253446Smrj static struct {
1263446Smrj boolean_t initialized;
1273446Smrj enum { KB_LED_IDLE, KB_LED_COMMAND_SENT, KB_LED_VALUE_SENT }
1283446Smrj led_state;
1293446Smrj int led_commanded;
1303446Smrj /*
1313446Smrj * Possible values:
1323446Smrj *
1333446Smrj * -1 Nothing pending
1343446Smrj * 0x000-0x0ff Pending byte
1353446Smrj * 0x100-0x1ff Needs leading zero, then low byte next.
1363446Smrj *
1373446Smrj * Others undefined.
1383446Smrj */
1393446Smrj int pending;
1403446Smrj } kb = {
1413446Smrj B_FALSE, /* initialized? */
1423446Smrj KB_LED_IDLE, /* LED command state */
1433446Smrj -1, /* commanded LEDs - force refresh */
1443446Smrj -1, /* pending */
1453446Smrj };
1463446Smrj
1473446Smrj static int kb_translate(unsigned char code);
1483446Smrj static void kb_send(unsigned char cmd);
1493446Smrj static void kb_update_leds(void);
1503446Smrj static uchar_t kb_calculate_leds(void);
1513446Smrj
1523446Smrj int
kb_getchar(void)1533446Smrj kb_getchar(void)
1543446Smrj {
1553446Smrj int ret;
1563446Smrj
1573446Smrj while (!kb_ischar())
1583446Smrj /* LOOP */;
1593446Smrj
1603446Smrj /*
1613446Smrj * kb_ischar() doesn't succeed without leaving kb.pending
1623446Smrj * set.
1633446Smrj */
1643446Smrj ASSERT(kb.pending >= 0);
1653446Smrj
1663446Smrj if (kb.pending & 0x100) {
1673446Smrj ret = 0;
1683446Smrj kb.pending &= 0xff;
1693446Smrj } else {
1703446Smrj ret = kb.pending;
1713446Smrj kb.pending = -1;
1723446Smrj }
1733446Smrj
1743446Smrj return (ret);
1753446Smrj }
1763446Smrj
1773446Smrj int
kb_ischar(void)1783446Smrj kb_ischar(void)
1793446Smrj {
1803446Smrj unsigned char buffer_stat;
1813446Smrj unsigned char code;
1823446Smrj unsigned char leds;
1833446Smrj
1843446Smrj if (!kb.initialized) {
1853446Smrj kb_init();
1863446Smrj kb.initialized = B_TRUE;
1873446Smrj }
1883446Smrj
1893446Smrj if (kb.pending >= 0)
1903446Smrj return (1);
1913446Smrj
1923446Smrj for (;;) {
1933446Smrj buffer_stat = inb(I8042_STAT);
1943446Smrj if (buffer_stat == 0xff)
1953446Smrj return (0);
1963446Smrj buffer_stat &= (I8042_STAT_OUTBF | I8042_STAT_AUXBF);
1973446Smrj
1983446Smrj switch (buffer_stat) {
1993446Smrj case 0:
2003446Smrj case I8042_STAT_AUXBF:
2013446Smrj return (0);
2023446Smrj case (I8042_STAT_OUTBF | I8042_STAT_AUXBF):
2033446Smrj /*
2043446Smrj * Discard unwanted mouse data.
2053446Smrj */
2063446Smrj (void) inb(I8042_DATA);
2073446Smrj continue;
2083446Smrj }
2093446Smrj
2103446Smrj code = inb(I8042_DATA);
2113446Smrj
2123446Smrj switch (code) {
2133446Smrj /*
2143446Smrj * case 0xAA:
2153446Smrj *
2163446Smrj * You might think that we should ignore 0xAA on the
2173446Smrj * grounds that it is the BAT Complete response and will
2183446Smrj * occur on keyboard detach/reattach. Unfortunately,
2193446Smrj * it is ambiguous - this is also the code for a break
2203446Smrj * of the left shift key. Since it will be harmless for
2213446Smrj * us to "spuriously" process a break of Left Shift,
2223446Smrj * we just let the normal code handle it. Perhaps we
2233446Smrj * should take a hint and refresh the LEDs, but I
2243446Smrj * refuse to get very worried about hot-plug issues
2253446Smrj * in this mini-driver.
2263446Smrj */
2273446Smrj case 0xFA:
2283446Smrj
2293446Smrj switch (kb.led_state) {
2303446Smrj case KB_LED_IDLE:
2313446Smrj /*
2323446Smrj * Spurious. Oh well, ignore it.
2333446Smrj */
2343446Smrj break;
2353446Smrj case KB_LED_COMMAND_SENT:
2363446Smrj leds = kb_calculate_leds();
2373446Smrj kb_send(leds);
2383446Smrj kb.led_commanded = leds;
2393446Smrj kb.led_state = KB_LED_VALUE_SENT;
2403446Smrj break;
2413446Smrj case KB_LED_VALUE_SENT:
2423446Smrj kb.led_state = KB_LED_IDLE;
2433446Smrj /*
2443446Smrj * Check for changes made while we were
2453446Smrj * working on the last change.
2463446Smrj */
2473446Smrj kb_update_leds();
2483446Smrj break;
2493446Smrj }
2503446Smrj continue;
2513446Smrj
2523446Smrj case 0xE0:
2533446Smrj case 0xE1:
2543446Smrj /*
2553446Smrj * These are used to distinguish the keys added on
2563446Smrj * the AT-101 keyboard from the original 84 keys.
2573446Smrj * We don't care, and the codes are carefully arranged
2583446Smrj * so that we don't have to.
2593446Smrj */
2603446Smrj continue;
2613446Smrj
2623446Smrj default:
2633446Smrj if (code & 0x80) {
2643446Smrj /* Release */
2653446Smrj code &= 0x7f;
2663446Smrj switch (keyboard_translate[code].normal) {
2673446Smrj case KBTYPE_SPEC_LSHIFT:
2683446Smrj poke8(kb_flag, peek8(kb_flag) &
2693446Smrj ~BIOS_LEFT_SHIFT);
2703446Smrj break;
2713446Smrj case KBTYPE_SPEC_RSHIFT:
2723446Smrj poke8(kb_flag, peek8(kb_flag) &
2733446Smrj ~BIOS_RIGHT_SHIFT);
2743446Smrj break;
2753446Smrj case KBTYPE_SPEC_CTRL:
2763446Smrj poke8(kb_flag, peek8(kb_flag) &
2773446Smrj ~BIOS_CTL_SHIFT);
2783446Smrj break;
2793446Smrj case KBTYPE_SPEC_ALT:
2803446Smrj poke8(kb_flag, peek8(kb_flag) &
2813446Smrj ~BIOS_ALT_SHIFT);
2823446Smrj break;
2833446Smrj case KBTYPE_SPEC_CAPS_LOCK:
2843446Smrj poke8(kb_flag_1, peek8(kb_flag_1) &
2853446Smrj ~BIOS_CAPS_SHIFT);
2863446Smrj break;
2873446Smrj case KBTYPE_SPEC_NUM_LOCK:
2883446Smrj poke8(kb_flag_1, peek8(kb_flag_1) &
2893446Smrj ~BIOS_NUM_SHIFT);
2903446Smrj break;
2913446Smrj case KBTYPE_SPEC_SCROLL_LOCK:
2923446Smrj poke8(kb_flag_1, peek8(kb_flag_1) &
2933446Smrj ~BIOS_SCROLL_SHIFT);
2943446Smrj break;
2953446Smrj default:
2963446Smrj /*
2973446Smrj * Ignore all other releases.
2983446Smrj */
2993446Smrj break;
3003446Smrj }
3013446Smrj } else {
3023446Smrj /* Press */
3033446Smrj
3043446Smrj kb.pending = kb_translate(code);
3053446Smrj if (kb.pending >= 0) {
3063446Smrj return (1);
3073446Smrj }
3083446Smrj }
3093446Smrj }
3103446Smrj }
3113446Smrj }
3123446Smrj
3133446Smrj int
kb_translate(unsigned char code)3143446Smrj kb_translate(unsigned char code)
3153446Smrj {
3163446Smrj struct keyboard_translate *k;
3173446Smrj unsigned short action;
3183446Smrj boolean_t shifted;
3193446Smrj
3203446Smrj k = keyboard_translate + code;
3213446Smrj
3223446Smrj shifted = (peek8(kb_flag) & BIOS_EITHER_SHIFT) != 0;
3233446Smrj
3243446Smrj switch (k->normal & 0xFF00) {
3253446Smrj case KBTYPE_NUMPAD:
3263446Smrj if (peek8(kb_flag) & BIOS_NUM_STATE)
3273446Smrj shifted = !shifted;
3283446Smrj break;
3293446Smrj case KBTYPE_ALPHA:
3303446Smrj if (peek8(kb_flag) & BIOS_CAPS_STATE)
3313446Smrj shifted = !shifted;
3323446Smrj break;
3333446Smrj }
3343446Smrj
3353446Smrj if (peek8(kb_flag) & BIOS_ALT_SHIFT)
3363446Smrj action = k->alted;
3373446Smrj else if (peek8(kb_flag) & BIOS_CTL_SHIFT)
3383446Smrj action = k->ctrled;
3393446Smrj else if (shifted)
3403446Smrj action = k->shifted;
3413446Smrj else
3423446Smrj action = k->normal;
3433446Smrj
3443446Smrj switch (action & 0xFF00) {
3453446Smrj case KBTYPE_NORMAL:
3463446Smrj case KBTYPE_ALPHA:
3473446Smrj return (action & 0xFF);
3483446Smrj
3493446Smrj case KBTYPE_NUMPAD:
3503446Smrj case KBTYPE_FUNC:
3513446Smrj return ((action & 0xFF) | 0x100);
3523446Smrj
3533446Smrj case KBTYPE_SPEC:
3543446Smrj break;
3553446Smrj
3563446Smrj default:
3573446Smrj /*
3583446Smrj * Bad entry.
3593446Smrj */
3603446Smrj ASSERT(0);
3613446Smrj return (-1);
3623446Smrj }
3633446Smrj
3643446Smrj /*
3653446Smrj * Handle special keys, mostly shifts.
3663446Smrj */
3673446Smrj switch (action) {
3683446Smrj case KBTYPE_SPEC_NOP:
3693446Smrj case KBTYPE_SPEC_UNDEF:
3703446Smrj break;
3713446Smrj
3723446Smrj case KBTYPE_SPEC_LSHIFT:
3733446Smrj poke8(kb_flag, peek8(kb_flag) | BIOS_LEFT_SHIFT);
3743446Smrj break;
3753446Smrj
3763446Smrj case KBTYPE_SPEC_RSHIFT:
3773446Smrj poke8(kb_flag, peek8(kb_flag) | BIOS_RIGHT_SHIFT);
3783446Smrj break;
3793446Smrj
3803446Smrj case KBTYPE_SPEC_CTRL:
3813446Smrj poke8(kb_flag, peek8(kb_flag) | BIOS_CTL_SHIFT);
3823446Smrj break;
3833446Smrj
3843446Smrj case KBTYPE_SPEC_ALT:
3853446Smrj poke8(kb_flag, peek8(kb_flag) | BIOS_ALT_SHIFT);
3863446Smrj break;
3873446Smrj
3883446Smrj case KBTYPE_SPEC_CAPS_LOCK:
3893446Smrj if (!(peek8(kb_flag_1) & BIOS_CAPS_SHIFT)) {
3903446Smrj poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_CAPS_SHIFT);
3913446Smrj poke8(kb_flag, peek8(kb_flag) ^ BIOS_CAPS_STATE);
3923446Smrj }
3933446Smrj break;
3943446Smrj
3953446Smrj case KBTYPE_SPEC_NUM_LOCK:
3963446Smrj if (!(peek8(kb_flag_1) & BIOS_NUM_SHIFT)) {
3973446Smrj poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_NUM_SHIFT);
3983446Smrj poke8(kb_flag, peek8(kb_flag) ^ BIOS_NUM_STATE);
3993446Smrj }
4003446Smrj break;
4013446Smrj
4023446Smrj case KBTYPE_SPEC_SCROLL_LOCK:
4033446Smrj if (!(peek8(kb_flag_1) & BIOS_SCROLL_SHIFT)) {
4043446Smrj poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_SCROLL_SHIFT);
4053446Smrj poke8(kb_flag, peek8(kb_flag) ^ BIOS_SCROLL_STATE);
4063446Smrj }
4073446Smrj break;
4083446Smrj
4093446Smrj case KBTYPE_SPEC_MAYBE_REBOOT:
4103446Smrj #if 0 /* Solaris doesn't reboot via ctrl-alt-del */
4113446Smrj if ((peek8(kb_flag) & (BIOS_CTL_SHIFT|BIOS_ALT_SHIFT)) ==
4123446Smrj (BIOS_CTL_SHIFT|BIOS_ALT_SHIFT)) {
4133446Smrj reset();
4143446Smrj /* NOTREACHED */
4153446Smrj }
4163446Smrj #endif
4173446Smrj break;
4183446Smrj
4193446Smrj default:
4203446Smrj /*
4213446Smrj * Bad entry
4223446Smrj */
4233446Smrj ASSERT(0);
4243446Smrj break;
4253446Smrj }
4263446Smrj
4273446Smrj /*
4283446Smrj * Consider updating the LEDs. This does nothing if nothing
4293446Smrj * needs to be done.
4303446Smrj */
4313446Smrj kb_update_leds();
4323446Smrj
4333446Smrj return (-1);
4343446Smrj }
4353446Smrj
4363446Smrj void
kb_send(unsigned char cmd)4373446Smrj kb_send(unsigned char cmd)
4383446Smrj {
4393446Smrj int retries;
4403446Smrj
4413446Smrj for (retries = 0;
4423446Smrj (inb(I8042_STAT) & I8042_STAT_INBF) != 0 && retries < 100000;
4433446Smrj retries++)
4443446Smrj /* LOOP */;
4453446Smrj outb(I8042_DATA, cmd);
4463446Smrj }
4473446Smrj
4483446Smrj void
kb_update_leds(void)4493446Smrj kb_update_leds(void)
4503446Smrj {
4513446Smrj if (kb.led_state != KB_LED_IDLE) {
4523446Smrj /*
4533446Smrj * The state machine will take care of any additional
4543446Smrj * changes that are necessary.
4553446Smrj */
4563446Smrj return;
4573446Smrj }
4583446Smrj
4593446Smrj if (kb_calculate_leds() == kb.led_commanded) {
4603446Smrj kb.led_state = KB_LED_IDLE;
4613446Smrj } else {
4623446Smrj kb_send(KB_SET_LED);
4633446Smrj kb.led_state = KB_LED_COMMAND_SENT;
4643446Smrj }
4653446Smrj }
4663446Smrj
4673446Smrj #define MIMR_PORT 0x21 /* Mask register for master PIC */
4683446Smrj #define MIMR_KB 2 /* Keyboard mask bit in master PIC */
4693446Smrj
4703446Smrj void
kb_init(void)4713446Smrj kb_init(void)
4723446Smrj {
4733446Smrj /*
474*5084Sjohnlev * Resist the urge to muck with the keyboard/mouse. Just assume
475*5084Sjohnlev * that the bios, grub, and any optional hypervisor have left
476*5084Sjohnlev * the keyboard in a sane and usable state. Messing with it now
477*5084Sjohnlev * could result it making it unusuable, which would break early
478*5084Sjohnlev * kmdb debugging support. Note that we don't actually need to
479*5084Sjohnlev * disable interrupts for the keyboard/mouse since we're already
480*5084Sjohnlev * in protected mode and we're not compeating with the bios for
481*5084Sjohnlev * keyboard access. Also, we don't need to disable the mouse
482*5084Sjohnlev * port since our polled input routine will just drop any mouse
483*5084Sjohnlev * data that it recieves.
4843446Smrj */
4853446Smrj kb_update_leds();
4863446Smrj }
4873446Smrj
4883446Smrj unsigned char
kb_calculate_leds(void)4893446Smrj kb_calculate_leds(void)
4903446Smrj {
4913446Smrj int res;
4923446Smrj
4933446Smrj res = 0;
4943446Smrj
4953446Smrj if (peek8(kb_flag) & BIOS_CAPS_STATE)
4963446Smrj res |= KB_LED_CAPS_LOCK;
4973446Smrj
4983446Smrj if (peek8(kb_flag) & BIOS_NUM_STATE)
4993446Smrj res |= KB_LED_NUM_LOCK;
5003446Smrj
5013446Smrj if (peek8(kb_flag) & BIOS_SCROLL_STATE)
5023446Smrj res |= KB_LED_SCROLL_LOCK;
5033446Smrj
5043446Smrj return ((char)res);
5053446Smrj }
506