pckbc.c revision 1.2 1 /* $NetBSD: pckbc.c,v 1.2 2000/03/23 07:01:32 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1998
5 * Matthias Drochner. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the NetBSD Project
18 * by Matthias Drochner.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/callout.h>
37 #include <sys/kernel.h>
38 #include <sys/proc.h>
39 #include <sys/device.h>
40 #include <sys/malloc.h>
41 #include <sys/errno.h>
42 #include <sys/queue.h>
43 #include <sys/lock.h>
44
45 #include <machine/bus.h>
46
47 #include <dev/ic/i8042reg.h>
48 #include <dev/ic/pckbcvar.h>
49
50 #include "locators.h"
51
52 #ifdef __HAVE_NWSCONS /* XXX: this port uses sys/dev/pckbc */
53 #include "pckbd.h"
54 #else /* ie: only md drivers attach to pckbc */
55 #define NPCKBD 0
56 #endif
57 #if (NPCKBD > 0)
58 #include <dev/pckbc/pckbdvar.h>
59 #endif
60
61 /* descriptor for one device command */
62 struct pckbc_devcmd {
63 TAILQ_ENTRY(pckbc_devcmd) next;
64 int flags;
65 #define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
66 #define KBC_CMDFLAG_SLOW 2
67 u_char cmd[4];
68 int cmdlen, cmdidx, retries;
69 u_char response[4];
70 int status, responselen, responseidx;
71 };
72
73 /* data per slave device */
74 struct pckbc_slotdata {
75 int polling; /* don't read data port in interrupt handler */
76 TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* active commands */
77 TAILQ_HEAD(, pckbc_devcmd) freequeue; /* free commands */
78 #define NCMD 5
79 struct pckbc_devcmd cmds[NCMD];
80 };
81
82 #define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
83
84 void pckbc_init_slotdata __P((struct pckbc_slotdata *));
85 int pckbc_attach_slot __P((struct pckbc_softc *, pckbc_slot_t));
86 int pckbc_submatch __P((struct device *, struct cfdata *, void *));
87 int pckbcprint __P((void *, const char *));
88
89 struct pckbc_internal pckbc_consdata;
90 int pckbc_console_attached;
91
92 static int pckbc_console;
93 static struct pckbc_slotdata pckbc_cons_slotdata;
94
95 static int pckbc_wait_output __P((bus_space_tag_t, bus_space_handle_t));
96
97 static int pckbc_get8042cmd __P((struct pckbc_internal *));
98 static int pckbc_put8042cmd __P((struct pckbc_internal *));
99 static int pckbc_send_devcmd __P((struct pckbc_internal *, pckbc_slot_t,
100 u_char));
101 static void pckbc_poll_cmd1 __P((struct pckbc_internal *, pckbc_slot_t,
102 struct pckbc_devcmd *));
103
104 void pckbc_cleanqueue __P((struct pckbc_slotdata *));
105 void pckbc_cleanup __P((void *));
106 int pckbc_cmdresponse __P((struct pckbc_internal *, pckbc_slot_t, u_char));
107 void pckbc_start __P((struct pckbc_internal *, pckbc_slot_t));
108
109 const char *pckbc_slot_names[] = { "kbd", "aux" };
110
111 #define KBC_DEVCMD_ACK 0xfa
112 #define KBC_DEVCMD_RESEND 0xfe
113
114 #define KBD_DELAY DELAY(8)
115
116 static inline int
117 pckbc_wait_output(iot, ioh_c)
118 bus_space_tag_t iot;
119 bus_space_handle_t ioh_c;
120 {
121 u_int i;
122
123 for (i = 100000; i; i--)
124 if (!(bus_space_read_1(iot, ioh_c, 0) & KBS_IBF)) {
125 KBD_DELAY;
126 return (1);
127 }
128 return (0);
129 }
130
131 int
132 pckbc_send_cmd(iot, ioh_c, val)
133 bus_space_tag_t iot;
134 bus_space_handle_t ioh_c;
135 u_char val;
136 {
137 if (!pckbc_wait_output(iot, ioh_c))
138 return (0);
139 bus_space_write_1(iot, ioh_c, 0, val);
140 return (1);
141 }
142
143 int
144 pckbc_poll_data1(iot, ioh_d, ioh_c, slot, checkaux)
145 bus_space_tag_t iot;
146 bus_space_handle_t ioh_d, ioh_c;
147 pckbc_slot_t slot;
148 int checkaux;
149 {
150 int i;
151 u_char stat;
152
153 /* if 1 port read takes 1us (?), this polls for 100ms */
154 for (i = 100000; i; i--) {
155 stat = bus_space_read_1(iot, ioh_c, 0);
156 if (stat & KBS_DIB) {
157 register u_char c;
158
159 KBD_DELAY;
160 c = bus_space_read_1(iot, ioh_d, 0);
161 if (checkaux && (stat & 0x20)) { /* aux data */
162 if (slot != PCKBC_AUX_SLOT) {
163 #ifdef PCKBCDEBUG
164 printf("lost aux 0x%x\n", c);
165 #endif
166 continue;
167 }
168 } else {
169 if (slot == PCKBC_AUX_SLOT) {
170 #ifdef PCKBCDEBUG
171 printf("lost kbd 0x%x\n", c);
172 #endif
173 continue;
174 }
175 }
176 return (c);
177 }
178 }
179 return (-1);
180 }
181
182 /*
183 * Get the current command byte.
184 */
185 static int
186 pckbc_get8042cmd(t)
187 struct pckbc_internal *t;
188 {
189 bus_space_tag_t iot = t->t_iot;
190 bus_space_handle_t ioh_d = t->t_ioh_d;
191 bus_space_handle_t ioh_c = t->t_ioh_c;
192 int data;
193
194 if (!pckbc_send_cmd(iot, ioh_c, K_RDCMDBYTE))
195 return (0);
196 data = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT,
197 t->t_haveaux);
198 if (data == -1)
199 return (0);
200 t->t_cmdbyte = data;
201 return (1);
202 }
203
204 /*
205 * Pass command byte to keyboard controller (8042).
206 */
207 static int
208 pckbc_put8042cmd(t)
209 struct pckbc_internal *t;
210 {
211 bus_space_tag_t iot = t->t_iot;
212 bus_space_handle_t ioh_d = t->t_ioh_d;
213 bus_space_handle_t ioh_c = t->t_ioh_c;
214
215 if (!pckbc_send_cmd(iot, ioh_c, K_LDCMDBYTE))
216 return (0);
217 if (!pckbc_wait_output(iot, ioh_c))
218 return (0);
219 bus_space_write_1(iot, ioh_d, 0, t->t_cmdbyte);
220 return (1);
221 }
222
223 static int
224 pckbc_send_devcmd(t, slot, val)
225 struct pckbc_internal *t;
226 pckbc_slot_t slot;
227 u_char val;
228 {
229 bus_space_tag_t iot = t->t_iot;
230 bus_space_handle_t ioh_d = t->t_ioh_d;
231 bus_space_handle_t ioh_c = t->t_ioh_c;
232
233 if (slot == PCKBC_AUX_SLOT) {
234 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE))
235 return (0);
236 }
237 if (!pckbc_wait_output(iot, ioh_c))
238 return (0);
239 bus_space_write_1(iot, ioh_d, 0, val);
240 return (1);
241 }
242
243 int
244 pckbc_is_console(iot, addr)
245 bus_space_tag_t iot;
246 bus_addr_t addr;
247 {
248 if (pckbc_console && !pckbc_console_attached &&
249 pckbc_consdata.t_iot == iot &&
250 pckbc_consdata.t_addr == addr)
251 return (1);
252 return (0);
253 }
254
255 int
256 pckbc_submatch(parent, cf, aux)
257 struct device *parent;
258 struct cfdata *cf;
259 void *aux;
260 {
261 struct pckbc_attach_args *pa = aux;
262
263 if (cf->cf_loc[PCKBCCF_SLOT] != PCKBCCF_SLOT_DEFAULT &&
264 cf->cf_loc[PCKBCCF_SLOT] != pa->pa_slot)
265 return (0);
266 return ((*cf->cf_attach->ca_match)(parent, cf, aux));
267 }
268
269 int
270 pckbc_attach_slot(sc, slot)
271 struct pckbc_softc *sc;
272 pckbc_slot_t slot;
273 {
274 struct pckbc_internal *t = sc->id;
275 struct pckbc_attach_args pa;
276 int found;
277
278 pa.pa_tag = t;
279 pa.pa_slot = slot;
280 found = (config_found_sm((struct device *)sc, &pa,
281 pckbcprint, pckbc_submatch) != NULL);
282
283 if (found && !t->t_slotdata[slot]) {
284 t->t_slotdata[slot] = malloc(sizeof(struct pckbc_slotdata),
285 M_DEVBUF, M_NOWAIT);
286 pckbc_init_slotdata(t->t_slotdata[slot]);
287 }
288 return (found);
289 }
290
291 void
292 pckbc_attach(sc)
293 struct pckbc_softc *sc;
294 {
295 struct pckbc_internal *t;
296 bus_space_tag_t iot;
297 bus_space_handle_t ioh_d, ioh_c;
298 int res;
299 u_char cmdbits = 0;
300
301 t = sc->id;
302 iot = t->t_iot;
303 ioh_d = t->t_ioh_d;
304 ioh_c = t->t_ioh_c;
305
306 /* flush */
307 (void) pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
308
309 /* set initial cmd byte */
310 if (!pckbc_put8042cmd(t)) {
311 printf("kbc: 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(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
326
327 /*
328 * Normally, we should get a "0" 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("kbc: 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("kbc: 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 */
350 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXTEST))
351 return;
352 res = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
353
354 if (res == 0 || res == 0xfa || res == 0x01) {
355 #ifdef PCKBCDEBUG
356 if (res != 0)
357 printf("kbc: returned %x on aux slot test\n", res);
358 #endif
359 t->t_haveaux = 1;
360 if (pckbc_attach_slot(sc, PCKBC_AUX_SLOT))
361 cmdbits |= KC8_MENABLE;
362 }
363 #ifdef PCKBCDEBUG
364 else
365 printf("kbc: aux port test: %x\n", res);
366 #endif
367
368 /* enable needed interrupts */
369 t->t_cmdbyte |= cmdbits;
370 if (!pckbc_put8042cmd(t))
371 printf("kbc: cmd word write error\n");
372 }
373
374 int
375 pckbcprint(aux, pnp)
376 void *aux;
377 const char *pnp;
378 {
379 struct pckbc_attach_args *pa = aux;
380
381 if (!pnp)
382 printf(" (%s slot)", pckbc_slot_names[pa->pa_slot]);
383 return (QUIET);
384 }
385
386 void
387 pckbc_init_slotdata(q)
388 struct pckbc_slotdata *q;
389 {
390 int i;
391 TAILQ_INIT(&q->cmdqueue);
392 TAILQ_INIT(&q->freequeue);
393
394 for (i=0; i<NCMD; i++) {
395 TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next);
396 }
397 q->polling = 0;
398 }
399
400 void
401 pckbc_flush(self, slot)
402 pckbc_tag_t self;
403 pckbc_slot_t slot;
404 {
405 struct pckbc_internal *t = self;
406
407 (void) pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_c,
408 slot, t->t_haveaux);
409 }
410
411 int
412 pckbc_poll_data(self, slot)
413 pckbc_tag_t self;
414 pckbc_slot_t slot;
415 {
416 struct pckbc_internal *t = self;
417 struct pckbc_slotdata *q = t->t_slotdata[slot];
418 int c;
419
420 c = pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_c,
421 slot, t->t_haveaux);
422 if (c != -1 && q && CMD_IN_QUEUE(q)) {
423 /* we jumped into a running command - try to
424 deliver the response */
425 if (pckbc_cmdresponse(t, slot, c))
426 return (-1);
427 }
428 return (c);
429 }
430
431 /*
432 * switch scancode translation on / off
433 * return nonzero on success
434 */
435 int
436 pckbc_xt_translation(self, slot, on)
437 pckbc_tag_t self;
438 pckbc_slot_t slot;
439 int on;
440 {
441 struct pckbc_internal *t = self;
442 int ison;
443
444 if (slot != PCKBC_KBD_SLOT) {
445 /* translation only for kbd slot */
446 if (on)
447 return (0);
448 else
449 return (1);
450 }
451
452 ison = t->t_cmdbyte & KC8_TRANS;
453 if ((on && ison) || (!on && !ison))
454 return (1);
455
456 t->t_cmdbyte ^= KC8_TRANS;
457 if (!pckbc_put8042cmd(t))
458 return (0);
459
460 /* read back to be sure */
461 if (!pckbc_get8042cmd(t))
462 return (0);
463
464 ison = t->t_cmdbyte & KC8_TRANS;
465 if ((on && ison) || (!on && !ison))
466 return (1);
467 return (0);
468 }
469
470 static struct pckbc_portcmd {
471 u_char cmd_en, cmd_dis;
472 } pckbc_portcmd[2] = {
473 {
474 KBC_KBDENABLE, KBC_KBDDISABLE,
475 }, {
476 KBC_AUXENABLE, KBC_AUXDISABLE,
477 }
478 };
479
480 void
481 pckbc_slot_enable(self, slot, on)
482 pckbc_tag_t self;
483 pckbc_slot_t slot;
484 int on;
485 {
486 struct pckbc_internal *t = (struct pckbc_internal *)self;
487 struct pckbc_portcmd *cmd;
488
489 cmd = &pckbc_portcmd[slot];
490
491 if (!pckbc_send_cmd(t->t_iot, t->t_ioh_c,
492 on ? cmd->cmd_en : cmd->cmd_dis))
493 printf("pckbc_slot_enable(%d) failed\n", on);
494 }
495
496 void
497 pckbc_set_poll(self, slot, on)
498 pckbc_tag_t self;
499 pckbc_slot_t slot;
500 int on;
501 {
502 struct pckbc_internal *t = (struct pckbc_internal *)self;
503
504 t->t_slotdata[slot]->polling = on;
505
506 if (!on) {
507 int s;
508
509 /*
510 * If disabling polling on a device that's been configured,
511 * make sure there are no bytes left in the FIFO, holding up
512 * the interrupt line. Otherwise we won't get any further
513 * interrupts.
514 */
515 if (t->t_sc) {
516 s = spltty();
517 pckbcintr(t->t_sc);
518 splx(s);
519 }
520 }
521 }
522
523 /*
524 * Pass command to device, poll for ACK and data.
525 * to be called at spltty()
526 */
527 static void
528 pckbc_poll_cmd1(t, slot, cmd)
529 struct pckbc_internal *t;
530 pckbc_slot_t slot;
531 struct pckbc_devcmd *cmd;
532 {
533 bus_space_tag_t iot = t->t_iot;
534 bus_space_handle_t ioh_d = t->t_ioh_d;
535 bus_space_handle_t ioh_c = t->t_ioh_c;
536 int i, c = 0;
537
538 while (cmd->cmdidx < cmd->cmdlen) {
539 if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
540 printf("pckbc_cmd: send error\n");
541 cmd->status = EIO;
542 return;
543 }
544 for (i = 10; i; i--) { /* 1s ??? */
545 c = pckbc_poll_data1(iot, ioh_d, ioh_c, slot,
546 t->t_haveaux);
547 if (c != -1)
548 break;
549 }
550
551 if (c == KBC_DEVCMD_ACK) {
552 cmd->cmdidx++;
553 continue;
554 }
555 if (c == KBC_DEVCMD_RESEND) {
556 #ifdef PCKBCDEBUG
557 printf("pckbc_cmd: RESEND\n");
558 #endif
559 if (cmd->retries++ < 5)
560 continue;
561 else {
562 #ifdef PCKBCDEBUG
563 printf("pckbc: cmd failed\n");
564 #endif
565 cmd->status = EIO;
566 return;
567 }
568 }
569 if (c == -1) {
570 #ifdef PCKBCDEBUG
571 printf("pckbc_cmd: timeout\n");
572 #endif
573 cmd->status = EIO;
574 return;
575 }
576 #ifdef PCKBCDEBUG
577 printf("pckbc_cmd: lost 0x%x\n", c);
578 #endif
579 }
580
581 while (cmd->responseidx < cmd->responselen) {
582 if (cmd->flags & KBC_CMDFLAG_SLOW)
583 i = 100; /* 10s ??? */
584 else
585 i = 10; /* 1s ??? */
586 while (i--) {
587 c = pckbc_poll_data1(iot, ioh_d, ioh_c, slot,
588 t->t_haveaux);
589 if (c != -1)
590 break;
591 }
592 if (c == -1) {
593 #ifdef PCKBCDEBUG
594 printf("pckbc_cmd: no data\n");
595 #endif
596 cmd->status = ETIMEDOUT;
597 return;
598 } else
599 cmd->response[cmd->responseidx++] = c;
600 }
601 }
602
603 /* for use in autoconfiguration */
604 int
605 pckbc_poll_cmd(self, slot, cmd, len, responselen, respbuf, slow)
606 pckbc_tag_t self;
607 pckbc_slot_t slot;
608 u_char *cmd;
609 int len, responselen;
610 u_char *respbuf;
611 int slow;
612 {
613 struct pckbc_internal *t = self;
614 struct pckbc_devcmd nc;
615
616 if ((len > 4) || (responselen > 4))
617 return (EINVAL);
618
619 bzero(&nc, sizeof(nc));
620 bcopy(cmd, nc.cmd, len);
621 nc.cmdlen = len;
622 nc.responselen = responselen;
623 nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);
624
625 pckbc_poll_cmd1(t, slot, &nc);
626
627 if (nc.status == 0 && respbuf)
628 bcopy(nc.response, respbuf, responselen);
629
630 return (nc.status);
631 }
632
633 /*
634 * Clean up a command queue, throw away everything.
635 */
636 void
637 pckbc_cleanqueue(q)
638 struct pckbc_slotdata *q;
639 {
640 struct pckbc_devcmd *cmd;
641 #ifdef PCKBCDEBUG
642 int i;
643 #endif
644
645 while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
646 TAILQ_REMOVE(&q->cmdqueue, cmd, next);
647 #ifdef PCKBCDEBUG
648 printf("pckbc_cleanqueue: removing");
649 for (i = 0; i < cmd->cmdlen; i++)
650 printf(" %02x", cmd->cmd[i]);
651 printf("\n");
652 #endif
653 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
654 }
655 }
656
657 /*
658 * Timeout error handler: clean queues and data port.
659 * XXX could be less invasive.
660 */
661 void
662 pckbc_cleanup(self)
663 void *self;
664 {
665 struct pckbc_internal *t = self;
666 int s;
667
668 printf("pckbc: command timeout\n");
669
670 s = spltty();
671
672 if (t->t_slotdata[PCKBC_KBD_SLOT])
673 pckbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]);
674 if (t->t_slotdata[PCKBC_AUX_SLOT])
675 pckbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]);
676
677 while (bus_space_read_1(t->t_iot, t->t_ioh_c, 0) & KBS_DIB) {
678 KBD_DELAY;
679 (void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
680 }
681
682 /* reset KBC? */
683
684 splx(s);
685 }
686
687 /*
688 * Pass command to device during normal operation.
689 * to be called at spltty()
690 */
691 void
692 pckbc_start(t, slot)
693 struct pckbc_internal *t;
694 pckbc_slot_t slot;
695 {
696 struct pckbc_slotdata *q = t->t_slotdata[slot];
697 struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
698
699 if (q->polling) {
700 do {
701 pckbc_poll_cmd1(t, slot, cmd);
702 if (cmd->status)
703 printf("pckbc_start: command error\n");
704
705 TAILQ_REMOVE(&q->cmdqueue, cmd, next);
706 if (cmd->flags & KBC_CMDFLAG_SYNC)
707 wakeup(cmd);
708 else {
709 callout_stop(&t->t_cleanup);
710 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
711 }
712 cmd = TAILQ_FIRST(&q->cmdqueue);
713 } while (cmd);
714 return;
715 }
716
717 if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
718 printf("pckbc_start: send error\n");
719 /* XXX what now? */
720 return;
721 }
722 }
723
724 /*
725 * Handle command responses coming in asynchonously,
726 * return nonzero if valid response.
727 * to be called at spltty()
728 */
729 int
730 pckbc_cmdresponse(t, slot, data)
731 struct pckbc_internal *t;
732 pckbc_slot_t slot;
733 u_char data;
734 {
735 struct pckbc_slotdata *q = t->t_slotdata[slot];
736 struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
737 #ifdef DIAGNOSTIC
738 if (!cmd)
739 panic("pckbc_cmdresponse: no active command");
740 #endif
741 if (cmd->cmdidx < cmd->cmdlen) {
742 if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
743 return (0);
744
745 if (data == KBC_DEVCMD_RESEND) {
746 if (cmd->retries++ < 5) {
747 /* try again last command */
748 goto restart;
749 } else {
750 printf("pckbc: cmd failed\n");
751 cmd->status = EIO;
752 /* dequeue */
753 }
754 } else {
755 if (++cmd->cmdidx < cmd->cmdlen)
756 goto restart;
757 if (cmd->responselen)
758 return (1);
759 /* else dequeue */
760 }
761 } else if (cmd->responseidx < cmd->responselen) {
762 cmd->response[cmd->responseidx++] = data;
763 if (cmd->responseidx < cmd->responselen)
764 return (1);
765 /* else dequeue */
766 } else
767 return (0);
768
769 /* dequeue: */
770 TAILQ_REMOVE(&q->cmdqueue, cmd, next);
771 if (cmd->flags & KBC_CMDFLAG_SYNC)
772 wakeup(cmd);
773 else {
774 callout_stop(&t->t_cleanup);
775 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
776 }
777 if (!CMD_IN_QUEUE(q))
778 return (1);
779 restart:
780 pckbc_start(t, slot);
781 return (1);
782 }
783
784 /*
785 * Put command into the device's command queue, return zero or errno.
786 */
787 int
788 pckbc_enqueue_cmd(self, slot, cmd, len, responselen, sync, respbuf)
789 pckbc_tag_t self;
790 pckbc_slot_t slot;
791 u_char *cmd;
792 int len, responselen, sync;
793 u_char *respbuf;
794 {
795 struct pckbc_internal *t = self;
796 struct pckbc_slotdata *q = t->t_slotdata[slot];
797 struct pckbc_devcmd *nc;
798 int s, isactive, res = 0;
799
800 if ((len > 4) || (responselen > 4))
801 return (EINVAL);
802 s = spltty();
803 nc = TAILQ_FIRST(&q->freequeue);
804 if (nc) {
805 TAILQ_REMOVE(&q->freequeue, nc, next);
806 }
807 splx(s);
808 if (!nc)
809 return (ENOMEM);
810
811 bzero(nc, sizeof(*nc));
812 bcopy(cmd, nc->cmd, len);
813 nc->cmdlen = len;
814 nc->responselen = responselen;
815 nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);
816
817 s = spltty();
818
819 if (q->polling && sync) {
820 /*
821 * XXX We should poll until the queue is empty.
822 * But we don't come here normally, so make
823 * it simple and throw away everything.
824 */
825 pckbc_cleanqueue(q);
826 }
827
828 isactive = CMD_IN_QUEUE(q);
829 TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
830 if (!isactive)
831 pckbc_start(t, slot);
832
833 if (q->polling)
834 res = (sync ? nc->status : 0);
835 else if (sync) {
836 if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) {
837 TAILQ_REMOVE(&q->cmdqueue, nc, next);
838 pckbc_cleanup(t);
839 } else
840 res = nc->status;
841 } else
842 callout_reset(&t->t_cleanup, hz, pckbc_cleanup, t);
843
844 if (sync) {
845 if (respbuf)
846 bcopy(nc->response, respbuf, responselen);
847 TAILQ_INSERT_TAIL(&q->freequeue, nc, next);
848 }
849
850 splx(s);
851
852 return (res);
853 }
854
855 void
856 pckbc_set_inputhandler(self, slot, func, arg)
857 pckbc_tag_t self;
858 pckbc_slot_t slot;
859 pckbc_inputfcn func;
860 void *arg;
861 {
862 struct pckbc_internal *t = (struct pckbc_internal *)self;
863 struct pckbc_softc *sc = t->t_sc;
864
865 if (slot >= PCKBC_NSLOTS)
866 panic("pckbc_set_inputhandler: bad slot %d", slot);
867
868 (*sc->intr_establish)(sc, slot);
869
870 sc->inputhandler[slot] = func;
871 sc->inputarg[slot] = arg;
872 }
873
874 int
875 pckbcintr(vsc)
876 void *vsc;
877 {
878 struct pckbc_softc *sc = (struct pckbc_softc *)vsc;
879 struct pckbc_internal *t = sc->id;
880 u_char stat;
881 pckbc_slot_t slot;
882 struct pckbc_slotdata *q;
883 int served = 0, data;
884
885 for(;;) {
886 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0);
887 if (!(stat & KBS_DIB))
888 break;
889
890 served = 1;
891
892 slot = (t->t_haveaux && (stat & 0x20)) ?
893 PCKBC_AUX_SLOT : PCKBC_KBD_SLOT;
894 q = t->t_slotdata[slot];
895
896 if (!q) {
897 /* XXX do something for live insertion? */
898 printf("pckbcintr: no dev for slot %d\n", slot);
899 KBD_DELAY;
900 (void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
901 continue;
902 }
903
904 if (q->polling)
905 break; /* pckbc_poll_data() will get it */
906
907 KBD_DELAY;
908 data = bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
909
910 if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data))
911 continue;
912
913 if (sc->inputhandler[slot])
914 (*sc->inputhandler[slot])(sc->inputarg[slot], data);
915 #ifdef PCKBCDEBUG
916 else
917 printf("pckbcintr: slot %d lost %d\n", slot, data);
918 #endif
919 }
920
921 return (served);
922 }
923
924 int
925 pckbc_cnattach(iot, addr, slot)
926 bus_space_tag_t iot;
927 bus_addr_t addr;
928 pckbc_slot_t slot;
929 {
930 bus_space_handle_t ioh_d, ioh_c;
931 int res = 0;
932
933 if (bus_space_map(iot, addr + KBDATAP, 1, 0, &ioh_d))
934 return (ENXIO);
935 if (bus_space_map(iot, addr + KBCMDP, 1, 0, &ioh_c)) {
936 bus_space_unmap(iot, ioh_d, 1);
937 return (ENXIO);
938 }
939
940 pckbc_consdata.t_iot = iot;
941 pckbc_consdata.t_ioh_d = ioh_d;
942 pckbc_consdata.t_ioh_c = ioh_c;
943 pckbc_consdata.t_addr = addr;
944 callout_init(&pckbc_consdata.t_cleanup);
945
946 /* flush */
947 (void) pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
948
949 /* selftest? */
950
951 /* init cmd byte, enable ports */
952 pckbc_consdata.t_cmdbyte = KC8_CPU;
953 if (!pckbc_put8042cmd(&pckbc_consdata)) {
954 printf("kbc: cmd word write error\n");
955 res = EIO;
956 }
957
958 if (!res) {
959 #if (NPCKBD > 0)
960 res = pckbd_cnattach(&pckbc_consdata, slot);
961 #else
962 /*
963 * XXX This should be replaced with the `notyet' case
964 * XXX when all of the old PC-style console drivers
965 * XXX have gone away. When that happens, all of
966 * XXX the pckbc_machdep_cnattach() should be purged,
967 * XXX as well.
968 */
969 #ifdef notyet
970 res = ENXIO;
971 #else
972 res = pckbc_machdep_cnattach(&pckbc_consdata, slot);
973 #endif
974 #endif /* NPCKBD > 0 */
975 }
976
977 if (res) {
978 bus_space_unmap(iot, pckbc_consdata.t_ioh_d, 1);
979 bus_space_unmap(iot, pckbc_consdata.t_ioh_c, 1);
980 } else {
981 pckbc_consdata.t_slotdata[slot] = &pckbc_cons_slotdata;
982 pckbc_init_slotdata(&pckbc_cons_slotdata);
983 pckbc_console = 1;
984 }
985
986 return (res);
987 }
988