xref: /plan9-contrib/sys/src/9/pc/kbd.c (revision 25fc69938fdecc61cd09e795cbe2d2f72f1082b1)
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 produced 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 	static int badkbd;
257 
258 	if(badkbd)
259 		return -1;
260 	c = 0;
261 	tries = 0;
262 
263 	ilock(&i8042lock);
264 	do{
265 		if(tries++ > 2)
266 			break;
267 		if(outready() < 0)
268 			break;
269 		outb(Cmd, 0xD4);
270 		if(outready() < 0)
271 			break;
272 		outb(Data, cmd);
273 		if(outready() < 0)
274 			break;
275 		if(inready() < 0)
276 			break;
277 		c = inb(Data);
278 	} while(c == 0xFE || c == 0);
279 	iunlock(&i8042lock);
280 
281 	if(c != 0xFA){
282 		print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
283 		badkbd = 1;	/* don't keep trying; there might not be one */
284 		return -1;
285 	}
286 	return 0;
287 }
288 
289 int
290 i8042auxcmds(uchar *cmd, int ncmd)
291 {
292 	int i;
293 
294 	ilock(&i8042lock);
295 	for(i=0; i<ncmd; i++){
296 		if(outready() < 0)
297 			break;
298 		outb(Cmd, 0xD4);
299 		if(outready() < 0)
300 			break;
301 		outb(Data, cmd[i]);
302 	}
303 	iunlock(&i8042lock);
304 	return i;
305 }
306 
307 typedef struct Kbscan Kbscan;
308 struct Kbscan {
309 	int	esc1;
310 	int	esc2;
311 	int	alt;
312 	int	altgr;
313 	int	caps;
314 	int	ctl;
315 	int	num;
316 	int	shift;
317 	int	collecting;
318 	int	nk;
319 	Rune	kc[5];
320 	int	buttons;
321 };
322 
323 Kbscan kbscans[2];	/* kernel and external scan code state */
324 static int kdebug;
325 /*
326  * Scan code processing
327  */
328 void
329 kbdputsc(int c, int external)
330 {
331 	int i, keyup;
332 	Kbscan *kbscan;
333 
334 	if(external)
335 		kbscan = &kbscans[1];
336 	else
337 		kbscan = &kbscans[0];
338 
339 	if(kdebug)
340 		print("sc %x ms %d\n", c, mouseshifted);
341 	/*
342 	 *  e0's is the first of a 2 character sequence, e1 the first
343 	 *  of a 3 character sequence (on the safari)
344 	 */
345 	if(c == 0xe0){
346 		kbscan->esc1 = 1;
347 		return;
348 	} else if(c == 0xe1){
349 		kbscan->esc2 = 2;
350 		return;
351 	}
352 
353 	keyup = c & 0x80;
354 	c &= 0x7f;
355 	if(c > sizeof kbtab){
356 		c |= keyup;
357 		if(c != 0xFF)	/* these come fairly often: CAPSLOCK U Y */
358 			print("unknown key %ux\n", c);
359 		return;
360 	}
361 
362 	if(kbscan->esc1){
363 		c = kbtabesc1[c];
364 		kbscan->esc1 = 0;
365 	} else if(kbscan->esc2){
366 		kbscan->esc2--;
367 		return;
368 	} else if(kbscan->shift)
369 		c = kbtabshift[c];
370 	else if(kbscan->altgr)
371 		c = kbtabaltgr[c];
372 	else if(kbscan->ctl)
373 		c = kbtabctrl[c];
374 	else
375 		c = kbtab[c];
376 
377 	if(kbscan->caps && c<='z' && c>='a')
378 		c += 'A' - 'a';
379 
380 	/*
381 	 *  keyup only important for shifts
382 	 */
383 	if(keyup){
384 		switch(c){
385 		case Latin:
386 			kbscan->alt = 0;
387 			break;
388 		case Shift:
389 			kbscan->shift = 0;
390 			mouseshifted = 0;
391 if(kdebug)
392 	print("shiftclr\n");
393 			break;
394 		case Ctrl:
395 			kbscan->ctl = 0;
396 			break;
397 		case Altgr:
398 			kbscan->altgr = 0;
399 			break;
400 		case Kmouse|1:
401 		case Kmouse|2:
402 		case Kmouse|3:
403 		case Kmouse|4:
404 		case Kmouse|5:
405 			kbscan->buttons &= ~(1<<(c-Kmouse-1));
406 			if(kbdmouse)
407 				kbdmouse(kbscan->buttons);
408 			break;
409 		}
410 		return;
411 	}
412 
413 	/*
414 	 *  normal character
415 	 */
416 	if(!(c & (Spec|KF))){
417 		if(kbscan->ctl)
418 			if(kbscan->alt && c == Del)
419 				exit(0);
420 		if(!kbscan->collecting){
421 			kbdputc(kbdq, c);
422 			return;
423 		}
424 		kbscan->kc[kbscan->nk++] = c;
425 		c = latin1(kbscan->kc, kbscan->nk);
426 		if(c < -1)	/* need more keystrokes */
427 			return;
428 		if(c != -1)	/* valid sequence */
429 			kbdputc(kbdq, c);
430 		else	/* dump characters */
431 			for(i=0; i<kbscan->nk; i++)
432 				kbdputc(kbdq, kbscan->kc[i]);
433 		kbscan->nk = 0;
434 		kbscan->collecting = 0;
435 		return;
436 	} else {
437 		switch(c){
438 		case Caps:
439 			kbscan->caps ^= 1;
440 			return;
441 		case Num:
442 			kbscan->num ^= 1;
443 			return;
444 		case Shift:
445 			kbscan->shift = 1;
446 if(kdebug)
447 	print("shift\n");
448 			mouseshifted = 1;
449 			return;
450 		case Latin:
451 			kbscan->alt = 1;
452 			/*
453 			 * VMware and Qemu use Ctl-Alt as the key combination
454 			 * to make the VM give up keyboard and mouse focus.
455 			 * This has the unfortunate side effect that when you
456 			 * come back into focus, Plan 9 thinks you want to type
457 			 * a compose sequence (you just typed alt).
458 			 *
459 			 * As a clumsy hack around this, we look for ctl-alt
460 			 * and don't treat it as the start of a compose sequence.
461 			 */
462 			if(!kbscan->ctl){
463 				kbscan->collecting = 1;
464 				kbscan->nk = 0;
465 			}
466 			return;
467 		case Ctrl:
468 			kbscan->ctl = 1;
469 			return;
470 		case Altgr:
471 			kbscan->altgr = 1;
472 			return;
473 		case Kmouse|1:
474 		case Kmouse|2:
475 		case Kmouse|3:
476 		case Kmouse|4:
477 		case Kmouse|5:
478 			kbscan->buttons |= 1<<(c-Kmouse-1);
479 			if(kbdmouse)
480 				kbdmouse(kbscan->buttons);
481 			return;
482 		case KF|11:
483 			kdebug = 1;
484 			break;
485 		case KF|12:
486 			kdebug = 0;
487 			break;
488 		}
489 	}
490 	kbdputc(kbdq, c);
491 }
492 
493 /*
494  *  keyboard interrupt
495  */
496 static void
497 i8042intr(Ureg*, void*)
498 {
499 	int s, c;
500 
501 	/*
502 	 *  get status
503 	 */
504 	ilock(&i8042lock);
505 	s = inb(Status);
506 	if(!(s&Inready)){
507 		iunlock(&i8042lock);
508 		return;
509 	}
510 
511 	/*
512 	 *  get the character
513 	 */
514 	c = inb(Data);
515 	iunlock(&i8042lock);
516 
517 	/*
518 	 *  if it's the aux port...
519 	 */
520 	if(s & Minready){
521 		if(auxputc != nil)
522 			auxputc(c, kbscans[0].shift);	/* internal source */
523 		return;
524 	}
525 
526 	kbdputsc(c, 0);			/* internal source */
527 }
528 
529 void
530 i8042auxenable(void (*putc)(int, int))
531 {
532 	char *err = "i8042: aux init failed\n";
533 
534 	/* enable kbd/aux xfers and interrupts */
535 	ccc &= ~Cauxdis;
536 	ccc |= Cauxint;
537 
538 	ilock(&i8042lock);
539 	if(outready() < 0)
540 		print(err);
541 	outb(Cmd, 0x60);			/* write control register */
542 	if(outready() < 0)
543 		print(err);
544 	outb(Data, ccc);
545 	if(outready() < 0)
546 		print(err);
547 	outb(Cmd, 0xA8);			/* auxiliary device enable */
548 	if(outready() < 0){
549 		iunlock(&i8042lock);
550 		return;
551 	}
552 	auxputc = putc;
553 	intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
554 	iunlock(&i8042lock);
555 }
556 
557 static char *initfailed = "i8042: kbdinit failed\n";
558 
559 static int
560 outbyte(int port, int c)
561 {
562 	outb(port, c);
563 	if(outready() < 0) {
564 		print(initfailed);
565 		return -1;
566 	}
567 	return 0;
568 }
569 
570 void
571 kbdinit(void)
572 {
573 	int c, try;
574 
575 	/* wait for a quiescent controller */
576 	try = 1000;
577 	while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
578 		if(c & Inready)
579 			inb(Data);
580 		delay(1);
581 	}
582 	if (try <= 0) {
583 		print(initfailed);
584 		return;
585 	}
586 
587 	/* get current controller command byte */
588 	outb(Cmd, 0x20);
589 	if(inready() < 0){
590 		print("i8042: kbdinit can't read ccc\n");
591 		ccc = 0;
592 	} else
593 		ccc = inb(Data);
594 
595 	/* enable kbd xfers and interrupts */
596 	ccc &= ~Ckbddis;
597 	ccc |= Csf | Ckbdint | Cscs1;
598 	if(outready() < 0) {
599 		print(initfailed);
600 		return;
601 	}
602 
603 	nokbd = 0;
604 
605 	/* disable mouse */
606 	if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0)
607 		print("i8042: kbdinit mouse disable failed\n");
608 }
609 
610 void
611 kbdenable(void)
612 {
613 	kbdq = qopen(4*1024, 0, 0, 0);
614 	if(kbdq == nil)
615 		panic("kbdinit");
616 	qnoblock(kbdq, 1);
617 
618 	ioalloc(Data, 1, 0, "kbd");
619 	ioalloc(Cmd, 1, 0, "kbd");
620 
621 	intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
622 }
623 
624 void
625 kbdputmap(ushort m, ushort scanc, Rune r)
626 {
627 	if(scanc >= Nscan)
628 		error(Ebadarg);
629 	switch(m) {
630 	default:
631 		error(Ebadarg);
632 	case 0:
633 		kbtab[scanc] = r;
634 		break;
635 	case 1:
636 		kbtabshift[scanc] = r;
637 		break;
638 	case 2:
639 		kbtabesc1[scanc] = r;
640 		break;
641 	case 3:
642 		kbtabaltgr[scanc] = r;
643 		break;
644 	case 4:
645 		kbtabctrl[scanc] = r;
646 		break;
647 	}
648 }
649 
650 int
651 kbdgetmap(uint offset, int *t, int *sc, Rune *r)
652 {
653 	if ((int)offset < 0)
654 		error(Ebadarg);
655 	*t = offset/Nscan;
656 	*sc = offset%Nscan;
657 	switch(*t) {
658 	default:
659 		return 0;
660 	case 0:
661 		*r = kbtab[*sc];
662 		return 1;
663 	case 1:
664 		*r = kbtabshift[*sc];
665 		return 1;
666 	case 2:
667 		*r = kbtabesc1[*sc];
668 		return 1;
669 	case 3:
670 		*r = kbtabaltgr[*sc];
671 		return 1;
672 	case 4:
673 		*r = kbtabctrl[*sc];
674 		return 1;
675 	}
676 }
677