FreeBSD/Linux Kernel Cross Reference
sys/dev/ic/pckbc.c
1 /* $NetBSD: pckbc.c,v 1.45 2008/06/04 16:29:14 drochner Exp $ */
2
3 /*
4 * Copyright (c) 2004 Ben Harris.
5 * Copyright (c) 1998
6 * Matthias Drochner. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pckbc.c,v 1.45 2008/06/04 16:29:14 drochner Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/callout.h>
35 #include <sys/kernel.h>
36 #include <sys/proc.h>
37 #include <sys/device.h>
38 #include <sys/malloc.h>
39 #include <sys/errno.h>
40 #include <sys/queue.h>
41
42 #include <sys/bus.h>
43
44 #include <dev/ic/i8042reg.h>
45 #include <dev/ic/pckbcvar.h>
46
47 #include <dev/pckbport/pckbportvar.h>
48
49 #include "rnd.h"
50 #include "locators.h"
51
52 #if NRND > 0
53 #include <sys/rnd.h>
54 #endif
55
56 /* data per slave device */
57 struct pckbc_slotdata {
58 int polling; /* don't process data in interrupt handler */
59 int poll_data; /* data read from inr handler if polling */
60 int poll_stat; /* status read from inr handler if polling */
61 #if NRND > 0
62 rndsource_element_t rnd_source;
63 #endif
64 };
65
66 static void pckbc_init_slotdata(struct pckbc_slotdata *);
67 static int pckbc_attach_slot(struct pckbc_softc *, pckbc_slot_t);
68
69 struct pckbc_internal pckbc_consdata;
70 int pckbc_console_attached;
71
72 static int pckbc_console;
73 static struct pckbc_slotdata pckbc_cons_slotdata;
74
75 static int pckbc_xt_translation(void *, pckbport_slot_t, int);
76 static int pckbc_send_devcmd(void *, pckbport_slot_t, u_char);
77 static void pckbc_slot_enable(void *, pckbport_slot_t, int);
78 static void pckbc_intr_establish(void *, pckbport_slot_t);
79 static void pckbc_set_poll(void *, pckbc_slot_t, int on);
80
81 static int pckbc_wait_output(bus_space_tag_t, bus_space_handle_t);
82
83 static int pckbc_get8042cmd(struct pckbc_internal *);
84 static int pckbc_put8042cmd(struct pckbc_internal *);
85
86 void pckbc_cleanqueue(struct pckbc_slotdata *);
87 void pckbc_cleanup(void *);
88 int pckbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char);
89 void pckbc_start(struct pckbc_internal *, pckbc_slot_t);
90
91 const char * const pckbc_slot_names[] = { "kbd", "aux" };
92
93 static struct pckbport_accessops const pckbc_ops = {
94 pckbc_xt_translation,
95 pckbc_send_devcmd,
96 pckbc_poll_data1,
97 pckbc_slot_enable,
98 pckbc_intr_establish,
99 pckbc_set_poll
100 };
101
102 #define KBD_DELAY DELAY(8)
103
104 static inline int
105 pckbc_wait_output(bus_space_tag_t iot, bus_space_handle_t ioh_c)
106 {
107 u_int i;
108
109 for (i = 100000; i; i--)
110 if (!(bus_space_read_1(iot, ioh_c, 0) & KBS_IBF)) {
111 KBD_DELAY;
112 return (1);
113 }
114 return (0);
115 }
116
117 int
118 pckbc_send_cmd(bus_space_tag_t iot, bus_space_handle_t ioh_c, u_char val)
119 {
120 if (!pckbc_wait_output(iot, ioh_c))
121 return (0);
122 bus_space_write_1(iot, ioh_c, 0, val);
123 return (1);
124 }
125
126 /*
127 * Note: the spl games here are to deal with some strange PC kbd controllers
128 * in some system configurations.
129 * This is not canonical way to handle polling input.
130 */
131 int
132 pckbc_poll_data1(void *pt, pckbc_slot_t slot)
133 {
134 struct pckbc_internal *t = pt;
135 struct pckbc_slotdata *q = t->t_slotdata[slot];
136 int s;
137 u_char stat, c;
138 int i = 100; /* polls for ~100ms */
139 int checkaux = t->t_haveaux;
140
141 s = splhigh();
142
143 if (q && q->polling && q->poll_data != -1 && q->poll_stat != -1) {
144 stat = q->poll_stat;
145 c = q->poll_data;
146 q->poll_data = -1;
147 q->poll_stat = -1;
148 goto process;
149 }
150
151 for (; i; i--, delay(1000)) {
152 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0);
153 if (stat & KBS_DIB) {
154 KBD_DELAY;
155 c = bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
156
157 process:
158 if (checkaux && (stat & 0x20)) { /* aux data */
159 if (slot != PCKBC_AUX_SLOT) {
160 #ifdef PCKBCDEBUG
161 printf("pckbc: lost aux 0x%x\n", c);
162 #endif
163 continue;
164 }
165 } else {
166 if (slot == PCKBC_AUX_SLOT) {
167 #ifdef PCKBCDEBUG
168 printf("pckbc: lost kbd 0x%x\n", c);
169 #endif
170 continue;
171 }
172 }
173 splx(s);
174 return (c);
175 }
176 }
177
178 splx(s);
179 return (-1);
180 }
181
182 /*
183 * Get the current command byte.
184 */
185 static int
186 pckbc_get8042cmd(struct pckbc_internal *t)
187 {
188 bus_space_tag_t iot = t->t_iot;
189 bus_space_handle_t ioh_c = t->t_ioh_c;
190 int data;
191
192 if (!pckbc_send_cmd(iot, ioh_c, K_RDCMDBYTE))
193 return (0);
194 data = pckbc_poll_data1(t, PCKBC_KBD_SLOT);
195 if (data == -1)
196 return (0);
197 t->t_cmdbyte = data;
198 return (1);
199 }
200
201 /*
202 * Pass command byte to keyboard controller (8042).
203 */
204 static int
205 pckbc_put8042cmd(struct pckbc_internal *t)
206 {
207 bus_space_tag_t iot = t->t_iot;
208 bus_space_handle_t ioh_d = t->t_ioh_d;
209 bus_space_handle_t ioh_c = t->t_ioh_c;
210
211 if (!pckbc_send_cmd(iot, ioh_c, K_LDCMDBYTE))
212 return (0);
213 if (!pckbc_wait_output(iot, ioh_c))
214 return (0);
215 bus_space_write_1(iot, ioh_d, 0, t->t_cmdbyte);
216 return (1);
217 }
218
219 static int
220 pckbc_send_devcmd(void *pt, pckbc_slot_t slot, u_char val)
221 {
222 struct pckbc_internal *t = pt;
223 bus_space_tag_t iot = t->t_iot;
224 bus_space_handle_t ioh_d = t->t_ioh_d;
225 bus_space_handle_t ioh_c = t->t_ioh_c;
226
227 if (slot == PCKBC_AUX_SLOT) {
228 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE))
229 return (0);
230 }
231 if (!pckbc_wait_output(iot, ioh_c))
232 return (0);
233 bus_space_write_1(iot, ioh_d, 0, val);
234 return (1);
235 }
236
237 int
238 pckbc_is_console(bus_space_tag_t iot, bus_addr_t addr)
239 {
240 if (pckbc_console && !pckbc_console_attached &&
241 pckbc_consdata.t_iot == iot &&
242 pckbc_consdata.t_addr == addr)
243 return (1);
244 return (0);
245 }
246
247 static int
248 pckbc_attach_slot(struct pckbc_softc *sc, pckbc_slot_t slot)
249 {
250 struct pckbc_internal *t = sc->id;
251 struct pckbc_attach_args pa;
252 void *sdata;
253 struct device *child;
254 int alloced = 0;
255
256 pa.pa_tag = t;
257 pa.pa_slot = slot;
258
259 if (t->t_slotdata[slot] == NULL) {
260 sdata = malloc(sizeof(struct pckbc_slotdata),
261 M_DEVBUF, M_NOWAIT);
262 if (sdata == NULL) {
263 aprint_error_dev(sc->sc_dv, "no memory\n");
264 return (0);
265 }
266 t->t_slotdata[slot] = sdata;
267 pckbc_init_slotdata(t->t_slotdata[slot]);
268 alloced++;
269 }
270
271 child = pckbport_attach_slot(sc->sc_dv, t->t_pt, slot);
272
273 if (child == NULL && alloced) {
274 free(t->t_slotdata[slot], M_DEVBUF);
275 t->t_slotdata[slot] = NULL;
276 }
277
278 #if NRND > 0
279 if (child != NULL && t->t_slotdata[slot] != NULL)
280 rnd_attach_source(&t->t_slotdata[slot]->rnd_source,
281 device_xname(child), RND_TYPE_TTY, 0);
282 #endif
283 return child != NULL;
284 }
285
286 void
287 pckbc_attach(struct pckbc_softc *sc)
288 {
289 struct pckbc_internal *t;
290 bus_space_tag_t iot;
291 bus_space_handle_t ioh_d, ioh_c;
292 int res;
293 u_char cmdbits = 0;
294
295 t = sc->id;
296 iot = t->t_iot;
297 ioh_d = t->t_ioh_d;
298 ioh_c = t->t_ioh_c;
299
300 t->t_pt = pckbport_attach(t, &pckbc_ops);
301 if (t->t_pt == NULL) {
302 aprint_error(": attach failed\n");
303 return;
304 }
305
306 /* flush */
307 (void) pckbc_poll_data1(t, PCKBC_KBD_SLOT);
308
309 /* set initial cmd byte */
310 if (!pckbc_put8042cmd(t)) {
311 printf("pckbc: cmd word write error\n");
312 return;
313 }
314
315 /*
316 * XXX Don't check the keyboard port. There are broken keyboard controllers
317 * which don't pass the test but work normally otherwise.
318 */
319 #if 0
320 /*
321 * check kbd port ok
322 */
323 if (!pckbc_send_cmd(iot, ioh_c, KBC_KBDTEST))
324 return;
325 res = pckbc_poll_data1(t, PCKBC_KBD_SLOT, 0);
326
327 /*
328 * Normally, we should get a "" here.
329 * But there are keyboard controllers behaving differently.
330 */
331 if (res == 0 || res == 0xfa || res == 0x01 || res == 0xab) {
332 #ifdef PCKBCDEBUG
333 if (res != 0)
334 printf("pckbc: returned %x on kbd slot test\n", res);
335 #endif
336 if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT))
337 cmdbits |= KC8_KENABLE;
338 } else {
339 printf("pckbc: kbd port test: %x\n", res);
340 return;
341 }
342 #else
343 if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT))
344 cmdbits |= KC8_KENABLE;
345 #endif /* 0 */
346
347 /*
348 * Check aux port ok.
349 * Avoid KBC_AUXTEST because it hangs some older controllers
350 * (eg UMC880?).
351 */
352 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXECHO)) {
353 printf("pckbc: aux echo error 1\n");
354 goto nomouse;
355 }
356 if (!pckbc_wait_output(iot, ioh_c)) {
357 printf("pckbc: aux echo error 2\n");
358 goto nomouse;
359 }
360 t->t_haveaux = 1;
361 bus_space_write_1(iot, ioh_d, 0, 0x5a); /* a random value */
362 res = pckbc_poll_data1(t, PCKBC_AUX_SLOT);
363 if (res != -1) {
364 /*
365 * In most cases, the 0x5a gets echoed.
366 * Some older controllers (Gateway 2000 circa 1993)
367 * return 0xfe here.
368 * We are satisfied if there is anything in the
369 * aux output buffer.
370 */
371 if (pckbc_attach_slot(sc, PCKBC_AUX_SLOT))
372 cmdbits |= KC8_MENABLE;
373 } else {
374 #ifdef PCKBCDEBUG
375 printf("pckbc: aux echo test failed\n");
376 #endif
377 t->t_haveaux = 0;
378 }
379
380 nomouse:
381 /* enable needed interrupts */
382 t->t_cmdbyte |= cmdbits;
383 if (!pckbc_put8042cmd(t))
384 printf("pckbc: cmd word write error\n");
385 }
386
387 static void
388 pckbc_init_slotdata(struct pckbc_slotdata *q)
389 {
390
391 q->polling = 0;
392 }
393
394 /*
395 * switch scancode translation on / off
396 * return nonzero on success
397 */
398 static int
399 pckbc_xt_translation(void *self, pckbc_slot_t slot, int on)
400 {
401 struct pckbc_internal *t = self;
402 int ison;
403
404 if (slot != PCKBC_KBD_SLOT) {
405 /* translation only for kbd slot */
406 if (on)
407 return (0);
408 else
409 return (1);
410 }
411
412 ison = t->t_cmdbyte & KC8_TRANS;
413 if ((on && ison) || (!on && !ison))
414 return (1);
415
416 t->t_cmdbyte ^= KC8_TRANS;
417 if (!pckbc_put8042cmd(t))
418 return (0);
419
420 /* read back to be sure */
421 if (!pckbc_get8042cmd(t))
422 return (0);
423
424 ison = t->t_cmdbyte & KC8_TRANS;
425 if ((on && ison) || (!on && !ison))
426 return (1);
427 return (0);
428 }
429
430 static const struct pckbc_portcmd {
431 u_char cmd_en, cmd_dis;
432 } pckbc_portcmd[2] = {
433 {
434 KBC_KBDENABLE, KBC_KBDDISABLE,
435 }, {
436 KBC_AUXENABLE, KBC_AUXDISABLE,
437 }
438 };
439
440 void
441 pckbc_slot_enable(void *self, pckbc_slot_t slot, int on)
442 {
443 struct pckbc_internal *t = (struct pckbc_internal *)self;
444 const struct pckbc_portcmd *cmd;
445
446 cmd = &pckbc_portcmd[slot];
447
448 if (!pckbc_send_cmd(t->t_iot, t->t_ioh_c,
449 on ? cmd->cmd_en : cmd->cmd_dis))
450 printf("pckbc: pckbc_slot_enable(%d) failed\n", on);
451 }
452
453 static void
454 pckbc_set_poll(void *self, pckbc_slot_t slot, int on)
455 {
456 struct pckbc_internal *t = (struct pckbc_internal *)self;
457
458 t->t_slotdata[slot]->polling = on;
459
460 if (on) {
461 t->t_slotdata[slot]->poll_data = -1;
462 t->t_slotdata[slot]->poll_stat = -1;
463 } else {
464 int s;
465
466 /*
467 * If disabling polling on a device that's been configured,
468 * make sure there are no bytes left in the FIFO, holding up
469 * the interrupt line. Otherwise we won't get any further
470 * interrupts.
471 */
472 if (t->t_sc) {
473 s = spltty();
474 pckbcintr(t->t_sc);
475 splx(s);
476 }
477 }
478 }
479
480 static void
481 pckbc_intr_establish(void *pt, pckbport_slot_t slot)
482 {
483 struct pckbc_internal *t = pt;
484
485 (*t->t_sc->intr_establish)(t->t_sc, slot);
486 }
487
488 int
489 pckbcintr_hard(void *vsc)
490 {
491 struct pckbc_softc *sc = (struct pckbc_softc *)vsc;
492 struct pckbc_internal *t = sc->id;
493 u_char stat;
494 pckbc_slot_t slot;
495 struct pckbc_slotdata *q;
496 int served = 0, data, next, s;
497
498 for(;;) {
499 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0);
500 if (!(stat & KBS_DIB))
501 break;
502
503 served = 1;
504
505 slot = (t->t_haveaux && (stat & 0x20)) ?
506 PCKBC_AUX_SLOT : PCKBC_KBD_SLOT;
507 q = t->t_slotdata[slot];
508
509 if (!q) {
510 /* XXX do something for live insertion? */
511 printf("pckbc: no dev for slot %d\n", slot);
512 KBD_DELAY;
513 (void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
514 continue;
515 }
516
517 KBD_DELAY;
518 data = bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
519
520 #if NRND > 0
521 rnd_add_uint32(&q->rnd_source, (stat<<8)|data);
522 #endif
523
524 if (q->polling) {
525 q->poll_data = data;
526 q->poll_stat = stat;
527 break; /* pckbc_poll_data() will get it */
528 }
529
530 #if 0 /* XXXBJH */
531 if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data))
532 continue;
533 #endif
534
535 s = splhigh();
536 next = (t->rbuf_write+1) % PCKBC_RBUF_SIZE;
537 if (next == t->rbuf_read) {
538 splx(s);
539 break;
540 }
541 t->rbuf[t->rbuf_write].data = data;
542 t->rbuf[t->rbuf_write].slot = slot;
543 t->rbuf_write = next;
544 splx(s);
545 }
546
547 return (served);
548 }
549
550 void
551 pckbcintr_soft(void *vsc)
552 {
553 struct pckbc_softc *sc = vsc;
554 struct pckbc_internal *t = sc->id;
555 int data, slot, s;
556 #ifndef __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS
557 int st;
558
559 st = spltty();
560 #endif
561
562 s = splhigh();
563 while (t->rbuf_read != t->rbuf_write) {
564 slot = t->rbuf[t->rbuf_read].slot;
565 data = t->rbuf[t->rbuf_read].data;
566 t->rbuf_read = (t->rbuf_read+1) % PCKBC_RBUF_SIZE;
567 splx(s);
568 pckbportintr(t->t_pt, slot, data);
569 s = splhigh();
570 }
571 splx(s);
572
573
574 #ifndef __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS
575 splx(st);
576 #endif
577 }
578
579 int
580 pckbcintr(void *vsc)
581 {
582 struct pckbc_softc *sc = (struct pckbc_softc *)vsc;
583 struct pckbc_internal *t = sc->id;
584 u_char stat;
585 pckbc_slot_t slot;
586 struct pckbc_slotdata *q;
587 int served = 0, data;
588
589 for(;;) {
590 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0);
591 if (!(stat & KBS_DIB))
592 break;
593
594 served = 1;
595
596 slot = (t->t_haveaux && (stat & 0x20)) ?
597 PCKBC_AUX_SLOT : PCKBC_KBD_SLOT;
598 q = t->t_slotdata[slot];
599
600 KBD_DELAY;
601 data = bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
602
603 #if NRND > 0
604 rnd_add_uint32(&q->rnd_source, (stat<<8)|data);
605 #endif
606
607 pckbportintr(t->t_pt, slot, data);
608 }
609
610 return (served);
611 }
612
613 int
614 pckbc_cnattach(bus_space_tag_t iot, bus_addr_t addr,
615 bus_size_t cmd_offset, pckbc_slot_t slot)
616 {
617 bus_space_handle_t ioh_d, ioh_c;
618 #ifdef PCKBC_CNATTACH_SELFTEST
619 int reply;
620 #endif
621 int res = 0;
622
623 if (bus_space_map(iot, addr + KBDATAP, 1, 0, &ioh_d))
624 return (ENXIO);
625 if (bus_space_map(iot, addr + cmd_offset, 1, 0, &ioh_c)) {
626 bus_space_unmap(iot, ioh_d, 1);
627 return (ENXIO);
628 }
629
630 memset(&pckbc_consdata, 0, sizeof(pckbc_consdata));
631 pckbc_consdata.t_iot = iot;
632 pckbc_consdata.t_ioh_d = ioh_d;
633 pckbc_consdata.t_ioh_c = ioh_c;
634 pckbc_consdata.t_addr = addr;
635 callout_init(&pckbc_consdata.t_cleanup, 0);
636
637 /* flush */
638 (void) pckbc_poll_data1(&pckbc_consdata, PCKBC_KBD_SLOT);
639
640 #ifdef PCKBC_CNATTACH_SELFTEST
641 /*
642 * In some machines (e.g. netwinder) pckbc refuses to talk at
643 * all until we request a self-test.
644 */
645 if (!pckbc_send_cmd(iot, ioh_c, KBC_SELFTEST)) {
646 printf("pckbc: unable to request selftest\n");
647 res = EIO;
648 goto out;
649 }
650
651 reply = pckbc_poll_data1(&pckbc_consdata, PCKBC_KBD_SLOT);
652 if (reply != 0x55) {
653 printf("pckbc: selftest returned 0x%02x\n", reply);
654 res = EIO;
655 goto out;
656 }
657 #endif /* PCKBC_CNATTACH_SELFTEST */
658
659 /* init cmd byte, enable ports */
660 pckbc_consdata.t_cmdbyte = KC8_CPU;
661 if (!pckbc_put8042cmd(&pckbc_consdata)) {
662 printf("pckbc: cmd word write error\n");
663 res = EIO;
664 goto out;
665 }
666
667 res = pckbport_cnattach(&pckbc_consdata, &pckbc_ops, slot);
668
669 out:
670 if (res) {
671 bus_space_unmap(iot, pckbc_consdata.t_ioh_d, 1);
672 bus_space_unmap(iot, pckbc_consdata.t_ioh_c, 1);
673 } else {
674 pckbc_consdata.t_slotdata[slot] = &pckbc_cons_slotdata;
675 pckbc_init_slotdata(&pckbc_cons_slotdata);
676 pckbc_console = 1;
677 }
678
679 return (res);
680 }
681
682 bool
683 pckbc_resume(device_t dv PMF_FN_ARGS)
684 {
685 struct pckbc_softc *sc = device_private(dv);
686 struct pckbc_internal *t;
687
688 t = sc->id;
689 (void)pckbc_poll_data1(t, PCKBC_KBD_SLOT);
690 if (!pckbc_send_cmd(t->t_iot, t->t_ioh_c, KBC_SELFTEST))
691 return false;
692 (void)pckbc_poll_data1(t, PCKBC_KBD_SLOT);
693 (void)pckbc_put8042cmd(t);
694 pckbcintr(t->t_sc);
695
696 return true;
697 }
Cache object: 776d7961ad672101b406c58573e21b5d
|