diff options
Diffstat (limited to 'sys/arch/vax/dec')
-rw-r--r-- | sys/arch/vax/dec/files.dec | 6 | ||||
-rw-r--r-- | sys/arch/vax/dec/sii.c | 1798 | ||||
-rw-r--r-- | sys/arch/vax/dec/siireg.h | 235 | ||||
-rw-r--r-- | sys/arch/vax/dec/siivar.h | 71 |
4 files changed, 2109 insertions, 1 deletions
diff --git a/sys/arch/vax/dec/files.dec b/sys/arch/vax/dec/files.dec index fe2848d1163..1f81a9f177a 100644 --- a/sys/arch/vax/dec/files.dec +++ b/sys/arch/vax/dec/files.dec @@ -1,4 +1,4 @@ -# $OpenBSD: files.dec,v 1.5 2008/08/18 23:04:28 miod Exp $ +# $OpenBSD: files.dec,v 1.6 2008/08/18 23:19:22 miod Exp $ # $NetBSD: files.dec,v 1.4 1999/08/04 07:17:51 nisimura Exp $ # # Config file and device description for machine-independent @@ -14,3 +14,7 @@ file arch/vax/dec/wskbdmap_lk201.c lkkbd # VSxxx mouse file arch/vax/dec/vsms_ws.c lkms + +# DC7061 SII DSSI/SCSI controller +device sii: scsi +file arch/vax/dec/sii.c sii diff --git a/sys/arch/vax/dec/sii.c b/sys/arch/vax/dec/sii.c new file mode 100644 index 00000000000..0a677fa557e --- /dev/null +++ b/sys/arch/vax/dec/sii.c @@ -0,0 +1,1798 @@ +/* $OpenBSD: sii.c,v 1.1 2008/08/18 23:19:22 miod Exp $ */ +/* $NetBSD: sii.c,v 1.42 2000/06/02 20:20:29 mhitch Exp $ */ +/* + * Copyright (c) 2008 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)sii.c 8.2 (Berkeley) 11/30/93 + * + * from: Header: /sprite/src/kernel/dev/ds3100.md/RCS/devSII.c, + * v 9.2 89/09/14 13:37:41 jhh Exp $ SPRITE (DECWRL)"; + */ + +/* + * SCSI interface driver + */ +#include <sys/param.h> +#include <sys/buf.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/systm.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/scsi_message.h> +#ifdef DEBUG +#include <scsi/scsi_disk.h> +#endif + +#include <vax/dec/siireg.h> /* device registers */ +#include <vax/dec/siivar.h> /* softc and prototypes */ + +/* XXX not in scsi/scsi_message.h */ +#define MSG_EXT_MODIFY_DATA_PTR 0x00 + +struct cfdriver sii_cd = { + NULL, "sii", DV_DULL +}; + +/* + * MACROS for timing out spin loops. + * + * Wait until expression is true. + * + * Control register bits can change at any time so when the CPU + * reads a register, the bits might change and + * invalidate the setup and hold times for the CPU. + * This macro reads the register twice to be sure the value is stable. + * + * args: var - variable to save control register contents + * reg - control register to read + * expr - expression to spin on + * spincount - maximum number of times through the loop + * cntr - variable for number of tries + */ +#define SII_WAIT_UNTIL(var, reg, expr, spincount, cntr) { \ + u_int tmp = reg; \ + for (cntr = 0; cntr < spincount; cntr++) { \ + while (tmp != (var = reg)) \ + tmp = var; \ + if (expr) \ + break; \ + if (cntr >= 100) \ + DELAY(100); \ + } \ + } + +#ifdef DEBUG +int sii_debug = 1; +int sii_debug_cmd; +int sii_debug_bn; +int sii_debug_sz; +#define NLOG 16 +struct sii_log { + u_short cstat; + u_short dstat; + u_short comm; + u_short msg; + int rlen; + int dlen; + int target; +} sii_log[NLOG], *sii_logp = sii_log; +#endif + +#define NORESET 0 +#define RESET 1 +#define NOWAIT 0 +#define WAIT 1 + +#if 0 +/* + * XXX That hardcoded 7 below implies SII_NCMD <= 7, which is not the case... + * XXX Thankfully all uses of SII_BUF_ADDR are in #if 0 sections (and this + * XXX should be reasonably safe as long as we use 7 as the host id). + */ +/* + * Define a safe address in the SCSI buffer for doing status & message DMA + * XXX why not add another field to softc? + */ +#define SII_BUF_ADDR(sc) (SII_MAX_DMA_XFER_LENGTH * 7 * 2) +#endif + +/* + * Forward references + */ + +void sii_Reset(struct sii_softc *sc, int resetbus); +void sii_StartCmd(struct sii_softc *sc, int target); +void sii_CmdDone(struct sii_softc *sc, int target, int error); +void sii_DoIntr(struct sii_softc *sc, u_int dstat); +void sii_StateChg(struct sii_softc *sc, u_int cstat); +int sii_GetByte(SIIRegs *regs, int phase, int ack); +void sii_DoSync(struct sii_softc *, State *); +void sii_StartDMA(SIIRegs *regs, int phase, u_int dmaAddr, int size); +int sii_scsi_cmd(struct scsi_xfer *); +#ifdef DEBUG +void sii_DumpLog(void); +#endif + +struct scsi_adapter sii_scsiswitch = { + sii_scsi_cmd, + minphys, + NULL, + NULL, + NULL +}; + +struct scsi_device sii_scsidev = { + NULL, + NULL, + NULL, + NULL +}; + +void +sii_attach(sc) + struct sii_softc *sc; +{ + struct scsibus_attach_args saa; + int i; + + sc->sc_target = -1; /* no command active */ + + /* + * Give each target its own DMA buffer region. + * Make it big enough for 2 max transfers so we can ping pong buffers + * while we copy the data. + */ + for (i = 0; i < SII_NCMD; i++) { + sc->sc_st[i].dmaAddr[0] = + SII_MAX_DMA_XFER_LENGTH * 2 * i; + sc->sc_st[i].dmaAddr[1] = sc->sc_st[i].dmaAddr[0] + + SII_MAX_DMA_XFER_LENGTH; + } + + sii_Reset(sc, RESET); + printf("\n"); + + /* + * fill in the prototype scsi_link. + */ + sc->sc_link.adapter = &sii_scsiswitch; + sc->sc_link.adapter_buswidth = 8; + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = sc->sc_regs->id & SII_IDMSK; + sc->sc_link.device = &sii_scsidev; + sc->sc_link.openings = 1; /* driver can't queue requests yet */ + + /* + * Now try to attach all the sub-devices + */ + bzero(&saa, sizeof(saa)); + saa.saa_sc_link = &sc->sc_link; + config_found(&sc->sc_dev, &saa, scsiprint); +} + +/* + * Start activity on a SCSI device. + * We maintain information on each device separately since devices can + * connect/disconnect during an operation. + */ + +int +sii_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + int target = xs->sc_link->target; + struct sii_softc *sc = xs->sc_link->adapter_softc; + int s; + int count; + + s = splbio(); + if (sc->sc_cmd[target]) { + splx(s); +#ifdef DEBUG + printf("[busy at start]\n"); +#endif + return (TRY_AGAIN_LATER); + } + /* + * Build a ScsiCmd for this command and start it. + */ + sc->sc_xs[target] = xs; + sc->sc_cmd[target] = &sc->sc_cmd_fake[target]; /* XXX */ + sc->sc_cmd[target]->unit = 0; + sc->sc_cmd[target]->flags = 0; + sc->sc_cmd[target]->buflen = xs->datalen; + sc->sc_cmd[target]->buf = xs->data; + sc->sc_cmd[target]->cmdlen = xs->cmdlen; + sc->sc_cmd[target]->cmd = (u_char *)xs->cmd; + sc->sc_cmd[target]->lun = xs->sc_link->lun; + sii_StartCmd(sc, target); + splx(s); + + if ((xs->flags & ITSDONE) != 0) + return (COMPLETE); + if ((xs->flags & SCSI_POLL) == 0) + return (SUCCESSFULLY_QUEUED); + count = xs->timeout; + while (count) { + s = splbio(); +#if 0 + (void)sii_intr(sc); +#else +{ + u_int dstat; + + /* + * Find which controller caused the interrupt. + */ + dstat = sc->sc_regs->dstat; + if (dstat & (SII_CI | SII_DI)) + sii_DoIntr(sc, dstat); +} +#endif + splx(s); + if ((xs->flags & ITSDONE) != 0) + break; + /* XXX schedule another command? */ + DELAY(1000); + --count; + } + if ((xs->flags & ITSDONE) != 0) + return (COMPLETE); + xs->error = XS_TIMEOUT; + xs->flags |= ITSDONE; + s = splbio(); + scsi_done(xs); + splx(s); + return (COMPLETE); +} + +/* + * Check to see if any SII chips have pending interrupts + * and process as appropriate. + */ +int +sii_intr(xxxsc) + void *xxxsc; +{ + struct sii_softc *sc = xxxsc; + u_int dstat; + + /* + * Find which controller caused the interrupt. + */ + dstat = sc->sc_regs->dstat; + if (dstat & (SII_CI | SII_DI)) { + sii_DoIntr(sc, dstat); + return (1); + } + + return (0); +} + +/* + * Reset the SII chip and do a SCSI reset if 'reset' is true. + * NOTE: if !cold && reset, should probably probe for devices + * since a SCSI bus reset will set UNIT_ATTENTION. + */ +void +sii_Reset(sc, reset) + struct sii_softc* sc; + int reset; /* TRUE => reset SCSI bus */ +{ + SIIRegs *regs = sc->sc_regs; + +#ifdef DEBUG + if (sii_debug > 1) + printf("sii: RESET\n"); +#endif + /* + * Reset the SII chip. + */ + regs->comm = SII_CHRESET; + /* + * Set arbitrated bus mode. + */ + regs->csr = SII_HPM; + /* + * Set host adapter ID. + */ + regs->id = SII_ID_IO | sc->sc_hostid; + /* + * Enable SII to drive the SCSI bus. + */ + regs->dictrl = SII_PRE; + regs->dmctrl = 0; + + if (reset) { + int i; + + /* + * Assert SCSI bus reset for at least 25 Usec to clear the + * world. SII_DO_RST is self clearing. + * Delay 250 ms before doing any commands. + */ + regs->comm = SII_DO_RST; + DELAY(250000); + + /* rearbitrate synchronous offset */ + for (i = 0; i < SII_NCMD; i++) + sc->sc_st[i].dmaReqAck = 0; + } + + /* + * Clear any pending interrupts from the reset. + */ + regs->cstat = regs->cstat; + regs->dstat = regs->dstat; + /* + * Set up SII for arbitrated bus mode, SCSI parity checking, + * Reselect Enable, and Interrupt Enable. + */ + regs->csr = SII_HPM | SII_RSE | SII_PCE | SII_IE; +} + +/* + * Start a SCSI command by sending the cmd data + * to a SCSI controller via the SII. + * Call the device done procedure if it can't be started. + * NOTE: we should be called with interrupts disabled. + */ +void +sii_StartCmd(sc, target) + struct sii_softc *sc; /* which SII to use */ + int target; /* which command to start */ +{ + SIIRegs *regs; + ScsiCmd *scsicmd; + State *state; + u_int status; + int error, retval; + + splassert(IPL_BIO); + + /* if another command is currently in progress, just wait */ + if (sc->sc_target >= 0) + return; + + /* initialize state information for this command */ + scsicmd = sc->sc_cmd[target]; + state = &sc->sc_st[target]; + state->flags = FIRST_DMA; + state->prevComm = 0; + state->dmalen = 0; + state->dmaCurPhase = -1; + state->dmaPrevPhase = -1; + state->dmaBufIndex = 0; + state->cmd = scsicmd->cmd; + state->cmdlen = scsicmd->cmdlen; + if ((state->buflen = scsicmd->buflen) == 0) { + state->dmaDataPhase = -1; /* illegal phase. shouldn't happen */ + state->buf = (char *)0; + } else { + state->buf = scsicmd->buf; + } + +#ifdef DEBUG + if (sii_debug > 1) { + printf("sii_StartCmd: %s target %d cmd 0x%x addr %p size %d DMA %d\n", + sc->sc_dev.dv_xname, + target, scsicmd->cmd[0], scsicmd->buf, scsicmd->buflen, + state->dmaDataPhase); + } + sii_debug_cmd = scsicmd->cmd[0]; + if (scsicmd->cmd[0] == READ_BIG || + scsicmd->cmd[0] == WRITE_BIG) { + sii_debug_bn = (scsicmd->cmd[2] << 24) | + (scsicmd->cmd[3] << 16) | + (scsicmd->cmd[4] << 8) | + scsicmd->cmd[5]; + sii_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8]; + } else { + sii_debug_bn = 0; + sii_debug_sz = 0; + } +#endif + + /* try to select the target */ + regs = sc->sc_regs; + + /* + * Another device may have selected us; in which case, + * this command will be restarted later. + */ + if ((status = regs->dstat) & (SII_CI | SII_DI)) { + sii_DoIntr(sc, status); + return; + } + + sc->sc_target = target; +#if 0 + /* seem to have problems with synchronous transfers */ + if (scsicmd->flags & SCSICMD_USE_SYNC) { + printf("sii_StartCmd: doing extended msg\n"); /* XXX */ + /* + * Setup to send both the identify message and the synchronous + * data transfer request. + */ + sc->sc_buf[0] = MSG_IDENTIFYFLAG | MSG_IDENTIFY_DISCFLAG; + sc->sc_buf[1] = MSG_EXTENDED; + sc->sc_buf[2] = MSG_EXT_SDTR_LEN; + sc->sc_buf[3] = MSG_EXT_SDTR; + sc->sc_buf[4] = 0; + sc->sc_buf[5] = 3; /* maximum SII chip supports */ + + state->dmaCurPhase = SII_MSG_OUT_PHASE, + state->dmalen = 6; + sc->sii_copytobuf(sc, sc->sc_buf, SII_BUF_ADDR(sc), 6); + regs->slcsr = target; + regs->dmctrl = state->dmaReqAck; + regs->dmaddrl = SII_BUF_ADDR(sc); + regs->dmaddrh = SII_BUF_ADDR(sc) >> 16; + regs->dmlotc = 6; + regs->comm = SII_DMA | SII_INXFER | SII_SELECT | SII_ATN | + SII_CON | SII_MSG_OUT_PHASE; + } else +#endif + { + /* do a chained, select with ATN and programmed I/O command */ + regs->data = MSG_IDENTIFYFLAG | MSG_IDENTIFY_DISCFLAG | + scsicmd->lun; + regs->slcsr = target; + regs->dmctrl = state->dmaReqAck; + regs->comm = SII_INXFER | SII_SELECT | SII_ATN | SII_CON | + SII_MSG_OUT_PHASE; + } + + /* + * Wait for something to happen + * (should happen soon or we would use interrupts). + */ + SII_WAIT_UNTIL(status, regs->cstat, status & (SII_CI | SII_DI), + SII_WAIT_COUNT/4, retval); + + /* check to see if we are connected OK */ + if ((status & (SII_RST | SII_SCH | SII_STATE_MSK)) == + (SII_SCH | SII_CON)) { + regs->cstat = status; + +#ifdef DEBUG + sii_logp->target = target; + sii_logp->cstat = status; + sii_logp->dstat = 0; + sii_logp->comm = regs->comm; + sii_logp->msg = -1; + sii_logp->rlen = state->buflen; + sii_logp->dlen = state->dmalen; + if (++sii_logp >= &sii_log[NLOG]) + sii_logp = sii_log; +#endif + + /* wait a short time for command phase */ + SII_WAIT_UNTIL(status, regs->dstat, status & SII_MIS, + SII_WAIT_COUNT, retval); +#ifdef DEBUG + if (sii_debug > 2) + printf("sii_StartCmd: ds %x cnt %d\n", status, retval); +#endif + if ((status & (SII_CI | SII_MIS | SII_PHASE_MSK)) != + (SII_MIS | SII_CMD_PHASE)) { + printf("sii_StartCmd: timeout cs %x ds %x cnt %d\n", + regs->cstat, status, retval); /* XXX */ + /* process interrupt or continue until it happens */ + if (status & (SII_CI | SII_DI)) + sii_DoIntr(sc, status); + return; + } + regs->dstat = SII_DNE; /* clear Msg Out DMA done */ + + /* send command data */ + sc->sii_copytobuf(sc, state->cmd, state->dmaAddr[0], + state->cmdlen); + sii_StartDMA(regs, state->dmaCurPhase = SII_CMD_PHASE, + state->dmaAddr[0], state->dmalen = scsicmd->cmdlen); + + /* wait a little while for DMA to finish */ + SII_WAIT_UNTIL(status, regs->dstat, status & (SII_CI | SII_DI), + SII_WAIT_COUNT, retval); +#ifdef DEBUG + if (sii_debug > 2) + printf("sii_StartCmd: ds %x, cnt %d\n", status, retval); +#endif + if (status & (SII_CI | SII_DI)) + sii_DoIntr(sc, status); +#ifdef DEBUG + if (sii_debug > 2) + printf("sii_StartCmd: DONE ds %x\n", regs->dstat); +#endif + return; + } + + /* + * Another device may have selected us; in which case, + * this command will be restarted later. + */ + if (status & (SII_CI | SII_DI)) { + sii_DoIntr(sc, regs->dstat); + return; + } + + /* + * Disconnect if selection command still in progress. + */ + if (status & SII_SIP) { + error = XS_SELTIMEOUT; /* device didn't respond */ + regs->comm = SII_DISCON; + SII_WAIT_UNTIL(status, regs->cstat, + !(status & (SII_CON | SII_SIP)), + SII_WAIT_COUNT, retval); + } else + error = XS_BUSY; /* couldn't get the bus */ +#ifdef DEBUG + if (sii_debug > 1) + printf("sii_StartCmd: Couldn't select target %d error %d\n", + target, error); +#endif + sc->sc_target = -1; + regs->cstat = 0xffff; + regs->dstat = 0xffff; + regs->comm = 0; + sii_CmdDone(sc, target, error); +} + +/* + * Process interrupt conditions. + */ +void +sii_DoIntr(sc, dstat) + struct sii_softc *sc; + u_int dstat; +{ + SIIRegs *regs = sc->sc_regs; + State *state; + u_int cstat; + int i, msg; + u_int comm; + +again: + comm = regs->comm; + +#ifdef DEBUG + if (sii_debug > 3) + printf("sii_DoIntr: cs %x, ds %x cm %x ", + regs->cstat, dstat, comm); + sii_logp->target = sc->sc_target; + sii_logp->cstat = regs->cstat; + sii_logp->dstat = dstat; + sii_logp->comm = comm; + sii_logp->msg = -1; + if (sc->sc_target >= 0) { + sii_logp->rlen = sc->sc_st[sc->sc_target].buflen; + sii_logp->dlen = sc->sc_st[sc->sc_target].dmalen; + } else { + sii_logp->rlen = 0; + sii_logp->dlen = 0; + } + if (++sii_logp >= &sii_log[NLOG]) + sii_logp = sii_log; +#endif + + regs->dstat = dstat; /* acknowledge everything */ + + if (dstat & SII_CI) { + /* deglitch cstat register */ + msg = regs->cstat; + while (msg != (cstat = regs->cstat)) + msg = cstat; + regs->cstat = cstat; /* acknowledge everything */ +#ifdef DEBUG + if (sii_logp > sii_log) + sii_logp[-1].cstat = cstat; + else + sii_log[NLOG - 1].cstat = cstat; +#endif + + /* check for a BUS RESET */ + if (cstat & SII_RST) { + printf("%s: SCSI bus reset\n", sc->sc_dev.dv_xname); + /* need to flush disconnected commands */ + for (i = 0; i < SII_NCMD; i++) { + if (!sc->sc_cmd[i]) + continue; + sii_CmdDone(sc, i, XS_DRIVER_STUFFUP /* EIO */); + } + /* rearbitrate synchronous offset */ + for (i = 0; i < SII_NCMD; i++) + sc->sc_st[i].dmaReqAck = 0; + sc->sc_target = -1; + return; + } + +#ifdef notdef + /* + * Check for a BUS ERROR. + * According to DEC, this feature doesn't really work + * and to just clear the bit if it's set. + */ + if (cstat & SII_BER) { + regs->cstat = SII_BER; + } +#endif + + /* check for state change */ + if (cstat & SII_SCH) { + sii_StateChg(sc, cstat); + comm = regs->comm; + } + } + + /* check for DMA completion */ + if (dstat & SII_DNE) { + u_int dma; + char *buf; + + /* + * There is a race condition with SII_SCH. There is a short + * window between the time a SII_SCH is seen after a disconnect + * and when the SII_SCH is cleared. A reselect can happen + * in this window and we will clear the SII_SCH without + * processing the reconnect. + */ + if (sc->sc_target < 0) { + cstat = regs->cstat; + printf("%s: target %d DNE?? dev %d,%d cs %x\n", + sc->sc_dev.dv_xname, sc->sc_target, + regs->slcsr, regs->destat, + cstat); /* XXX */ + if (cstat & SII_DST) { + sc->sc_target = regs->destat; + state = &sc->sc_st[sc->sc_target]; + state->prevComm = 0; + } else + panic("sc_target 1"); + } + state = &sc->sc_st[sc->sc_target]; + /* check for a PARITY ERROR */ + if (dstat & SII_IPE) { + state->flags |= PARITY_ERR; + printf("%s: Parity error\n", sc->sc_dev.dv_xname); + goto abort; + } + /* dmalen = amount left to transfer, i = amount transfered */ + i = state->dmalen; + state->dmalen = 0; + state->dmaCurPhase = -1; +#ifdef DEBUG + if (sii_debug > 4) { + printf("DNE: amt %d ", i); + if (!(dstat & SII_TCZ)) + printf("no TCZ?? (%d) ", regs->dmlotc); + } else if (!(dstat & SII_TCZ)) { + printf("%s: device %d: no TCZ?? (%d)\n", + sc->sc_dev.dv_xname, sc->sc_target, regs->dmlotc); + sii_DumpLog(); /* XXX */ + } +#endif + switch (comm & SII_PHASE_MSK) { + case SII_CMD_PHASE: + state->cmdlen -= i; + break; + + case SII_DATA_IN_PHASE: + /* check for more data for the same phase */ + dma = state->dmaAddr[state->dmaBufIndex]; + buf = state->buf; + state->buf += i; + state->buflen -= i; + if (state->buflen > 0 && !(dstat & SII_MIS)) { + int len; + + /* start reading next chunk */ + len = state->buflen; + if (len > SII_MAX_DMA_XFER_LENGTH) + len = SII_MAX_DMA_XFER_LENGTH; + state->dmaBufIndex = !state->dmaBufIndex; + sii_StartDMA(regs, + state->dmaCurPhase = SII_DATA_IN_PHASE, + state->dmaAddr[state->dmaBufIndex], + state->dmaCnt = state->dmalen = len); + dstat &= ~(SII_IBF | SII_TBE); + } + /* copy in the data */ + sc->sii_copyfrombuf(sc, dma, buf, i); + break; + + case SII_DATA_OUT_PHASE: + state->dmaBufIndex = !state->dmaBufIndex; + state->buf += i; + state->buflen -= i; + + /* check for more data for the same phase */ + if (state->buflen <= 0 || (dstat & SII_MIS)) + break; + + /* start next chunk */ + i = state->buflen; + if (i > SII_MAX_DMA_XFER_LENGTH) { + sii_StartDMA(regs, state->dmaCurPhase = + SII_DATA_OUT_PHASE, + state->dmaAddr[state->dmaBufIndex], + state->dmaCnt = state->dmalen = + SII_MAX_DMA_XFER_LENGTH); + /* prepare for next chunk */ + i -= SII_MAX_DMA_XFER_LENGTH; + if (i > SII_MAX_DMA_XFER_LENGTH) + i = SII_MAX_DMA_XFER_LENGTH; + sc->sii_copytobuf(sc, (u_char *)(state->buf + + SII_MAX_DMA_XFER_LENGTH), + state->dmaAddr[!state->dmaBufIndex], i); + } else { + sii_StartDMA(regs, state->dmaCurPhase = + SII_DATA_OUT_PHASE, + state->dmaAddr[state->dmaBufIndex], + state->dmaCnt = state->dmalen = i); + } + dstat &= ~(SII_IBF | SII_TBE); + } + } + + /* check for phase change or another MsgIn/Out */ + if (dstat & (SII_MIS | SII_IBF | SII_TBE)) { + /* + * There is a race condition with SII_SCH. There is a short + * window between the time a SII_SCH is seen after a disconnect + * and when the SII_SCH is cleared. A reselect can happen + * in this window and we will clear the SII_SCH without + * processing the reconnect. + */ + if (sc->sc_target < 0) { + cstat = regs->cstat; + printf("%s: target %d MIS?? dev %d,%d cs %x ds %x\n", + sc->sc_dev.dv_xname, sc->sc_target, + regs->slcsr, regs->destat, + cstat, dstat); /* XXX */ + if (cstat & SII_DST) { + sc->sc_target = regs->destat; + state = &sc->sc_st[sc->sc_target]; + state->prevComm = 0; + } else { +#ifdef DEBUG + sii_DumpLog(); +#endif + panic("sc_target 2"); + } + } + state = &sc->sc_st[sc->sc_target]; + switch (dstat & SII_PHASE_MSK) { + case SII_CMD_PHASE: + if (state->dmaPrevPhase >= 0) { + /* restart DMA after disconnect/reconnect */ + if (state->dmaPrevPhase != SII_CMD_PHASE) { + printf("%s: device %d: DMA reselect phase doesn't match\n", + sc->sc_dev.dv_xname, sc->sc_target); + goto abort; + } + state->dmaCurPhase = SII_CMD_PHASE; + state->dmaPrevPhase = -1; + regs->dmaddrl = state->dmaAddrL; + regs->dmaddrh = state->dmaAddrH; + regs->dmlotc = state->dmaCnt; + if (state->dmaCnt & 1) + regs->dmabyte = state->dmaByte; + regs->comm = SII_DMA | SII_INXFER | + (comm & SII_STATE_MSK) | SII_CMD_PHASE; +#ifdef DEBUG + if (sii_debug > 4) + printf("Cmd dcnt %d dadr %x ", + state->dmaCnt, + (state->dmaAddrH << 16) | + state->dmaAddrL); +#endif + } else { + /* send command data */ + i = state->cmdlen; + if (i == 0) { + printf("%s: device %d: cmd count exceeded\n", + sc->sc_dev.dv_xname, sc->sc_target); + goto abort; + } + sc->sii_copytobuf(sc, state->cmd, + state->dmaAddr[0], i); + sii_StartDMA(regs, state->dmaCurPhase = + SII_CMD_PHASE, state->dmaAddr[0], + state->dmaCnt = state->dmalen = i); + } + /* wait a short time for XFER complete */ + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & (SII_CI | SII_DI), SII_WAIT_COUNT, i); + if (dstat & (SII_CI | SII_DI)) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt %d\n", i); + else if (sii_debug > 0) + printf("sii_DoIntr: cmd wait ds %x cnt %d\n", + dstat, i); +#endif + goto again; + } + break; + + case SII_DATA_IN_PHASE: + case SII_DATA_OUT_PHASE: + if (state->cmdlen > 0) { + printf("%s: device %d: cmd 0x%x: command data not all sent (%d) 1\n", + sc->sc_dev.dv_xname, sc->sc_target, + sc->sc_cmd[sc->sc_target]->cmd[0], + state->cmdlen); + state->cmdlen = 0; +#ifdef DEBUG + sii_DumpLog(); +#endif + } + if (state->dmaPrevPhase >= 0) { + /* restart DMA after disconnect/reconnect */ + if (state->dmaPrevPhase != + (dstat & SII_PHASE_MSK)) { + printf("%s: device %d: DMA reselect phase doesn't match\n", + sc->sc_dev.dv_xname, sc->sc_target); + goto abort; + } + state->dmaCurPhase = state->dmaPrevPhase; + state->dmaPrevPhase = -1; + regs->dmaddrl = state->dmaAddrL; + regs->dmaddrh = state->dmaAddrH; + regs->dmlotc = state->dmaCnt; + if (state->dmaCnt & 1) + regs->dmabyte = state->dmaByte; + regs->comm = SII_DMA | SII_INXFER | + (comm & SII_STATE_MSK) | + state->dmaCurPhase; +#ifdef DEBUG + if (sii_debug > 4) + printf("Data %d dcnt %d dadr %x ", + state->dmaDataPhase, + state->dmaCnt, + (state->dmaAddrH << 16) | + state->dmaAddrL); +#endif + break; + } +#ifdef DEBUG + if (sii_debug > 4) { + printf("Data %d ", state->dmaDataPhase); + if (sii_debug > 5) + printf("\n"); + } +#endif + i = state->buflen; + if (i == 0) { + printf("%s: device %d: data count exceeded\n", + sc->sc_dev.dv_xname, sc->sc_target); + goto abort; + } + if (i > SII_MAX_DMA_XFER_LENGTH) + i = SII_MAX_DMA_XFER_LENGTH; + if ((dstat & SII_PHASE_MSK) == SII_DATA_IN_PHASE) { + sii_StartDMA(regs, + state->dmaCurPhase = SII_DATA_IN_PHASE, + state->dmaAddr[state->dmaBufIndex], + state->dmaCnt = state->dmalen = i); + break; + } + /* start first chunk */ + if (state->flags & FIRST_DMA) { + state->flags &= ~FIRST_DMA; + sc->sii_copytobuf(sc, (u_char *)state->buf, + state->dmaAddr[state->dmaBufIndex], i); + } + sii_StartDMA(regs, + state->dmaCurPhase = SII_DATA_OUT_PHASE, + state->dmaAddr[state->dmaBufIndex], + state->dmaCnt = state->dmalen = i); + i = state->buflen - SII_MAX_DMA_XFER_LENGTH; + if (i > 0) { + /* prepare for next chunk */ + if (i > SII_MAX_DMA_XFER_LENGTH) + i = SII_MAX_DMA_XFER_LENGTH; + sc->sii_copytobuf(sc, (u_char *)(state->buf + + SII_MAX_DMA_XFER_LENGTH), + state->dmaAddr[!state->dmaBufIndex], i); + } + break; + + case SII_STATUS_PHASE: + if (state->cmdlen > 0) { + printf("%s: device %d: cmd 0x%x: command data not all sent (%d) 2\n", + sc->sc_dev.dv_xname, sc->sc_target, + sc->sc_cmd[sc->sc_target]->cmd[0], + state->cmdlen); + state->cmdlen = 0; +#ifdef DEBUG + sii_DumpLog(); +#endif + } + + /* read amount transfered if DMA didn't finish */ + if (state->dmalen > 0) { + i = state->dmalen - regs->dmlotc; + state->dmalen = 0; + state->dmaCurPhase = -1; + regs->dmlotc = 0; + regs->comm = comm & + (SII_STATE_MSK | SII_PHASE_MSK); + regs->dstat = SII_DNE; +#ifdef DEBUG + if (sii_debug > 4) + printf("DMA amt %d ", i); +#endif + switch (comm & SII_PHASE_MSK) { + case SII_DATA_IN_PHASE: + /* copy in the data */ + sc->sii_copyfrombuf(sc, + state->dmaAddr[state->dmaBufIndex], + state->buf, i); + + case SII_CMD_PHASE: + case SII_DATA_OUT_PHASE: + state->buflen -= i; + } + } + + /* read a one byte status message */ + state->statusByte = msg = + sii_GetByte(regs, SII_STATUS_PHASE, 1); + if (msg < 0) { + dstat = regs->dstat; + goto again; + } +#ifdef DEBUG + if (sii_debug > 4) + printf("Status %x ", msg); + if (sii_logp > sii_log) + sii_logp[-1].msg = msg; + else + sii_log[NLOG - 1].msg = msg; +#endif + + /* do a quick wait for COMMAND_COMPLETE */ + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & (SII_CI | SII_DI), SII_WAIT_COUNT, i); + if (dstat & (SII_CI | SII_DI)) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt2 %d\n", i); +#endif + goto again; + } + break; + + case SII_MSG_IN_PHASE: + /* + * Save DMA state if DMA didn't finish. + * Be careful not to save state again after reconnect + * and see RESTORE_POINTER message. + * Note that the SII DMA address is not incremented + * as DMA proceeds. + */ + if (state->dmaCurPhase >= 0) { + /* save DMA registers */ + state->dmaPrevPhase = state->dmaCurPhase; + state->dmaCurPhase = -1; + if (dstat & SII_OBB) + state->dmaByte = regs->dmabyte; + i = regs->dmlotc; + if (i != 0) + i = state->dmaCnt - i; + /* note: no carry from dmaddrl to dmaddrh */ + state->dmaAddrL = regs->dmaddrl + i; + state->dmaAddrH = regs->dmaddrh; + state->dmaCnt = regs->dmlotc; + if (state->dmaCnt == 0) + state->dmaCnt = SII_MAX_DMA_XFER_LENGTH; + regs->comm = comm & + (SII_STATE_MSK | SII_PHASE_MSK); + regs->dstat = SII_DNE; +#ifdef DEBUG + if (sii_debug > 4) { + printf("SavP dcnt %d dadr %x ", + state->dmaCnt, + (state->dmaAddrH << 16) | + state->dmaAddrL); + if (((dstat & SII_OBB) != 0) ^ + (state->dmaCnt & 1)) + printf("OBB??? "); + } else if (sii_debug > 0) { + if (((dstat & SII_OBB) != 0) ^ + (state->dmaCnt & 1)) { + printf("sii_DoIntr: OBB??? ds %x cnt %d\n", + dstat, state->dmaCnt); + sii_DumpLog(); + } + } +#endif + } + + /* read a one byte message */ + msg = sii_GetByte(regs, SII_MSG_IN_PHASE, 0); + if (msg < 0) { + dstat = regs->dstat; + goto again; + } +#ifdef DEBUG + if (sii_debug > 4) + printf("MsgIn %x ", msg); + if (sii_logp > sii_log) + sii_logp[-1].msg = msg; + else + sii_log[NLOG - 1].msg = msg; +#endif + + /* process message */ + switch (msg) { + case MSG_CMDCOMPLETE: + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + msg = sc->sc_target; + sc->sc_target = -1; + /* + * Wait a short time for disconnect. + * Don't be fooled if SII_BER happens first. + * Note: a reselect may happen here. + */ + SII_WAIT_UNTIL(cstat, regs->cstat, + cstat & (SII_RST | SII_SCH), + SII_WAIT_COUNT, i); + if ((cstat & (SII_RST | SII_SCH | + SII_STATE_MSK)) == SII_SCH) { + regs->cstat = SII_SCH | SII_BER; + regs->comm = 0; + /* + * Double check that we didn't miss a + * state change between seeing it and + * clearing the SII_SCH bit. + */ + i = regs->cstat; + if (!(i & SII_SCH) && + (i & SII_STATE_MSK) != + (cstat & SII_STATE_MSK)) + sii_StateChg(sc, i); + } +#ifdef DEBUG + if (sii_debug > 4) + printf("cs %x\n", cstat); +#endif + sii_CmdDone(sc, msg, XS_NOERROR); + break; + + case MSG_EXTENDED: + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + /* read the message length */ + msg = sii_GetByte(regs, SII_MSG_IN_PHASE, 1); + if (msg < 0) { + dstat = regs->dstat; + goto again; + } + sc->sc_buf[1] = msg; /* message length */ + if (msg == 0) + msg = 256; + /* + * We read and acknowlege all the bytes + * except the last so we can assert ATN + * if needed before acknowledging the last. + */ + for (i = 0; i < msg; i++) { + dstat = sii_GetByte(regs, + SII_MSG_IN_PHASE, i < msg - 1); + if ((int)dstat < 0) { + dstat = regs->dstat; + goto again; + } + sc->sc_buf[i + 2] = dstat; + } + + switch (sc->sc_buf[2]) { + case MSG_EXT_MODIFY_DATA_PTR: + /* acknowledge last byte */ + regs->comm = SII_INXFER | + SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, + SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + i = (sc->sc_buf[3] << 24) | + (sc->sc_buf[4] << 16) | + (sc->sc_buf[5] << 8) | + sc->sc_buf[6]; + if (state->dmaPrevPhase >= 0) { + state->dmaAddrL += i; + state->dmaCnt -= i; + } + break; + + case MSG_EXT_SDTR_LEN: + /* + * Acknowledge last byte and + * signal a request for MSG_OUT. + */ + regs->comm = SII_INXFER | SII_ATN | + SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, + SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + sii_DoSync(sc, state); + break; + + default: + reject: + /* + * Acknowledge last byte and + * signal a request for MSG_OUT. + */ + regs->comm = SII_INXFER | SII_ATN | + SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, + SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + + /* wait for MSG_OUT phase */ + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_TBE, + SII_WAIT_COUNT, i); + + /* send a reject message */ + regs->data = MSG_MESSAGE_REJECT; + regs->comm = SII_INXFER | + (regs->cstat & SII_STATE_MSK) | + SII_MSG_OUT_PHASE; + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, + SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + } + break; + + case MSG_SAVEDATAPOINTER: + case MSG_RESTOREPOINTERS: + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + /* wait a short time for another msg */ + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & (SII_CI | SII_DI), + SII_WAIT_COUNT, i); + if (dstat & (SII_CI | SII_DI)) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt %d\n", i); +#endif + goto again; + } + break; + + case MSG_DISCONNECT: + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + state->prevComm = comm; +#ifdef DEBUG + if (sii_debug > 4) + printf("disconn %d ", sc->sc_target); +#endif + /* + * Wait a short time for disconnect. + * Don't be fooled if SII_BER happens first. + * Note: a reselect may happen here. + */ + SII_WAIT_UNTIL(cstat, regs->cstat, + cstat & (SII_RST | SII_SCH), + SII_WAIT_COUNT, i); + if ((cstat & (SII_RST | SII_SCH | + SII_STATE_MSK)) != SII_SCH) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt %d\n", i); +#endif + dstat = regs->dstat; + goto again; + } + regs->cstat = SII_SCH | SII_BER; + regs->comm = 0; + sc->sc_target = -1; + /* + * Double check that we didn't miss a state + * change between seeing it and clearing + * the SII_SCH bit. + */ + i = regs->cstat; + if (!(i & SII_SCH) && (i & SII_STATE_MSK) != + (cstat & SII_STATE_MSK)) + sii_StateChg(sc, i); + break; + + case MSG_MESSAGE_REJECT: + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + printf("%s: device %d: message reject.\n", + sc->sc_dev.dv_xname, sc->sc_target); + break; + + default: + if (!(msg & MSG_IDENTIFYFLAG)) { + printf("%s: device %d: couldn't handle " + "message 0x%x... rejecting.\n", + sc->sc_dev.dv_xname, sc->sc_target, + msg); +#ifdef DEBUG + sii_DumpLog(); +#endif + goto reject; + } + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + /* may want to check LUN some day */ + /* wait a short time for another msg */ + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & (SII_CI | SII_DI), + SII_WAIT_COUNT, i); + if (dstat & (SII_CI | SII_DI)) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt %d\n", i); +#endif + goto again; + } + } + break; + + case SII_MSG_OUT_PHASE: +#ifdef DEBUG + if (sii_debug > 4) + printf("MsgOut\n"); +#endif + printf("MsgOut %x\n", state->flags); /* XXX */ + + /* + * Check for parity error. + * Hardware will automatically set ATN + * to request the device for a MSG_OUT phase. + */ + if (state->flags & PARITY_ERR) { + state->flags &= ~PARITY_ERR; + regs->data = MSG_PARITY_ERROR; + } else + regs->data = MSG_NOOP; + regs->comm = SII_INXFER | (comm & SII_STATE_MSK) | + SII_MSG_OUT_PHASE; + + /* wait a short time for XFER complete */ + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE, + SII_WAIT_COUNT, i); +#ifdef DEBUG + if (sii_debug > 4) + printf("ds %x i %d\n", dstat, i); +#endif + /* just clear the DNE bit and check errors later */ + if (dstat & SII_DNE) { + regs->dstat = SII_DNE; + } + break; + + default: + printf("%s: Couldn't handle phase %d... ignoring.\n", + sc->sc_dev.dv_xname, dstat & SII_PHASE_MSK); + } + } + +#ifdef DEBUG + if (sii_debug > 3) + printf("\n"); +#endif + /* + * Check to make sure we won't be interrupted again. + * Deglitch dstat register. + */ + msg = regs->dstat; + while (msg != (dstat = regs->dstat)) + msg = dstat; + if (dstat & (SII_CI | SII_DI)) + goto again; + + if (sc->sc_target < 0) { + /* look for another device that is ready */ + for (i = 0; i < SII_NCMD; i++) { + /* don't restart a disconnected command */ + if (!sc->sc_cmd[i] || sc->sc_st[i].prevComm) + continue; + sii_StartCmd(sc, i); + break; + } + } + return; + +abort: + /* jump here to abort the current command */ + printf("%s: device %d: current command terminated\n", + sc->sc_dev.dv_xname, sc->sc_target); +#ifdef DEBUG + sii_DumpLog(); +#endif + + if ((cstat = regs->cstat) & SII_CON) { + /* try to send an abort msg for awhile */ + regs->dstat = SII_DNE; + regs->data = MSG_ABORT; + regs->comm = SII_INXFER | SII_ATN | (cstat & SII_STATE_MSK) | + SII_MSG_OUT_PHASE; + SII_WAIT_UNTIL(dstat, regs->dstat, + (dstat & (SII_DNE | SII_PHASE_MSK)) == + (SII_DNE | SII_MSG_OUT_PHASE), + 2 * SII_WAIT_COUNT, i); +#ifdef DEBUG + if (sii_debug > 0) + printf("Abort: cs %x ds %x i %d\n", cstat, dstat, i); +#endif + if ((dstat & (SII_DNE | SII_PHASE_MSK)) == + (SII_DNE | SII_MSG_OUT_PHASE)) { + /* disconnect if command in progress */ + regs->comm = SII_DISCON; + SII_WAIT_UNTIL(cstat, regs->cstat, + !(cstat & SII_CON), SII_WAIT_COUNT, i); + } + } else { +#ifdef DEBUG + if (sii_debug > 0) + printf("Abort: cs %x\n", cstat); +#endif + } + regs->cstat = 0xffff; + regs->dstat = 0xffff; + regs->comm = 0; + + i = sc->sc_target; + sc->sc_target = -1; + sii_CmdDone(sc, i, XS_DRIVER_STUFFUP /* EIO */); +#ifdef DEBUG + if (sii_debug > 4) + printf("sii_DoIntr: after CmdDone target %d\n", sc->sc_target); +#endif +} + +void +sii_StateChg(sc, cstat) + struct sii_softc *sc; + u_int cstat; +{ + SIIRegs *regs = sc->sc_regs; + State *state; + int i; + +#ifdef DEBUG + if (sii_debug > 4) + printf("SCH: "); +#endif + + switch (cstat & SII_STATE_MSK) { + case 0: + /* disconnect */ + i = sc->sc_target; + sc->sc_target = -1; +#ifdef DEBUG + if (sii_debug > 4) + printf("disconn %d ", i); +#endif + if (i >= 0 && !sc->sc_st[i].prevComm) { + printf("%s: device %d: spurrious disconnect (%d)\n", + sc->sc_dev.dv_xname, i, regs->slcsr); + sc->sc_st[i].prevComm = 0; + } + break; + + case SII_CON: + /* connected as initiator */ + i = regs->slcsr; + if (sc->sc_target == i) + break; + printf("%s: device %d: connect to device %d??\n", + sc->sc_dev.dv_xname, sc->sc_target, i); + sc->sc_target = i; + break; + + case SII_DST: + /* + * Wait for CON to become valid, + * chip is slow sometimes. + */ + SII_WAIT_UNTIL(cstat, regs->cstat, + cstat & SII_CON, SII_WAIT_COUNT, i); + if (!(cstat & SII_CON)) + panic("sii resel"); + /* FALLTHROUGH */ + + case SII_CON | SII_DST: + /* + * Its a reselection. Save the ID and wait for + * interrupts to tell us what to do next + * (should be MSG_IN of IDENTIFY). + * NOTE: sc_target may be >= 0 if we were in + * the process of trying to start a command + * and were reselected before the select + * command finished. + */ + sc->sc_target = i = regs->destat; + state = &sc->sc_st[i]; + regs->comm = SII_CON | SII_DST | SII_MSG_IN_PHASE; + regs->dmctrl = state->dmaReqAck; + if (!state->prevComm) { + printf("%s: device %d: spurious reselection\n", + sc->sc_dev.dv_xname, i); + break; + } + state->prevComm = 0; +#ifdef DEBUG + if (sii_debug > 4) + printf("resel %d ", sc->sc_target); +#endif + break; + +#ifdef notyet + case SII_DST | SII_TGT: + case SII_CON | SII_DST | SII_TGT: + /* connected as target */ + printf("%s: Selected by device %d as target!!\n", + sc->sc_dev.dv_xname, regs->destat); + regs->comm = SII_DISCON; + SII_WAIT_UNTIL(!(regs->cstat & SII_CON), + SII_WAIT_COUNT, i); + regs->cstat = 0xffff; + regs->dstat = 0xffff; + regs->comm = 0; + break; +#endif + + default: + printf("%s: Unknown state change (cs %x)!!\n", + sc->sc_dev.dv_xname, cstat); +#ifdef DEBUG + sii_DumpLog(); +#endif + } +} + +/* + * Read one byte of data. + * If 'ack' is true, acknowledge the byte. + */ +int +sii_GetByte(regs, phase, ack) + SIIRegs *regs; + int phase, ack; +{ + u_int dstat; + u_int state; + int i; + int data; + + dstat = regs->dstat; + state = regs->cstat & SII_STATE_MSK; + i = -1; + if (!(dstat & SII_IBF) || (dstat & SII_MIS)) { + regs->comm = state | phase; + /* wait a short time for IBF */ + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_IBF, + SII_WAIT_COUNT, i); +#ifdef DEBUG + if (!(dstat & SII_IBF)) + printf("status no IBF\n"); +#endif + } + if (dstat & SII_DNE) { /* XXX */ + printf("sii_GetByte: DNE set 5\n"); +#ifdef DEBUG + sii_DumpLog(); +#endif + regs->dstat = SII_DNE; + } + data = regs->data; + /* check for parity error */ + if (dstat & SII_IPE) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt0 %d\n", i); +#endif + printf("sii_GetByte: data %x ?? ds %x cm %x i %d\n", + data, dstat, regs->comm, i); /* XXX */ + data = -1; + ack = 1; + } + + if (ack) { + regs->comm = SII_INXFER | state | phase; + + /* wait a short time for XFER complete */ + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE, + SII_WAIT_COUNT, i); + + /* clear the DNE */ + if (dstat & SII_DNE) { + regs->dstat = SII_DNE; + } + } + + return (data); +} + +/* + * Exchange messages to initiate synchronous data transfers. + */ +void +sii_DoSync(sc, state) + struct sii_softc *sc; + State *state; +{ + SIIRegs *regs = sc->sc_regs; + u_int dstat, comm; + int i, j; + u_int len; + +#ifdef DEBUG + if (sii_debug) + printf("sii_DoSync: len %d per %d req/ack %d\n", + sc->sc_buf[1], sc->sc_buf[3], sc->sc_buf[4]); +#endif + + /* SII chip can only handle a minimum transfer period of ??? */ + if (sc->sc_buf[3] < 64) + sc->sc_buf[3] = 64; + /* SII chip can only handle a maximum REQ/ACK offset of 3 */ + len = sc->sc_buf[4]; + if (len > 3) + len = 3; + + sc->sc_buf[0] = MSG_EXTENDED; + sc->sc_buf[1] = MSG_EXT_SDTR_LEN; + sc->sc_buf[2] = MSG_EXT_SDTR; + sc->sc_buf[4] = len; +#if 1 + comm = SII_INXFER | SII_ATN | SII_MSG_OUT_PHASE | + (regs->cstat & SII_STATE_MSK); + regs->comm = comm & ~SII_INXFER; + for (j = 0; j < 5; j++) { + /* wait for target to request the next byte */ + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_TBE, + SII_WAIT_COUNT, i); + if (!(dstat & SII_TBE) || + (dstat & SII_PHASE_MSK) != SII_MSG_OUT_PHASE) { + printf("sii_DoSync: TBE? ds %x cm %x i %d\n", + dstat, comm, i); /* XXX */ + return; + } + + /* the last message byte should have ATN off */ + if (j == 4) + comm &= ~SII_ATN; + + regs->data = sc->sc_buf[j]; + regs->comm = comm; + + /* wait a short time for XFER complete */ + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE, + SII_WAIT_COUNT, i); + + if (!(dstat & SII_DNE)) { + printf("sii_DoSync: DNE? ds %x cm %x i %d\n", + dstat, comm, i); /* XXX */ + return; + } + + /* clear the DNE, other errors handled later */ + regs->dstat = SII_DNE; + } +#else /* 0 */ + sc->sii_copytobuf(sc, sc->sc_buf, SII_BUF_ADDR(sc), 5); + printf("sii_DoSync: %x %x %x ds %x\n", + ((u_short *)sc->sc_buf)[0], ((u_short *)sc->sc_buf)[1], + ((u_short *)sc->sc_buf)[2], regs->dstat); /* XXX */ + regs->dmaddrl = SII_BUF_ADDR(sc); + regs->dmaddrh = SII_BUF_ADDR(sc) >> 16; + regs->dmlotc = 5; + regs->comm = SII_DMA | SII_INXFER | SII_ATN | + (regs->cstat & SII_STATE_MSK) | SII_MSG_OUT_PHASE; + + /* wait a short time for XFER complete */ + SII_WAIT_UNTIL(dstat, regs->dstat, + (dstat & (SII_DNE | SII_TCZ)) == (SII_DNE | SII_TCZ), + SII_WAIT_COUNT, i); + + if ((dstat & (SII_DNE | SII_TCZ)) != (SII_DNE | SII_TCZ)) { + printf("sii_DoSync: ds %x cm %x i %d lotc %d\n", + dstat, regs->comm, i, regs->dmlotc); /* XXX */ + sii_DumpLog(); /* XXX */ + return; + } + /* clear the DNE, other errors handled later */ + regs->dstat = SII_DNE; +#endif /* 0 */ + +#if 0 + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & (SII_CI | SII_DI), + SII_WAIT_COUNT, i); + printf("sii_DoSync: ds %x cm %x i %d lotc %d\n", + dstat, regs->comm, i, regs->dmlotc); /* XXX */ +#endif + + state->dmaReqAck = len; +} + +/* + * Issue the sequence of commands to the controller to start DMA. + * NOTE: the data buffer should be word-aligned for DMA out. + */ +void +sii_StartDMA(regs, phase, dmaAddr, size) + SIIRegs *regs; /* which SII to use */ + int phase; /* phase to send/receive data */ + u_int dmaAddr; /* DMA buffer offset */ + int size; /* # of bytes to transfer */ +{ + + if (regs->dstat & SII_DNE) { /* XXX */ + regs->dstat = SII_DNE; + printf("sii_StartDMA: DNE set\n"); +#ifdef DEBUG + sii_DumpLog(); +#endif + } + regs->dmaddrl = dmaAddr; + regs->dmaddrh = dmaAddr >> 16; + regs->dmlotc = size; + regs->comm = SII_DMA | SII_INXFER | (regs->cstat & SII_STATE_MSK) | + phase; + +#ifdef DEBUG + if (sii_debug > 5) { + printf("sii_StartDMA: cs 0x%x, ds 0x%x, cm 0x%x, size %d\n", + regs->cstat, regs->dstat, regs->comm, size); + } +#endif +} + +/* + * Call the device driver's 'done' routine to let it know the command is done. + * The 'done' routine may try to start another command. + * To be fair, we should start pending commands for other devices + * before allowing the same device to start another command. + */ +void +sii_CmdDone(sc, target, error) + struct sii_softc *sc; /* which SII to use */ + int target; /* which device is done */ + int error; /* error code if any errors */ +{ + ScsiCmd *scsicmd; + struct scsi_xfer *xs; + int i; + + splassert(IPL_BIO); + + scsicmd = sc->sc_cmd[target]; +#ifdef DIAGNOSTIC + if (target < 0 || !scsicmd) + panic("sii_CmdDone"); +#endif + sc->sc_cmd[target] = NULL; +#ifdef DEBUG + if (sii_debug > 1) { + printf("sii_CmdDone: %s target %d cmd 0x%x err %d resid %d\n", + sc->sc_dev.dv_xname, + target, scsicmd->cmd[0], error, sc->sc_st[target].buflen); + } +#endif + + /* look for another device that is ready */ + for (i = 0; i < SII_NCMD; i++) { + /* don't restart a disconnected command */ + if (!sc->sc_cmd[i] || sc->sc_st[i].prevComm) + continue; + sii_StartCmd(sc, i); + break; + } + + xs = sc->sc_xs[target]; + xs->status = sc->sc_st[target].statusByte; + xs->error = error; + xs->resid = sc->sc_st[target].buflen; + xs->flags |= ITSDONE; + scsi_done(xs); +} + +#ifdef DEBUG +void +sii_DumpLog() +{ + struct sii_log *lp; + + printf("sii: cmd 0x%x bn %d cnt %d\n", sii_debug_cmd, sii_debug_bn, + sii_debug_sz); + lp = sii_logp; + do { + printf("target %d cs %x ds %x cm %x msg %x rlen %x dlen %x\n", + lp->target, lp->cstat, lp->dstat, lp->comm, lp->msg, + lp->rlen, lp->dlen); + if (++lp >= &sii_log[NLOG]) + lp = sii_log; + } while (lp != sii_logp); +} +#endif diff --git a/sys/arch/vax/dec/siireg.h b/sys/arch/vax/dec/siireg.h new file mode 100644 index 00000000000..c83f0df9e37 --- /dev/null +++ b/sys/arch/vax/dec/siireg.h @@ -0,0 +1,235 @@ +/* $OpenBSD: siireg.h,v 1.1 2008/08/18 23:19:22 miod Exp $ */ +/* $NetBSD: siireg.h,v 1.2 2006/07/29 19:10:57 ad Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)siireg.h 8.1 (Berkeley) 6/10/93 + * + * sii.h -- + * + * SII registers. + * + * Copyright (C) 1989 Digital Equipment Corporation. + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appears in all copies. + * Digital Equipment Corporation makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * from: Header: /sprite/src/kernel/dev/ds3100.md/RCS/sii.h, + * v 1.2 89/08/15 19:53:04 rab Exp SPRITE (DECWRL) + */ + +#ifndef _SII +#define _SII + +/* + * SII hardware registers + */ +typedef volatile struct { + u_short sdb; /* SCSI Data Bus and Parity */ + u_short pad0; + u_short sc1; /* SCSI Control Signals One */ + u_short pad1; + u_short sc2; /* SCSI Control Signals Two */ + u_short pad2; + u_short csr; /* Control/Status register */ + u_short pad3; + u_short id; /* Bus ID register */ + u_short pad4; + u_short slcsr; /* Select Control and Status Register */ + u_short pad5; + u_short destat; /* Selection Detector Status Register */ + u_short pad6; + u_short dstmo; /* DSSI Timeout Register */ + u_short pad7; + u_short data; /* Data Register */ + u_short pad8; + u_short dmctrl; /* DMA Control Register */ + u_short pad9; + u_short dmlotc; /* DMA Length of Transfer Counter */ + u_short pad10; + u_short dmaddrl; /* DMA Address Register Low */ + u_short pad11; + u_short dmaddrh; /* DMA Address Register High */ + u_short pad12; + u_short dmabyte; /* DMA Initial Byte Register */ + u_short pad13; + u_short stlp; /* DSSI Short Target List Pointer */ + u_short pad14; + u_short ltlp; /* DSSI Long Target List Pointer */ + u_short pad15; + u_short ilp; /* DSSI Initiator List Pointer */ + u_short pad16; + u_short dsctrl; /* DSSI Control Register */ + u_short pad17; + u_short cstat; /* Connection Status Register */ + u_short pad18; + u_short dstat; /* Data Transfer Status Register */ + u_short pad19; + u_short comm; /* Command Register */ + u_short pad20; + u_short dictrl; /* Diagnostic Control Register */ + u_short pad21; + u_short clock; /* Diagnostic Clock Register */ + u_short pad22; + u_short bhdiag; /* Bus Handler Diagnostic Register */ + u_short pad23; + u_short sidiag; /* SCSI IO Diagnostic Register */ + u_short pad24; + u_short dmdiag; /* Data Mover Diagnostic Register */ + u_short pad25; + u_short mcdiag; /* Main Control Diagnostic Register */ + u_short pad26; +} SIIRegs; + +/* + * SC1 - SCSI Control Signals One + */ +#define SII_SC1_MSK 0x1ff /* All possible signals on the bus */ +#define SII_SC1_SEL 0x80 /* SCSI SEL signal active on bus */ +#define SII_SC1_ATN 0x08 /* SCSI ATN signal active on bus */ + +/* + * SC2 - SCSI Control Signals Two + */ +#define SII_SC2_IGS 0x8 /* SCSI drivers for initiator mode */ + +/* + * CSR - Control/Status Register + */ +#define SII_HPM 0x10 /* SII in on an arbitrated SCSI bus */ +#define SII_RSE 0x08 /* 1 = respond to reselections */ +#define SII_SLE 0x04 /* 1 = respond to selections */ +#define SII_PCE 0x02 /* 1 = report parity errors */ +#define SII_IE 0x01 /* 1 = enable interrupts */ + +/* + * ID - Bus ID Register + */ +#define SII_ID_IO 0x8000 /* I/O */ + +/* + * DESTAT - Selection Detector Status Register + */ +#define SII_IDMSK 0x7 /* ID of target reselected the SII */ + +/* + * DMCTRL - DMA Control Register + */ +#define SII_ASYNC 0x00 /* REQ/ACK Offset for async mode */ +#define SII_SYNC 0x03 /* REQ/ACK Offset for sync mode */ + +/* + * DMLOTC - DMA Length Of Transfer Counter + */ +#define SII_TCMSK 0x1fff /* transfer count mask */ + +/* + * CSTAT - Connection Status Register + */ +#define SII_CI 0x8000 /* composite interrupt bit for CSTAT */ +#define SII_DI 0x4000 /* composite interrupt bit for DSTAT */ +#define SII_RST 0x2000 /* 1 if reset is asserted on SCSI bus */ +#define SII_BER 0x1000 /* Bus error */ +#define SII_OBC 0x0800 /* Out_en Bit Cleared (DSSI mode) */ +#define SII_TZ 0x0400 /* Target pointer Zero (STLP or LTLP is zero) */ +#define SII_BUF 0x0200 /* Buffer service - outbound pkt to non-DSSI */ +#define SII_LDN 0x0100 /* List element Done */ +#define SII_SCH 0x0080 /* State Change */ +#define SII_CON 0x0040 /* SII is Connected to another device */ +#define SII_DST 0x0020 /* SII was Destination of current transfer */ +#define SII_TGT 0x0010 /* SII is operating as a Target */ +#define SII_STATE_MSK 0x0070 /* State Mask */ +#define SII_SWA 0x0008 /* Selected With Attention */ +#define SII_SIP 0x0004 /* Selection In Progress */ +#define SII_LST 0x0002 /* Lost arbitration */ + +/* + * DSTAT - Data Transfer Status Register + */ +#define SII_DNE 0x2000 /* DMA transfer Done */ +#define SII_TCZ 0x1000 /* Transfer Count register is Zero */ +#define SII_TBE 0x0800 /* Transmit Buffer Empty */ +#define SII_IBF 0x0400 /* Input Buffer Full */ +#define SII_IPE 0x0200 /* Incoming Parity Error */ +#define SII_OBB 0x0100 /* Odd Byte Boundry */ +#define SII_MIS 0x0010 /* Phase Mismatch */ +#define SII_ATN 0x0008 /* ATN set by initiator if in Target mode */ +#define SII_MSG 0x0004 /* current bus state of MSG */ +#define SII_CD 0x0002 /* current bus state of C/D */ +#define SII_IO 0x0001 /* current bus state of I/O */ +#define SII_PHASE_MSK 0x0007 /* Phase Mask */ + +/* + * The different phases. + */ +#define SII_MSG_IN_PHASE 0x7 +#define SII_MSG_OUT_PHASE 0x6 +#define SII_STATUS_PHASE 0x3 +#define SII_CMD_PHASE 0x2 +#define SII_DATA_IN_PHASE 0x1 +#define SII_DATA_OUT_PHASE 0x0 + +/* + * COMM - Command Register + */ +#define SII_DMA 0x8000 /* DMA mode */ +#define SII_DO_RST 0x4000 /* Assert reset on SCSI bus for 25 usecs */ +#define SII_RSL 0x1000 /* 0 = select, 1 = reselect desired device */ + +/* Commands: I - Initiator, T - Target, D - Disconnected */ +#define SII_INXFER 0x0800 /* Information Transfer command (I,T) */ +#define SII_SELECT 0x0400 /* Select command (D) */ +#define SII_REQDATA 0x0200 /* Request Data command (T) */ +#define SII_DISCON 0x0100 /* Disconnect command (I,T,D) */ +#define SII_CHRESET 0x0080 /* Chip Reset command (I,T,D) */ + +/* Command state bits same as connection status register */ +/* Command phase bits same as data transfer status register */ + +/* + * DICTRL - Diagnostic Control Register + */ +#define SII_PRE 0x4 /* Enable the SII to drive the SCSI bus */ + +#define SII_WAIT_COUNT 10000 /* Delay count used for the SII chip */ +/* + * Max DMA transfer length for SII + * The SII chip only has a 13 bit counter. If 8192 is used as the max count, + * you can't tell the difference between a count of zero and 8192. + * 8190 is used instead of 8191 so the count is even. + */ +#define SII_MAX_DMA_XFER_LENGTH 8192 + +#endif /* _SII */ diff --git a/sys/arch/vax/dec/siivar.h b/sys/arch/vax/dec/siivar.h new file mode 100644 index 00000000000..82a92518d19 --- /dev/null +++ b/sys/arch/vax/dec/siivar.h @@ -0,0 +1,71 @@ +/* $OpenBSD: siivar.h,v 1.1 2008/08/18 23:19:22 miod Exp $ */ +/* $NetBSD: siivar.h,v 1.6 2000/06/02 20:16:51 mhitch Exp $ */ + +#ifndef _SIIVAR_H +#define _SIIVAR_H + +/* + * This structure contains information that a SCSI interface controller + * needs to execute a SCSI command. + */ +typedef struct ScsiCmd { + int unit; /* unit number passed to device done routine */ + int flags; /* control flags for this command (see below) */ + int buflen; /* length of the data buffer in bytes */ + char *buf; /* pointer to data buffer for this command */ + int cmdlen; /* length of data in cmdbuf */ + u_char *cmd; /* buffer for the SCSI command */ + int error; /* compatibility hack for new scsi */ + int lun; /* LUN for MI SCSI */ +} ScsiCmd; + +typedef struct scsi_state { + int statusByte; /* status byte returned during STATUS_PHASE */ + int dmaDataPhase; /* which data phase to expect */ + int dmaCurPhase; /* SCSI phase if DMA is in progress */ + int dmaPrevPhase; /* SCSI phase of DMA suspended by disconnect */ + u_int dmaAddr[2]; /* DMA buffer memory offsets */ + int dmaBufIndex; /* which of the above is currently in use */ + int dmalen; /* amount to transfer in this chunk */ + int cmdlen; /* total remaining amount of cmd to transfer */ + u_char *cmd; /* current pointer within scsicmd->cmd */ + int buflen; /* total remaining amount of data to transfer */ + char *buf; /* current pointer within scsicmd->buf */ + u_short flags; /* see below */ + u_short prevComm; /* command reg before disconnect */ + u_short dmaCtrl; /* DMA control register if disconnect */ + u_short dmaAddrL; /* DMA address register if disconnect */ + u_short dmaAddrH; /* DMA address register if disconnect */ + u_short dmaCnt; /* DMA count if disconnect */ + u_short dmaByte; /* DMA byte if disconnect on odd boundary */ + u_short dmaReqAck; /* DMA synchronous xfer offset or 0 if async */ +} State; + +/* state flags */ +#define FIRST_DMA 0x01 /* true if no data DMA started yet */ +#define PARITY_ERR 0x02 /* true if parity error seen */ + +#define SII_NCMD 8 +struct sii_softc { + struct device sc_dev; /* us as a device */ + struct scsi_link sc_link; /* scsi link struct */ + ScsiCmd sc_cmd_fake[SII_NCMD]; /* XXX - hack!!! */ + struct scsi_xfer *sc_xs[SII_NCMD]; /* XXX - hack!!! */ + SIIRegs *sc_regs; /* HW address of SII controller chip */ + int sc_flags; + int sc_target; /* target SCSI ID if connected */ + int sc_hostid; + ScsiCmd *sc_cmd[SII_NCMD]; /* active command indexed by ID */ + void (*sii_copytobuf)(void *, u_char *, u_int, int); + void (*sii_copyfrombuf)(void *, u_int, u_char *, int); + + State sc_st[SII_NCMD]; /* state info for each active command */ + + u_char sc_buf[258]; /* used for extended messages */ +}; + +/* Machine-independent back-end attach entry point */ +void sii_attach(struct sii_softc *sc); +int sii_intr(void *sc); + +#endif /* _SIIVAR_H */ |