Annotation of sys/dev/ic/pckbc.c, Revision 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