xref: /plan9/sys/src/9/pc/kbd.c (revision a8482e058adfb9039bb8aaa40ea4270fc64ff0a4)
1 /*
2  * keyboard input
3  */
4 #include	"u.h"
5 #include	"../port/lib.h"
6 #include	"mem.h"
7 #include	"dat.h"
8 #include	"fns.h"
9 #include	"io.h"
10 #include	"../port/error.h"
11 
12 enum {
13 	Data=		0x60,		/* data port */
14 
15 	Status=		0x64,		/* status port */
16 	 Inready=	0x01,		/*  input character ready */
17 	 Outbusy=	0x02,		/*  output busy */
18 	 Sysflag=	0x04,		/*  system flag */
19 	 Cmddata=	0x08,		/*  cmd==0, data==1 */
20 	 Inhibit=	0x10,		/*  keyboard/mouse inhibited */
21 	 Minready=	0x20,		/*  mouse character ready */
22 	 Rtimeout=	0x40,		/*  general timeout */
23 	 Parity=	0x80,
24 
25 	Cmd=		0x64,		/* command port (write only) */
26 
27 	Spec=		0xF800,		/* Unicode private space */
28 	PF=		Spec|0x20,	/* num pad function key */
29 	View=		Spec|0x00,	/* view (shift window up) */
30 	KF=		0xF000,		/* function key (begin Unicode private space) */
31 	Shift=		Spec|0x60,
32 	Break=		Spec|0x61,
33 	Ctrl=		Spec|0x62,
34 	Latin=		Spec|0x63,
35 	Caps=		Spec|0x64,
36 	Num=		Spec|0x65,
37 	Middle=		Spec|0x66,
38 	Altgr=		Spec|0x67,
39 	Kmouse=		Spec|0x100,
40 	No=		0x00,		/* peter */
41 
42 	Home=		KF|13,
43 	Up=		KF|14,
44 	Pgup=		KF|15,
45 	Print=		KF|16,
46 	Left=		KF|17,
47 	Right=		KF|18,
48 	End=		KF|24,
49 	Down=		View,
50 	Pgdown=		KF|19,
51 	Ins=		KF|20,
52 	Del=		0x7F,
53 	Scroll=		KF|21,
54 
55 	Nscan=	128,
56 };
57 
58 /*
59  * The codes at 0x79 and 0x7b are produed by the PFU Happy Hacking keyboard.
60  * A 'standard' keyboard doesn't produce anything above 0x58.
61  */
62 Rune kbtab[Nscan] =
63 {
64 [0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
65 [0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
66 [0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
67 [0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's',
68 [0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
69 [0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
70 [0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	'*',
71 [0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
72 [0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
73 [0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
74 [0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
75 [0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
76 [0x60]	No,	No,	No,	No,	No,	No,	No,	No,
77 [0x68]	No,	No,	No,	No,	No,	No,	No,	No,
78 [0x70]	No,	No,	No,	No,	No,	No,	No,	No,
79 [0x78]	No,	View,	No,	Up,	No,	No,	No,	No,
80 };
81 
82 Rune kbtabshift[Nscan] =
83 {
84 [0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
85 [0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
86 [0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
87 [0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
88 [0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
89 [0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
90 [0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	'*',
91 [0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
92 [0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
93 [0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
94 [0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
95 [0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
96 [0x60]	No,	No,	No,	No,	No,	No,	No,	No,
97 [0x68]	No,	No,	No,	No,	No,	No,	No,	No,
98 [0x70]	No,	No,	No,	No,	No,	No,	No,	No,
99 [0x78]	No,	Up,	No,	Up,	No,	No,	No,	No,
100 };
101 
102 Rune kbtabesc1[Nscan] =
103 {
104 [0x00]	No,	No,	No,	No,	No,	No,	No,	No,
105 [0x08]	No,	No,	No,	No,	No,	No,	No,	No,
106 [0x10]	No,	No,	No,	No,	No,	No,	No,	No,
107 [0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
108 [0x20]	No,	No,	No,	No,	No,	No,	No,	No,
109 [0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
110 [0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
111 [0x38]	Altgr,	No,	No,	No,	No,	No,	No,	No,
112 [0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
113 [0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
114 [0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
115 [0x58]	No,	No,	No,	No,	No,	No,	No,	No,
116 [0x60]	No,	No,	No,	No,	No,	No,	No,	No,
117 [0x68]	No,	No,	No,	No,	No,	No,	No,	No,
118 [0x70]	No,	No,	No,	No,	No,	No,	No,	No,
119 [0x78]	No,	Up,	No,	No,	No,	No,	No,	No,
120 };
121 
122 Rune kbtabaltgr[Nscan] =
123 {
124 [0x00]	No,	No,	No,	No,	No,	No,	No,	No,
125 [0x08]	No,	No,	No,	No,	No,	No,	No,	No,
126 [0x10]	No,	No,	No,	No,	No,	No,	No,	No,
127 [0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
128 [0x20]	No,	No,	No,	No,	No,	No,	No,	No,
129 [0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
130 [0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
131 [0x38]	Altgr,	No,	No,	No,	No,	No,	No,	No,
132 [0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
133 [0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
134 [0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
135 [0x58]	No,	No,	No,	No,	No,	No,	No,	No,
136 [0x60]	No,	No,	No,	No,	No,	No,	No,	No,
137 [0x68]	No,	No,	No,	No,	No,	No,	No,	No,
138 [0x70]	No,	No,	No,	No,	No,	No,	No,	No,
139 [0x78]	No,	Up,	No,	No,	No,	No,	No,	No,
140 };
141 
142 Rune kbtabctrl[Nscan] =
143 {
144 [0x00]	No,	'', 	'', 	'', 	'', 	'', 	'', 	'',
145 [0x08]	'', 	'', 	'', 	'', 	'
146 ', 	'', 	'\b',	'\t',
147 [0x10]	'', 	'', 	'', 	'', 	'', 	'', 	'', 	'\t',
148 [0x18]	'', 	'', 	'', 	'', 	'\n',	Ctrl,	'', 	'',
149 [0x20]	'', 	'', 	'', 	'\b',	'\n',	'', 	'', 	'',
150 [0x28]	'', 	No, 	Shift,	'', 	'', 	'', 	'', 	'',
151 [0x30]	'', 	'', 	'
152 ', 	'', 	'', 	'', 	Shift,	'\n',
153 [0x38]	Latin,	No, 	Ctrl,	'', 	'', 	'', 	'', 	'',
154 [0x40]	'', 	'', 	'', 	'
155 ', 	'', 	'', 	'', 	'',
156 [0x48]	'', 	'', 	'
157 ', 	'', 	'', 	'', 	'', 	'',
158 [0x50]	'', 	'', 	'', 	'', 	No,	No,	No,	'',
159 [0x58]	'', 	No,	No,	No,	No,	No,	No,	No,
160 [0x60]	No,	No,	No,	No,	No,	No,	No,	No,
161 [0x68]	No,	No,	No,	No,	No,	No,	No,	No,
162 [0x70]	No,	No,	No,	No,	No,	No,	No,	No,
163 [0x78]	No,	'', 	No,	'\b',	No,	No,	No,	No,
164 };
165 
166 enum
167 {
168 	/* controller command byte */
169 	Cscs1=		(1<<6),		/* scan code set 1 */
170 	Cauxdis=	(1<<5),		/* mouse disable */
171 	Ckbddis=	(1<<4),		/* kbd disable */
172 	Csf=		(1<<2),		/* system flag */
173 	Cauxint=	(1<<1),		/* mouse interrupt enable */
174 	Ckbdint=	(1<<0),		/* kbd interrupt enable */
175 };
176 
177 int mouseshifted;
178 void (*kbdmouse)(int);
179 
180 static Lock i8042lock;
181 static uchar ccc;
182 static void (*auxputc)(int, int);
183 static int nokbd = 1;
184 
185 /*
186  *  wait for output no longer busy
187  */
188 static int
189 outready(void)
190 {
191 	int tries;
192 
193 	for(tries = 0; (inb(Status) & Outbusy); tries++){
194 		if(tries > 500)
195 			return -1;
196 		delay(2);
197 	}
198 	return 0;
199 }
200 
201 /*
202  *  wait for input
203  */
204 static int
205 inready(void)
206 {
207 	int tries;
208 
209 	for(tries = 0; !(inb(Status) & Inready); tries++){
210 		if(tries > 500)
211 			return -1;
212 		delay(2);
213 	}
214 	return 0;
215 }
216 
217 /*
218  *  ask 8042 to reset the machine
219  */
220 void
221 i8042reset(void)
222 {
223 	int i, x;
224 
225 	if(nokbd)
226 		return;
227 
228 	*((ushort*)KADDR(0x472)) = 0x1234;	/* BIOS warm-boot flag */
229 
230 	/*
231 	 *  newer reset the machine command
232 	 */
233 	outready();
234 	outb(Cmd, 0xFE);
235 	outready();
236 
237 	/*
238 	 *  Pulse it by hand (old somewhat reliable)
239 	 */
240 	x = 0xDF;
241 	for(i = 0; i < 5; i++){
242 		x ^= 1;
243 		outready();
244 		outb(Cmd, 0xD1);
245 		outready();
246 		outb(Data, x);	/* toggle reset */
247 		delay(100);
248 	}
249 }
250 
251 int
252 i8042auxcmd(int cmd)
253 {
254 	unsigned int c;
255 	int tries;
256 
257 	c = 0;
258 	tries = 0;
259 
260 	ilock(&i8042lock);
261 	do{
262 		if(tries++ > 2)
263 			break;
264 		if(outready() < 0)
265 			break;
266 		outb(Cmd, 0xD4);
267 		if(outready() < 0)
268 			break;
269 		outb(Data, cmd);
270 		if(outready() < 0)
271 			break;
272 		if(inready() < 0)
273 			break;
274 		c = inb(Data);
275 	} while(c == 0xFE || c == 0);
276 	iunlock(&i8042lock);
277 
278 	if(c != 0xFA){
279 		print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
280 		return -1;
281 	}
282 	return 0;
283 }
284 
285 int
286 i8042auxcmds(uchar *cmd, int ncmd)
287 {
288 	int i;
289 
290 	ilock(&i8042lock);
291 	for(i=0; i<ncmd; i++){
292 		if(outready() < 0)
293 			break;
294 		outb(Cmd, 0xD4);
295 		if(outready() < 0)
296 			break;
297 		outb(Data, cmd[i]);
298 	}
299 	iunlock(&i8042lock);
300 	return i;
301 }
302 
303 typedef struct Kbscan Kbscan;
304 struct Kbscan {
305 	int	esc1;
306 	int	esc2;
307 	int	alt;
308 	int	altgr;
309 	int	caps;
310 	int	ctl;
311 	int	num;
312 	int	shift;
313 	int	collecting;
314 	int	nk;
315 	Rune	kc[5];
316 	int	buttons;
317 };
318 
319 Kbscan kbscans[2];	/* kernel and external scan code state */
320 static int kdebug;
321 /*
322  * Scan code processing
323  */
324 void
325 kbdputsc(int c, int external)
326 {
327 	int i, keyup;
328 	Kbscan *kbscan;
329 
330 	if(external)
331 		kbscan = &kbscans[1];
332 	else
333 		kbscan = &kbscans[0];
334 
335 	if(kdebug)
336 		print("sc %x ms %d\n", c, mouseshifted);
337 	/*
338 	 *  e0's is the first of a 2 character sequence, e1 the first
339 	 *  of a 3 character sequence (on the safari)
340 	 */
341 	if(c == 0xe0){
342 		kbscan->esc1 = 1;
343 		return;
344 	} else if(c == 0xe1){
345 		kbscan->esc2 = 2;
346 		return;
347 	}
348 
349 	keyup = c & 0x80;
350 	c &= 0x7f;
351 	if(c > sizeof kbtab){
352 		c |= keyup;
353 		if(c != 0xFF)	/* these come fairly often: CAPSLOCK U Y */
354 			print("unknown key %ux\n", c);
355 		return;
356 	}
357 
358 	if(kbscan->esc1){
359 		c = kbtabesc1[c];
360 		kbscan->esc1 = 0;
361 	} else if(kbscan->esc2){
362 		kbscan->esc2--;
363 		return;
364 	} else if(kbscan->shift)
365 		c = kbtabshift[c];
366 	else if(kbscan->altgr)
367 		c = kbtabaltgr[c];
368 	else if(kbscan->ctl)
369 		c = kbtabctrl[c];
370 	else
371 		c = kbtab[c];
372 
373 	if(kbscan->caps && c<='z' && c>='a')
374 		c += 'A' - 'a';
375 
376 	/*
377 	 *  keyup only important for shifts
378 	 */
379 	if(keyup){
380 		switch(c){
381 		case Latin:
382 			kbscan->alt = 0;
383 			break;
384 		case Shift:
385 			kbscan->shift = 0;
386 			mouseshifted = 0;
387 if(kdebug)
388 	print("shiftclr\n");
389 			break;
390 		case Ctrl:
391 			kbscan->ctl = 0;
392 			break;
393 		case Altgr:
394 			kbscan->altgr = 0;
395 			break;
396 		case Kmouse|1:
397 		case Kmouse|2:
398 		case Kmouse|3:
399 		case Kmouse|4:
400 		case Kmouse|5:
401 			kbscan->buttons &= ~(1<<(c-Kmouse-1));
402 			if(kbdmouse)
403 				kbdmouse(kbscan->buttons);
404 			break;
405 		}
406 		return;
407 	}
408 
409 	/*
410  	 *  normal character
411 	 */
412 	if(!(c & (Spec|KF))){
413 		if(kbscan->ctl)
414 			if(kbscan->alt && c == Del)
415 				exit(0);
416 		if(!kbscan->collecting){
417 			kbdputc(kbdq, c);
418 			return;
419 		}
420 		kbscan->kc[kbscan->nk++] = c;
421 		c = latin1(kbscan->kc, kbscan->nk);
422 		if(c < -1)	/* need more keystrokes */
423 			return;
424 		if(c != -1)	/* valid sequence */
425 			kbdputc(kbdq, c);
426 		else	/* dump characters */
427 			for(i=0; i<kbscan->nk; i++)
428 				kbdputc(kbdq, kbscan->kc[i]);
429 		kbscan->nk = 0;
430 		kbscan->collecting = 0;
431 		return;
432 	} else {
433 		switch(c){
434 		case Caps:
435 			kbscan->caps ^= 1;
436 			return;
437 		case Num:
438 			kbscan->num ^= 1;
439 			return;
440 		case Shift:
441 			kbscan->shift = 1;
442 if(kdebug)
443 	print("shift\n");
444 			mouseshifted = 1;
445 			return;
446 		case Latin:
447 			kbscan->alt = 1;
448 			/*
449 			 * VMware and Qemu use Ctl-Alt as the key combination
450 			 * to make the VM give up keyboard and mouse focus.
451 			 * This has the unfortunate side effect that when you
452 			 * come back into focus, Plan 9 thinks you want to type
453 			 * a compose sequence (you just typed alt).
454 			 *
455 			 * As a clumsy hack around this, we look for ctl-alt
456 			 * and don't treat it as the start of a compose sequence.
457 			 */
458 			if(!kbscan->ctl){
459 				kbscan->collecting = 1;
460 				kbscan->nk = 0;
461 			}
462 			return;
463 		case Ctrl:
464 			kbscan->ctl = 1;
465 			return;
466 		case Altgr:
467 			kbscan->altgr = 1;
468 			return;
469 		case Kmouse|1:
470 		case Kmouse|2:
471 		case Kmouse|3:
472 		case Kmouse|4:
473 		case Kmouse|5:
474 			kbscan->buttons |= 1<<(c-Kmouse-1);
475 			if(kbdmouse)
476 				kbdmouse(kbscan->buttons);
477 			return;
478 		case KF|11:
479 			kdebug = 1;
480 			break;
481 		case KF|12:
482 			kdebug = 0;
483 			break;
484 		}
485 	}
486 	kbdputc(kbdq, c);
487 }
488 
489 /*
490  *  keyboard interrupt
491  */
492 static void
493 i8042intr(Ureg*, void*)
494 {
495 	int s, c;
496 
497 	/*
498 	 *  get status
499 	 */
500 	ilock(&i8042lock);
501 	s = inb(Status);
502 	if(!(s&Inready)){
503 		iunlock(&i8042lock);
504 		return;
505 	}
506 
507 	/*
508 	 *  get the character
509 	 */
510 	c = inb(Data);
511 	iunlock(&i8042lock);
512 
513 	/*
514 	 *  if it's the aux port...
515 	 */
516 	if(s & Minready){
517 		if(auxputc != nil)
518 			auxputc(c, kbscans[0].shift);	/* internal source */
519 		return;
520 	}
521 
522 	kbdputsc(c, 0);			/* internal source */
523 }
524 
525 void
526 i8042auxenable(void (*putc)(int, int))
527 {
528 	char *err = "i8042: aux init failed\n";
529 
530 	/* enable kbd/aux xfers and interrupts */
531 	ccc &= ~Cauxdis;
532 	ccc |= Cauxint;
533 
534 	ilock(&i8042lock);
535 	if(outready() < 0)
536 		print(err);
537 	outb(Cmd, 0x60);			/* write control register */
538 	if(outready() < 0)
539 		print(err);
540 	outb(Data, ccc);
541 	if(outready() < 0)
542 		print(err);
543 	outb(Cmd, 0xA8);			/* auxilliary device enable */
544 	if(outready() < 0){
545 		iunlock(&i8042lock);
546 		return;
547 	}
548 	auxputc = putc;
549 	intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
550 	iunlock(&i8042lock);
551 }
552 
553 static char *initfailed = "i8042: kbdinit failed\n";
554 
555 static int
556 outbyte(int port, int c)
557 {
558 	outb(port, c);
559 	if(outready() < 0) {
560 		print(initfailed);
561 		return -1;
562 	}
563 	return 0;
564 }
565 
566 void
567 kbdinit(void)
568 {
569 	int c, try;
570 
571 	/* wait for a quiescent controller */
572 	try = 1000;
573 	while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
574 		if(c & Inready)
575 			inb(Data);
576 		delay(1);
577 	}
578 	if (try <= 0) {
579 		print(initfailed);
580 		return;
581 	}
582 
583 	/* get current controller command byte */
584 	outb(Cmd, 0x20);
585 	if(inready() < 0){
586 		print("i8042: kbdinit can't read ccc\n");
587 		ccc = 0;
588 	} else
589 		ccc = inb(Data);
590 
591 	/* enable kbd xfers and interrupts */
592 	ccc &= ~Ckbddis;
593 	ccc |= Csf | Ckbdint | Cscs1;
594 	if(outready() < 0) {
595 		print(initfailed);
596 		return;
597 	}
598 
599 	nokbd = 0;
600 
601 	/* disable mouse */
602 	if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0)
603 		print("i8042: kbdinit mouse disable failed\n");
604 }
605 
606 void
607 kbdenable(void)
608 {
609 	kbdq = qopen(4*1024, 0, 0, 0);
610 	if(kbdq == nil)
611 		panic("kbdinit");
612 	qnoblock(kbdq, 1);
613 
614 	ioalloc(Data, 1, 0, "kbd");
615 	ioalloc(Cmd, 1, 0, "kbd");
616 
617 	intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
618 }
619 
620 void
621 kbdputmap(ushort m, ushort scanc, Rune r)
622 {
623 	if(scanc >= Nscan)
624 		error(Ebadarg);
625 	switch(m) {
626 	default:
627 		error(Ebadarg);
628 	case 0:
629 		kbtab[scanc] = r;
630 		break;
631 	case 1:
632 		kbtabshift[scanc] = r;
633 		break;
634 	case 2:
635 		kbtabesc1[scanc] = r;
636 		break;
637 	case 3:
638 		kbtabaltgr[scanc] = r;
639 		break;
640 	case 4:
641 		kbtabctrl[scanc] = r;
642 		break;
643 	}
644 }
645 
646 int
647 kbdgetmap(uint offset, int *t, int *sc, Rune *r)
648 {
649 	if ((int)offset < 0)
650 		error(Ebadarg);
651 	*t = offset/Nscan;
652 	*sc = offset%Nscan;
653 	switch(*t) {
654 	default:
655 		return 0;
656 	case 0:
657 		*r = kbtab[*sc];
658 		return 1;
659 	case 1:
660 		*r = kbtabshift[*sc];
661 		return 1;
662 	case 2:
663 		*r = kbtabesc1[*sc];
664 		return 1;
665 	case 3:
666 		*r = kbtabaltgr[*sc];
667 		return 1;
668 	case 4:
669 		*r = kbtabctrl[*sc];
670 		return 1;
671 	}
672 }
673