i386 cpu.h
[minix3.git] / drivers / tty / arch / i386 / keyboard.c
blob26f5d4baac2b50ecbf0ebf33287579ab8a6ffb6b
1 /* Keyboard driver for PC's and AT's.
3 * Changes:
4 * Jul 13, 2004 processes can observe function keys (Jorrit N. Herder)
5 * Jun 15, 2004 removed wreboot(), except panic dumps (Jorrit N. Herder)
6 * Feb 04, 1994 loadable keymaps (Marcus Hampel)
7 */
9 #include <minix/drivers.h>
10 #include <sys/ioctl.h>
11 #include <sys/kbdio.h>
12 #include <sys/time.h>
13 #include <sys/reboot.h>
14 #include <sys/select.h>
15 #include <sys/termios.h>
16 #include <signal.h>
17 #include <machine/archtypes.h>
18 #include <minix/callnr.h>
19 #include <minix/com.h>
20 #include <minix/input.h>
21 #include <minix/keymap.h>
22 #include <minix/ds.h>
23 #include <assert.h>
24 #include "tty.h"
26 static u16_t keymap[NR_SCAN_CODES][MAP_COLS] = {
27 #include "keymaps/us-std.src"
30 #define KB_IN_BYTES 32 /* size of keyboard input buffer */
32 /* Scan codes in the input buffer are in the 0000h-00E7h range inclusive, plus
33 * the following bit if the key was released rather than pressed.
35 #define RELEASE_BIT 0x8000
37 static unsigned short inbuf[KB_IN_BYTES];
38 static unsigned short *inhead = inbuf;
39 static unsigned short *intail = inbuf;
40 static int incount;
42 static int alt_l; /* left alt key state */
43 static int alt_r; /* right alt key state */
44 static int alt; /* either alt key */
45 static int ctrl_l; /* left control key state */
46 static int ctrl_r; /* right control key state */
47 static int ctrl; /* either control key */
48 static int shift_l; /* left shift key state */
49 static int shift_r; /* right shift key state */
50 static int shift; /* either shift key */
51 static int num_down; /* num lock key depressed */
52 static int caps_down; /* caps lock key depressed */
53 static int scroll_down; /* scroll lock key depressed */
54 static int alt_down; /* alt key depressed */
55 static int locks[NR_CONS]; /* per console lock keys state */
57 /* Lock key active bits. Chosen to be equal to the input LED mask bits. */
58 #define SCROLL_LOCK (1 << INPUT_LED_SCROLLLOCK)
59 #define NUM_LOCK (1 << INPUT_LED_NUMLOCK)
60 #define CAPS_LOCK (1 << INPUT_LED_CAPSLOCK)
61 #define ALT_LOCK 0x10
63 static char numpad_map[12] =
64 {'H', 'Y', 'A', 'B', 'D', 'C', 'V', 'U', 'G', 'S', 'T', '@'};
66 static char *fkey_map[12] =
67 {"11", "12", "13", "14", "15", "17", /* F1-F6 */
68 "18", "19", "20", "21", "23", "24"}; /* F7-F12 */
70 /* Variables and definition for observed function keys. */
71 typedef struct observer { endpoint_t proc_nr; int events; } obs_t;
72 static obs_t fkey_obs[12]; /* observers for F1-F12 */
73 static obs_t sfkey_obs[12]; /* observers for SHIFT F1-F12 */
75 static endpoint_t input_endpt = NONE;
77 static long sticky_alt_mode = 0;
78 static long debug_fkeys = 1;
80 static int func_key(int scode);
81 static unsigned make_break(int scode);
82 static void set_leds(void);
83 static void show_key_mappings(void);
84 static unsigned map_key(int scode);
86 /*===========================================================================*
87 * map_key *
88 *===========================================================================*/
89 static unsigned map_key(scode)
90 int scode;
92 /* Map a scan code to an ASCII code. */
94 int caps, column, lk;
95 u16_t *keyrow;
97 keyrow = keymap[scode];
99 caps = shift;
100 lk = locks[ccurrent];
101 if ((lk & NUM_LOCK) && (keyrow[0] & HASNUM)) caps = !caps;
102 if ((lk & CAPS_LOCK) && (keyrow[0] & HASCAPS)) caps = !caps;
104 if (alt) {
105 column = 2;
106 if (ctrl || alt_r) column = 3; /* Ctrl + Alt == AltGr */
107 if (caps) column = 4;
108 } else {
109 if (sticky_alt_mode && (lk & ALT_LOCK)) {
110 column = 2;
111 if (caps) column = 4;
112 } else {
113 column = 0;
114 if (caps) column = 1;
115 if (ctrl) column = 5;
118 return keyrow[column] & ~(HASNUM | HASCAPS);
121 /*===========================================================================*
122 * do_input *
123 *===========================================================================*/
124 void do_input(message *msg)
126 unsigned short scode;
127 endpoint_t endpt;
128 int r;
130 switch (msg->m_type) {
131 case TTY_INPUT_UP:
132 if ((r = ds_retrieve_label_endpt("input", &endpt)) != OK) {
133 printf("TTY: unable to retrieve INPUT endpoint (%d)\n", r);
134 return;
136 if (endpt != msg->m_source) {
137 printf("TTY: up request from non-INPUT %u\n", msg->m_source);
138 return;
141 input_endpt = msg->m_source;
143 /* Pass the current state of the LEDs to INPUT. */
144 set_leds();
146 break;
148 case TTY_INPUT_EVENT:
149 if (msg->m_source != input_endpt) {
150 printf("TTY: input event from non-INPUT %u\n", msg->m_source);
151 return;
154 /* Only handle keyboard keys. */
155 if (msg->INPUT_PAGE != INPUT_PAGE_KEY)
156 return;
158 /* Only handle known USB HID keyboard codes (the 00h-E7h range). */
159 scode = msg->INPUT_CODE;
160 if (scode >= NR_SCAN_CODES)
161 return;
163 /* Is it a KEY RELEASE? */
164 if (msg->INPUT_VALUE == INPUT_RELEASE)
165 scode |= RELEASE_BIT;
167 if (incount < KB_IN_BYTES) {
168 *inhead++ = scode;
169 if (inhead == inbuf + KB_IN_BYTES) inhead = inbuf;
170 incount++;
171 tty_table[ccurrent].tty_events = 1;
174 break;
176 default:
177 panic("do_input called for unknown message type %x", msg->m_type);
181 /*===========================================================================*
182 * kb_read *
183 *===========================================================================*/
184 static int kb_read(tp, try)
185 tty_t *tp;
186 int try;
188 /* Process characters from the circular keyboard buffer. */
189 char buf[7], *p, suffix;
190 unsigned short scode;
191 unsigned ch;
193 /* always use the current console */
194 tp = &tty_table[ccurrent];
196 if (try)
197 return (incount > 0);
199 while (incount > 0) {
200 /* Take one key scan code. */
201 scode = *intail++;
202 if (intail == inbuf + KB_IN_BYTES) intail = inbuf;
203 incount--;
205 /* Function keys are being used for debug dumps (if enabled). */
206 if (debug_fkeys && func_key(scode)) continue;
208 /* Perform make/break processing. */
209 ch = make_break(scode);
211 if (ch <= 0xFF) {
212 /* A normal character. */
213 buf[0] = ch;
214 (void) in_process(tp, buf, 1);
215 } else
216 if (HOME <= ch && ch <= INSRT) {
217 /* An ASCII escape sequence generated by the numeric pad. */
218 buf[0] = ESC;
219 buf[1] = '[';
220 buf[2] = numpad_map[ch - HOME];
221 (void) in_process(tp, buf, 3);
222 } else
223 if ((F1 <= ch && ch <= F12) || (SF1 <= ch && ch <= SF12) ||
224 (CF1 <= ch && ch <= CF12 && !debug_fkeys)) {
225 /* An escape sequence generated by function keys. */
226 if (F1 <= ch && ch <= F12) {
227 ch -= F1;
228 suffix = 0;
229 } else
230 if (SF1 <= ch && ch <= SF12) {
231 ch -= SF1;
232 suffix = '2';
233 } else
234 /* (CF1 <= ch && ch <= CF12) */ {
235 ch -= CF1;
236 suffix = shift ? '6' : '5';
238 /* ^[[11~ for F1, ^[[24;5~ for CF12 etc */
239 buf[0] = ESC;
240 buf[1] = '[';
241 buf[2] = fkey_map[ch][0];
242 buf[3] = fkey_map[ch][1];
243 p = &buf[4];
244 if (suffix) {
245 *p++ = ';';
246 *p++ = suffix;
248 *p++ = '~';
249 (void) in_process(tp, buf, p - buf);
250 } else
251 if (ch == ALEFT) {
252 /* Choose lower numbered console as current console. */
253 select_console(ccurrent - 1);
254 set_leds();
255 } else
256 if (ch == ARIGHT) {
257 /* Choose higher numbered console as current console. */
258 select_console(ccurrent + 1);
259 set_leds();
260 } else
261 if (AF1 <= ch && ch <= AF12) {
262 /* Alt-F1 is console, Alt-F2 is ttyc1, etc. */
263 select_console(ch - AF1);
264 set_leds();
265 } else
266 if (CF1 <= ch && ch <= CF12) {
267 switch(ch) {
268 case CF1: show_key_mappings(); break;
269 case CF3: toggle_scroll(); break; /* hardware <-> software */
270 case CF7: sigchar(line2tty(CONS_MINOR), SIGQUIT, 1); break;
271 case CF8: sigchar(line2tty(CONS_MINOR), SIGINT, 1); break;
272 case CF9: sigchar(line2tty(CONS_MINOR), SIGKILL, 1); break;
277 return 1;
280 /*===========================================================================*
281 * make_break *
282 *===========================================================================*/
283 static unsigned make_break(int scode)
285 /* This routine can handle keyboards that interrupt only on key depression,
286 * as well as keyboards that interrupt on key depression and key release.
287 * For efficiency, the interrupt routine filters out most key releases.
289 int ch, make;
290 static int CAD_count = 0;
291 static int rebooting = 0;
293 /* Check for CTRL-ALT-DEL, and if found, halt the computer. */
294 if (ctrl && alt && (scode == INPUT_KEY_DELETE || scode == INPUT_KEY_INSERT))
296 if (++CAD_count == 3) {
297 cons_stop();
298 sys_abort(RB_AUTOBOOT);
300 sys_kill(INIT_PROC_NR, SIGABRT);
301 rebooting = 1;
304 if (rebooting)
305 return -1;
307 /* High-order bit set on key release. */
308 make = !(scode & RELEASE_BIT); /* true if pressed */
310 ch = map_key(scode &= ~RELEASE_BIT); /* map to ASCII */
312 switch (ch) {
313 case LCTRL: /* Left or right control key */
314 case RCTRL:
315 *(ch == RCTRL ? &ctrl_r : &ctrl_l) = make;
316 ctrl = ctrl_l | ctrl_r;
317 break;
318 case LSHIFT: /* Left or right shift key */
319 case RSHIFT:
320 *(ch == RSHIFT ? &shift_r : &shift_l) = make;
321 shift = shift_l | shift_r;
322 break;
323 case LALT: /* Left or right alt key */
324 case RALT:
325 *(ch == RALT ? &alt_r : &alt_l) = make;
326 alt = alt_l | alt_r;
327 if (sticky_alt_mode && (alt_r && (alt_down < make))) {
328 locks[ccurrent] ^= ALT_LOCK;
330 alt_down = make;
331 break;
332 case CALOCK: /* Caps lock - toggle on 0 -> 1 transition */
333 if (caps_down < make) {
334 locks[ccurrent] ^= CAPS_LOCK;
335 set_leds();
337 caps_down = make;
338 break;
339 case NLOCK: /* Num lock */
340 if (num_down < make) {
341 locks[ccurrent] ^= NUM_LOCK;
342 set_leds();
344 num_down = make;
345 break;
346 case SLOCK: /* Scroll lock */
347 if (scroll_down < make) {
348 locks[ccurrent] ^= SCROLL_LOCK;
349 set_leds();
351 scroll_down = make;
352 break;
353 default: /* A normal key */
354 if(!make)
355 return -1;
356 if(ch)
357 return ch;
358 /* Ignore unmapped key codes. */
359 return -1;
362 /* Key release, or a shift type key. */
363 return(-1);
366 /*===========================================================================*
367 * set_leds *
368 *===========================================================================*/
369 static void set_leds(void)
371 /* Make INPUT set the LEDs on the caps, num, and scroll lock keys. */
372 message m;
373 int r;
375 if (input_endpt == NONE)
376 return;
378 memset(&m, 0, sizeof(m));
380 m.m_type = INPUT_SETLEDS;
381 m.INPUT_LED_MASK = locks[ccurrent] & ~ALT_LOCK;
383 if ((r = asynsend3(input_endpt, &m, AMF_NOREPLY)) != OK)
384 printf("TTY: asynsend to INPUT %u failed (%d)\n", input_endpt, r);
387 /*===========================================================================*
388 * kb_init *
389 *===========================================================================*/
390 void kb_init(tp)
391 tty_t *tp;
393 /* Initialize the keyboard driver. */
395 tp->tty_devread = kb_read; /* input function */
398 /*===========================================================================*
399 * kb_init_once *
400 *===========================================================================*/
401 void kb_init_once(void)
403 int i;
405 env_parse("sticky_alt", "d", 0, &sticky_alt_mode, 0, 1);
406 env_parse("debug_fkeys", "d", 0, &debug_fkeys, 0, 1);
408 /* Clear the function key observers array. Also see func_key(). */
409 for (i = 0; i < 12; i++) {
410 fkey_obs[i].proc_nr = NONE; /* F1-F12 observers */
411 fkey_obs[i].events = 0; /* F1-F12 observers */
412 sfkey_obs[i].proc_nr = NONE; /* Shift F1-F12 observers */
413 sfkey_obs[i].events = 0; /* F1-F12 observers */
417 /*===========================================================================*
418 * kbd_loadmap *
419 *===========================================================================*/
420 int kbd_loadmap(endpoint_t endpt, cp_grant_id_t grant)
422 /* Load a new keymap. */
423 return sys_safecopyfrom(endpt, grant, 0, (vir_bytes) keymap, sizeof(keymap));
426 /*===========================================================================*
427 * do_fkey_ctl *
428 *===========================================================================*/
429 void do_fkey_ctl(m_ptr)
430 message *m_ptr; /* pointer to the request message */
432 /* This procedure allows processes to register a function key to receive
433 * notifications if it is pressed. At most one binding per key can exist.
435 int s, i;
436 int result = EINVAL;
438 switch (m_ptr->FKEY_REQUEST) { /* see what we must do */
439 case FKEY_MAP: /* request for new mapping */
440 result = OK; /* assume everything will be ok*/
441 for (i=0; i < 12; i++) { /* check F1-F12 keys */
442 if (bit_isset(m_ptr->FKEY_FKEYS, i+1) ) {
443 #if DEAD_CODE
444 /* Currently, we don't check if the slot is in use, so that IS
445 * can recover after a crash by overtaking its existing mappings.
446 * In future, a better solution will be implemented.
448 if (fkey_obs[i].proc_nr == NONE) {
449 #endif
450 fkey_obs[i].proc_nr = m_ptr->m_source;
451 fkey_obs[i].events = 0;
452 bit_unset(m_ptr->FKEY_FKEYS, i+1);
453 #if DEAD_CODE
454 } else {
455 printf("WARNING, fkey_map failed F%d\n", i+1);
456 result = EBUSY; /* report failure, but try rest */
458 #endif
461 for (i=0; i < 12; i++) { /* check Shift+F1-F12 keys */
462 if (bit_isset(m_ptr->FKEY_SFKEYS, i+1) ) {
463 #if DEAD_CODE
464 if (sfkey_obs[i].proc_nr == NONE) {
465 #endif
466 sfkey_obs[i].proc_nr = m_ptr->m_source;
467 sfkey_obs[i].events = 0;
468 bit_unset(m_ptr->FKEY_SFKEYS, i+1);
469 #if DEAD_CODE
470 } else {
471 printf("WARNING, fkey_map failed Shift F%d\n", i+1);
472 result = EBUSY; /* report failure but try rest */
474 #endif
477 break;
478 case FKEY_UNMAP:
479 result = OK; /* assume everything will be ok*/
480 for (i=0; i < 12; i++) { /* check F1-F12 keys */
481 if (bit_isset(m_ptr->FKEY_FKEYS, i+1) ) {
482 if (fkey_obs[i].proc_nr == m_ptr->m_source) {
483 fkey_obs[i].proc_nr = NONE;
484 fkey_obs[i].events = 0;
485 bit_unset(m_ptr->FKEY_FKEYS, i+1);
486 } else {
487 result = EPERM; /* report failure, but try rest */
491 for (i=0; i < 12; i++) { /* check Shift+F1-F12 keys */
492 if (bit_isset(m_ptr->FKEY_SFKEYS, i+1) ) {
493 if (sfkey_obs[i].proc_nr == m_ptr->m_source) {
494 sfkey_obs[i].proc_nr = NONE;
495 sfkey_obs[i].events = 0;
496 bit_unset(m_ptr->FKEY_SFKEYS, i+1);
497 } else {
498 result = EPERM; /* report failure, but try rest */
502 break;
503 case FKEY_EVENTS:
504 m_ptr->FKEY_FKEYS = m_ptr->FKEY_SFKEYS = 0;
505 for (i=0; i < 12; i++) { /* check (Shift+) F1-F12 keys */
506 if (fkey_obs[i].proc_nr == m_ptr->m_source) {
507 if (fkey_obs[i].events) {
508 bit_set(m_ptr->FKEY_FKEYS, i+1);
509 fkey_obs[i].events = 0;
512 if (sfkey_obs[i].proc_nr == m_ptr->m_source) {
513 if (sfkey_obs[i].events) {
514 bit_set(m_ptr->FKEY_SFKEYS, i+1);
515 sfkey_obs[i].events = 0;
519 break;
522 /* Almost done, return result to caller. */
523 m_ptr->m_type = result;
524 if ((s = ipc_sendnb(m_ptr->m_source, m_ptr)) != OK)
525 printf("TTY: unable to reply to %d: %d", m_ptr->m_source, s);
528 /*===========================================================================*
529 * func_key *
530 *===========================================================================*/
531 static int func_key(scode)
532 int scode; /* scan code for a function key */
534 /* This procedure traps function keys for debugging purposes. Observers of
535 * function keys are kept in a global array. If a subject (a key) is pressed
536 * the observer is notified of the event. Initialization of the arrays is done
537 * in kb_init, where NONE is set to indicate there is no interest in the key.
538 * Returns FALSE on a key release or if the key is not observable.
540 int key;
541 int proc_nr;
543 /* Ignore key releases. If this is a key press, get full key code. */
544 if (scode & RELEASE_BIT) return(FALSE); /* key release */
545 key = map_key(scode); /* include modifiers */
547 /* Key pressed, now see if there is an observer for the pressed key.
548 * F1-F12 observers are in fkey_obs array.
549 * SHIFT F1-F12 observers are in sfkey_req array.
550 * CTRL F1-F12 reserved (see kb_read)
551 * ALT F1-F12 reserved (see kb_read)
552 * Other combinations are not in use. Note that Alt+Shift+F1-F12 is yet
553 * defined in <minix/keymap.h>, and thus is easy for future extensions.
555 if (F1 <= key && key <= F12) { /* F1-F12 */
556 proc_nr = fkey_obs[key - F1].proc_nr;
557 fkey_obs[key - F1].events ++ ;
558 } else if (SF1 <= key && key <= SF12) { /* Shift F2-F12 */
559 proc_nr = sfkey_obs[key - SF1].proc_nr;
560 sfkey_obs[key - SF1].events ++;
562 else {
563 return(FALSE); /* not observable */
566 /* See if an observer is registered and send it a message. */
567 if (proc_nr != NONE) {
568 ipc_notify(proc_nr);
570 return(TRUE);
573 /*===========================================================================*
574 * show_key_mappings *
575 *===========================================================================*/
576 static void show_key_mappings()
578 int i,s;
580 printf("\n");
581 printf("System information. Known function key mappings to request debug dumps:\n");
582 printf("-------------------------------------------------------------------------\n");
583 for (i=0; i<12; i++) {
585 printf(" %sF%d: ", i+1<10? " ":"", i+1);
586 if (fkey_obs[i].proc_nr != NONE) {
587 printf("%-14u", fkey_obs[i].proc_nr);
588 } else {
589 printf("%-14.14s", "<none>");
592 printf(" %sShift-F%d: ", i+1<10? " ":"", i+1);
593 if (sfkey_obs[i].proc_nr != NONE) {
594 printf("%-14u", sfkey_obs[i].proc_nr);
595 } else {
596 printf("%-14.14s", "<none>");
598 printf("\n");
600 printf("\n");
601 printf("Press one of the registered function keys to trigger a debug dump.\n");
602 printf("\n");