123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928 |
- /* $OpenBSD: mkbc.c,v 1.13 2015/02/11 07:05:39 dlg Exp $ */
- /*
- * Copyright (c) 2006, 2007, Joel Sing
- * All rights reserved
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF MIND,
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- * Derived from sys/dev/ic/pckbc.c under the following terms:
- * $NetBSD: pckbc.c,v 1.5 2000/06/09 04:58:35 soda Exp $ */
- /*
- * Copyright (c) 1998
- * Matthias Drochner. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- * Driver for Moosehead PS/2 Controllers (mkbc)
- *
- * There are actually two separate controllers attached to the macebus.
- * However in the interest of reusing code, we want to act like a pckbc(4)
- * so that we can directly attach pckbd(4) and pms(4). As a result, we make
- * each controller look like a "slot" and combine them into a single device.
- *
- */
- #include <sys/param.h>
- #include <sys/systm.h>
- #include <sys/device.h>
- #include <sys/malloc.h>
- #include <sys/timeout.h>
- #include <sys/kernel.h>
- #include <sys/proc.h>
- #include <sys/signalvar.h>
- #include <sys/errno.h>
- #include <sys/queue.h>
- #include <machine/autoconf.h>
- #include <machine/bus.h>
- #include <mips64/archtype.h>
- #include <sgi/localbus/crimebus.h>
- #include <sgi/localbus/macebusvar.h>
- #include <dev/ic/pckbcvar.h>
- #include <dev/pckbc/pckbdvar.h>
- #include "mkbcreg.h"
- const char *mkbc_slot_names[] = { "kbd", "mouse" };
- #define KBC_DEVCMD_ACK 0xfa
- #define KBC_DEVCMD_RESEND 0xfe
- #define KBD_DELAY DELAY(8)
- struct mkbc_softc {
- struct pckbc_softc sc_pckbc;
- bus_space_tag_t iot;
- bus_space_handle_t ioh;
- };
- int mkbc_match(struct device *, void *, void *);
- void mkbc_attach(struct device *, struct device *, void *);
- struct cfattach mkbc_ca = {
- sizeof(struct mkbc_softc), mkbc_match, mkbc_attach
- };
- struct cfdriver mkbc_cd = {
- NULL, "mkbc", DV_DULL
- };
- /* Descriptor for one device command. */
- struct pckbc_devcmd {
- TAILQ_ENTRY(pckbc_devcmd) next;
- int flags;
- #define KBC_CMDFLAG_SYNC 1 /* Give descriptor back to caller. */
- #define KBC_CMDFLAG_SLOW 2
- u_char cmd[4];
- int cmdlen, cmdidx, retries;
- u_char response[4];
- int status, responselen, responseidx;
- };
- /* Data per slave device. */
- struct pckbc_slotdata {
- int polling; /* Don't read data port in interrupt handler. */
- TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* Active commands. */
- TAILQ_HEAD(, pckbc_devcmd) freequeue; /* Free commands. */
- #define NCMD 5
- struct pckbc_devcmd cmds[NCMD];
- bus_space_handle_t ioh;
- };
- #define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
- static int mkbc_console;
- static struct pckbc_slotdata mkbc_cons_slotdata;
- struct pckbc_internal mkbc_consdata;
- void mkbc_start(struct pckbc_internal *, pckbc_slot_t);
- int mkbc_attach_slot(struct mkbc_softc *, pckbc_slot_t);
- void mkbc_init_slotdata(struct pckbc_slotdata *);
- int mkbc_submatch(struct device *, void *, void *);
- int mkbcprint(void *, const char *);
- int mkbcintr(void *);
- int mkbcintr_internal(struct pckbc_internal *, struct pckbc_softc *);
- void mkbc_cleanqueue(struct pckbc_slotdata *);
- void mkbc_cleanup(void *);
- void mkbc_poll(void *);
- int mkbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char);
- int mkbc_poll_read(bus_space_tag_t, bus_space_handle_t);
- int mkbc_poll_write(bus_space_tag_t, bus_space_handle_t, int);
- int
- mkbc_match(struct device *parent, void *cf, void *aux)
- {
- return 1;
- }
- void
- mkbc_init_slotdata(struct pckbc_slotdata *q)
- {
- int i;
- TAILQ_INIT(&q->cmdqueue);
- TAILQ_INIT(&q->freequeue);
- for (i = 0; i < NCMD; i++) {
- TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next);
- }
- q->polling = 0;
- }
- int
- mkbcprint(void *aux, const char *pnp)
- {
- struct pckbc_attach_args *pa = aux;
- if (!pnp)
- printf(" (%s slot)", mkbc_slot_names[pa->pa_slot]);
- return (QUIET);
- }
- int
- mkbc_submatch(struct device *parent, void *match, void *aux)
- {
- struct cfdata *cf = match;
- struct pckbc_attach_args *pa = aux;
- if (cf->cf_loc[PCKBCCF_SLOT] != PCKBCCF_SLOT_DEFAULT &&
- cf->cf_loc[PCKBCCF_SLOT] != pa->pa_slot)
- return (0);
- return ((*cf->cf_attach->ca_match)(parent, cf, aux));
- }
- int
- mkbc_attach_slot(struct mkbc_softc *msc, pckbc_slot_t slot)
- {
- struct pckbc_softc *sc = &msc->sc_pckbc;
- struct pckbc_internal *t = sc->id;
- struct pckbc_attach_args pa;
- bus_space_handle_t ioh;
- int found;
- if (!t->t_slotdata[slot]) {
- t->t_slotdata[slot] = malloc(sizeof(struct pckbc_slotdata),
- M_DEVBUF, M_NOWAIT);
- if (t->t_slotdata[slot] == NULL) {
- printf("Failed to allocate slot data!\n");
- return 0;
- }
- mkbc_init_slotdata(t->t_slotdata[slot]);
- /* Map subregion of bus space for this "slot". */
- if (bus_space_subregion(msc->iot, msc->ioh,
- MKBC_PORTSIZE * slot, MKBC_PORTSIZE, &ioh)) {
- printf("Unable to map slot subregion!\n");
- return 0;
- }
- t->t_slotdata[slot]->ioh = ioh;
- /* Initialise controller. */
- bus_space_write_8(msc->iot, ioh, MKBC_CONTROL,
- MKBC_CONTROL_TX_CLOCK_DISABLE | MKBC_CONTROL_RESET);
- delay(100); /* 100us */
- /* Enable controller. */
- bus_space_write_8(t->t_iot, t->t_slotdata[slot]->ioh,
- MKBC_CONTROL,
- MKBC_CONTROL_RX_CLOCK_ENABLE | MKBC_CONTROL_TX_ENABLE);
- }
- pa.pa_tag = t;
- pa.pa_slot = slot;
- found = (config_found_sm((struct device *)msc, &pa,
- mkbcprint, mkbc_submatch) != NULL);
- return (found);
- }
- void
- mkbc_attach(struct device *parent, struct device *self, void *aux)
- {
- struct mkbc_softc *msc = (void*)self;
- struct macebus_attach_args *maa = aux;
- struct pckbc_softc *sc = &msc->sc_pckbc;
- struct pckbc_internal *t;
- if (mkbc_console == 0) {
- /* Setup bus space mapping. */
- msc->iot = maa->maa_iot;
- if (bus_space_map(msc->iot, maa->maa_baseaddr,
- MKBC_PORTSIZE * 2, 0, &msc->ioh)) {
- printf(": unable to map bus space!\n");
- return;
- }
- /* Setup pckbc_internal structure. */
- t = malloc(sizeof(struct pckbc_internal), M_DEVBUF,
- M_WAITOK | M_ZERO);
- t->t_iot = msc->iot;
- t->t_ioh_d = 0;
- t->t_ioh_c = 0;
- t->t_addr = maa->maa_baseaddr;
- t->t_sc = (struct pckbc_softc *)msc;
- sc->id = t;
- timeout_set(&t->t_cleanup, mkbc_cleanup, t);
- timeout_set(&t->t_poll, mkbc_poll, t);
- } else {
- /* Things have already been setup in mkbc_cnattach. */
- msc->iot = mkbc_consdata.t_iot;
- msc->ioh = mkbc_consdata.t_ioh_d;
- mkbc_consdata.t_sc = (struct pckbc_softc *)msc;
- sc->id = &mkbc_consdata;
- }
- /* Establish interrupt handler. */
- if (macebus_intr_establish(maa->maa_intr, maa->maa_mace_intr,
- IST_EDGE, IPL_TTY, mkbcintr, msc, sc->sc_dv.dv_xname))
- printf("\n");
- else
- printf(": unable to establish interrupt\n");
- /*
- * Attach "slots" - technically these are separate controllers
- * in the same bus space, however we want to act like pckbc(4) so
- * that we can attach pckbd(4) and pms(4).
- */
- mkbc_attach_slot(msc, PCKBC_KBD_SLOT);
- mkbc_attach_slot(msc, PCKBC_AUX_SLOT);
- }
- int
- mkbcintr(void *vsc)
- {
- struct mkbc_softc *msc = (struct mkbc_softc *)vsc;
- struct pckbc_softc *sc = &msc->sc_pckbc;
- struct pckbc_internal *t = sc->id;
- return mkbcintr_internal(t, sc);
- }
- int
- mkbcintr_internal(struct pckbc_internal *t, struct pckbc_softc *sc)
- {
- pckbc_slot_t slot;
- struct pckbc_slotdata *q;
- int served = 0;
- u_int64_t stat;
- u_int64_t data;
- /* Reschedule timeout further into the idle times. */
- if (timeout_pending(&t->t_poll))
- timeout_add_sec(&t->t_poll, 1);
- /*
- * Need to check both "slots" since interrupt could be from
- * either controller.
- */
- slot = PCKBC_KBD_SLOT;
- q = t->t_slotdata[slot];
- for(;;) {
- if (!q) {
- printf("mkbcintr: no kbd slot data!\n");
- break;
- }
- stat = bus_space_read_8(t->t_iot, q->ioh, MKBC_STATUS);
- if (!(stat & MKBC_STATUS_RX_FULL))
- break;
- served = 1;
- if (q->polling)
- break; /* pckbc_poll_data() will get it */
- KBD_DELAY;
- data = bus_space_read_8(t->t_iot, q->ioh, MKBC_RX_PORT) & 0xff;
- if (CMD_IN_QUEUE(q) && mkbc_cmdresponse(t, slot, data))
- continue;
- if (sc->inputhandler[slot])
- (*sc->inputhandler[slot])(sc->inputarg[slot], data);
- #ifdef MKBCDEBUG
- else
- printf("mkbcintr: slot %d lost %d\n", slot, data);
- #endif
- }
-
- /* Mouse controller/slot. */
- slot = PCKBC_AUX_SLOT;
- q = t->t_slotdata[slot];
- for(;;) {
- if (!q) {
- printf("mkbcintr: no mouse slot data!\n");
- break;
- }
- stat = bus_space_read_8(t->t_iot, q->ioh, MKBC_STATUS);
- if (!(stat & MKBC_STATUS_RX_FULL))
- break;
- served = 1;
- if (q->polling)
- break; /* pckbc_poll_data() will get it. */
- KBD_DELAY;
- data = bus_space_read_8(t->t_iot, q->ioh, MKBC_RX_PORT) & 0xff;
- if (CMD_IN_QUEUE(q) && mkbc_cmdresponse(t, slot, data))
- continue;
- if (sc->inputhandler[slot])
- (*sc->inputhandler[slot])(sc->inputarg[slot], data);
- #ifdef MKBCDEBUG
- else
- printf("mkbcintr: slot %d lost %d\n", slot, data);
- #endif
- }
- return (served);
- }
- int
- mkbc_poll_write(bus_space_tag_t iot, bus_space_handle_t ioh, int val)
- {
- int timeout = 10000;
- u_int64_t stat;
- /* Attempt to write a value to the controller. */
- while (timeout--) {
- stat = bus_space_read_8(iot, ioh, MKBC_STATUS);
- if (stat & MKBC_STATUS_TX_EMPTY) {
- bus_space_write_8(iot, ioh, MKBC_TX_PORT, val & 0xff);
- return 0;
- }
- delay(50);
- }
- return -1;
- }
- int
- mkbc_poll_read(bus_space_tag_t iot, bus_space_handle_t ioh)
- {
- int timeout = 10000;
- u_int64_t stat, val;
- /* Poll input from controller. */
- while (timeout--) {
- stat = bus_space_read_8(iot, ioh, MKBC_STATUS);
- if (stat & MKBC_STATUS_RX_FULL) {
- val = bus_space_read_8(iot, ioh, MKBC_RX_PORT);
- return val & 0xff;
- }
- delay(50);
- }
- return -1;
- }
- /*
- * Pass command to device, poll for ACK and data.
- * to be called at spltty()
- */
- static void
- mkbc_poll_cmd(struct pckbc_internal *t, pckbc_slot_t slot,
- struct pckbc_devcmd *cmd)
- {
- bus_space_tag_t iot = t->t_iot;
- bus_space_handle_t ioh = t->t_slotdata[slot]->ioh;
- int i, c = 0;
- while (cmd->cmdidx < cmd->cmdlen) {
- if (mkbc_poll_write(iot, ioh, cmd->cmd[cmd->cmdidx]) == -1) {
- printf("mkbc_poll_cmd: send error\n");
- cmd->status = EIO;
- return;
- }
- for (i = 10; i; i--) { /* 1s ??? */
- c = mkbc_poll_read(iot, ioh);
- if (c != -1)
- break;
- }
- if (c == KBC_DEVCMD_ACK) {
- cmd->cmdidx++;
- continue;
- }
- if (c == KBC_DEVCMD_RESEND) {
- #ifdef MKBCDEBUG
- printf("mkbc_cmd: RESEND\n");
- #endif
- if (cmd->retries++ < 5)
- continue;
- else {
- #ifdef MKBCDEBUG
- printf("mkbc: cmd failed\n");
- #endif
- cmd->status = EIO;
- return;
- }
- }
- if (c == -1) {
- #ifdef MKBCDEBUG
- printf("mkbc_cmd: timeout\n");
- #endif
- cmd->status = EIO;
- return;
- }
- #ifdef MKBCDEBUG
- printf("mkbc_cmd: lost 0x%x\n", c);
- #endif
- }
- while (cmd->responseidx < cmd->responselen) {
- if (cmd->flags & KBC_CMDFLAG_SLOW)
- i = 100; /* 10s ??? */
- else
- i = 10; /* 1s ??? */
- while (i--) {
- c = mkbc_poll_read(iot, ioh);
- if (c != -1)
- break;
- }
- if (c == -1) {
- #ifdef MKBCDEBUG
- printf("mkbc_poll_cmd: no data\n");
- #endif
- cmd->status = ETIMEDOUT;
- return;
- } else
- cmd->response[cmd->responseidx++] = c;
- }
- }
- /*
- * Clean up a command queue, throw away everything.
- */
- void
- mkbc_cleanqueue(struct pckbc_slotdata *q)
- {
- struct pckbc_devcmd *cmd;
- #ifdef MKBCDEBUG
- int i;
- #endif
- while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
- TAILQ_REMOVE(&q->cmdqueue, cmd, next);
- #ifdef MKBCDEBUG
- printf("mkbc_cleanqueue: removing");
- for (i = 0; i < cmd->cmdlen; i++)
- printf(" %02x", cmd->cmd[i]);
- printf("\n");
- #endif
- TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
- }
- }
- /*
- * Timeout error handler: clean queues and data port.
- * XXX could be less invasive.
- */
- void
- mkbc_cleanup(void *self)
- {
- struct pckbc_internal *t = self;
- int s;
- printf("mkbc: command timeout\n");
- s = spltty();
- if (t->t_slotdata[PCKBC_KBD_SLOT])
- mkbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]);
- if (t->t_slotdata[PCKBC_AUX_SLOT])
- mkbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]);
- while (mkbc_poll_read(t->t_iot, t->t_slotdata[PCKBC_KBD_SLOT]->ioh)
- != -1) ;
- while (mkbc_poll_read(t->t_iot, t->t_slotdata[PCKBC_AUX_SLOT]->ioh)
- != -1) ;
- /* Reset KBC? */
- splx(s);
- }
- /*
- * Pass command to device during normal operation.
- * to be called at spltty()
- */
- void
- mkbc_start(struct pckbc_internal *t, pckbc_slot_t slot)
- {
- struct pckbc_slotdata *q = t->t_slotdata[slot];
- struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
- if (q->polling) {
- do {
- mkbc_poll_cmd(t, slot, cmd);
- if (cmd->status)
- printf("mkbc_start: command error\n");
- TAILQ_REMOVE(&q->cmdqueue, cmd, next);
- if (cmd->flags & KBC_CMDFLAG_SYNC)
- wakeup(cmd);
- else {
- timeout_del(&t->t_cleanup);
- TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
- }
- cmd = TAILQ_FIRST(&q->cmdqueue);
- } while (cmd);
- return;
- }
- if (mkbc_poll_write(t->t_iot, t->t_slotdata[slot]->ioh,
- cmd->cmd[cmd->cmdidx])) {
- printf("mkbc_start: send error\n");
- /* XXX what now? */
- return;
- }
- }
- /*
- * Handle command responses coming in asynchronously,
- * return nonzero if valid response.
- * to be called at spltty()
- */
- int
- mkbc_cmdresponse(struct pckbc_internal *t, pckbc_slot_t slot, u_char data)
- {
- struct pckbc_slotdata *q = t->t_slotdata[slot];
- struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
- #ifdef DIAGNOSTIC
- if (!cmd)
- panic("mkbc_cmdresponse: no active command");
- #endif
- if (cmd->cmdidx < cmd->cmdlen) {
- if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
- return (0);
- if (data == KBC_DEVCMD_RESEND) {
- if (cmd->retries++ < 5) {
- /* try again last command */
- goto restart;
- } else {
- #ifdef MKBCDEBUG
- printf("mkbc: cmd failed\n");
- #endif
- cmd->status = EIO;
- /* dequeue */
- }
- } else {
- if (++cmd->cmdidx < cmd->cmdlen)
- goto restart;
- if (cmd->responselen)
- return (1);
- /* else dequeue */
- }
- } else if (cmd->responseidx < cmd->responselen) {
- cmd->response[cmd->responseidx++] = data;
- if (cmd->responseidx < cmd->responselen)
- return (1);
- /* else dequeue */
- } else
- return (0);
- /* dequeue: */
- TAILQ_REMOVE(&q->cmdqueue, cmd, next);
- if (cmd->flags & KBC_CMDFLAG_SYNC)
- wakeup(cmd);
- else {
- timeout_del(&t->t_cleanup);
- TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
- }
- if (!CMD_IN_QUEUE(q))
- return (1);
- restart:
- mkbc_start(t, slot);
- return (1);
- }
- /*
- * Interfaces to act like pckbc(4).
- */
- int
- pckbc_xt_translation(pckbc_tag_t self)
- {
- /* Translation isn't supported... */
- return (-1);
- }
- /* For use in autoconfiguration. */
- int
- pckbc_poll_cmd(pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd, int len,
- int responselen, u_char *respbuf, int slow)
- {
- struct pckbc_devcmd nc;
- int s;
- if ((len > 4) || (responselen > 4))
- return (EINVAL);
- bzero(&nc, sizeof(nc));
- bcopy(cmd, nc.cmd, len);
- nc.cmdlen = len;
- nc.responselen = responselen;
- nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);
- s = spltty();
- mkbc_poll_cmd(self, slot, &nc);
- splx(s);
- if (nc.status == 0 && respbuf)
- bcopy(nc.response, respbuf, responselen);
- return (nc.status);
- }
- void
- pckbc_flush(pckbc_tag_t self, pckbc_slot_t slot)
- {
- /* Read any data and discard. */
- struct pckbc_internal *t = self;
- (void) mkbc_poll_read(t->t_iot, t->t_slotdata[slot]->ioh);
- }
- /*
- * Put command into the device's command queue, return zero or errno.
- */
- int
- pckbc_enqueue_cmd(pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd, int len,
- int responselen, int sync, u_char *respbuf)
- {
- struct pckbc_internal *t = self;
- struct pckbc_slotdata *q = t->t_slotdata[slot];
- struct pckbc_devcmd *nc;
- int s, isactive, res = 0;
- if ((len > 4) || (responselen > 4))
- return (EINVAL);
- s = spltty();
- nc = TAILQ_FIRST(&q->freequeue);
- if (nc) {
- TAILQ_REMOVE(&q->freequeue, nc, next);
- }
- splx(s);
- if (!nc)
- return (ENOMEM);
- bzero(nc, sizeof(*nc));
- bcopy(cmd, nc->cmd, len);
- nc->cmdlen = len;
- nc->responselen = responselen;
- nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);
- s = spltty();
- if (q->polling && sync) {
- /*
- * XXX We should poll until the queue is empty.
- * But we don't come here normally, so make
- * it simple and throw away everything.
- */
- mkbc_cleanqueue(q);
- }
- isactive = CMD_IN_QUEUE(q);
- TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
- if (!isactive)
- mkbc_start(t, slot);
- if (q->polling)
- res = (sync ? nc->status : 0);
- else if (sync) {
- if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) {
- TAILQ_REMOVE(&q->cmdqueue, nc, next);
- mkbc_cleanup(t);
- } else
- res = nc->status;
- } else
- timeout_add_sec(&t->t_cleanup, 1);
- if (sync) {
- if (respbuf)
- bcopy(nc->response, respbuf, responselen);
- TAILQ_INSERT_TAIL(&q->freequeue, nc, next);
- }
- splx(s);
- return (res);
- }
- int
- pckbc_poll_data(pckbc_tag_t self, pckbc_slot_t slot)
- {
- struct pckbc_internal *t = self;
- struct pckbc_slotdata *q = t->t_slotdata[slot];
- int c;
- c = mkbc_poll_read(t->t_iot, q->ioh);
- if (c != -1 && q && CMD_IN_QUEUE(q)) {
- /* We jumped into a running command - try to deliver the
- response. */
- if (mkbc_cmdresponse(t, slot, c))
- return (-1);
- }
- return (c);
- }
- void
- pckbc_set_inputhandler(pckbc_tag_t self, pckbc_slot_t slot, pckbc_inputfcn func,
- void *arg, char *name)
- {
- struct pckbc_internal *t = (struct pckbc_internal *)self;
- struct pckbc_softc *sc = t->t_sc;
- if (slot >= PCKBC_NSLOTS)
- panic("mkbc_set_inputhandler: bad slot %d", slot);
- sc->inputhandler[slot] = func;
- sc->inputarg[slot] = arg;
- sc->subname[slot] = name;
- if (mkbc_console && slot == PCKBC_KBD_SLOT)
- timeout_add_sec(&t->t_poll, 1);
- }
- void
- pckbc_slot_enable(pckbc_tag_t self, pckbc_slot_t slot, int on)
- {
- struct pckbc_internal *t = (struct pckbc_internal *)self;
- /*
- * Should we also enable/disable the controller??
- * If we did then none of the poll_ functions would work...
- */
- if (on) {
-
- /* Enable controller interrupts. */
- bus_space_write_8(t->t_iot, t->t_slotdata[slot]->ioh,
- MKBC_CONTROL,
- MKBC_CONTROL_RX_CLOCK_ENABLE | MKBC_CONTROL_TX_ENABLE
- | MKBC_CONTROL_RX_INT_ENABLE);
- } else {
- /* Disable controller interrupts. */
- bus_space_write_8(t->t_iot, t->t_slotdata[slot]->ioh,
- MKBC_CONTROL,
- MKBC_CONTROL_RX_CLOCK_ENABLE | MKBC_CONTROL_TX_ENABLE);
- }
- if (slot == PCKBC_KBD_SLOT) {
- if (on)
- timeout_add_sec(&t->t_poll, 1);
- else
- timeout_del(&t->t_poll);
- }
- }
- void
- pckbc_set_poll(pckbc_tag_t self, pckbc_slot_t slot, int on)
- {
- struct pckbc_internal *t = (struct pckbc_internal *)self;
- t->t_slotdata[slot]->polling = on;
- if (!on) {
- int s;
- /*
- * If disabling polling on a device that's been configured,
- * make sure there are no bytes left in the FIFO, holding up
- * the interrupt line. Otherwise we won't get any further
- * interrupts.
- */
- if (t->t_sc) {
- s = spltty();
- mkbcintr(t->t_sc);
- splx(s);
- }
- }
- }
- int
- mkbc_cnattach(bus_space_tag_t iot, bus_addr_t addr)
- {
- bus_space_handle_t ioh, slot_ioh;
- int res = 0;
- /* Ensure that we're on an O2. */
- if (sys_config.system_type != SGI_O2)
- return (ENXIO);
- if (bus_space_map(iot, addr, MKBC_PORTSIZE * 2, 0, &ioh))
- return (ENXIO);
- mkbc_consdata.t_addr = addr;
- mkbc_consdata.t_iot = iot;
- mkbc_consdata.t_ioh_d = ioh;
- /* Map subregion of bus space for this "slot". */
- if (bus_space_subregion(iot, ioh, 0, MKBC_PORTSIZE, &slot_ioh)) {
- bus_space_unmap(iot, ioh, MKBC_PORTSIZE * 2);
- return (ENXIO);
- }
- mkbc_cons_slotdata.ioh = slot_ioh;
- mkbc_init_slotdata(&mkbc_cons_slotdata);
- mkbc_consdata.t_slotdata[PCKBC_KBD_SLOT] = &mkbc_cons_slotdata;
- /* Initialise controller. */
- bus_space_write_8(iot, slot_ioh, MKBC_CONTROL,
- MKBC_CONTROL_TX_CLOCK_DISABLE | MKBC_CONTROL_RESET);
- delay(100); /* 100us */
- /* Enable controller. */
- bus_space_write_8(iot, slot_ioh, MKBC_CONTROL,
- MKBC_CONTROL_RX_CLOCK_ENABLE | MKBC_CONTROL_TX_ENABLE
- | MKBC_CONTROL_RX_INT_ENABLE);
- timeout_set(&mkbc_consdata.t_cleanup, mkbc_cleanup, &mkbc_consdata);
- timeout_set(&mkbc_consdata.t_poll, mkbc_poll, &mkbc_consdata);
- /* Flush input buffer. */
- (void) mkbc_poll_read(iot, slot_ioh);
- res = pckbd_cnattach(&mkbc_consdata);
- if (res) {
- bus_space_unmap(iot, ioh, MKBC_PORTSIZE * 2);
- } else {
- mkbc_console = 1;
- }
- return (res);
- }
- void
- mkbc_poll(void *self)
- {
- struct pckbc_internal *t = self;
- int s;
- s = spltty();
- (void)mkbcintr_internal(t, t->t_sc);
- timeout_add_sec(&t->t_poll, 1);
- splx(s);
- }
|