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