[BACK]Return to pckbc.c CVS log [TXT][DIR] Up to [local] / sys / dev / ic

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