xref: /onnv-gate/usr/src/uts/i86pc/boot/boot_keyboard.c (revision 5084:7d838c5c0eed)
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