jcs's openbsd hax
openbsd
1/* $OpenBSD: pckbc.c,v 1.55 2023/08/26 15:01:00 jmc 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/device.h>
34#include <sys/malloc.h>
35#include <sys/errno.h>
36#include <sys/queue.h>
37
38#include <machine/bus.h>
39#include <machine/cpu.h>
40
41#include <dev/ic/i8042reg.h>
42#include <dev/ic/pckbcvar.h>
43
44#include "pckbd.h"
45
46#if NPCKBD > 0
47#include <dev/pckbc/pckbdvar.h>
48#endif
49
50#ifdef PCKBCDEBUG
51#define DPRINTF(x...) do { printf(x); } while (0);
52#else
53#define DPRINTF(x...)
54#endif
55
56/* descriptor for one device command */
57struct pckbc_devcmd {
58 TAILQ_ENTRY(pckbc_devcmd) next;
59 int flags;
60#define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
61#define KBC_CMDFLAG_SLOW 2
62#define KBC_CMDFLAG_QUEUED 4 /* descriptor on cmdqueue */
63 u_char cmd[4];
64 int cmdlen, cmdidx, retries;
65 u_char response[4];
66 int status, responselen, responseidx;
67};
68
69/* data per slave device */
70struct pckbc_slotdata {
71 int polling; /* don't read data port in interrupt handler */
72 TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* active commands */
73 TAILQ_HEAD(, pckbc_devcmd) freequeue; /* free commands */
74#define NCMD 5
75 struct pckbc_devcmd cmds[NCMD];
76};
77
78#define CMD_IN_QUEUE(q) (!TAILQ_EMPTY(&(q)->cmdqueue))
79
80void pckbc_init_slotdata(struct pckbc_slotdata *);
81int pckbc_attach_slot(struct pckbc_softc *, pckbc_slot_t, int);
82int pckbc_submatch_locators(struct device *, void *, void *);
83int pckbc_submatch(struct device *, void *, void *);
84int pckbcprint(void *, const char *);
85
86struct pckbc_internal pckbc_consdata;
87int pckbc_console_attached;
88
89int pckbc_console;
90static struct pckbc_slotdata pckbc_cons_slotdata;
91
92static int pckbc_wait_output(bus_space_tag_t, bus_space_handle_t);
93
94static int pckbc_get8042cmd(struct pckbc_internal *);
95static int pckbc_put8042cmd(struct pckbc_internal *);
96static int pckbc_send_devcmd(struct pckbc_internal *, pckbc_slot_t,
97 u_char);
98static void pckbc_poll_cmd1(struct pckbc_internal *, pckbc_slot_t,
99 struct pckbc_devcmd *);
100
101void pckbc_cleanqueues(struct pckbc_internal *);
102void pckbc_cleanqueue(struct pckbc_slotdata *);
103void pckbc_cleanup(void *);
104void pckbc_poll(void *);
105int pckbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char);
106void pckbc_start(struct pckbc_internal *, pckbc_slot_t);
107int pckbcintr_internal(struct pckbc_internal *, struct pckbc_softc *);
108
109const char *pckbc_slot_names[] = { "kbd", "aux" };
110
111#define KBC_DEVCMD_ACK 0xfa
112#define KBC_DEVCMD_RESEND 0xfe
113#define KBC_DEVCMD_BAT_DONE 0xaa
114#define KBC_DEVCMD_BAT_FAIL 0xfc
115
116#define KBD_DELAY DELAY(8)
117
118static inline int
119pckbc_wait_output(bus_space_tag_t iot, 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
131int
132pckbc_send_cmd(bus_space_tag_t iot, bus_space_handle_t ioh_c, u_char val)
133{
134 if (!pckbc_wait_output(iot, ioh_c))
135 return (0);
136 bus_space_write_1(iot, ioh_c, 0, val);
137 return (1);
138}
139
140int
141pckbc_poll_data1(bus_space_tag_t iot, bus_space_handle_t ioh_d,
142 bus_space_handle_t ioh_c, pckbc_slot_t slot, int checkaux)
143{
144 int i;
145 u_char stat;
146
147 /* polls for ~100ms */
148 for (i = 100; i; i--, delay(1000)) {
149 stat = bus_space_read_1(iot, ioh_c, 0);
150 if (stat & KBS_DIB) {
151 register u_char c;
152
153 KBD_DELAY;
154 CPU_BUSY_CYCLE();
155 c = bus_space_read_1(iot, ioh_d, 0);
156 if (checkaux && (stat & KBS_AUXDATA)) {
157 if (slot != PCKBC_AUX_SLOT) {
158 DPRINTF("lost aux 0x%x\n", c);
159 continue;
160 }
161 } else {
162 if (slot == PCKBC_AUX_SLOT) {
163 DPRINTF("lost kbd 0x%x\n", c);
164 continue;
165 } else if (stat & KBS_AUXDATA) {
166 DPRINTF("discard aux data 0x%x\n", c);
167 continue;
168 }
169 }
170 return (c);
171 }
172 }
173 return (-1);
174}
175
176/*
177 * Get the current command byte.
178 */
179static int
180pckbc_get8042cmd(struct pckbc_internal *t)
181{
182 bus_space_tag_t iot = t->t_iot;
183 bus_space_handle_t ioh_d = t->t_ioh_d;
184 bus_space_handle_t ioh_c = t->t_ioh_c;
185 int data;
186
187 if (!pckbc_send_cmd(iot, ioh_c, K_RDCMDBYTE))
188 return (0);
189 data = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT,
190 t->t_haveaux);
191 if (data == -1)
192 return (0);
193 t->t_cmdbyte = data;
194 return (1);
195}
196
197/*
198 * Pass command byte to keyboard controller (8042).
199 */
200static int
201pckbc_put8042cmd(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
215static int
216pckbc_send_devcmd(struct pckbc_internal *t, pckbc_slot_t slot, u_char val)
217{
218 bus_space_tag_t iot = t->t_iot;
219 bus_space_handle_t ioh_d = t->t_ioh_d;
220 bus_space_handle_t ioh_c = t->t_ioh_c;
221
222 if (slot == PCKBC_AUX_SLOT) {
223 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE))
224 return (0);
225 }
226 if (!pckbc_wait_output(iot, ioh_c))
227 return (0);
228 bus_space_write_1(iot, ioh_d, 0, val);
229 return (1);
230}
231
232int
233pckbc_is_console(bus_space_tag_t iot, bus_addr_t addr)
234{
235 if (pckbc_console && !pckbc_console_attached &&
236 pckbc_consdata.t_iot == iot &&
237 pckbc_consdata.t_addr == addr)
238 return (1);
239 return (0);
240}
241
242int
243pckbc_submatch_locators(struct device *parent, void *match, void *aux)
244{
245 struct cfdata *cf = match;
246 struct pckbc_attach_args *pa = aux;
247
248 if (cf->cf_loc[PCKBCCF_SLOT] != PCKBCCF_SLOT_DEFAULT &&
249 cf->cf_loc[PCKBCCF_SLOT] != pa->pa_slot)
250 return (0);
251 return (1);
252}
253
254int
255pckbc_submatch(struct device *parent, void *match, void *aux)
256{
257 struct cfdata *cf = match;
258
259 if (pckbc_submatch_locators(parent, match, aux) == 0)
260 return (0);
261 return ((*cf->cf_attach->ca_match)(parent, cf, aux));
262}
263
264int
265pckbc_attach_slot(struct pckbc_softc *sc, pckbc_slot_t slot, int force)
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, pckbcprint,
274 force ? pckbc_submatch_locators : pckbc_submatch) != NULL);
275
276 if ((found || slot == PCKBC_AUX_SLOT) && !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 if (!found && slot == PCKBC_AUX_SLOT) {
284 /*
285 * Some machines don't handle disabling the aux slot
286 * completely and still generate data when the mouse is
287 * moved, so setup a dummy interrupt handler to discard
288 * this slot's data.
289 */
290 pckbc_set_inputhandler(t, PCKBC_AUX_SLOT, NULL, sc,
291 NULL);
292 found = 1;
293 }
294 }
295 return (found);
296}
297
298void
299pckbc_attach(struct pckbc_softc *sc, int flags)
300{
301 struct pckbc_internal *t;
302 bus_space_tag_t iot;
303 bus_space_handle_t ioh_d, ioh_c;
304 int haskbd = 0, res;
305 u_char cmdbits = 0;
306
307 t = sc->id;
308 iot = t->t_iot;
309 ioh_d = t->t_ioh_d;
310 ioh_c = t->t_ioh_c;
311
312 if (pckbc_console == 0) {
313 timeout_set(&t->t_cleanup, pckbc_cleanup, t);
314 timeout_set(&t->t_poll, pckbc_poll, t);
315 }
316
317 /* flush */
318 (void) pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
319
320 /* set initial cmd byte */
321 if (!pckbc_put8042cmd(t)) {
322#if defined(__i386__) || defined(__amd64__)
323 if (!ISSET(flags, PCKBCF_FORCE_KEYBOARD_PRESENT)) {
324 pckbc_release_console();
325 return;
326 }
327#endif
328 printf("kbc: cmd word write error\n");
329 return;
330 }
331
332/*
333 * XXX Don't check the keyboard port. There are broken keyboard controllers
334 * which don't pass the test but work normally otherwise.
335 */
336#if 0
337 /*
338 * check kbd port ok
339 */
340 if (!pckbc_send_cmd(iot, ioh_c, KBC_KBDTEST))
341 return;
342 res = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
343
344 /*
345 * Normally, we should get a "0" here.
346 * But there are keyboard controllers behaving differently.
347 */
348 if (res == 0 || res == 0xfa || res == 0x01 || res == 0xab) {
349#ifdef PCKBCDEBUG
350 if (res != 0)
351 printf("kbc: returned %x on kbd slot test\n", res);
352#endif
353 if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT, 0)) {
354 cmdbits |= KC8_KENABLE;
355 haskbd = 1;
356 }
357 } else {
358 printf("kbc: kbd port test: %x\n", res);
359 return;
360 }
361#else
362 if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT, 0)) {
363 cmdbits |= KC8_KENABLE;
364 haskbd = 1;
365 }
366#endif /* 0 */
367
368 /*
369 * Check aux port ok.
370 * Avoid KBC_AUXTEST because it hangs some older controllers
371 * (eg UMC880?).
372 */
373 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXECHO)) {
374 printf("kbc: aux echo error 1\n");
375 goto nomouse;
376 }
377 if (!pckbc_wait_output(iot, ioh_c)) {
378 printf("kbc: aux echo error 2\n");
379 goto nomouse;
380 }
381 bus_space_write_1(iot, ioh_d, 0, 0x5a); /* a random value */
382 res = pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_AUX_SLOT, 1);
383
384 if (ISSET(t->t_flags, PCKBC_NEED_AUXWRITE)) {
385 /*
386 * The following code is necessary to find the aux port on the
387 * oqo-1 machine, among others. However if confuses old
388 * (non-ps/2) keyboard controllers (at least UMC880x again).
389 */
390 if (res == -1) {
391 /* Read of aux echo timed out, try again */
392 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE))
393 goto nomouse;
394 if (!pckbc_wait_output(iot, ioh_c))
395 goto nomouse;
396 bus_space_write_1(iot, ioh_d, 0, 0x5a);
397 res = pckbc_poll_data1(iot, ioh_d, ioh_c,
398 PCKBC_AUX_SLOT, 1);
399 DPRINTF("kbc: aux echo: %x\n", res);
400 }
401 }
402
403 if (res != -1) {
404 /*
405 * In most cases, the 0x5a gets echoed.
406 * Some old controllers (Gateway 2000 circa 1993)
407 * return 0xfe here.
408 * We are satisfied if there is anything in the
409 * aux output buffer.
410 */
411 DPRINTF("kbc: aux echo: %x\n", res);
412 t->t_haveaux = 1;
413 if (pckbc_attach_slot(sc, PCKBC_AUX_SLOT, 0))
414 cmdbits |= KC8_MENABLE;
415 }
416#ifdef PCKBCDEBUG
417 else
418 printf("kbc: aux echo test failed\n");
419#endif
420
421#if defined(__i386__) || defined(__amd64__)
422 if (haskbd == 0 && !ISSET(flags, PCKBCF_FORCE_KEYBOARD_PRESENT)) {
423 if (t->t_haveaux) {
424 if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT, 1))
425 cmdbits |= KC8_KENABLE;
426 } else {
427 pckbc_release_console();
428 }
429 }
430#endif
431
432nomouse:
433 /* enable needed interrupts */
434 t->t_cmdbyte |= cmdbits;
435 if (!pckbc_put8042cmd(t))
436 printf("kbc: cmd word write error\n");
437}
438
439int
440pckbcprint(void *aux, const char *pnp)
441{
442 struct pckbc_attach_args *pa = aux;
443
444 if (!pnp)
445 printf(" (%s slot)", pckbc_slot_names[pa->pa_slot]);
446 return (QUIET);
447}
448
449void
450pckbc_release_console(void)
451{
452#if defined(__i386__) || defined(__amd64__)
453 /*
454 * If there is no keyboard present, yet we are the console,
455 * we might be on a legacy-free PC where the PS/2 emulated
456 * keyboard was elected as console, but went away as soon
457 * as the USB controller drivers attached.
458 *
459 * In that case, we want to release ourselves from console
460 * duties, unless we have been able to attach a mouse,
461 * which would mean this is a real PS/2 controller
462 * after all.
463 */
464 if (pckbc_console != 0) {
465 extern void wscn_input_init(int);
466
467 pckbc_console = 0;
468 wscn_input_init(1);
469 }
470#endif
471}
472
473void
474pckbc_init_slotdata(struct pckbc_slotdata *q)
475{
476 int i;
477 TAILQ_INIT(&q->cmdqueue);
478 TAILQ_INIT(&q->freequeue);
479
480 for (i = 0; i < NCMD; i++) {
481 TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next);
482 }
483 q->polling = 0;
484}
485
486void
487pckbc_flush(pckbc_tag_t self, pckbc_slot_t slot)
488{
489 struct pckbc_internal *t = self;
490
491 (void) pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_c,
492 slot, t->t_haveaux);
493}
494
495int
496pckbc_poll_data(pckbc_tag_t self, pckbc_slot_t slot)
497{
498 struct pckbc_internal *t = self;
499 struct pckbc_slotdata *q = t->t_slotdata[slot];
500 int c;
501
502 c = pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_c,
503 slot, t->t_haveaux);
504 if (c != -1 && q && CMD_IN_QUEUE(q)) {
505 /* we jumped into a running command - try to
506 deliver the response */
507 if (pckbc_cmdresponse(t, slot, c))
508 return (-1);
509 }
510 return (c);
511}
512
513/*
514 * set scancode translation on
515 */
516int
517pckbc_xt_translation(pckbc_tag_t self, int *table)
518{
519 struct pckbc_internal *t = self;
520
521#ifdef __sparc64__ /* only pckbc@ebus on sparc64 uses this */
522 if ((t->t_flags & PCKBC_CANT_TRANSLATE) != 0) {
523 /* Hardware lacks translation capability. Nothing to do! */
524 if (t->t_flags & PCKBC_FIXED_SET2)
525 *table = 2;
526 else /* PCKBC_FIXED_SET3 */
527 *table = 3;
528 return (-1);
529 }
530#endif
531
532 if (t->t_cmdbyte & KC8_TRANS)
533 return (0);
534
535 t->t_cmdbyte |= KC8_TRANS;
536 if (!pckbc_put8042cmd(t))
537 return (-1);
538
539 /* read back to be sure */
540 if (!pckbc_get8042cmd(t))
541 return (-1);
542
543 return (t->t_cmdbyte & KC8_TRANS) ? (0) : (-1);
544}
545
546static struct pckbc_portcmd {
547 u_char cmd_en, cmd_dis;
548} pckbc_portcmd[2] = {
549 {
550 KBC_KBDENABLE, KBC_KBDDISABLE,
551 }, {
552 KBC_AUXENABLE, KBC_AUXDISABLE,
553 }
554};
555
556void
557pckbc_slot_enable(pckbc_tag_t self, pckbc_slot_t slot, int on)
558{
559 struct pckbc_internal *t = (struct pckbc_internal *)self;
560 struct pckbc_portcmd *cmd;
561
562 cmd = &pckbc_portcmd[slot];
563
564 if (!pckbc_send_cmd(t->t_iot, t->t_ioh_c,
565 on ? cmd->cmd_en : cmd->cmd_dis))
566 printf("pckbc_slot_enable(%d) failed\n", on);
567
568 if (slot == PCKBC_KBD_SLOT) {
569 if (on)
570 timeout_add_sec(&t->t_poll, 1);
571 else
572 timeout_del(&t->t_poll);
573 }
574}
575
576void
577pckbc_set_poll(pckbc_tag_t self, pckbc_slot_t slot, int on)
578{
579 struct pckbc_internal *t = (struct pckbc_internal *)self;
580
581 t->t_slotdata[slot]->polling = on;
582
583 if (!on) {
584 int s;
585
586 /*
587 * If disabling polling on a device that's been configured,
588 * make sure there are no bytes left in the FIFO, holding up
589 * the interrupt line. Otherwise we won't get any further
590 * interrupts.
591 */
592 if (t->t_sc) {
593 s = spltty();
594 pckbcintr_internal(t, t->t_sc);
595 splx(s);
596 }
597 }
598}
599
600/*
601 * Pass command to device, poll for ACK and data.
602 * to be called at spltty()
603 */
604static void
605pckbc_poll_cmd1(struct pckbc_internal *t, pckbc_slot_t slot,
606 struct pckbc_devcmd *cmd)
607{
608 bus_space_tag_t iot = t->t_iot;
609 bus_space_handle_t ioh_d = t->t_ioh_d;
610 bus_space_handle_t ioh_c = t->t_ioh_c;
611 int i, c = 0;
612
613 while (cmd->cmdidx < cmd->cmdlen) {
614 if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
615 printf("pckbc_cmd: send error\n");
616 cmd->status = EIO;
617 return;
618 }
619 for (i = 10; i; i--) { /* 1s ??? */
620 c = pckbc_poll_data1(iot, ioh_d, ioh_c, slot,
621 t->t_haveaux);
622 if (c != -1)
623 break;
624 }
625
626 switch (c) {
627 case KBC_DEVCMD_ACK:
628 cmd->cmdidx++;
629 continue;
630 /*
631 * Some legacy free PCs keep returning Basic Assurance Test
632 * (BAT) instead of something usable, so fail gracefully.
633 */
634 case KBC_DEVCMD_RESEND:
635 case KBC_DEVCMD_BAT_DONE:
636 case KBC_DEVCMD_BAT_FAIL:
637 DPRINTF("pckbc_cmd: %s\n",
638 c == KBC_DEVCMD_RESEND ? "RESEND": "BAT");
639 if (cmd->retries++ < 5)
640 continue;
641
642 DPRINTF("pckbc_cmd: cmd failed\n");
643 cmd->status = ENXIO;
644 return;
645 case -1:
646 DPRINTF("pckbc_cmd: timeout\n");
647 cmd->status = EIO;
648 return;
649 default:
650 DPRINTF("pckbc_cmd: lost 0x%x\n", c);
651 }
652 }
653
654 while (cmd->responseidx < cmd->responselen) {
655 if (cmd->flags & KBC_CMDFLAG_SLOW)
656 i = 100; /* 10s ??? */
657 else
658 i = 10; /* 1s ??? */
659 while (i--) {
660 c = pckbc_poll_data1(iot, ioh_d, ioh_c, slot,
661 t->t_haveaux);
662 if (c != -1)
663 break;
664 }
665 if (c == -1) {
666 DPRINTF("pckbc_cmd: no data\n");
667 cmd->status = ETIMEDOUT;
668 return;
669 } else
670 cmd->response[cmd->responseidx++] = c;
671 }
672}
673
674/* for use in autoconfiguration */
675int
676pckbc_poll_cmd(pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd, int len,
677 int responselen, u_char *respbuf, int slow)
678{
679 struct pckbc_devcmd nc;
680
681 if ((len > 4) || (responselen > 4))
682 return (EINVAL);
683
684 bzero(&nc, sizeof(nc));
685 memcpy(nc.cmd, cmd, len);
686 nc.cmdlen = len;
687 nc.responselen = responselen;
688 nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);
689
690 pckbc_poll_cmd1(self, slot, &nc);
691
692 if (nc.status == 0 && respbuf)
693 memcpy(respbuf, nc.response, responselen);
694
695 return (nc.status);
696}
697
698/*
699 * Clean up a command queue, throw away everything.
700 */
701void
702pckbc_cleanqueue(struct pckbc_slotdata *q)
703{
704 struct pckbc_devcmd *cmd;
705#ifdef PCKBCDEBUG
706 int i;
707#endif
708
709 while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
710 TAILQ_REMOVE(&q->cmdqueue, cmd, next);
711 cmd->flags &= ~KBC_CMDFLAG_QUEUED;
712#ifdef PCKBCDEBUG
713 printf("pckbc_cleanqueue: removing");
714 for (i = 0; i < cmd->cmdlen; i++)
715 printf(" %02x", cmd->cmd[i]);
716 printf("\n");
717#endif
718 /*
719 * A synchronous command on the cmdqueue is currently owned by a
720 * sleeping proc. The same proc is responsible for putting it
721 * back on the freequeue once awake.
722 */
723 if (cmd->flags & KBC_CMDFLAG_SYNC)
724 continue;
725
726 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
727 }
728}
729
730void
731pckbc_cleanqueues(struct pckbc_internal *t)
732{
733 if (t->t_slotdata[PCKBC_KBD_SLOT])
734 pckbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]);
735 if (t->t_slotdata[PCKBC_AUX_SLOT])
736 pckbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]);
737}
738
739/*
740 * Timeout error handler: clean queues and data port.
741 * XXX could be less invasive.
742 */
743void
744pckbc_cleanup(void *self)
745{
746 struct pckbc_internal *t = self;
747 int s;
748
749 printf("pckbc: command timeout\n");
750
751 s = spltty();
752
753 pckbc_cleanqueues(t);
754
755 while (bus_space_read_1(t->t_iot, t->t_ioh_c, 0) & KBS_DIB) {
756 KBD_DELAY;
757 (void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
758 }
759
760 /* reset KBC? */
761
762 splx(s);
763}
764
765/*
766 * Stop the keyboard controller when we are going to suspend
767 */
768void
769pckbc_stop(struct pckbc_softc *sc)
770{
771 struct pckbc_internal *t = sc->id;
772
773 timeout_del(&t->t_poll);
774 pckbc_cleanqueues(t);
775 timeout_del(&t->t_cleanup);
776}
777
778/*
779 * Reset the keyboard controller in a violent fashion; normally done
780 * after suspend/resume when we do not trust the machine.
781 */
782void
783pckbc_reset(struct pckbc_softc *sc)
784{
785 struct pckbc_internal *t = sc->id;
786 bus_space_tag_t iot = t->t_iot;
787 bus_space_handle_t ioh_d = t->t_ioh_d, ioh_c = t->t_ioh_c;
788
789 pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
790 /* KBC selftest */
791 if (pckbc_send_cmd(iot, ioh_c, KBC_SELFTEST) == 0)
792 return;
793 pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
794 (void)pckbc_put8042cmd(t);
795 pckbcintr_internal(t->t_sc->id, t->t_sc);
796}
797
798/*
799 * Pass command to device during normal operation.
800 * to be called at spltty()
801 */
802void
803pckbc_start(struct pckbc_internal *t, pckbc_slot_t slot)
804{
805 struct pckbc_slotdata *q = t->t_slotdata[slot];
806 struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
807
808 if (q->polling) {
809 do {
810 pckbc_poll_cmd1(t, slot, cmd);
811 if (cmd->status)
812 printf("pckbc_start: command error\n");
813
814 TAILQ_REMOVE(&q->cmdqueue, cmd, next);
815 cmd->flags &= ~KBC_CMDFLAG_QUEUED;
816 if (cmd->flags & KBC_CMDFLAG_SYNC) {
817 wakeup(cmd);
818 } else {
819 timeout_del(&t->t_cleanup);
820 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
821 }
822 cmd = TAILQ_FIRST(&q->cmdqueue);
823 } while (cmd);
824 return;
825 }
826
827 if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
828 printf("pckbc_start: send error\n");
829 /* XXX what now? */
830 return;
831 }
832}
833
834/*
835 * Handle command responses coming in asynchronously,
836 * return nonzero if valid response.
837 * to be called at spltty()
838 */
839int
840pckbc_cmdresponse(struct pckbc_internal *t, pckbc_slot_t slot, u_char data)
841{
842 struct pckbc_slotdata *q = t->t_slotdata[slot];
843 struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
844#ifdef DIAGNOSTIC
845 if (!cmd)
846 panic("pckbc_cmdresponse: no active command");
847#endif
848 if (cmd->cmdidx < cmd->cmdlen) {
849 if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
850 return (0);
851
852 if (data == KBC_DEVCMD_RESEND) {
853 if (cmd->retries++ < 5) {
854 /* try again last command */
855 goto restart;
856 } else {
857 DPRINTF("pckbc: cmd failed\n");
858 cmd->status = ENXIO;
859 /* dequeue */
860 }
861 } else {
862 if (++cmd->cmdidx < cmd->cmdlen)
863 goto restart;
864 if (cmd->responselen)
865 return (1);
866 /* else dequeue */
867 }
868 } else if (cmd->responseidx < cmd->responselen) {
869 cmd->response[cmd->responseidx++] = data;
870 if (cmd->responseidx < cmd->responselen)
871 return (1);
872 /* else dequeue */
873 } else
874 return (0);
875
876 /* dequeue: */
877 TAILQ_REMOVE(&q->cmdqueue, cmd, next);
878 cmd->flags &= ~KBC_CMDFLAG_QUEUED;
879 if (cmd->flags & KBC_CMDFLAG_SYNC) {
880 wakeup(cmd);
881 } else {
882 timeout_del(&t->t_cleanup);
883 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
884 }
885 cmd = TAILQ_FIRST(&q->cmdqueue);
886 if (cmd == NULL)
887 return (1);
888restart:
889 pckbc_start(t, slot);
890 return (1);
891}
892
893/*
894 * Put command into the device's command queue, return zero or errno.
895 */
896int
897pckbc_enqueue_cmd(pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd, int len,
898 int responselen, int sync, u_char *respbuf)
899{
900 struct pckbc_internal *t = self;
901 struct pckbc_slotdata *q = t->t_slotdata[slot];
902 struct pckbc_devcmd *nc;
903 int s, isactive, res = 0;
904
905 if ((len > 4) || (responselen > 4))
906 return (EINVAL);
907 s = spltty();
908 nc = TAILQ_FIRST(&q->freequeue);
909 if (nc) {
910 TAILQ_REMOVE(&q->freequeue, nc, next);
911 }
912 splx(s);
913 if (!nc)
914 return (ENOMEM);
915
916 bzero(nc, sizeof(*nc));
917 memcpy(nc->cmd, cmd, len);
918 nc->cmdlen = len;
919 nc->responselen = responselen;
920 nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);
921
922 s = spltty();
923
924 if (q->polling && sync) {
925 /*
926 * XXX We should poll until the queue is empty.
927 * But we don't come here normally, so make
928 * it simple and throw away everything.
929 */
930 pckbc_cleanqueue(q);
931 }
932
933 isactive = CMD_IN_QUEUE(q);
934 nc->flags |= KBC_CMDFLAG_QUEUED;
935 TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
936 if (!isactive)
937 pckbc_start(t, slot);
938
939 if (q->polling)
940 res = (sync ? nc->status : 0);
941 else if (sync) {
942 if ((res = tsleep_nsec(nc, 0, "kbccmd", SEC_TO_NSEC(1)))) {
943 pckbc_cleanup(t);
944 } else {
945 /*
946 * Under certain circumstances, such as during suspend,
947 * tsleep() becomes a no-op and the command is left on
948 * the cmdqueue.
949 */
950 if (nc->flags & KBC_CMDFLAG_QUEUED) {
951 TAILQ_REMOVE(&q->cmdqueue, nc, next);
952 nc->flags &= ~KBC_CMDFLAG_QUEUED;
953 }
954 res = nc->status;
955 }
956 } else
957 timeout_add_sec(&t->t_cleanup, 1);
958
959 if (sync) {
960 if (respbuf)
961 memcpy(respbuf, nc->response, responselen);
962 TAILQ_INSERT_TAIL(&q->freequeue, nc, next);
963 }
964
965 splx(s);
966
967 return (res);
968}
969
970void
971pckbc_set_inputhandler(pckbc_tag_t self, pckbc_slot_t slot, pckbc_inputfcn func,
972 void *arg, char *name)
973{
974 struct pckbc_internal *t = (struct pckbc_internal *)self;
975 struct pckbc_softc *sc = t->t_sc;
976
977 if (slot >= PCKBC_NSLOTS)
978 panic("pckbc_set_inputhandler: bad slot %d", slot);
979
980 sc->inputhandler[slot] = func;
981 sc->inputarg[slot] = arg;
982 sc->subname[slot] = name;
983
984 if (pckbc_console && slot == PCKBC_KBD_SLOT)
985 timeout_add_sec(&t->t_poll, 1);
986}
987
988void
989pckbc_poll(void *v)
990{
991 struct pckbc_internal *t = v;
992 int s;
993
994 s = spltty();
995 (void)pckbcintr_internal(t, t->t_sc);
996 timeout_add_sec(&t->t_poll, 1);
997 splx(s);
998}
999
1000int
1001pckbcintr(void *vsc)
1002{
1003 struct pckbc_softc *sc = (struct pckbc_softc *)vsc;
1004
1005 return (pckbcintr_internal(sc->id, sc));
1006}
1007
1008int
1009pckbcintr_internal(struct pckbc_internal *t, struct pckbc_softc *sc)
1010{
1011 u_char stat;
1012 pckbc_slot_t slot;
1013 struct pckbc_slotdata *q;
1014 int served = 0, data;
1015
1016 /* reschedule timeout further into the idle times */
1017 if (timeout_pending(&t->t_poll))
1018 timeout_add_sec(&t->t_poll, 1);
1019
1020 for(;;) {
1021 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0);
1022 if (!(stat & KBS_DIB))
1023 break;
1024
1025 served = 1;
1026
1027 slot = (t->t_haveaux && (stat & KBS_AUXDATA)) ?
1028 PCKBC_AUX_SLOT : PCKBC_KBD_SLOT;
1029 q = t->t_slotdata[slot];
1030
1031 if (!q) {
1032 /* XXX do something for live insertion? */
1033#ifdef PCKBCDEBUG
1034 printf("pckbcintr: no dev for slot %d\n", slot);
1035#endif
1036 KBD_DELAY;
1037 (void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
1038 continue;
1039 }
1040
1041 if (q->polling)
1042 break; /* pckbc_poll_data() will get it */
1043
1044 KBD_DELAY;
1045 data = bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
1046
1047 if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data))
1048 continue;
1049
1050 if (sc != NULL) {
1051 if (sc->inputhandler[slot])
1052 (*sc->inputhandler[slot])(sc->inputarg[slot],
1053 data);
1054#ifdef PCKBCDEBUG
1055 else
1056 printf("pckbcintr: slot %d lost %d\n",
1057 slot, data);
1058#endif
1059 }
1060 }
1061
1062 return (served);
1063}
1064
1065int
1066pckbc_cnattach(bus_space_tag_t iot, bus_addr_t addr, bus_size_t cmd_offset,
1067 int flags)
1068{
1069 bus_space_handle_t ioh_d, ioh_c;
1070 int res = 0;
1071
1072 if (bus_space_map(iot, addr + KBDATAP, 1, 0, &ioh_d))
1073 return (ENXIO);
1074 if (bus_space_map(iot, addr + cmd_offset, 1, 0, &ioh_c)) {
1075 bus_space_unmap(iot, ioh_d, 1);
1076 return (ENXIO);
1077 }
1078
1079 pckbc_consdata.t_iot = iot;
1080 pckbc_consdata.t_ioh_d = ioh_d;
1081 pckbc_consdata.t_ioh_c = ioh_c;
1082 pckbc_consdata.t_addr = addr;
1083 pckbc_consdata.t_flags = flags;
1084 timeout_set(&pckbc_consdata.t_cleanup, pckbc_cleanup, &pckbc_consdata);
1085 timeout_set(&pckbc_consdata.t_poll, pckbc_poll, &pckbc_consdata);
1086
1087 /* flush */
1088 (void) pckbc_poll_data1(iot, ioh_d, ioh_c, PCKBC_KBD_SLOT, 0);
1089
1090 /* selftest? */
1091
1092 /* init cmd byte, enable ports */
1093 pckbc_consdata.t_cmdbyte = KC8_CPU;
1094 if (!pckbc_put8042cmd(&pckbc_consdata)) {
1095 printf("kbc: cmd word write error\n");
1096 res = EIO;
1097 }
1098
1099 if (!res) {
1100#if (NPCKBD > 0)
1101 res = pckbd_cnattach(&pckbc_consdata);
1102#else
1103 res = ENXIO;
1104#endif /* NPCKBD > 0 */
1105 }
1106
1107 if (res) {
1108 bus_space_unmap(iot, pckbc_consdata.t_ioh_d, 1);
1109 bus_space_unmap(iot, pckbc_consdata.t_ioh_c, 1);
1110 } else {
1111 pckbc_consdata.t_slotdata[PCKBC_KBD_SLOT] = &pckbc_cons_slotdata;
1112 pckbc_init_slotdata(&pckbc_cons_slotdata);
1113 pckbc_console = 1;
1114 }
1115
1116 return (res);
1117}
1118
1119struct cfdriver pckbc_cd = {
1120 NULL, "pckbc", DV_DULL
1121};