Annotation of sys/dev/ic/pckbc.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: pckbc.c,v 1.14 2007/01/31 14:38:54 mickey Exp $ */
2: /* $NetBSD: pckbc.c,v 1.5 2000/06/09 04:58:35 soda Exp $ */
3:
4: /*
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/param.h>
30: #include <sys/systm.h>
31: #include <sys/timeout.h>
32: #include <sys/kernel.h>
33: #include <sys/proc.h>
34: #include <sys/device.h>
35: #include <sys/malloc.h>
36: #include <sys/errno.h>
37: #include <sys/queue.h>
38: #include <sys/lock.h>
39:
40: #include <machine/bus.h>
41:
42: #include <dev/ic/i8042reg.h>
43: #include <dev/ic/pckbcvar.h>
44:
45: #include "pckbd.h"
46:
47: #if (NPCKBD > 0)
48: #include <dev/pckbc/pckbdvar.h>
49: #endif
50:
51: /* descriptor for one device command */
52: struct pckbc_devcmd {
53: TAILQ_ENTRY(pckbc_devcmd) next;
54: int flags;
55: #define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
56: #define KBC_CMDFLAG_SLOW 2
57: u_char cmd[4];
58: int cmdlen, cmdidx, retries;
59: u_char response[4];
60: int status, responselen, responseidx;
61: };
62:
63: /* data per slave device */
64: struct pckbc_slotdata {
65: int polling; /* don't read data port in interrupt handler */
66: TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* active commands */
67: TAILQ_HEAD(, pckbc_devcmd) freequeue; /* free commands */
68: #define NCMD 5
69: struct pckbc_devcmd cmds[NCMD];
70: };
71:
72: #define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
73:
74: void pckbc_init_slotdata(struct pckbc_slotdata *);
75: int pckbc_attach_slot(struct pckbc_softc *, pckbc_slot_t);
76: int pckbc_submatch(struct device *, void *, void *);
77: int pckbcprint(void *, const char *);
78:
79: struct pckbc_internal pckbc_consdata;
80: int pckbc_console_attached;
81:
82: static int pckbc_console;
83: static struct pckbc_slotdata pckbc_cons_slotdata;
84:
85: static int pckbc_wait_output(bus_space_tag_t, bus_space_handle_t);
86:
87: static int pckbc_get8042cmd(struct pckbc_internal *);
88: static int pckbc_put8042cmd(struct pckbc_internal *);
89: static int pckbc_send_devcmd(struct pckbc_internal *, pckbc_slot_t,
90: u_char);
91: static void pckbc_poll_cmd1(struct pckbc_internal *, pckbc_slot_t,
92: struct pckbc_devcmd *);
93:
94: void pckbc_cleanqueue(struct pckbc_slotdata *);
95: void pckbc_cleanup(void *);
96: void pckbc_poll(void *);
97: int pckbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char);
98: void pckbc_start(struct pckbc_internal *, pckbc_slot_t);
99: int pckbcintr_internal(struct pckbc_internal *, struct pckbc_softc *);
100:
101: const char *pckbc_slot_names[] = { "kbd", "aux" };
102:
103: #define KBC_DEVCMD_ACK 0xfa
104: #define KBC_DEVCMD_RESEND 0xfe
105:
106: #define KBD_DELAY DELAY(8)
107:
108: static inline int
109: pckbc_wait_output(iot, ioh_c)
110: bus_space_tag_t iot;
111: bus_space_handle_t ioh_c;
112: {
113: u_int i;
114:
115: for (i = 100000; i; i--)
116: if (!(bus_space_read_1(iot, ioh_c, 0) & KBS_IBF)) {
117: KBD_DELAY;
118: return (1);
119: }
120: return (0);
121: }
122:
123: int
124: pckbc_send_cmd(iot, ioh_c, val)
125: bus_space_tag_t iot;
126: bus_space_handle_t ioh_c;
127: u_char val;
128: {
129: if (!pckbc_wait_output(iot, ioh_c))
130: return (0);
131: bus_space_write_1(iot, ioh_c, 0, val);
132: return (1);
133: }
134:
135: int
136: pckbc_poll_data1(iot, ioh_d, ioh_c, slot, checkaux)
137: bus_space_tag_t iot;
138: bus_space_handle_t ioh_d, ioh_c;
139: pckbc_slot_t slot;
140: int checkaux;
141: {
142: int i;
143: u_char stat;
144:
145: /* if 1 port read takes 1us (?), this polls for 100ms */
146: for (i = 100000; i; i--) {
147: stat = bus_space_read_1(iot, ioh_c, 0);
148: if (stat & KBS_DIB) {
149: register u_char c;
150:
151: KBD_DELAY;
152: c = bus_space_read_1(iot, ioh_d, 0);
153: if (checkaux && (stat & 0x20)) { /* aux data */
154: if (slot != PCKBC_AUX_SLOT) {
155: #ifdef PCKBCDEBUG
156: printf("lost aux 0x%x\n", c);
157: #endif
158: continue;
159: }
160: } else {
161: if (slot == PCKBC_AUX_SLOT) {
162: #ifdef PCKBCDEBUG
163: printf("lost kbd 0x%x\n", c);
164: #endif
165: continue;
166: }
167: }
168: return (c);
169: }
170: }
171: return (-1);
172: }
173:
174: /*
175: * Get the current command byte.
176: */
177: static int
178: pckbc_get8042cmd(t)
179: struct pckbc_internal *t;
180: {
181: bus_space_tag_t iot = t->t_iot;
182: bus_space_handle_t ioh_d = t->t_ioh_d;
183: bus_space_handle_t ioh_c = t->t_ioh_c;
184: int data;
185:
186: if (!pckbc_send_cmd(iot, ioh_c, K_RDCMDBYTE))
187: return (0);
188: data = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT,
189: t->t_haveaux);
190: if (data == -1)
191: return (0);
192: t->t_cmdbyte = data;
193: return (1);
194: }
195:
196: /*
197: * Pass command byte to keyboard controller (8042).
198: */
199: static int
200: pckbc_put8042cmd(t)
201: struct pckbc_internal *t;
202: {
203: bus_space_tag_t iot = t->t_iot;
204: bus_space_handle_t ioh_d = t->t_ioh_d;
205: bus_space_handle_t ioh_c = t->t_ioh_c;
206:
207: if (!pckbc_send_cmd(iot, ioh_c, K_LDCMDBYTE))
208: return (0);
209: if (!pckbc_wait_output(iot, ioh_c))
210: return (0);
211: bus_space_write_1(iot, ioh_d, 0, t->t_cmdbyte);
212: return (1);
213: }
214:
215: static int
216: pckbc_send_devcmd(t, slot, val)
217: struct pckbc_internal *t;
218: pckbc_slot_t slot;
219: u_char val;
220: {
221: bus_space_tag_t iot = t->t_iot;
222: bus_space_handle_t ioh_d = t->t_ioh_d;
223: bus_space_handle_t ioh_c = t->t_ioh_c;
224:
225: if (slot == PCKBC_AUX_SLOT) {
226: if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE))
227: return (0);
228: }
229: if (!pckbc_wait_output(iot, ioh_c))
230: return (0);
231: bus_space_write_1(iot, ioh_d, 0, val);
232: return (1);
233: }
234:
235: int
236: pckbc_is_console(iot, addr)
237: bus_space_tag_t iot;
238: 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: int
248: pckbc_submatch(parent, match, aux)
249: struct device *parent;
250: void *match;
251: void *aux;
252: {
253: struct cfdata *cf = match;
254: struct pckbc_attach_args *pa = aux;
255:
256: if (cf->cf_loc[PCKBCCF_SLOT] != PCKBCCF_SLOT_DEFAULT &&
257: cf->cf_loc[PCKBCCF_SLOT] != pa->pa_slot)
258: return (0);
259: return ((*cf->cf_attach->ca_match)(parent, cf, aux));
260: }
261:
262: int
263: pckbc_attach_slot(sc, slot)
264: struct pckbc_softc *sc;
265: pckbc_slot_t slot;
266: {
267: struct pckbc_internal *t = sc->id;
268: struct pckbc_attach_args pa;
269: int found;
270:
271: pa.pa_tag = t;
272: pa.pa_slot = slot;
273: found = (config_found_sm((struct device *)sc, &pa,
274: pckbcprint, pckbc_submatch) != NULL);
275:
276: if (found && !t->t_slotdata[slot]) {
277: t->t_slotdata[slot] = malloc(sizeof(struct pckbc_slotdata),
278: M_DEVBUF, M_NOWAIT);
279: if (t->t_slotdata[slot] == NULL)
280: return 0;
281: pckbc_init_slotdata(t->t_slotdata[slot]);
282: }
283: return (found);
284: }
285:
286: void
287: pckbc_attach(sc)
288: struct pckbc_softc *sc;
289: {
290: struct pckbc_internal *t;
291: bus_space_tag_t iot;
292: bus_space_handle_t ioh_d, ioh_c;
293: int res;
294: u_char cmdbits = 0;
295:
296: t = sc->id;
297: iot = t->t_iot;
298: ioh_d = t->t_ioh_d;
299: ioh_c = t->t_ioh_c;
300:
301: if (pckbc_console == 0) {
302: timeout_set(&t->t_cleanup, pckbc_cleanup, t);
303: timeout_set(&t->t_poll, pckbc_poll, t);
304: }
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: * 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("kbc: aux echo error 1\n");
354: goto nomouse;
355: }
356: if (!pckbc_wait_output(iot, ioh_c)) {
357: printf("kbc: aux echo error 2\n");
358: goto nomouse;
359: }
360: bus_space_write_1(iot, ioh_d, 0, 0x5a); /* a random value */
361: res = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_AUX_SLOT, 1);
362: if (res == -1) {
363: /* Read of aux echo timed out, try again */
364: if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE))
365: goto nomouse;
366: if (!pckbc_wait_output(iot, ioh_c))
367: goto nomouse;
368: bus_space_write_1(iot, ioh_d, 0, 0x5a);
369: res = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_AUX_SLOT, 1);
370: #ifdef PCKBCDEBUG
371: printf("kbc: aux echo: %x\n", res);
372: #endif
373: }
374: if (res != -1) {
375: /*
376: * In most cases, the 0x5a gets echoed.
377: * Some old controllers (Gateway 2000 circa 1993)
378: * return 0xfe here.
379: * We are satisfied if there is anything in the
380: * aux output buffer.
381: */
382: #ifdef PCKBCDEBUG
383: printf("kbc: aux echo: %x\n", res);
384: #endif
385: t->t_haveaux = 1;
386: if (pckbc_attach_slot(sc, PCKBC_AUX_SLOT))
387: cmdbits |= KC8_MENABLE;
388: }
389: #ifdef PCKBCDEBUG
390: else
391: printf("kbc: aux echo test failed\n");
392: #endif
393:
394: nomouse:
395: /* enable needed interrupts */
396: t->t_cmdbyte |= cmdbits;
397: if (!pckbc_put8042cmd(t))
398: printf("kbc: cmd word write error\n");
399: }
400:
401: int
402: pckbcprint(aux, pnp)
403: void *aux;
404: const char *pnp;
405: {
406: struct pckbc_attach_args *pa = aux;
407:
408: if (!pnp)
409: printf(" (%s slot)", pckbc_slot_names[pa->pa_slot]);
410: return (QUIET);
411: }
412:
413: void
414: pckbc_init_slotdata(q)
415: struct pckbc_slotdata *q;
416: {
417: int i;
418: TAILQ_INIT(&q->cmdqueue);
419: TAILQ_INIT(&q->freequeue);
420:
421: for (i = 0; i < NCMD; i++) {
422: TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next);
423: }
424: q->polling = 0;
425: }
426:
427: void
428: pckbc_flush(self, slot)
429: pckbc_tag_t self;
430: pckbc_slot_t slot;
431: {
432: struct pckbc_internal *t = self;
433:
434: (void) pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_c,
435: slot, t->t_haveaux);
436: }
437:
438: int
439: pckbc_poll_data(self, slot)
440: pckbc_tag_t self;
441: pckbc_slot_t slot;
442: {
443: struct pckbc_internal *t = self;
444: struct pckbc_slotdata *q = t->t_slotdata[slot];
445: int c;
446:
447: c = pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_c,
448: slot, t->t_haveaux);
449: if (c != -1 && q && CMD_IN_QUEUE(q)) {
450: /* we jumped into a running command - try to
451: deliver the response */
452: if (pckbc_cmdresponse(t, slot, c))
453: return (-1);
454: }
455: return (c);
456: }
457:
458: /*
459: * switch scancode translation on / off
460: * return nonzero on success
461: */
462: int
463: pckbc_xt_translation(self, slot, on)
464: pckbc_tag_t self;
465: pckbc_slot_t slot;
466: int on;
467: {
468: struct pckbc_internal *t = self;
469: int ison;
470:
471: if (slot != PCKBC_KBD_SLOT) {
472: /* translation only for kbd slot */
473: if (on)
474: return (0);
475: else
476: return (1);
477: }
478:
479: ison = t->t_cmdbyte & KC8_TRANS;
480: if ((on && ison) || (!on && !ison))
481: return (1);
482:
483: t->t_cmdbyte ^= KC8_TRANS;
484: if (!pckbc_put8042cmd(t))
485: return (0);
486:
487: /* read back to be sure */
488: if (!pckbc_get8042cmd(t))
489: return (0);
490:
491: ison = t->t_cmdbyte & KC8_TRANS;
492: if ((on && ison) || (!on && !ison))
493: return (1);
494: return (0);
495: }
496:
497: static struct pckbc_portcmd {
498: u_char cmd_en, cmd_dis;
499: } pckbc_portcmd[2] = {
500: {
501: KBC_KBDENABLE, KBC_KBDDISABLE,
502: }, {
503: KBC_AUXENABLE, KBC_AUXDISABLE,
504: }
505: };
506:
507: void
508: pckbc_slot_enable(self, slot, on)
509: pckbc_tag_t self;
510: pckbc_slot_t slot;
511: int on;
512: {
513: struct pckbc_internal *t = (struct pckbc_internal *)self;
514: struct pckbc_portcmd *cmd;
515:
516: cmd = &pckbc_portcmd[slot];
517:
518: if (!pckbc_send_cmd(t->t_iot, t->t_ioh_c,
519: on ? cmd->cmd_en : cmd->cmd_dis))
520: printf("pckbc_slot_enable(%d) failed\n", on);
521:
522: if (slot == PCKBC_KBD_SLOT) {
523: if (on)
524: timeout_add(&t->t_poll, hz);
525: else
526: timeout_del(&t->t_poll);
527: }
528: }
529:
530: void
531: pckbc_set_poll(self, slot, on)
532: pckbc_tag_t self;
533: pckbc_slot_t slot;
534: int on;
535: {
536: struct pckbc_internal *t = (struct pckbc_internal *)self;
537:
538: t->t_slotdata[slot]->polling = on;
539:
540: if (!on) {
541: int s;
542:
543: /*
544: * If disabling polling on a device that's been configured,
545: * make sure there are no bytes left in the FIFO, holding up
546: * the interrupt line. Otherwise we won't get any further
547: * interrupts.
548: */
549: if (t->t_sc) {
550: s = spltty();
551: pckbcintr_internal(t, t->t_sc);
552: splx(s);
553: }
554: }
555: }
556:
557: /*
558: * Pass command to device, poll for ACK and data.
559: * to be called at spltty()
560: */
561: static void
562: pckbc_poll_cmd1(t, slot, cmd)
563: struct pckbc_internal *t;
564: pckbc_slot_t slot;
565: struct pckbc_devcmd *cmd;
566: {
567: bus_space_tag_t iot = t->t_iot;
568: bus_space_handle_t ioh_d = t->t_ioh_d;
569: bus_space_handle_t ioh_c = t->t_ioh_c;
570: int i, c = 0;
571:
572: while (cmd->cmdidx < cmd->cmdlen) {
573: if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
574: printf("pckbc_cmd: send error\n");
575: cmd->status = EIO;
576: return;
577: }
578: for (i = 10; i; i--) { /* 1s ??? */
579: c = pckbc_poll_data1(iot, ioh_d, ioh_c, slot,
580: t->t_haveaux);
581: if (c != -1)
582: break;
583: }
584:
585: if (c == KBC_DEVCMD_ACK) {
586: cmd->cmdidx++;
587: continue;
588: }
589: if (c == KBC_DEVCMD_RESEND) {
590: #ifdef PCKBCDEBUG
591: printf("pckbc_cmd: RESEND\n");
592: #endif
593: if (cmd->retries++ < 5)
594: continue;
595: else {
596: #ifdef PCKBCDEBUG
597: printf("pckbc: cmd failed\n");
598: #endif
599: cmd->status = EIO;
600: return;
601: }
602: }
603: if (c == -1) {
604: #ifdef PCKBCDEBUG
605: printf("pckbc_cmd: timeout\n");
606: #endif
607: cmd->status = EIO;
608: return;
609: }
610: #ifdef PCKBCDEBUG
611: printf("pckbc_cmd: lost 0x%x\n", c);
612: #endif
613: }
614:
615: while (cmd->responseidx < cmd->responselen) {
616: if (cmd->flags & KBC_CMDFLAG_SLOW)
617: i = 100; /* 10s ??? */
618: else
619: i = 10; /* 1s ??? */
620: while (i--) {
621: c = pckbc_poll_data1(iot, ioh_d, ioh_c, slot,
622: t->t_haveaux);
623: if (c != -1)
624: break;
625: }
626: if (c == -1) {
627: #ifdef PCKBCDEBUG
628: printf("pckbc_cmd: no data\n");
629: #endif
630: cmd->status = ETIMEDOUT;
631: return;
632: } else
633: cmd->response[cmd->responseidx++] = c;
634: }
635: }
636:
637: /* for use in autoconfiguration */
638: int
639: pckbc_poll_cmd(self, slot, cmd, len, responselen, respbuf, slow)
640: pckbc_tag_t self;
641: pckbc_slot_t slot;
642: u_char *cmd;
643: int len, responselen;
644: u_char *respbuf;
645: int slow;
646: {
647: struct pckbc_devcmd nc;
648:
649: if ((len > 4) || (responselen > 4))
650: return (EINVAL);
651:
652: bzero(&nc, sizeof(nc));
653: bcopy(cmd, nc.cmd, len);
654: nc.cmdlen = len;
655: nc.responselen = responselen;
656: nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);
657:
658: pckbc_poll_cmd1(self, slot, &nc);
659:
660: if (nc.status == 0 && respbuf)
661: bcopy(nc.response, respbuf, responselen);
662:
663: return (nc.status);
664: }
665:
666: /*
667: * Clean up a command queue, throw away everything.
668: */
669: void
670: pckbc_cleanqueue(q)
671: struct pckbc_slotdata *q;
672: {
673: struct pckbc_devcmd *cmd;
674: #ifdef PCKBCDEBUG
675: int i;
676: #endif
677:
678: while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
679: TAILQ_REMOVE(&q->cmdqueue, cmd, next);
680: #ifdef PCKBCDEBUG
681: printf("pckbc_cleanqueue: removing");
682: for (i = 0; i < cmd->cmdlen; i++)
683: printf(" %02x", cmd->cmd[i]);
684: printf("\n");
685: #endif
686: TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
687: }
688: }
689:
690: /*
691: * Timeout error handler: clean queues and data port.
692: * XXX could be less invasive.
693: */
694: void
695: pckbc_cleanup(self)
696: void *self;
697: {
698: struct pckbc_internal *t = self;
699: int s;
700:
701: printf("pckbc: command timeout\n");
702:
703: s = spltty();
704:
705: if (t->t_slotdata[PCKBC_KBD_SLOT])
706: pckbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]);
707: if (t->t_slotdata[PCKBC_AUX_SLOT])
708: pckbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]);
709:
710: while (bus_space_read_1(t->t_iot, t->t_ioh_c, 0) & KBS_DIB) {
711: KBD_DELAY;
712: (void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
713: }
714:
715: /* reset KBC? */
716:
717: splx(s);
718: }
719:
720: /*
721: * Pass command to device during normal operation.
722: * to be called at spltty()
723: */
724: void
725: pckbc_start(t, slot)
726: struct pckbc_internal *t;
727: pckbc_slot_t slot;
728: {
729: struct pckbc_slotdata *q = t->t_slotdata[slot];
730: struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
731:
732: if (q->polling) {
733: do {
734: pckbc_poll_cmd1(t, slot, cmd);
735: if (cmd->status)
736: printf("pckbc_start: command error\n");
737:
738: TAILQ_REMOVE(&q->cmdqueue, cmd, next);
739: if (cmd->flags & KBC_CMDFLAG_SYNC)
740: wakeup(cmd);
741: else {
742: timeout_del(&t->t_cleanup);
743: TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
744: }
745: cmd = TAILQ_FIRST(&q->cmdqueue);
746: } while (cmd);
747: return;
748: }
749:
750: if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
751: printf("pckbc_start: send error\n");
752: /* XXX what now? */
753: return;
754: }
755: }
756:
757: /*
758: * Handle command responses coming in asynchronously,
759: * return nonzero if valid response.
760: * to be called at spltty()
761: */
762: int
763: pckbc_cmdresponse(t, slot, data)
764: struct pckbc_internal *t;
765: pckbc_slot_t slot;
766: u_char data;
767: {
768: struct pckbc_slotdata *q = t->t_slotdata[slot];
769: struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
770: #ifdef DIAGNOSTIC
771: if (!cmd)
772: panic("pckbc_cmdresponse: no active command");
773: #endif
774: if (cmd->cmdidx < cmd->cmdlen) {
775: if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
776: return (0);
777:
778: if (data == KBC_DEVCMD_RESEND) {
779: if (cmd->retries++ < 5) {
780: /* try again last command */
781: goto restart;
782: } else {
783: #ifdef PCKBCDEBUG
784: printf("pckbc: cmd failed\n");
785: #endif
786: cmd->status = EIO;
787: /* dequeue */
788: }
789: } else {
790: if (++cmd->cmdidx < cmd->cmdlen)
791: goto restart;
792: if (cmd->responselen)
793: return (1);
794: /* else dequeue */
795: }
796: } else if (cmd->responseidx < cmd->responselen) {
797: cmd->response[cmd->responseidx++] = data;
798: if (cmd->responseidx < cmd->responselen)
799: return (1);
800: /* else dequeue */
801: } else
802: return (0);
803:
804: /* dequeue: */
805: TAILQ_REMOVE(&q->cmdqueue, cmd, next);
806: if (cmd->flags & KBC_CMDFLAG_SYNC)
807: wakeup(cmd);
808: else {
809: timeout_del(&t->t_cleanup);
810: TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
811: }
812: if (!CMD_IN_QUEUE(q))
813: return (1);
814: restart:
815: pckbc_start(t, slot);
816: return (1);
817: }
818:
819: /*
820: * Put command into the device's command queue, return zero or errno.
821: */
822: int
823: pckbc_enqueue_cmd(self, slot, cmd, len, responselen, sync, respbuf)
824: pckbc_tag_t self;
825: pckbc_slot_t slot;
826: u_char *cmd;
827: int len, responselen, sync;
828: u_char *respbuf;
829: {
830: struct pckbc_internal *t = self;
831: struct pckbc_slotdata *q = t->t_slotdata[slot];
832: struct pckbc_devcmd *nc;
833: int s, isactive, res = 0;
834:
835: if ((len > 4) || (responselen > 4))
836: return (EINVAL);
837: s = spltty();
838: nc = TAILQ_FIRST(&q->freequeue);
839: if (nc) {
840: TAILQ_REMOVE(&q->freequeue, nc, next);
841: }
842: splx(s);
843: if (!nc)
844: return (ENOMEM);
845:
846: bzero(nc, sizeof(*nc));
847: bcopy(cmd, nc->cmd, len);
848: nc->cmdlen = len;
849: nc->responselen = responselen;
850: nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);
851:
852: s = spltty();
853:
854: if (q->polling && sync) {
855: /*
856: * XXX We should poll until the queue is empty.
857: * But we don't come here normally, so make
858: * it simple and throw away everything.
859: */
860: pckbc_cleanqueue(q);
861: }
862:
863: isactive = CMD_IN_QUEUE(q);
864: TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
865: if (!isactive)
866: pckbc_start(t, slot);
867:
868: if (q->polling)
869: res = (sync ? nc->status : 0);
870: else if (sync) {
871: if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) {
872: TAILQ_REMOVE(&q->cmdqueue, nc, next);
873: pckbc_cleanup(t);
874: } else
875: res = nc->status;
876: } else
877: timeout_add(&t->t_cleanup, hz);
878:
879: if (sync) {
880: if (respbuf)
881: bcopy(nc->response, respbuf, responselen);
882: TAILQ_INSERT_TAIL(&q->freequeue, nc, next);
883: }
884:
885: splx(s);
886:
887: return (res);
888: }
889:
890: void
891: pckbc_set_inputhandler(self, slot, func, arg, name)
892: pckbc_tag_t self;
893: pckbc_slot_t slot;
894: pckbc_inputfcn func;
895: void *arg;
896: char *name;
897: {
898: struct pckbc_internal *t = (struct pckbc_internal *)self;
899: struct pckbc_softc *sc = t->t_sc;
900:
901: if (slot >= PCKBC_NSLOTS)
902: panic("pckbc_set_inputhandler: bad slot %d", slot);
903:
904: (*sc->intr_establish)(sc, slot);
905:
906: sc->inputhandler[slot] = func;
907: sc->inputarg[slot] = arg;
908: sc->subname[slot] = name;
909:
910: if (pckbc_console && slot == PCKBC_KBD_SLOT)
911: timeout_add(&t->t_poll, hz);
912: }
913:
914: void
915: pckbc_poll(v)
916: void *v;
917: {
918: struct pckbc_internal *t = v;
919: int s;
920:
921: s = spltty();
922: (void)pckbcintr_internal(t, t->t_sc);
923: timeout_add(&t->t_poll, hz);
924: splx(s);
925: }
926:
927: int
928: pckbcintr(vsc)
929: void *vsc;
930: {
931: struct pckbc_softc *sc = (struct pckbc_softc *)vsc;
932:
933: return (pckbcintr_internal(sc->id, sc));
934: }
935:
936: int
937: pckbcintr_internal(t, sc)
938: struct pckbc_internal *t;
939: struct pckbc_softc *sc;
940: {
941: u_char stat;
942: pckbc_slot_t slot;
943: struct pckbc_slotdata *q;
944: int served = 0, data;
945:
946: /* reschedule timeout further into the idle times */
947: if (timeout_pending(&t->t_poll))
948: timeout_add(&t->t_poll, hz);
949:
950: for(;;) {
951: stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0);
952: if (!(stat & KBS_DIB))
953: break;
954:
955: served = 1;
956:
957: slot = (t->t_haveaux && (stat & 0x20)) ?
958: PCKBC_AUX_SLOT : PCKBC_KBD_SLOT;
959: q = t->t_slotdata[slot];
960:
961: if (!q) {
962: /* XXX do something for live insertion? */
963: printf("pckbcintr: no dev for slot %d\n", slot);
964: KBD_DELAY;
965: (void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
966: continue;
967: }
968:
969: if (q->polling)
970: break; /* pckbc_poll_data() will get it */
971:
972: KBD_DELAY;
973: data = bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
974:
975: if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data))
976: continue;
977:
978: if (sc != NULL) {
979: if (sc->inputhandler[slot])
980: (*sc->inputhandler[slot])(sc->inputarg[slot],
981: data);
982: #ifdef PCKBCDEBUG
983: else
984: printf("pckbcintr: slot %d lost %d\n",
985: slot, data);
986: #endif
987: }
988: }
989:
990: return (served);
991: }
992:
993: int
994: pckbc_cnattach(iot, addr, cmd_offset, slot)
995: bus_space_tag_t iot;
996: bus_addr_t addr;
997: bus_size_t cmd_offset;
998: pckbc_slot_t slot;
999: {
1000: bus_space_handle_t ioh_d, ioh_c;
1001: int res = 0;
1002:
1003: if (bus_space_map(iot, addr + KBDATAP, 1, 0, &ioh_d))
1004: return (ENXIO);
1005: if (bus_space_map(iot, addr + cmd_offset, 1, 0, &ioh_c)) {
1006: bus_space_unmap(iot, ioh_d, 1);
1007: return (ENXIO);
1008: }
1009:
1010: pckbc_consdata.t_iot = iot;
1011: pckbc_consdata.t_ioh_d = ioh_d;
1012: pckbc_consdata.t_ioh_c = ioh_c;
1013: pckbc_consdata.t_addr = addr;
1014: timeout_set(&pckbc_consdata.t_cleanup, pckbc_cleanup, &pckbc_consdata);
1015: timeout_set(&pckbc_consdata.t_poll, pckbc_poll, &pckbc_consdata);
1016:
1017: /* flush */
1018: (void) pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
1019:
1020: /* selftest? */
1021:
1022: /* init cmd byte, enable ports */
1023: pckbc_consdata.t_cmdbyte = KC8_CPU;
1024: if (!pckbc_put8042cmd(&pckbc_consdata)) {
1025: printf("kbc: cmd word write error\n");
1026: res = EIO;
1027: }
1028:
1029: if (!res) {
1030: #if (NPCKBD > 0)
1031: res = pckbd_cnattach(&pckbc_consdata, slot);
1032: #else
1033: res = ENXIO;
1034: #endif /* NPCKBD > 0 */
1035: }
1036:
1037: if (res) {
1038: bus_space_unmap(iot, pckbc_consdata.t_ioh_d, 1);
1039: bus_space_unmap(iot, pckbc_consdata.t_ioh_c, 1);
1040: } else {
1041: pckbc_consdata.t_slotdata[slot] = &pckbc_cons_slotdata;
1042: pckbc_init_slotdata(&pckbc_cons_slotdata);
1043: pckbc_console = 1;
1044: }
1045:
1046: return (res);
1047: }
1048:
1049: struct cfdriver pckbc_cd = {
1050: NULL, "pckbc", DV_DULL
1051: };
CVSweb