xref: /minix/minix/drivers/tty/tty/arch/i386/keyboard.c (revision 7f5f010b)
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