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