]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/devkbd.c
devkbd: remove unused constants
[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 kbdputc(int);
54 static void (*auxputc)(int, int);
55 static int nokbd = 1;                   /* flag: no PS/2 keyboard */
56
57 static struct {
58         Ref ref;
59         Queue *q;
60 } kbd;
61
62 /*
63  *  wait for output no longer busy
64  */
65 static int
66 outready(void)
67 {
68         int tries;
69
70         for(tries = 0; (inb(Status) & Outbusy); tries++){
71                 if(tries > 500)
72                         return -1;
73                 delay(2);
74         }
75         return 0;
76 }
77
78 /*
79  *  wait for input
80  */
81 static int
82 inready(void)
83 {
84         int tries;
85
86         for(tries = 0; !(inb(Status) & Inready); tries++){
87                 if(tries > 500)
88                         return -1;
89                 delay(2);
90         }
91         return 0;
92 }
93
94 /*
95  *  ask 8042 to reset the machine
96  */
97 void
98 i8042reset(void)
99 {
100         int i, x;
101
102         if(nokbd)
103                 return;
104
105         *((ushort*)KADDR(0x472)) = 0x1234;      /* BIOS warm-boot flag */
106
107         /*
108          *  newer reset the machine command
109          */
110         outready();
111         outb(Cmd, 0xFE);
112         outready();
113
114         /*
115          *  Pulse it by hand (old somewhat reliable)
116          */
117         x = 0xDF;
118         for(i = 0; i < 5; i++){
119                 x ^= 1;
120                 outready();
121                 outb(Cmd, 0xD1);
122                 outready();
123                 outb(Data, x);  /* toggle reset */
124                 delay(100);
125         }
126 }
127
128 int
129 i8042auxcmd(int cmd)
130 {
131         unsigned int c;
132         int tries;
133
134         c = 0;
135         tries = 0;
136
137         ilock(&i8042lock);
138         do{
139                 if(tries++ > 2)
140                         break;
141                 if(outready() < 0)
142                         break;
143                 outb(Cmd, 0xD4);
144                 if(outready() < 0)
145                         break;
146                 outb(Data, cmd);
147                 if(outready() < 0)
148                         break;
149                 if(inready() < 0)
150                         break;
151                 c = inb(Data);
152         } while(c == 0xFE || c == 0);
153         iunlock(&i8042lock);
154
155         if(c != 0xFA){
156                 print("i8042: %2.2ux returned to the %2.2ux command (pc=%#p)\n", 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         /* enable kbd/aux xfers and interrupts */
258         ccc &= ~Cauxdis;
259         ccc |= Cauxint;
260
261         ilock(&i8042lock);
262         if(outready() < 0)
263                 print(err);
264         outb(Cmd, 0x60);                        /* write control register */
265         if(outready() < 0)
266                 print(err);
267         outb(Data, ccc);
268         if(outready() < 0)
269                 print(err);
270         outb(Cmd, 0xA8);                        /* auxiliary device enable */
271         if(outready() < 0){
272                 print(err);
273                 iunlock(&i8042lock);
274                 return;
275         }
276         auxputc = putc;
277         intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
278         iunlock(&i8042lock);
279 }
280
281 static Chan *
282 kbdattach(char *spec)
283 {
284         return devattach(L'b', spec);
285 }
286
287 static Walkqid*
288 kbdwalk(Chan *c, Chan *nc, char **name, int nname)
289 {
290         return devwalk(c, nc, name, nname, kbdtab, nelem(kbdtab), devgen);
291 }
292
293 static int
294 kbdstat(Chan *c, uchar *dp, int n)
295 {
296         return devstat(c, dp, n, kbdtab, nelem(kbdtab), devgen);
297 }
298
299 static Chan*
300 kbdopen(Chan *c, int omode)
301 {
302         if(!iseve())
303                 error(Eperm);
304         if(c->qid.path == Qscancode){
305                 if(waserror()){
306                         decref(&kbd.ref);
307                         nexterror();
308                 }
309                 if(incref(&kbd.ref) != 1)
310                         error(Einuse);
311                 c = devopen(c, omode, kbdtab, nelem(kbdtab), devgen);
312                 poperror();
313                 return c;
314         }
315         return devopen(c, omode, kbdtab, nelem(kbdtab), devgen);
316 }
317
318 static void
319 kbdclose(Chan *c)
320 {
321         if((c->flag & COPEN) && c->qid.path == Qscancode)
322                 decref(&kbd.ref);
323 }
324
325 static Block*
326 kbdbread(Chan *c, long n, ulong off)
327 {
328         if(c->qid.path == Qscancode)
329                 return qbread(kbd.q, n);
330         else
331                 return devbread(c, n, off);
332 }
333
334 static long
335 kbdread(Chan *c, void *a, long n, vlong)
336 {
337         if(c->qid.path == Qscancode)
338                 return qread(kbd.q, a, n);
339         if(c->qid.path == Qdir)
340                 return devdirread(c, a, n, kbdtab, nelem(kbdtab), devgen);
341
342         error(Egreg);
343         return 0;
344 }
345
346 static long
347 kbdwrite(Chan *c, void *a, long n, vlong)
348 {
349         char tmp[8+1], *p;
350
351         if(c->qid.path != Qleds)
352                 error(Egreg);
353
354         p = tmp + n;
355         if(n >= sizeof(tmp))
356                 p = tmp + sizeof(tmp)-1;
357         memmove(tmp, a, p - tmp);
358         *p = 0;
359
360         setleds(atoi(tmp));
361
362         return n;
363 }
364
365 Dev kbddevtab = {
366         L'b',
367         "kbd",
368
369         devreset,
370         devinit,
371         devshutdown,
372         kbdattach,
373         kbdwalk,
374         kbdstat,
375         kbdopen,
376         devcreate,
377         kbdclose,
378         kbdread,
379         kbdbread,
380         kbdwrite,
381         devbwrite,
382         devremove,
383         devwstat,
384 };
385
386
387 static char *initfailed = "i8042: kbdinit failed\n";
388
389 static int
390 outbyte(int port, int c)
391 {
392         outb(port, c);
393         if(outready() < 0) {
394                 print(initfailed);
395                 return -1;
396         }
397         return 0;
398 }
399
400 void
401 kbdenable(void)
402 {
403         kbd.q = qopen(1024, Qcoalesce, 0, 0);
404         if(kbd.q == nil)
405                 panic("kbdenable");
406         qnoblock(kbd.q, 1);
407
408         ioalloc(Data, 1, 0, "kbd");
409         ioalloc(Cmd, 1, 0, "kbd");
410
411         intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
412 }
413
414 void
415 kbdinit(void)
416 {
417         int c, try;
418
419         /* wait for a quiescent controller */
420         try = 1000;
421         while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
422                 if(c & Inready)
423                         inb(Data);
424                 delay(1);
425         }
426         if (try <= 0) {
427                 print(initfailed);
428                 return;
429         }
430
431         /* get current controller command byte */
432         outb(Cmd, 0x20);
433         if(inready() < 0){
434                 print("i8042: kbdinit can't read ccc\n");
435                 ccc = 0;
436         } else
437                 ccc = inb(Data);
438
439         /* enable kbd xfers and interrupts */
440         ccc &= ~Ckbddis;
441         ccc |= Csf | Ckbdint | Cscs1;
442         if(outready() < 0) {
443                 print(initfailed);
444                 return;
445         }
446
447         nokbd = 0;
448
449         /* disable mouse */
450         if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0)
451                 print("i8042: kbdinit mouse disable failed\n");
452 }