]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/sgi/devkbd.c
merge
[plan9front.git] / sys / src / 9 / sgi / 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=           0x40+3,         /* data port */
14         Cmd=            0x44+3,         /* command port (write) */
15         Status=         0x44+3,         /* status port (read) */
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
26 enum
27 {
28         /* controller command byte */
29         Cscs1=          (1<<6),         /* scan code set 1 */
30         Cauxdis=        (1<<5),         /* mouse disable */
31         Ckbddis=        (1<<4),         /* kbd disable */
32         Csf=            (1<<2),         /* system flag */
33         Cauxint=        (1<<1),         /* mouse interrupt enable */
34         Ckbdint=        (1<<0),         /* kbd interrupt enable */
35 };
36
37 enum {
38         Qdir,
39         Qscancode,
40         Qleds,
41 };
42
43 static Dirtab kbdtab[] = {
44         ".",            {Qdir, 0, QTDIR},       0,      0555,
45         "scancode",     {Qscancode, 0},         0,      0440,
46         "leds",         {Qleds, 0},             0,      0220,
47 };
48
49 static Lock i8042lock;
50 static uchar ccc, dummy;
51
52 static struct {
53         Ref ref;
54         Queue *q;
55         uchar *io;
56 } kbd;
57
58
59 #define inb(r)          (dummy=kbd.io[r])
60 #define outb(r,b)       (kbd.io[r]=b)
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  * set keyboard's leds for lock states (scroll, numeric, caps).
96  *
97  * at least one keyboard (from Qtronics) also sets its numeric-lock
98  * behaviour to match the led state, though it has no numeric keypad,
99  * and some BIOSes bring the system up with numeric-lock set and no
100  * setting to change that.  this combination steals the keys for these
101  * characters and makes it impossible to generate them: uiolkjm&*().
102  * thus we'd like to be able to force the numeric-lock led (and behaviour) off.
103  */
104 static void
105 setleds(int leds)
106 {
107         static int old = -1;
108
109         if(!conf.keyboard || leds == old)
110                 return;
111         leds &= 7;
112         ilock(&i8042lock);
113         for(;;){
114                 if(outready() < 0)
115                         break;
116                 outb(Data, 0xed);               /* `reset keyboard lock states' */
117                 if(outready() < 0)
118                         break;
119                 outb(Data, leds);
120                 if(outready() < 0)
121                         break;
122                 old = leds;
123                 break;
124         }
125         iunlock(&i8042lock);
126 }
127
128 /*
129  *  keyboard interrupt
130  */
131 static void
132 i8042intr(Ureg*, void*)
133 {
134         extern void sgimouseputc(int);
135
136         int s, c;
137         uchar b;
138
139         /*
140          *  get status
141          */
142         ilock(&i8042lock);
143         s = inb(Status);
144         if(!(s&Inready)){
145                 iunlock(&i8042lock);
146                 return;
147         }
148
149         /*
150          *  get the character
151          */
152         c = inb(Data);
153         iunlock(&i8042lock);
154
155         b = c & 0xff;
156
157         /*
158          *  if it's the aux port...
159          */
160         if(s & Minready){
161                 sgimouseputc(b);
162                 return;
163         }
164
165         qproduce(kbd.q, &b, 1);
166 }
167
168 static void
169 pollintr(void)
170 {
171         i8042intr(nil, nil);
172 }
173
174 static Chan *
175 kbdattach(char *spec)
176 {
177         return devattach(L'b', spec);
178 }
179
180 static Walkqid*
181 kbdwalk(Chan *c, Chan *nc, char **name, int nname)
182 {
183         return devwalk(c, nc, name, nname, kbdtab, nelem(kbdtab), devgen);
184 }
185
186 static int
187 kbdstat(Chan *c, uchar *dp, int n)
188 {
189         return devstat(c, dp, n, kbdtab, nelem(kbdtab), devgen);
190 }
191
192 static Chan*
193 kbdopen(Chan *c, int omode)
194 {
195         if(!iseve())
196                 error(Eperm);
197         if(c->qid.path == Qscancode){
198                 if(waserror()){
199                         decref(&kbd.ref);
200                         nexterror();
201                 }
202                 if(incref(&kbd.ref) != 1)
203                         error(Einuse);
204                 c = devopen(c, omode, kbdtab, nelem(kbdtab), devgen);
205                 poperror();
206                 return c;
207         }
208         return devopen(c, omode, kbdtab, nelem(kbdtab), devgen);
209 }
210
211 static void
212 kbdclose(Chan *c)
213 {
214         if((c->flag & COPEN) && c->qid.path == Qscancode)
215                 decref(&kbd.ref);
216 }
217
218 static Block*
219 kbdbread(Chan *c, long n, ulong off)
220 {
221         if(c->qid.path == Qscancode)
222                 return qbread(kbd.q, n);
223         else
224                 return devbread(c, n, off);
225 }
226
227 static long
228 kbdread(Chan *c, void *a, long n, vlong)
229 {
230         if(c->qid.path == Qscancode)
231                 return qread(kbd.q, a, n);
232         if(c->qid.path == Qdir)
233                 return devdirread(c, a, n, kbdtab, nelem(kbdtab), devgen);
234
235         error(Egreg);
236         return 0;
237 }
238
239 static long
240 kbdwrite(Chan *c, void *a, long n, vlong)
241 {
242         char tmp[8+1], *p;
243
244         if(c->qid.path != Qleds)
245                 error(Egreg);
246
247         p = tmp + n;
248         if(n >= sizeof(tmp))
249                 p = tmp + sizeof(tmp)-1;
250         memmove(tmp, a, p - tmp);
251         *p = 0;
252
253         setleds(atoi(tmp));
254
255         return n;
256 }
257
258
259 static char *initfailed = "i8042: kbdinit failed\n";
260
261 static int
262 outbyte(int port, int c)
263 {
264         outb(port, c);
265         if(outready() < 0) {
266                 print(initfailed);
267                 return -1;
268         }
269         return 0;
270 }
271
272 static void
273 kbdinit(void)
274 {
275         int c, try;
276
277         kbd.io = IO(uchar, HPC3_KBDMS);
278         kbd.q = qopen(1024, Qcoalesce, 0, 0);
279         if(kbd.q == nil)
280                 panic("kbdinit: qopen");
281         qnoblock(kbd.q, 1);
282
283         /* wait for a quiescent controller */
284         try = 1000;
285         while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
286                 if(c & Inready)
287                         inb(Data);
288                 delay(1);
289         }
290         if (try <= 0) {
291                 print(initfailed);
292                 return;
293         }
294
295         /* get current controller command byte */
296         outb(Cmd, 0x20);
297         if(inready() < 0){
298                 print("i8042: kbdinit can't read ccc\n");
299                 ccc = 0;
300         } else
301                 ccc = inb(Data);
302
303         /* enable kbd xfers and interrupts */
304         ccc &= ~Ckbddis;
305         ccc |= Csf | Ckbdint | Cscs1;
306         if(outready() < 0) {
307                 print(initfailed);
308                 return;
309         }
310         /* disable mouse */
311         if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0){
312                 print("i8042: kbdinit mouse disable failed\n");
313                 return;
314         }
315
316         conf.keyboard = 1;
317         addclock0link(pollintr, 5);
318 }
319
320 Dev kbddevtab = {
321         L'b',
322         "kbd",
323
324         devreset,
325         kbdinit,
326         devshutdown,
327         kbdattach,
328         kbdwalk,
329         kbdstat,
330         kbdopen,
331         devcreate,
332         kbdclose,
333         kbdread,
334         kbdbread,
335         kbdwrite,
336         devbwrite,
337         devremove,
338         devwstat,
339 };