1 /* Keyboard driver for PC's and AT's. 2 * 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 */ 8 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" 25 26 static u16_t keymap[NR_SCAN_CODES][MAP_COLS] = { 27 #include "keymaps/us-std.src" 28 }; 29 30 #define KB_IN_BYTES 32 /* size of keyboard input buffer */ 31 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. 34 */ 35 #define RELEASE_BIT 0x8000 36 37 static unsigned short inbuf[KB_IN_BYTES]; 38 static unsigned short *inhead = inbuf; 39 static unsigned short *intail = inbuf; 40 static int incount; 41 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 */ 56 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 62 63 static char numpad_map[12] = 64 {'H', 'Y', 'A', 'B', 'D', 'C', 'V', 'U', 'G', 'S', 'T', '@'}; 65 66 static char *fkey_map[12] = 67 {"11", "12", "13", "14", "15", "17", /* F1-F6 */ 68 "18", "19", "20", "21", "23", "24"}; /* F7-F12 */ 69 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 */ 74 75 static endpoint_t input_endpt = NONE; 76 77 static long sticky_alt_mode = 0; 78 static long debug_fkeys = 1; 79 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); 85 86 /*===========================================================================* 87 * map_key * 88 *===========================================================================*/ 89 static unsigned map_key(scode) 90 int scode; 91 { 92 /* Map a scan code to an ASCII code. */ 93 94 int caps, column, lk; 95 u16_t *keyrow; 96 97 keyrow = keymap[scode]; 98 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; 103 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; 116 } 117 } 118 return keyrow[column] & ~(HASNUM | HASCAPS); 119 } 120 121 /*===========================================================================* 122 * do_input * 123 *===========================================================================*/ 124 void do_input(message *msg) 125 { 126 unsigned short scode; 127 endpoint_t endpt; 128 int r; 129 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; 135 } 136 if (endpt != msg->m_source) { 137 printf("TTY: up request from non-INPUT %u\n", msg->m_source); 138 return; 139 } 140 141 input_endpt = msg->m_source; 142 143 /* Pass the current state of the LEDs to INPUT. */ 144 set_leds(); 145 146 break; 147 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; 152 } 153 154 /* Only handle keyboard keys. */ 155 if (msg->m_input_tty_event.page != INPUT_PAGE_KEY) 156 return; 157 158 /* Only handle known USB HID keyboard codes (the 00h-E7h range). */ 159 scode = msg->m_input_tty_event.code; 160 if (scode >= NR_SCAN_CODES) 161 return; 162 163 /* Is it a KEY RELEASE? */ 164 if (msg->m_input_tty_event.value == INPUT_RELEASE) 165 scode |= RELEASE_BIT; 166 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; 172 } 173 174 break; 175 176 default: 177 panic("do_input called for unknown message type %x", msg->m_type); 178 } 179 } 180 181 /*===========================================================================* 182 * kb_read * 183 *===========================================================================*/ 184 static int kb_read(tp, try) 185 tty_t *tp; 186 int try; 187 { 188 /* Process characters from the circular keyboard buffer. */ 189 char buf[7], *p, suffix; 190 unsigned short scode; 191 unsigned ch; 192 193 /* always use the current console */ 194 tp = &tty_table[ccurrent]; 195 196 if (try) 197 return (incount > 0); 198 199 while (incount > 0) { 200 /* Take one key scan code. */ 201 scode = *intail++; 202 if (intail == inbuf + KB_IN_BYTES) intail = inbuf; 203 incount--; 204 205 /* Function keys are being used for debug dumps (if enabled). */ 206 if (debug_fkeys && func_key(scode)) continue; 207 208 /* Perform make/break processing. */ 209 ch = make_break(scode); 210 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'; 237 } 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; 247 } 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; 273 } 274 } 275 } 276 277 return 1; 278 } 279 280 /*===========================================================================* 281 * make_break * 282 *===========================================================================*/ 283 static unsigned make_break(int scode) 284 { 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. 288 */ 289 int ch, make; 290 static int CAD_count = 0; 291 static int rebooting = 0; 292 293 /* Check for CTRL-ALT-DEL, and if found, halt the computer. */ 294 if (ctrl && alt && (scode == INPUT_KEY_DELETE || scode == INPUT_KEY_INSERT)) 295 { 296 if (++CAD_count == 3) { 297 cons_stop(); 298 sys_abort(RB_AUTOBOOT); 299 } 300 sys_kill(INIT_PROC_NR, SIGABRT); 301 rebooting = 1; 302 } 303 304 if (rebooting) 305 return -1; 306 307 /* High-order bit set on key release. */ 308 make = !(scode & RELEASE_BIT); /* true if pressed */ 309 310 ch = map_key(scode &= ~RELEASE_BIT); /* map to ASCII */ 311 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; 329 } 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(); 336 } 337 caps_down = make; 338 break; 339 case NLOCK: /* Num lock */ 340 if (num_down < make) { 341 locks[ccurrent] ^= NUM_LOCK; 342 set_leds(); 343 } 344 num_down = make; 345 break; 346 case SLOCK: /* Scroll lock */ 347 if (scroll_down < make) { 348 locks[ccurrent] ^= SCROLL_LOCK; 349 set_leds(); 350 } 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; 360 } 361 362 /* Key release, or a shift type key. */ 363 return(-1); 364 } 365 366 /*===========================================================================* 367 * set_leds * 368 *===========================================================================*/ 369 static void set_leds(void) 370 { 371 /* Make INPUT set the LEDs on the caps, num, and scroll lock keys. */ 372 message m; 373 int r; 374 375 if (input_endpt == NONE) 376 return; 377 378 memset(&m, 0, sizeof(m)); 379 380 m.m_type = INPUT_SETLEDS; 381 m.m_input_linputdriver_setleds.led_mask = locks[ccurrent] & ~ALT_LOCK; 382 383 if ((r = asynsend3(input_endpt, &m, AMF_NOREPLY)) != OK) 384 printf("TTY: asynsend to INPUT %u failed (%d)\n", input_endpt, r); 385 } 386 387 /*===========================================================================* 388 * kb_init * 389 *===========================================================================*/ 390 void kb_init(tp) 391 tty_t *tp; 392 { 393 /* Initialize the keyboard driver. */ 394 395 tp->tty_devread = kb_read; /* input function */ 396 } 397 398 /*===========================================================================* 399 * kb_init_once * 400 *===========================================================================*/ 401 void kb_init_once(void) 402 { 403 int i; 404 405 env_parse("sticky_alt", "d", 0, &sticky_alt_mode, 0, 1); 406 env_parse("debug_fkeys", "d", 0, &debug_fkeys, 0, 1); 407 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 */ 414 } 415 } 416 417 /*===========================================================================* 418 * kbd_loadmap * 419 *===========================================================================*/ 420 int kbd_loadmap(endpoint_t endpt, cp_grant_id_t grant) 421 { 422 /* Load a new keymap. */ 423 return sys_safecopyfrom(endpt, grant, 0, (vir_bytes) keymap, sizeof(keymap)); 424 } 425 426 /*===========================================================================* 427 * do_fkey_ctl * 428 *===========================================================================*/ 429 void do_fkey_ctl(m_ptr) 430 message *m_ptr; /* pointer to the request message */ 431 { 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. 434 */ 435 int s, i; 436 int result = EINVAL; 437 438 switch (m_ptr->m_lsys_tty_fkey_ctl.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->m_lsys_tty_fkey_ctl.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. 447 */ 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->m_lsys_tty_fkey_ctl.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 */ 457 } 458 #endif 459 } 460 } 461 for (i=0; i < 12; i++) { /* check Shift+F1-F12 keys */ 462 if (bit_isset(m_ptr->m_lsys_tty_fkey_ctl.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->m_lsys_tty_fkey_ctl.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 */ 473 } 474 #endif 475 } 476 } 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->m_lsys_tty_fkey_ctl.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->m_lsys_tty_fkey_ctl.fkeys, i+1); 486 } else { 487 result = EPERM; /* report failure, but try rest */ 488 } 489 } 490 } 491 for (i=0; i < 12; i++) { /* check Shift+F1-F12 keys */ 492 if (bit_isset(m_ptr->m_lsys_tty_fkey_ctl.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->m_lsys_tty_fkey_ctl.sfkeys, i+1); 497 } else { 498 result = EPERM; /* report failure, but try rest */ 499 } 500 } 501 } 502 break; 503 case FKEY_EVENTS: 504 result = OK; /* everything will be ok*/ 505 m_ptr->m_tty_lsys_fkey_ctl.fkeys = m_ptr->m_tty_lsys_fkey_ctl.sfkeys = 0; 506 for (i=0; i < 12; i++) { /* check (Shift+) F1-F12 keys */ 507 if (fkey_obs[i].proc_nr == m_ptr->m_source) { 508 if (fkey_obs[i].events) { 509 bit_set(m_ptr->m_tty_lsys_fkey_ctl.fkeys, i+1); 510 fkey_obs[i].events = 0; 511 } 512 } 513 if (sfkey_obs[i].proc_nr == m_ptr->m_source) { 514 if (sfkey_obs[i].events) { 515 bit_set(m_ptr->m_tty_lsys_fkey_ctl.sfkeys, i+1); 516 sfkey_obs[i].events = 0; 517 } 518 } 519 } 520 break; 521 } 522 523 /* Almost done, return result to caller. */ 524 m_ptr->m_type = result; 525 if ((s = ipc_sendnb(m_ptr->m_source, m_ptr)) != OK) 526 printf("TTY: unable to reply to %d: %d", m_ptr->m_source, s); 527 } 528 529 /*===========================================================================* 530 * func_key * 531 *===========================================================================*/ 532 static int func_key(scode) 533 int scode; /* scan code for a function key */ 534 { 535 /* This procedure traps function keys for debugging purposes. Observers of 536 * function keys are kept in a global array. If a subject (a key) is pressed 537 * the observer is notified of the event. Initialization of the arrays is done 538 * in kb_init, where NONE is set to indicate there is no interest in the key. 539 * Returns FALSE on a key release or if the key is not observable. 540 */ 541 int key; 542 int proc_nr; 543 544 /* Ignore key releases. If this is a key press, get full key code. */ 545 if (scode & RELEASE_BIT) return(FALSE); /* key release */ 546 key = map_key(scode); /* include modifiers */ 547 548 /* Key pressed, now see if there is an observer for the pressed key. 549 * F1-F12 observers are in fkey_obs array. 550 * SHIFT F1-F12 observers are in sfkey_req array. 551 * CTRL F1-F12 reserved (see kb_read) 552 * ALT F1-F12 reserved (see kb_read) 553 * Other combinations are not in use. Note that Alt+Shift+F1-F12 is yet 554 * defined in <minix/keymap.h>, and thus is easy for future extensions. 555 */ 556 if (F1 <= key && key <= F12) { /* F1-F12 */ 557 proc_nr = fkey_obs[key - F1].proc_nr; 558 fkey_obs[key - F1].events ++ ; 559 } else if (SF1 <= key && key <= SF12) { /* Shift F2-F12 */ 560 proc_nr = sfkey_obs[key - SF1].proc_nr; 561 sfkey_obs[key - SF1].events ++; 562 } 563 else { 564 return(FALSE); /* not observable */ 565 } 566 567 /* See if an observer is registered and send it a message. */ 568 if (proc_nr != NONE) { 569 ipc_notify(proc_nr); 570 } 571 return(TRUE); 572 } 573 574 /*===========================================================================* 575 * show_key_mappings * 576 *===========================================================================*/ 577 static void show_key_mappings() 578 { 579 int i,s; 580 581 printf("\n"); 582 printf("System information. Known function key mappings to request debug dumps:\n"); 583 printf("-------------------------------------------------------------------------\n"); 584 for (i=0; i<12; i++) { 585 586 printf(" %sF%d: ", i+1<10? " ":"", i+1); 587 if (fkey_obs[i].proc_nr != NONE) { 588 printf("%-14u", fkey_obs[i].proc_nr); 589 } else { 590 printf("%-14.14s", "<none>"); 591 } 592 593 printf(" %sShift-F%d: ", i+1<10? " ":"", i+1); 594 if (sfkey_obs[i].proc_nr != NONE) { 595 printf("%-14u", sfkey_obs[i].proc_nr); 596 } else { 597 printf("%-14.14s", "<none>"); 598 } 599 printf("\n"); 600 } 601 printf("\n"); 602 printf("Press one of the registered function keys to trigger a debug dump.\n"); 603 printf("\n"); 604 } 605