diff --git a/sys/arch/arm/sa11x0/sa1111_kbc.c b/sys/arch/arm/sa11x0/sa1111_kbc.c new file mode 100644 index 000000000000..5f414acb1c10 --- /dev/null +++ b/sys/arch/arm/sa11x0/sa1111_kbc.c @@ -0,0 +1,825 @@ +/* $NetBSD: sa1111_kbc.c,v 1.1 2002/12/20 04:12:51 bsh Exp $ */ + +/* + * Copyright (c) 2002 Genetec Corporation. All rights reserved. + * Written by Hiroyuki Bessho for Genetec Corporation. + * + * 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. + * 3. The name of Genetec Corporation may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``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 GENETEC CORPORATION + * 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 keyboard controller in SA-1111 companion chip. + * + * PC keyboard driver (sys/dev/pckbc/pckbd.c) works only with 8042 + * keyboard controller driver (sys/dev/ic/pckbc.c). This file + * provides same functions as those of 8042 driver. + * + * XXX: we need cleaner interface between the keyboard driver and + * keyboard controller drivers. + */ +/* + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by Matthias Drochner. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include /* for prototypes */ + +#include "pckbd.h" +#include "rnd.h" +#include "locators.h" + +/* 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; +}; + +struct sackbc_softc { + struct device dev; + + bus_space_tag_t iot; + bus_space_handle_t ioh; + + void *ih_rx; /* receive interrupt */ + int intr; /* interrupt number */ + + int polling; /* don't process data in interrupt handler */ + int poll_stat; /* data read from inr handler if polling */ + int poll_data; /* status read from intr handler if polling */ + + TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* active commands */ + TAILQ_HEAD(, pckbc_devcmd) freequeue; /* free commands */ +#define NCMD 5 + struct pckbc_devcmd cmd[NCMD]; + + struct callout t_cleanup; + pckbc_inputfcn inputhandler; + void *inputarg; + const char *subname; + +}; + +#define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL) + +#define N_KBC_SLOTS 2 +/*static struct sackbc_softc *sackbc_slot[N_KBC_SLOTS] = { NULL, NULL };*/ + +static int sackbc_match(struct device *, struct cfdata *, void *); +static void sackbc_attach(struct device *, struct device *, void *); +static int sackbc_cmdresponse( struct sackbc_softc *, int ); + +CFATTACH_DECL(sackbc, sizeof(struct sackbc_softc), sackbc_match, + sackbc_attach, NULL, NULL); + +/* XXX should not be here */ +#define KBC_DEVCMD_ACK 0xfa +#define KBC_DEVCMD_RESEND 0xfe + +#define KBD_DELAY DELAY(8) + +/*#define SACKBCDEBUG*/ + +#ifdef SACKBCDEBUG +#define DPRINTF(arg) printf arg +#else +#define DPRINTF(arg) +#endif + +static void sackbc_poll_cmd1( struct sackbc_softc *, struct pckbc_devcmd * ); + + + +static int +sackbc_match(struct device *parent, struct cfdata *cf, void *aux) +{ + struct sa1111_attach_args *aa = (struct sa1111_attach_args *)aux; + + switch( aa->sa_addr ){ + case SACC_KBD0: case SACC_KBD1: + return 1; + } + return 0; +} + +#if 0 +static int +sackbc_txint( void *cookie ) +{ + struct sackbc_softc *sc = cookie; + + bus_space_read_4( sc->iot, sc->ioh, SACCKBD_STAT ); + + return 0; +} +#endif + +static int +sackbc_rxint( void *cookie ) +{ + struct sackbc_softc *sc = cookie; + int stat, code=-1; + + stat = bus_space_read_4( sc->iot, sc->ioh, SACCKBD_STAT ); + DPRINTF(( "sackbc_rxint stat=%x\n", stat )); + if( stat & KBDSTAT_RXF ){ + code = bus_space_read_4( sc->iot, sc->ioh, SACCKBD_DATA ); + + if( sc->polling ){ + sc->poll_data = code; + sc->poll_stat = stat; + } + else if (CMD_IN_QUEUE(sc) && sackbc_cmdresponse(sc, code)) + ; + else if( sc->inputhandler ){ + (* sc->inputhandler)( sc->inputarg, code ); + } + return 1; + } + + return 0; +} + +static int +sackbcprint(void *aux, const char *pnp) +{ + return (QUIET); +} + +static void +sackbc_setup_intrhandler(struct sackbc_softc *sc) +{ + if( !(sc->polling) && sc->ih_rx==NULL ){ + sc->ih_rx = sacc_intr_establish( + (sacc_chipset_tag_t *)(sc->dev.dv_parent), + sc->intr+1, IST_EDGE_RAISE, IPL_TTY, sackbc_rxint, sc ); + if( sc->ih_rx == NULL ){ + printf( "%s: can't establish interrupt\n", + sc->dev.dv_xname ); + } + } +} + +static void +sackbc_disable_intrhandler( struct sackbc_softc *sc ) +{ + if( sc->polling && sc->ih_rx ){ + sacc_intr_disestablish( + (sacc_chipset_tag_t *)(sc->dev.dv_parent), + sc->ih_rx ); + sc->ih_rx = NULL; + } +} + +static int +sackbc_submatch(struct device *parent, struct cfdata *cf, void *aux) +{ + struct pckbc_attach_args *pa = aux; + + DPRINTF(( "slot = %d ", cf->cf_loc[SACKBCCF_SLOT] )); + + if( pa->pa_slot == PCKBCCF_SLOT_DEFAULT ) + pa->pa_slot = cf->cf_loc[SACKBCCF_SLOT]; + + return config_match(parent, cf, aux); +} + +static void +sackbc_attach(struct device *parent, struct device *self, void *aux) +{ + struct sackbc_softc *sc = (struct sackbc_softc *)self; + struct sacc_softc *psc = (struct sacc_softc *)parent; + struct sa1111_attach_args *aa = (struct sa1111_attach_args *)aux; + uint32_t tmp, clock_bit; + int i, found, intr; + + switch( aa->sa_addr ){ + case SACC_KBD0: clock_bit = (1<<6); intr = 21; break; + case SACC_KBD1: clock_bit = (1<<5); intr = 18; break; + default: + return; + } + + if( aa->sa_size <= 0 ) + aa->sa_size = SACCKBD_SIZE; + if( aa->sa_intr == SACCCF_INTR_DEFAULT ) + aa->sa_intr = intr; + + sc->iot = psc->sc_iot; + if( bus_space_subregion( psc->sc_iot, psc->sc_ioh, + aa->sa_addr, aa->sa_size, &sc->ioh ) ){ + printf( ": can't map subregion\n" ); + return; + } + + /* enable clock for PS/2 kbd or mouse */ + tmp = bus_space_read_4( psc->sc_iot, psc->sc_ioh, SACCSC_SKPCR ); + bus_space_write_4( psc->sc_iot, psc->sc_ioh, SACCSC_SKPCR, + tmp | clock_bit ); + + sc->ih_rx = NULL; + sc->intr = aa->sa_intr; + sc->inputhandler = NULL; + sc->subname = sc->dev.dv_xname; + + TAILQ_INIT(&sc->cmdqueue); + TAILQ_INIT(&sc->freequeue); + + for (i = 0; i < NCMD; i++) { + TAILQ_INSERT_TAIL(&sc->freequeue, &(sc->cmd[i]), next); + } + sc->polling = 0; + + tmp = bus_space_read_4( sc->iot, sc->ioh, SACCKBD_CR ); + bus_space_write_4( sc->iot, sc->ioh, SACCKBD_CR, tmp | KBDCR_ENA ); + + /* XXX: this is necessary to get keyboard working. but I don't know why */ + bus_space_write_4( sc->iot, sc->ioh, SACCKBD_CLKDIV, 2 ); + + tmp = bus_space_read_4( sc->iot, sc->ioh, SACCKBD_STAT ); + if( (tmp & KBDSTAT_ENA) == 0 ){ + printf("??? can't enable KBD controller\n"); + return; + } + + printf("\n"); + + { + struct pckbc_attach_args pa; + + pa.pa_tag = sc; + pa.pa_slot = PCKBCCF_SLOT_DEFAULT; /* Bogus */ + + found = (config_found_sm(self, &pa, + sackbcprint, sackbc_submatch) != NULL); + +#if 0 && NRND > 0 /* XXX: not yet */ + if (found && (t->t_slotdata[slot] != NULL)) + rnd_attach_source(&t->t_slotdata[slot]->rnd_source, + sc->subname[slot], RND_TYPE_TTY, 0); +#endif + } + +} + + +static inline int +sackbc_wait_output( struct sackbc_softc *sc ) +{ + u_int i, stat; + + for (i = 100000; i; i--){ + stat = bus_space_read_4(sc->iot, sc->ioh, SACCKBD_STAT); + delay(100); + if( stat & KBDSTAT_TXE) + return 1; + } + return 0; +} + +static int +sackbc_poll_data1( struct sackbc_softc *sc ) +{ + int i, s, stat, c = -1; + + s = spltty(); + + if (sc->polling){ + stat = sc->poll_stat; + c = sc->poll_data; + sc->poll_data = -1; + sc->poll_stat = -1; + if( stat >= 0 && + (stat & (KBDSTAT_RXF|KBDSTAT_STP)) == KBDSTAT_RXF ){ + splx(s); + return c; + } + } + + /* if 1 port read takes 1us (?), this polls for 100ms */ + for (i = 100000; i; i--) { + stat = bus_space_read_4(sc->iot, sc->ioh, SACCKBD_STAT); + if( (stat & (KBDSTAT_RXF|KBDSTAT_STP)) == KBDSTAT_RXF ){ + KBD_DELAY; + c = bus_space_read_4(sc->iot, sc->ioh, SACCKBD_DATA); + break; + } + } + + splx(s); + return (c); +} + +static int +sackbc_send_cmd( struct sackbc_softc *sc, int val ) +{ + if ( !sackbc_wait_output(sc) ) + return (0); + bus_space_write_1( sc->iot, sc->ioh, SACCKBD_DATA, val ); + return (1); +} + +#define sackbc_send_devcmd sackbc_send_cmd + +/* + * Clean up a command queue, throw away everything. + */ +static void +sackbc_cleanqueue( struct sackbc_softc *sc ) +{ + struct pckbc_devcmd *cmd; +#ifdef SACKBCDEBUG + int i; +#endif + + while ((cmd = TAILQ_FIRST(&sc->cmdqueue))) { + TAILQ_REMOVE(&sc->cmdqueue, cmd, next); +#ifdef SACKBCDEBUG + printf("sackbc_cleanqueue: removing"); + for (i = 0; i < cmd->cmdlen; i++) + printf(" %02x", cmd->cmd[i]); + printf("\n"); +#endif + TAILQ_INSERT_TAIL(&sc->freequeue, cmd, next); + } +} + +/* + * Timeout error handler: clean queues and data port. + * XXX could be less invasive. + */ +static void +sackbc_cleanup(void *self) +{ + struct sackbc_softc *sc = self; + int s; + + printf("sackbc: command timeout\n"); + + s = spltty(); + + sackbc_cleanqueue(sc); + + while (bus_space_read_4(sc->iot, sc->ioh, SACCKBD_STAT) & KBDSTAT_RXF) { + KBD_DELAY; + (void) bus_space_read_4(sc->iot, sc->ioh, SACCKBD_DATA); + } + + /* reset KBC? */ + + splx(s); +} + + +/* + * Pass command to device during normal operation. + * to be called at spltty() + */ +static void +sackbc_start( struct sackbc_softc *sc ) +{ + struct pckbc_devcmd *cmd = TAILQ_FIRST(&sc->cmdqueue); + + if (sc->polling) { + while(cmd){ + sackbc_poll_cmd1(sc, cmd); + if (cmd->status) + printf("sackbc_start: command error\n"); + + TAILQ_REMOVE(&sc->cmdqueue, cmd, next); + if (cmd->flags & KBC_CMDFLAG_SYNC) + wakeup(cmd); + else { + callout_stop(&sc->t_cleanup); + TAILQ_INSERT_TAIL(&sc->freequeue, cmd, next); + } + cmd = TAILQ_FIRST(&sc->cmdqueue); + } + return; + } + + if (!sackbc_send_devcmd(sc, cmd->cmd[cmd->cmdidx])) { + printf("sackbc_start: send error\n"); + /* XXX what now? */ + return; + } +} + +/* + * Handle command responses coming in asynchonously, + * return nonzero if valid response. + * to be called at spltty() + */ +static int +sackbc_cmdresponse( struct sackbc_softc *sc, int data) +{ + struct pckbc_devcmd *cmd = TAILQ_FIRST(&sc->cmdqueue); +#ifdef DIAGNOSTIC + if (!cmd) + panic("sackbc_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 { + printf("pckbc: cmd failed\n"); + 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(&sc->cmdqueue, cmd, next); + if (cmd->flags & KBC_CMDFLAG_SYNC) + wakeup(cmd); + else { + callout_stop(&sc->t_cleanup); + TAILQ_INSERT_TAIL(&sc->freequeue, cmd, next); + } + if (!CMD_IN_QUEUE(sc)) + return (1); +restart: + sackbc_start(sc); + return (1); +} + +/* + * Pass command to device, poll for ACK and data. + * to be called at spltty() + */ +static void +sackbc_poll_cmd1( struct sackbc_softc *sc, struct pckbc_devcmd *cmd ) +{ + int i, c = 0; + + while (cmd->cmdidx < cmd->cmdlen) { + DPRINTF((" tx: %x ", cmd->cmd[cmd->cmdidx])); + if (!sackbc_send_devcmd(sc, cmd->cmd[cmd->cmdidx])) { + printf("sackbc_cmd: send error\n"); + cmd->status = EIO; + return; + } + delay(1000); + for (i = 10; i; i--) { /* 1s ??? */ + c = sackbc_poll_data1(sc); + if (c != -1){ + DPRINTF((" rx: %x", c )); + break; + } + } + + if (c == KBC_DEVCMD_ACK) { + cmd->cmdidx++; + continue; + } + if (c == KBC_DEVCMD_RESEND) { + DPRINTF(("sackbc_cmd: RESEND\n")); + + if (cmd->retries++ < 5) + continue; + else { + DPRINTF(("sackbc: cmd failed\n")); + + cmd->status = EIO; + return; + } + } + if (c == -1) { + DPRINTF(("pckbc_cmd: timeout\n")); + + cmd->status = EIO; + return; + } + DPRINTF(("pckbc_cmd: lost 0x%x\n", c)); + + } + + while (cmd->responseidx < cmd->responselen) { + if (cmd->flags & KBC_CMDFLAG_SLOW) + i = 100; /* 10s ??? */ + else + i = 10; /* 1s ??? */ + while (i--) { + c = sackbc_poll_data1(sc); + if (c != -1){ + DPRINTF((" resp: %x", c)); + break; + } + } + if (c == -1) { + DPRINTF(("pckbc_cmd: no response")); + + cmd->status = ETIMEDOUT; + return; + } else + cmd->response[cmd->responseidx++] = c; + } + DPRINTF(("\n")); +} + + +/* + * Glue functions for pckbd on sackbc. + * These functions emulate those in dev/ic/pckbc.c. + * + */ + +void +pckbc_set_inputhandler( pckbc_tag_t self, pckbc_slot_t slot, + pckbc_inputfcn func, void *arg, char *name) +{ + struct sackbc_softc *sc = (struct sackbc_softc *) self; + + if( sc == NULL ) + return; + + DPRINTF(( "set_inputhandler %p %p\n", func, arg )); + + sc->inputhandler = func; + sc->inputarg = arg; + sc->subname = name; + + sackbc_setup_intrhandler(sc); +} + + +/* 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; +struct sackbc_softc *sc = (struct sackbc_softc *) self; + + if( sc == NULL ) + return EINVAL; + + if ((len > 4) || (responselen > 4)) + return EINVAL; + + memset(&nc, 0, sizeof(nc)); + memcpy(nc.cmd, cmd, len); + nc.cmdlen = len; + nc.responselen = responselen; + nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0); + + sackbc_poll_cmd1(sc, &nc); + + if (nc.status == 0 && respbuf) + memcpy(respbuf, nc.response, responselen); + + return (nc.status); +} + + +/* + * switch scancode translation on / off + * return nonzero on success + */ +int +pckbc_xt_translation(pckbc_tag_t self, pckbc_slot_t slot, int on) +{ + /* KBD/Mouse controller doesn't have scancode translation */ + return !on; +} + +void +pckbc_slot_enable(pckbc_tag_t self, pckbc_slot_t slot, int on) +{ +#if 0 + struct sackbc_softc *sc = (struct sackbc_softc *) self; + int cmd; + + cmd = on ? KBC_KBDENABLE : KBC_KBDDISABLE; + if ( !sackbc_send_cmd(sc, cmd ) ) + printf("sackbc_slot_enable(%d) failed\n", on); +#endif +} + + +void +pckbc_flush(pckbc_tag_t self, pckbc_slot_t slot) +{ + struct sackbc_softc *sc = (struct sackbc_softc *)self; + + (void) sackbc_poll_data1(sc); +} + +#if 0 +int +sackbc_poll_data( struct sackbc_softc *sc ) +{ + struct pckbc_internal *t = self; + struct pckbc_slotdata *q = t->t_slotdata[slot]; + int c; + + c = pckbc_poll_data1(t, slot, t->t_haveaux); + if (c != -1 && q && CMD_IN_QUEUE(q)) { + /* we jumped into a running command - try to + deliver the response */ + if (pckbc_cmdresponse(t, slot, c)) + return (-1); + } + return (c); +} +#endif + +void +pckbc_set_poll(pckbc_tag_t self, pckbc_slot_t slot, int on) +{ + struct sackbc_softc *sc = (struct sackbc_softc *)self; + int s; + + s = spltty(); + + if( sc->polling != on ){ + + sc->polling = on; + + if( on ){ + sc->poll_data = sc->poll_stat = -1; + sackbc_disable_intrhandler(sc); + } + else { + /* + * 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. + */ + sackbc_rxint(sc); + sackbc_setup_intrhandler(sc); + } + } + splx(s); +} + +/* + * 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 sackbc_softc *sc = (struct sackbc_softc *)self; + struct pckbc_devcmd *nc; + int s, isactive, res = 0; + + if ( sc == NULL || (len > 4) || (responselen > 4) ) + return (EINVAL); + + s = spltty(); + nc = TAILQ_FIRST(&sc->freequeue); + if (nc) { + TAILQ_REMOVE(&sc->freequeue, nc, next); + } + splx(s); + if (!nc) + return (ENOMEM); + + memset(nc, 0, sizeof(*nc)); + memcpy(nc->cmd, cmd, len); + nc->cmdlen = len; + nc->responselen = responselen; + nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0); + + s = spltty(); + + if (sc->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. + */ + sackbc_cleanqueue(sc); + } + + isactive = CMD_IN_QUEUE(sc); + TAILQ_INSERT_TAIL(&sc->cmdqueue, nc, next); + if (!isactive) + sackbc_start(sc); + + if (sc->polling) + res = (sync ? nc->status : 0); + else if (sync) { + if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) { + TAILQ_REMOVE(&sc->cmdqueue, nc, next); + sackbc_cleanup(sc); + } else + res = nc->status; + } else + callout_reset(&sc->t_cleanup, hz, sackbc_cleanup, sc); + + if (sync) { + if (respbuf) + memcpy(respbuf, nc->response, responselen); + TAILQ_INSERT_TAIL(&sc->freequeue, nc, next); + } + + splx(s); + + return (res); +} + +int +pckbc_poll_data(pckbc_tag_t self, pckbc_slot_t slot) +{ + struct sackbc_softc *sc = (struct sackbc_softc *)self; + int c; + + c = sackbc_poll_data1(sc); + if (c != -1 && CMD_IN_QUEUE(sc)) { + /* we jumped into a running command - try to + deliver the response */ + if (sackbc_cmdresponse(sc, c)) + return -1; + } + return (c); +}