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