xref: /illumos-gate/usr/src/uts/i86pc/boot/boot_keyboard.c (revision 4e93fb0f6383eaac21897dcdae56b87118131e4d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Miniature keyboard driver for bootstrap.  This allows keyboard
30  * support to continue after we take over interrupts and disable
31  * BIOS keyboard support.
32  */
33 
34 #include <sys/types.h>
35 #include <sys/archsystm.h>
36 #include <sys/boot_console.h>
37 #include "boot_keyboard_table.h"
38 
39 #if defined(_BOOT)
40 #include "dboot/dboot_xboot.h"
41 #endif
42 
43 /*
44  * Definitions for BIOS keyboard state.  We use BIOS's variable to store
45  * state, ensuring that we stay in sync with it.
46  */
47 #define	BIOS_KB_FLAG		0x417
48 #define	BIOS_RIGHT_SHIFT	0x01
49 #define	BIOS_LEFT_SHIFT		0x02
50 #define	BIOS_EITHER_SHIFT	(BIOS_LEFT_SHIFT | BIOS_RIGHT_SHIFT)
51 #define	BIOS_CTL_SHIFT		0x04
52 #define	BIOS_ALT_SHIFT		0x08
53 #define	BIOS_SCROLL_STATE	0x10
54 #define	BIOS_NUM_STATE		0x20
55 #define	BIOS_CAPS_STATE		0x40
56 #define	BIOS_INS_STATE		0x80
57 
58 #define	BIOS_KB_FLAG_1		0x418
59 #define	BIOS_SYS_SHIFT		0x04
60 #define	BIOS_HOLD_STATE		0x08
61 #define	BIOS_SCROLL_SHIFT	0x10
62 #define	BIOS_NUM_SHIFT		0x20
63 #define	BIOS_CAPS_SHIFT		0x40
64 #define	BIOS_INS_SHIFT		0x80
65 
66 #define	kb_flag		((unsigned char *)BIOS_KB_FLAG)
67 #define	kb_flag_1	((unsigned char *)BIOS_KB_FLAG_1)
68 
69 /*
70  * Keyboard controller registers
71  */
72 #define	I8042_DATA		0x60
73 #define	I8042_STAT		0x64
74 #define	I8042_CMD		0x64
75 
76 /*
77  * Keyboard controller status register bits
78  */
79 #define	I8042_STAT_OUTBF	0x01
80 #define	I8042_STAT_INBF		0x02
81 #define	I8042_STAT_AUXBF	0x20
82 
83 /*
84  * Keyboard controller commands
85  */
86 #define	I8042_RCB		0x20
87 #define	I8042_WCB		0x60
88 
89 /*
90  * Keyboard commands
91  */
92 #define	KB_SET_LED		0xED	/* LED byte follows... */
93 #define	KB_LED_SCROLL_LOCK	0x01	/* Bits for LED byte */
94 #define	KB_LED_NUM_LOCK		0x02
95 #define	KB_LED_CAPS_LOCK	0x04
96 
97 #ifndef ASSERT
98 #define	ASSERT(x)
99 #endif
100 
101 #define	peek8(p)	(*(p))
102 #define	poke8(p, val)	(*(p) = (val))
103 
104 static struct {
105 	boolean_t	initialized;
106 	enum { KB_LED_IDLE, KB_LED_COMMAND_SENT, KB_LED_VALUE_SENT }
107 			led_state;
108 	int		led_commanded;
109 	/*
110 	 * Possible values:
111 	 *
112 	 * -1		Nothing pending
113 	 * 0x000-0x0ff	Pending byte
114 	 * 0x100-0x1ff	Needs leading zero, then low byte next.
115 	 *
116 	 * Others undefined.
117 	 */
118 	int		pending;
119 } kb = {
120 	B_FALSE,	/* initialized? */
121 	KB_LED_IDLE,	/* LED command state */
122 	-1,		/* commanded LEDs - force refresh */
123 	-1,		/* pending */
124 };
125 
126 static int kb_translate(unsigned char code);
127 static void kb_send(unsigned char cmd);
128 static void kb_update_leds(void);
129 static uchar_t kb_calculate_leds(void);
130 
131 int
132 kb_getchar(void)
133 {
134 	int ret;
135 
136 	while (!kb_ischar())
137 		/* LOOP */;
138 
139 	/*
140 	 * kb_ischar() doesn't succeed without leaving kb.pending
141 	 * set.
142 	 */
143 	ASSERT(kb.pending >= 0);
144 
145 	if (kb.pending & 0x100) {
146 		ret = 0;
147 		kb.pending &= 0xff;
148 	} else {
149 		ret = kb.pending;
150 		kb.pending = -1;
151 	}
152 
153 	return (ret);
154 }
155 
156 int
157 kb_ischar(void)
158 {
159 	unsigned char buffer_stat;
160 	unsigned char code;
161 	unsigned char leds;
162 
163 	if (!kb.initialized) {
164 		kb_init();
165 		kb.initialized = B_TRUE;
166 	}
167 
168 	if (kb.pending >= 0)
169 		return (1);
170 
171 	for (;;) {
172 		buffer_stat = inb(I8042_STAT);
173 		if (buffer_stat == 0xff)
174 			return (0);
175 		buffer_stat &= (I8042_STAT_OUTBF | I8042_STAT_AUXBF);
176 
177 		switch (buffer_stat) {
178 		case 0:
179 		case I8042_STAT_AUXBF:
180 			return (0);
181 		case (I8042_STAT_OUTBF | I8042_STAT_AUXBF):
182 			/*
183 			 * Discard unwanted mouse data.
184 			 */
185 			(void) inb(I8042_DATA);
186 			continue;
187 		}
188 
189 		code = inb(I8042_DATA);
190 
191 		switch (code) {
192 		/*
193 		 * case 0xAA:
194 		 *
195 		 * You might think that we should ignore 0xAA on the
196 		 * grounds that it is the BAT Complete response and will
197 		 * occur on keyboard detach/reattach.  Unfortunately,
198 		 * it is ambiguous - this is also the code for a break
199 		 * of the left shift key.  Since it will be harmless for
200 		 * us to "spuriously" process a break of Left Shift,
201 		 * we just let the normal code handle it.  Perhaps we
202 		 * should take a hint and refresh the LEDs, but I
203 		 * refuse to get very worried about hot-plug issues
204 		 * in this mini-driver.
205 		 */
206 		case 0xFA:
207 
208 			switch (kb.led_state) {
209 			case KB_LED_IDLE:
210 				/*
211 				 * Spurious.  Oh well, ignore it.
212 				 */
213 				break;
214 			case KB_LED_COMMAND_SENT:
215 				leds = kb_calculate_leds();
216 				kb_send(leds);
217 				kb.led_commanded = leds;
218 				kb.led_state = KB_LED_VALUE_SENT;
219 				break;
220 			case KB_LED_VALUE_SENT:
221 				kb.led_state = KB_LED_IDLE;
222 				/*
223 				 * Check for changes made while we were
224 				 * working on the last change.
225 				 */
226 				kb_update_leds();
227 				break;
228 			}
229 			continue;
230 
231 		case 0xE0:
232 		case 0xE1:
233 			/*
234 			 * These are used to distinguish the keys added on
235 			 * the AT-101 keyboard from the original 84 keys.
236 			 * We don't care, and the codes are carefully arranged
237 			 * so that we don't have to.
238 			 */
239 			continue;
240 
241 		default:
242 			if (code & 0x80) {
243 				/* Release */
244 				code &= 0x7f;
245 				switch (keyboard_translate[code].normal) {
246 				case KBTYPE_SPEC_LSHIFT:
247 					poke8(kb_flag, peek8(kb_flag) &
248 					    ~BIOS_LEFT_SHIFT);
249 					break;
250 				case KBTYPE_SPEC_RSHIFT:
251 					poke8(kb_flag, peek8(kb_flag) &
252 					    ~BIOS_RIGHT_SHIFT);
253 					break;
254 				case KBTYPE_SPEC_CTRL:
255 					poke8(kb_flag, peek8(kb_flag) &
256 					    ~BIOS_CTL_SHIFT);
257 					break;
258 				case KBTYPE_SPEC_ALT:
259 					poke8(kb_flag, peek8(kb_flag) &
260 					    ~BIOS_ALT_SHIFT);
261 					break;
262 				case KBTYPE_SPEC_CAPS_LOCK:
263 					poke8(kb_flag_1, peek8(kb_flag_1) &
264 					    ~BIOS_CAPS_SHIFT);
265 					break;
266 				case KBTYPE_SPEC_NUM_LOCK:
267 					poke8(kb_flag_1, peek8(kb_flag_1) &
268 					    ~BIOS_NUM_SHIFT);
269 					break;
270 				case KBTYPE_SPEC_SCROLL_LOCK:
271 					poke8(kb_flag_1, peek8(kb_flag_1) &
272 					    ~BIOS_SCROLL_SHIFT);
273 					break;
274 				default:
275 					/*
276 					 * Ignore all other releases.
277 					 */
278 					break;
279 				}
280 			} else {
281 				/* Press */
282 
283 				kb.pending = kb_translate(code);
284 				if (kb.pending >= 0) {
285 					return (1);
286 				}
287 			}
288 		}
289 	}
290 }
291 
292 int
293 kb_translate(unsigned char code)
294 {
295 	struct keyboard_translate *k;
296 	unsigned short action;
297 	boolean_t shifted;
298 
299 	k = keyboard_translate + code;
300 
301 	shifted = (peek8(kb_flag) & BIOS_EITHER_SHIFT) != 0;
302 
303 	switch (k->normal & 0xFF00) {
304 	case KBTYPE_NUMPAD:
305 		if (peek8(kb_flag) & BIOS_NUM_STATE)
306 			shifted = !shifted;
307 		break;
308 	case KBTYPE_ALPHA:
309 		if (peek8(kb_flag) & BIOS_CAPS_STATE)
310 			shifted = !shifted;
311 		break;
312 	}
313 
314 	if (peek8(kb_flag) & BIOS_ALT_SHIFT)
315 		action = k->alted;
316 	else if (peek8(kb_flag) & BIOS_CTL_SHIFT)
317 		action = k->ctrled;
318 	else if (shifted)
319 		action = k->shifted;
320 	else
321 		action = k->normal;
322 
323 	switch (action & 0xFF00) {
324 	case KBTYPE_NORMAL:
325 	case KBTYPE_ALPHA:
326 		return (action & 0xFF);
327 
328 	case KBTYPE_NUMPAD:
329 	case KBTYPE_FUNC:
330 		return ((action & 0xFF) | 0x100);
331 
332 	case KBTYPE_SPEC:
333 		break;
334 
335 	default:
336 		/*
337 		 * Bad entry.
338 		 */
339 		ASSERT(0);
340 		return (-1);
341 	}
342 
343 	/*
344 	 * Handle special keys, mostly shifts.
345 	 */
346 	switch (action) {
347 	case KBTYPE_SPEC_NOP:
348 	case KBTYPE_SPEC_UNDEF:
349 		break;
350 
351 	case KBTYPE_SPEC_LSHIFT:
352 		poke8(kb_flag, peek8(kb_flag) | BIOS_LEFT_SHIFT);
353 		break;
354 
355 	case KBTYPE_SPEC_RSHIFT:
356 		poke8(kb_flag, peek8(kb_flag) | BIOS_RIGHT_SHIFT);
357 		break;
358 
359 	case KBTYPE_SPEC_CTRL:
360 		poke8(kb_flag, peek8(kb_flag) | BIOS_CTL_SHIFT);
361 		break;
362 
363 	case KBTYPE_SPEC_ALT:
364 		poke8(kb_flag, peek8(kb_flag) | BIOS_ALT_SHIFT);
365 		break;
366 
367 	case KBTYPE_SPEC_CAPS_LOCK:
368 		if (!(peek8(kb_flag_1) & BIOS_CAPS_SHIFT)) {
369 			poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_CAPS_SHIFT);
370 			poke8(kb_flag, peek8(kb_flag) ^ BIOS_CAPS_STATE);
371 		}
372 		break;
373 
374 	case KBTYPE_SPEC_NUM_LOCK:
375 		if (!(peek8(kb_flag_1) & BIOS_NUM_SHIFT)) {
376 			poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_NUM_SHIFT);
377 			poke8(kb_flag, peek8(kb_flag) ^ BIOS_NUM_STATE);
378 		}
379 		break;
380 
381 	case KBTYPE_SPEC_SCROLL_LOCK:
382 		if (!(peek8(kb_flag_1) & BIOS_SCROLL_SHIFT)) {
383 			poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_SCROLL_SHIFT);
384 			poke8(kb_flag, peek8(kb_flag) ^ BIOS_SCROLL_STATE);
385 		}
386 		break;
387 
388 	case KBTYPE_SPEC_MAYBE_REBOOT:
389 #if 0	/* Solaris doesn't reboot via ctrl-alt-del */
390 		if ((peek8(kb_flag) & (BIOS_CTL_SHIFT|BIOS_ALT_SHIFT)) ==
391 		    (BIOS_CTL_SHIFT|BIOS_ALT_SHIFT)) {
392 			reset();
393 			/* NOTREACHED */
394 		}
395 #endif
396 		break;
397 
398 	default:
399 		/*
400 		 * Bad entry
401 		 */
402 		ASSERT(0);
403 		break;
404 	}
405 
406 	/*
407 	 * Consider updating the LEDs.  This does nothing if nothing
408 	 * needs to be done.
409 	 */
410 	kb_update_leds();
411 
412 	return (-1);
413 }
414 
415 void
416 kb_send(unsigned char cmd)
417 {
418 	int retries;
419 
420 	for (retries = 0;
421 	    (inb(I8042_STAT) & I8042_STAT_INBF) != 0 && retries < 100000;
422 	    retries++)
423 		/* LOOP */;
424 	outb(I8042_DATA, cmd);
425 }
426 
427 void
428 kb_update_leds(void)
429 {
430 	if (kb.led_state != KB_LED_IDLE) {
431 		/*
432 		 * The state machine will take care of any additional
433 		 * changes that are necessary.
434 		 */
435 		return;
436 	}
437 
438 	if (kb_calculate_leds() == kb.led_commanded) {
439 		kb.led_state = KB_LED_IDLE;
440 	} else {
441 		kb_send(KB_SET_LED);
442 		kb.led_state = KB_LED_COMMAND_SENT;
443 	}
444 }
445 
446 #define	MIMR_PORT	0x21	/* Mask register for master PIC */
447 #define	MIMR_KB		2	/* Keyboard mask bit in master PIC */
448 
449 void
450 kb_init(void)
451 {
452 	unsigned char pic_mask;
453 	int retries;
454 
455 	/*
456 	 * Write the command byte to turn off interrupts and
457 	 * disable the auxiliary port.
458 	 *
459 	 * 0x80:  0 = Reserved, must be zero.
460 	 * 0x40:  1 = Translate to XT codes.
461 	 *		Solaris turns this off later, but we have a legacy
462 	 *		of using XT codes.
463 	 * 0x20:  1 = Disable aux (mouse) port.
464 	 * 0x10:  0 = Enable main (keyboard) port.
465 	 * 0x08:  0 = Reserved, must be zero.
466 	 * 0x04:  1 = System flag, 1 means passed self-test.
467 	 *		Caution:  setting this bit to zero causes some
468 	 *		systems (HP Kayak XA) to fail to reboot without
469 	 *		a hard reset.
470 	 * 0x02:  0 = Disable aux interrupts.
471 	 * 0x01:  0 = Disable aux interrupts.
472 	 */
473 
474 	for (retries = 0;
475 	    (inb(I8042_STAT) & I8042_STAT_INBF) != 0 && retries < 100000;
476 	    retries++)
477 		/* LOOP */;
478 	outb(I8042_CMD, I8042_WCB);
479 
480 	for (retries = 0;
481 	    (inb(I8042_STAT) & I8042_STAT_INBF) != 0 && retries < 100000;
482 	    retries++)
483 		/* LOOP */;
484 	outb(I8042_DATA, 0x64);
485 
486 	/*
487 	 * If we're running on a system with an emulated 8042 (with
488 	 * USB and SMI emulation), the above command *might* not
489 	 * have turned off keyboard interrupts.  If it didn't,
490 	 * we will lose keystrokes to the BIOS int handler every
491 	 * time someone hits a key while BIOS and STI are active..
492 	 * that is, every time we're in bootconf.exe, for example.
493 	 * Turn off ints at the PIC to prevent this from happening.
494 	 *
495 	 * Yes, this is yet another workaround for buggy BIOS
496 	 * emulation.
497 	 */
498 
499 	pic_mask = inb(MIMR_PORT);
500 	outb(MIMR_PORT, pic_mask | MIMR_KB);
501 
502 	kb_update_leds();
503 }
504 
505 unsigned char
506 kb_calculate_leds(void)
507 {
508 	int res;
509 
510 	res = 0;
511 
512 	if (peek8(kb_flag) & BIOS_CAPS_STATE)
513 		res |= KB_LED_CAPS_LOCK;
514 
515 	if (peek8(kb_flag) & BIOS_NUM_STATE)
516 		res |= KB_LED_NUM_LOCK;
517 
518 	if (peek8(kb_flag) & BIOS_SCROLL_STATE)
519 		res |= KB_LED_SCROLL_LOCK;
520 
521 	return ((char)res);
522 }
523