]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/devkbd.c
sdide: never timeout or retry scsi commands from the controller driver
[plan9front.git] / sys / src / 9 / pc / devkbd.c
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 enum
63 {
64         /* controller command byte */
65         Cscs1=          (1<<6),         /* scan code set 1 */
66         Cauxdis=        (1<<5),         /* mouse disable */
67         Ckbddis=        (1<<4),         /* kbd disable */
68         Csf=            (1<<2),         /* system flag */
69         Cauxint=        (1<<1),         /* mouse interrupt enable */
70         Ckbdint=        (1<<0),         /* kbd interrupt enable */
71 };
72
73 enum {
74         Qdir,
75         Qscancode,
76         Qleds,
77 };
78
79 static Dirtab kbdtab[] = {
80         ".",            {Qdir, 0, QTDIR},       0,      0555,
81         "scancode",     {Qscancode, 0},         0,      0440,
82         "leds",         {Qleds, 0},             0,      0220,
83 };
84
85 static Lock i8042lock;
86 static uchar ccc;
87 static void kbdputc(int);
88 static void (*auxputc)(int, int);
89 static int nokbd = 1;                   /* flag: no PS/2 keyboard */
90
91 static struct {
92         Ref ref;
93         Queue *q;
94 } kbd;
95
96 /*
97  *  wait for output no longer busy
98  */
99 static int
100 outready(void)
101 {
102         int tries;
103
104         for(tries = 0; (inb(Status) & Outbusy); tries++){
105                 if(tries > 500)
106                         return -1;
107                 delay(2);
108         }
109         return 0;
110 }
111
112 /*
113  *  wait for input
114  */
115 static int
116 inready(void)
117 {
118         int tries;
119
120         for(tries = 0; !(inb(Status) & Inready); tries++){
121                 if(tries > 500)
122                         return -1;
123                 delay(2);
124         }
125         return 0;
126 }
127
128 /*
129  *  ask 8042 to reset the machine
130  */
131 void
132 i8042reset(void)
133 {
134         int i, x;
135
136         if(nokbd)
137                 return;
138
139         *((ushort*)KADDR(0x472)) = 0x1234;      /* BIOS warm-boot flag */
140
141         /*
142          *  newer reset the machine command
143          */
144         outready();
145         outb(Cmd, 0xFE);
146         outready();
147
148         /*
149          *  Pulse it by hand (old somewhat reliable)
150          */
151         x = 0xDF;
152         for(i = 0; i < 5; i++){
153                 x ^= 1;
154                 outready();
155                 outb(Cmd, 0xD1);
156                 outready();
157                 outb(Data, x);  /* toggle reset */
158                 delay(100);
159         }
160 }
161
162 int
163 i8042auxcmd(int cmd)
164 {
165         unsigned int c;
166         int tries;
167
168         c = 0;
169         tries = 0;
170
171         ilock(&i8042lock);
172         do{
173                 if(tries++ > 2)
174                         break;
175                 if(outready() < 0)
176                         break;
177                 outb(Cmd, 0xD4);
178                 if(outready() < 0)
179                         break;
180                 outb(Data, cmd);
181                 if(outready() < 0)
182                         break;
183                 if(inready() < 0)
184                         break;
185                 c = inb(Data);
186         } while(c == 0xFE || c == 0);
187         iunlock(&i8042lock);
188
189         if(c != 0xFA){
190                 print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
191                 return -1;
192         }
193         return 0;
194 }
195
196 int
197 i8042auxcmds(uchar *cmd, int ncmd)
198 {
199         int i;
200
201         ilock(&i8042lock);
202         for(i=0; i<ncmd; i++){
203                 if(outready() < 0)
204                         break;
205                 outb(Cmd, 0xD4);
206                 if(outready() < 0)
207                         break;
208                 outb(Data, cmd[i]);
209         }
210         iunlock(&i8042lock);
211         return i;
212 }
213
214 /*
215  * set keyboard's leds for lock states (scroll, numeric, caps).
216  *
217  * at least one keyboard (from Qtronics) also sets its numeric-lock
218  * behaviour to match the led state, though it has no numeric keypad,
219  * and some BIOSes bring the system up with numeric-lock set and no
220  * setting to change that.  this combination steals the keys for these
221  * characters and makes it impossible to generate them: uiolkjm&*().
222  * thus we'd like to be able to force the numeric-lock led (and behaviour) off.
223  */
224 static void
225 setleds(int leds)
226 {
227         static int old = -1;
228
229         if(nokbd || leds == old)
230                 return;
231         leds &= 7;
232         ilock(&i8042lock);
233         for(;;){
234                 if(outready() < 0)
235                         break;
236                 outb(Data, 0xed);               /* `reset keyboard lock states' */
237                 if(outready() < 0)
238                         break;
239                 outb(Data, leds);
240                 if(outready() < 0)
241                         break;
242                 old = leds;
243                 break;
244         }
245         iunlock(&i8042lock);
246 }
247
248 /*
249  *  keyboard interrupt
250  */
251 static void
252 i8042intr(Ureg*, void*)
253 {
254         int s, c;
255         uchar b;
256
257         /*
258          *  get status
259          */
260         ilock(&i8042lock);
261         s = inb(Status);
262         if(!(s&Inready)){
263                 iunlock(&i8042lock);
264                 return;
265         }
266
267         /*
268          *  get the character
269          */
270         c = inb(Data);
271         iunlock(&i8042lock);
272
273         /*
274          *  if it's the aux port...
275          */
276         if(s & Minready){
277                 if(auxputc != nil)
278                         auxputc(c, 0);
279                 return;
280         }
281
282         b = c & 0xff;
283         qproduce(kbd.q, &b, 1);
284 }
285
286 void
287 i8042auxenable(void (*putc)(int, int))
288 {
289         char *err = "i8042: aux init failed\n";
290
291         /* enable kbd/aux xfers and interrupts */
292         ccc &= ~Cauxdis;
293         ccc |= Cauxint;
294
295         ilock(&i8042lock);
296         if(outready() < 0)
297                 print(err);
298         outb(Cmd, 0x60);                        /* write control register */
299         if(outready() < 0)
300                 print(err);
301         outb(Data, ccc);
302         if(outready() < 0)
303                 print(err);
304         outb(Cmd, 0xA8);                        /* auxiliary device enable */
305         if(outready() < 0){
306                 iunlock(&i8042lock);
307                 return;
308         }
309         auxputc = putc;
310         intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
311         iunlock(&i8042lock);
312 }
313
314 static Chan *
315 kbdattach(char *spec)
316 {
317         return devattach(L'b', spec);
318 }
319
320 static Walkqid*
321 kbdwalk(Chan *c, Chan *nc, char **name, int nname)
322 {
323         return devwalk(c, nc, name, nname, kbdtab, nelem(kbdtab), devgen);
324 }
325
326 static int
327 kbdstat(Chan *c, uchar *dp, int n)
328 {
329         return devstat(c, dp, n, kbdtab, nelem(kbdtab), devgen);
330 }
331
332 static Chan*
333 kbdopen(Chan *c, int omode)
334 {
335         if(!iseve())
336                 error(Eperm);
337         if(c->qid.path == Qscancode){
338                 if(waserror()){
339                         decref(&kbd.ref);
340                         nexterror();
341                 }
342                 if(incref(&kbd.ref) != 1)
343                         error(Einuse);
344                 c = devopen(c, omode, kbdtab, nelem(kbdtab), devgen);
345                 poperror();
346                 return c;
347         }
348         return devopen(c, omode, kbdtab, nelem(kbdtab), devgen);
349 }
350
351 static void
352 kbdclose(Chan *c)
353 {
354         if((c->flag & COPEN) && c->qid.path == Qscancode)
355                 decref(&kbd.ref);
356 }
357
358 static Block*
359 kbdbread(Chan *c, long n, ulong off)
360 {
361         if(c->qid.path == Qscancode)
362                 return qbread(kbd.q, n);
363         else
364                 return devbread(c, n, off);
365 }
366
367 static long
368 kbdread(Chan *c, void *a, long n, vlong)
369 {
370         if(c->qid.path == Qscancode)
371                 return qread(kbd.q, a, n);
372         if(c->qid.path == Qdir)
373                 return devdirread(c, a, n, kbdtab, nelem(kbdtab), devgen);
374
375         error(Egreg);
376         return 0;
377 }
378
379 static long
380 kbdwrite(Chan *c, void *a, long n, vlong)
381 {
382         char tmp[8+1], *p;
383
384         if(c->qid.path != Qleds)
385                 error(Egreg);
386
387         p = tmp + n;
388         if(n >= sizeof(tmp))
389                 p = tmp + sizeof(tmp)-1;
390         memmove(tmp, a, p - tmp);
391         *p = 0;
392
393         setleds(atoi(tmp));
394
395         return n;
396 }
397
398 Dev kbddevtab = {
399         L'b',
400         "kbd",
401
402         devreset,
403         devinit,
404         devshutdown,
405         kbdattach,
406         kbdwalk,
407         kbdstat,
408         kbdopen,
409         devcreate,
410         kbdclose,
411         kbdread,
412         kbdbread,
413         kbdwrite,
414         devbwrite,
415         devremove,
416         devwstat,
417 };
418
419
420 static char *initfailed = "i8042: kbdinit failed\n";
421
422 static int
423 outbyte(int port, int c)
424 {
425         outb(port, c);
426         if(outready() < 0) {
427                 print(initfailed);
428                 return -1;
429         }
430         return 0;
431 }
432
433 void
434 kbdenable(void)
435 {
436         kbd.q = qopen(1024, Qcoalesce, 0, 0);
437         if(kbd.q == nil)
438                 panic("kbdenable");
439         qnoblock(kbd.q, 1);
440
441         ioalloc(Data, 1, 0, "kbd");
442         ioalloc(Cmd, 1, 0, "kbd");
443
444         intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
445 }
446
447 void
448 kbdinit(void)
449 {
450         int c, try;
451
452         /* wait for a quiescent controller */
453         try = 1000;
454         while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
455                 if(c & Inready)
456                         inb(Data);
457                 delay(1);
458         }
459         if (try <= 0) {
460                 print(initfailed);
461                 return;
462         }
463
464         /* get current controller command byte */
465         outb(Cmd, 0x20);
466         if(inready() < 0){
467                 print("i8042: kbdinit can't read ccc\n");
468                 ccc = 0;
469         } else
470                 ccc = inb(Data);
471
472         /* enable kbd xfers and interrupts */
473         ccc &= ~Ckbddis;
474         ccc |= Csf | Ckbdint | Cscs1;
475         if(outready() < 0) {
476                 print(initfailed);
477                 return;
478         }
479
480         nokbd = 0;
481
482         /* disable mouse */
483         if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0)
484                 print("i8042: kbdinit mouse disable failed\n");
485 }