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