b7a2ec3f01
be instead of hardcoding values. + Print the host SCSI ID in siiattach(). XXX - probably should do the same to the ASC driver?? The manuals for the 5000 series machines don't mention the scsiidN env variable, but it is present on all the models I've looked at...
1812 lines
46 KiB
C
1812 lines
46 KiB
C
/* $NetBSD: sii.c,v 1.36 1999/06/08 23:41:59 simonb 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 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. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. 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)";
|
|
*/
|
|
|
|
#include "sii.h"
|
|
#if NSII > 0
|
|
/*
|
|
* SCSI interface driver
|
|
*/
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/dkstat.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/device.h>
|
|
|
|
#ifdef notyet
|
|
#include <dev/scsipi/scsi_all.h>
|
|
#include <dev/scsipi/scsipi_all.h>
|
|
#include <dev/scsipi/scsiconf.h>
|
|
#endif
|
|
|
|
#include <machine/autoconf.h>
|
|
#include <machine/bus.h> /* XXX wbflush() */
|
|
|
|
/* old 4.4bsd/pmax scsi drivers */
|
|
#include <pmax/dev/device.h>
|
|
#include <pmax/dev/scsi.h>
|
|
|
|
#include <pmax/dev/siireg.h> /* device registers */
|
|
#include <pmax/dev/siivar.h> /* softc and prototypes */
|
|
|
|
#include <pmax/pmax/pmaxtype.h> /* Definition of DS_PMAX */
|
|
#include <pmax/pmax/machdep.h> /* prom_scsiid prototype */
|
|
|
|
/* Machine-indepedent back-end attach entry point */
|
|
void siiattach __P((struct siisoftc *sc));
|
|
|
|
|
|
extern struct cfdriver sii_cd;
|
|
|
|
int siiprint(void*, char*);
|
|
int siiintr __P((void *sc));
|
|
|
|
#ifdef USE_NEW_SCSI
|
|
/* Glue to the machine-independent scsi */
|
|
struct scsipi_adapter asc_switch = {
|
|
NULL, /* XXX - asc_scsi_cmd */
|
|
#if 0
|
|
/*XXX*/ minphys, /* no max transfer size; DMA engine deals */
|
|
#else
|
|
SII_MAX_DMA_XFER_LENGTH,
|
|
#endif
|
|
NULL, /* scsipi_ioctl */
|
|
};
|
|
|
|
struct scsipi_device sii_dev = {
|
|
/*XXX*/ NULL, /* Use default error handler */
|
|
/*XXX*/ NULL, /* have a queue, served by this */
|
|
/*XXX*/ NULL, /* have no async handler */
|
|
/*XXX*/ NULL, /* Use default 'done' routine */
|
|
};
|
|
#endif
|
|
|
|
|
|
/*
|
|
* 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
|
|
|
|
u_char sii_buf[256]; /* used for extended messages */
|
|
|
|
#define NORESET 0
|
|
#define RESET 1
|
|
#define NOWAIT 0
|
|
#define WAIT 1
|
|
|
|
|
|
/*
|
|
* 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) ((sc)->sc_buf + SII_MAX_DMA_XFER_LENGTH * 14)
|
|
|
|
/*
|
|
* Other forward references
|
|
*/
|
|
|
|
static void sii_Reset __P((register struct siisoftc *sc, int resetbus));
|
|
static void sii_StartCmd __P((register struct siisoftc *sc, int target));
|
|
static void sii_CmdDone __P((register struct siisoftc *sc, int target,
|
|
int error));
|
|
static void sii_DoIntr __P((register struct siisoftc *sc, u_int dstat));
|
|
static void sii_StateChg __P((register struct siisoftc *sc, u_int cstat));
|
|
static int sii_GetByte __P((register SIIRegs *regs, int phase, int ack));
|
|
static void sii_DoSync __P((register SIIRegs *regs, register State *state));
|
|
static void sii_StartDMA __P((register SIIRegs *regs, int phase,
|
|
u_short *dmaAddr, int size));
|
|
|
|
void siistart __P((register ScsiCmd *scsicmd));
|
|
void sii_DumpLog __P((void));
|
|
|
|
|
|
/*
|
|
* Definition of the controller for the old-style, non-MI
|
|
* pmax scsi drivers, and for autoconfiguring devices via those
|
|
* drivers.
|
|
*/
|
|
struct pmax_driver siidriver = {
|
|
"sii", NULL, siistart, 0,
|
|
};
|
|
|
|
/*
|
|
* Match driver based on name
|
|
*/
|
|
void
|
|
siiattach(sc)
|
|
struct siisoftc *sc;
|
|
{
|
|
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] = (u_short *)
|
|
sc->sc_buf + 2 * SII_MAX_DMA_XFER_LENGTH * i;
|
|
sc->sc_st[i].dmaAddr[1] = sc->sc_st[i].dmaAddr[0] +
|
|
SII_MAX_DMA_XFER_LENGTH;
|
|
}
|
|
|
|
/* Hack for old-sytle SCSI-device probe */
|
|
(void) pmax_add_scsi(&siidriver, sc->sc_dev.dv_unit);
|
|
|
|
sii_Reset(sc, RESET);
|
|
#ifdef USE_NEW_SCSI
|
|
/* XXX probe SCSI bus and attach slave devices */
|
|
#endif
|
|
printf(": target %d", sc->sc_regs->id & SII_IDMSK);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Start activity on a SCSI device.
|
|
* We maintain information on each device separately since devices can
|
|
* connect/disconnect during an operation.
|
|
*/
|
|
void
|
|
siistart(scsicmd)
|
|
ScsiCmd *scsicmd; /* command to start */
|
|
{
|
|
struct pmax_scsi_device *sdp = scsicmd->sd;
|
|
struct siisoftc *sc = sii_cd.cd_devs[sdp->sd_ctlr];
|
|
int s;
|
|
|
|
s = splbio();
|
|
/*
|
|
* Check if another command is already in progress.
|
|
* We may have to change this if we allow SCSI devices with
|
|
* separate LUNs.
|
|
*/
|
|
if (sc->sc_cmd[sdp->sd_drive]) {
|
|
printf("%s: device %s busy at start\n", sc->sc_dev.dv_xname,
|
|
sdp->sd_driver->d_name);
|
|
(*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY,
|
|
scsicmd->buflen, 0);
|
|
splx(s);
|
|
}
|
|
sc->sc_cmd[sdp->sd_drive] = scsicmd;
|
|
sii_StartCmd(sc, sdp->sd_drive);
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* Check to see if any SII chips have pending interrupts
|
|
* and process as appropriate.
|
|
*/
|
|
int
|
|
siiintr(xxxsc)
|
|
void *xxxsc;
|
|
{
|
|
struct siisoftc *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 (0); /* XXX */
|
|
}
|
|
|
|
return (1); /* XXX spurious interrupt? */
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static void
|
|
sii_Reset(sc, reset)
|
|
struct siisoftc* 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 (from PROM sciiidN variable).
|
|
*/
|
|
regs->id = SII_ID_IO | prom_scsiid(sc->sc_dev.dv_unit);
|
|
/*
|
|
* 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;
|
|
wbflush();
|
|
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;
|
|
wbflush();
|
|
}
|
|
|
|
/*
|
|
* Start a SCSI command by sending the cmd data
|
|
* to a SCSI controller via the SII.
|
|
* Call the device done proceedure if it can't be started.
|
|
* NOTE: we should be called with interrupts disabled.
|
|
*/
|
|
static void
|
|
sii_StartCmd(sc, target)
|
|
struct siisoftc *sc; /* which SII to use */
|
|
int target; /* which command to start */
|
|
{
|
|
SIIRegs *regs;
|
|
ScsiCmd *scsicmd;
|
|
State *state;
|
|
u_int status;
|
|
int error, retval;
|
|
|
|
/* 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->dmaDataPhase =
|
|
(scsicmd->flags & SCSICMD_DATA_TO_DEVICE) ?
|
|
SII_DATA_OUT_PHASE : SII_DATA_IN_PHASE;
|
|
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",
|
|
scsicmd->sd->sd_driver->d_name, target,
|
|
scsicmd->cmd[0], scsicmd->buf, scsicmd->buflen,
|
|
state->dmaDataPhase);
|
|
}
|
|
sii_debug_cmd = scsicmd->cmd[0];
|
|
if (scsicmd->cmd[0] == SCSI_READ_EXT ||
|
|
scsicmd->cmd[0] == SCSI_WRITE_EXT) {
|
|
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.
|
|
*/
|
|
sii_buf[0] = SCSI_DIS_REC_IDENTIFY;
|
|
sii_buf[1] = SCSI_EXTENDED_MSG;
|
|
sii_buf[2] = 3; /* message length */
|
|
sii_buf[3] = SCSI_SYNCHRONOUS_XFER;
|
|
sii_buf[4] = 0;
|
|
sii_buf[5] = 3; /* maximum SII chip supports */
|
|
|
|
state->dmaCurPhase = SII_MSG_OUT_PHASE,
|
|
state->dmalen = 6;
|
|
sc->sii_copytobuf((u_short *)sii_buf,
|
|
(volatile u_short *)SII_BUF_ADDR(sc), 6);
|
|
regs->slcsr = target;
|
|
regs->dmctrl = state->dmaReqAck;
|
|
regs->dmaddrl = (u_short)(SII_BUF_ADDR(sc) >> 1);
|
|
regs->dmaddrh = (u_short)(SII_BUF_ADDR(sc) >> 17) & 03;
|
|
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 = SCSI_DIS_REC_IDENTIFY;
|
|
regs->slcsr = target;
|
|
regs->dmctrl = state->dmaReqAck;
|
|
regs->comm = SII_INXFER | SII_SELECT | SII_ATN | SII_CON |
|
|
SII_MSG_OUT_PHASE;
|
|
}
|
|
wbflush();
|
|
|
|
/*
|
|
* 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;
|
|
wbflush();
|
|
|
|
#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((u_short *)state->cmd,
|
|
(volatile u_short *)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 = ENXIO; /* device didn't respond */
|
|
regs->comm = SII_DISCON;
|
|
wbflush();
|
|
SII_WAIT_UNTIL(status, regs->cstat,
|
|
!(status & (SII_CON | SII_SIP)),
|
|
SII_WAIT_COUNT, retval);
|
|
} else
|
|
error = EBUSY; /* 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;
|
|
wbflush();
|
|
sii_CmdDone(sc, target, error);
|
|
}
|
|
|
|
/*
|
|
* Process interrupt conditions.
|
|
*/
|
|
static void
|
|
sii_DoIntr(sc, dstat)
|
|
struct siisoftc *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 */
|
|
wbflush();
|
|
|
|
if (dstat & SII_CI) {
|
|
/* deglitch cstat register */
|
|
msg = regs->cstat;
|
|
while (msg != (cstat = regs->cstat))
|
|
msg = cstat;
|
|
regs->cstat = cstat; /* acknowledge everything */
|
|
wbflush();
|
|
#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, 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;
|
|
wbflush();
|
|
}
|
|
#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_short *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((volatile u_short *)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((u_short *)(state->buf +
|
|
SII_MAX_DMA_XFER_LENGTH),
|
|
(volatile u_short *)
|
|
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;
|
|
wbflush();
|
|
#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((u_short *)state->cmd,
|
|
(volatile u_short *)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 %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;
|
|
wbflush();
|
|
#ifdef DEBUG
|
|
if (sii_debug > 4)
|
|
printf("Data %d dcnt %d dadr %x ",
|
|
state->dmaDataPhase,
|
|
state->dmaCnt,
|
|
(state->dmaAddrH << 16) |
|
|
state->dmaAddrL);
|
|
#endif
|
|
break;
|
|
}
|
|
if (state->dmaDataPhase != (dstat & SII_PHASE_MSK)) {
|
|
printf("%s: device %d: cmd %x: dma phase doesn't match\n",
|
|
sc->sc_dev.dv_xname, sc->sc_target,
|
|
sc->sc_cmd[sc->sc_target]->cmd[0]);
|
|
goto abort;
|
|
}
|
|
#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((u_short *)state->buf,
|
|
(volatile u_short *)
|
|
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((u_short *)(state->buf +
|
|
SII_MAX_DMA_XFER_LENGTH),
|
|
(volatile u_short *)
|
|
state->dmaAddr[!state->dmaBufIndex], i);
|
|
}
|
|
break;
|
|
|
|
case SII_STATUS_PHASE:
|
|
if (state->cmdlen > 0) {
|
|
printf("%s: device %d: cmd %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);
|
|
wbflush();
|
|
regs->dstat = SII_DNE;
|
|
wbflush();
|
|
#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((volatile u_short*)
|
|
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);
|
|
wbflush();
|
|
regs->dstat = SII_DNE;
|
|
wbflush();
|
|
#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 SCSI_COMMAND_COMPLETE:
|
|
/* 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;
|
|
wbflush();
|
|
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;
|
|
wbflush();
|
|
/*
|
|
* 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, 0);
|
|
break;
|
|
|
|
case SCSI_EXTENDED_MSG:
|
|
/* 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;
|
|
wbflush();
|
|
/* read the message length */
|
|
msg = sii_GetByte(regs, SII_MSG_IN_PHASE, 1);
|
|
if (msg < 0) {
|
|
dstat = regs->dstat;
|
|
goto again;
|
|
}
|
|
sii_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;
|
|
}
|
|
sii_buf[i + 2] = dstat;
|
|
}
|
|
|
|
switch (sii_buf[2]) {
|
|
case SCSI_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;
|
|
wbflush();
|
|
i = (sii_buf[3] << 24) |
|
|
(sii_buf[4] << 16) |
|
|
(sii_buf[5] << 8) |
|
|
sii_buf[6];
|
|
if (state->dmaPrevPhase >= 0) {
|
|
state->dmaAddrL += i;
|
|
state->dmaCnt -= i;
|
|
}
|
|
break;
|
|
|
|
case SCSI_SYNCHRONOUS_XFER:
|
|
/*
|
|
* 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;
|
|
wbflush();
|
|
sii_DoSync(regs, 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;
|
|
wbflush();
|
|
|
|
/* wait for MSG_OUT phase */
|
|
SII_WAIT_UNTIL(dstat, regs->dstat,
|
|
dstat & SII_TBE,
|
|
SII_WAIT_COUNT, i);
|
|
|
|
/* send a reject message */
|
|
regs->data = SCSI_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;
|
|
wbflush();
|
|
}
|
|
break;
|
|
|
|
case SCSI_SAVE_DATA_POINTER:
|
|
case SCSI_RESTORE_POINTERS:
|
|
/* 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;
|
|
wbflush();
|
|
/* 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 SCSI_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;
|
|
wbflush();
|
|
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;
|
|
wbflush();
|
|
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 SCSI_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;
|
|
wbflush();
|
|
printf("%s: device %d: message reject.\n",
|
|
sc->sc_dev.dv_xname, sc->sc_target);
|
|
break;
|
|
|
|
default:
|
|
if (!(msg & SCSI_IDENTIFY)) {
|
|
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;
|
|
wbflush();
|
|
/* 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 = SCSI_MESSAGE_PARITY_ERROR;
|
|
} else
|
|
regs->data = SCSI_NO_OP;
|
|
regs->comm = SII_INXFER | (comm & SII_STATE_MSK) |
|
|
SII_MSG_OUT_PHASE;
|
|
wbflush();
|
|
|
|
/* 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;
|
|
wbflush();
|
|
}
|
|
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 = SCSI_ABORT;
|
|
regs->comm = SII_INXFER | SII_ATN | (cstat & SII_STATE_MSK) |
|
|
SII_MSG_OUT_PHASE;
|
|
wbflush();
|
|
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;
|
|
wbflush();
|
|
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;
|
|
wbflush();
|
|
|
|
i = sc->sc_target;
|
|
sc->sc_target = -1;
|
|
sii_CmdDone(sc, i, EIO);
|
|
#ifdef DEBUG
|
|
if (sii_debug > 4)
|
|
printf("sii_DoIntr: after CmdDone target %d\n", sc->sc_target);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
sii_StateChg(sc, cstat)
|
|
struct siisoftc *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;
|
|
wbflush();
|
|
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;
|
|
wbflush();
|
|
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.
|
|
*/
|
|
static 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;
|
|
wbflush();
|
|
/* 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;
|
|
wbflush();
|
|
|
|
/* 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;
|
|
wbflush();
|
|
}
|
|
}
|
|
|
|
return (data);
|
|
}
|
|
|
|
/*
|
|
* Exchange messages to initiate synchronous data transfers.
|
|
*/
|
|
static void
|
|
sii_DoSync(regs, state)
|
|
SIIRegs *regs;
|
|
State *state;
|
|
{
|
|
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",
|
|
sii_buf[1], sii_buf[3], sii_buf[4]);
|
|
#endif
|
|
|
|
/* SII chip can only handle a minimum transfer period of ??? */
|
|
if (sii_buf[3] < 64)
|
|
sii_buf[3] = 64;
|
|
/* SII chip can only handle a maximum REQ/ACK offset of 3 */
|
|
len = sii_buf[4];
|
|
if (len > 3)
|
|
len = 3;
|
|
|
|
sii_buf[0] = SCSI_EXTENDED_MSG;
|
|
sii_buf[1] = 3; /* message length */
|
|
sii_buf[2] = SCSI_SYNCHRONOUS_XFER;
|
|
sii_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 = sii_buf[j];
|
|
regs->comm = comm;
|
|
wbflush();
|
|
|
|
/* 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;
|
|
wbflush();
|
|
}
|
|
#else /* 0 */
|
|
sc->sii_copytobuf((u_short *)sii_buf,
|
|
(volatile u_short *)SII_BUF_ADDR(sc), 5);
|
|
printf("sii_DoSync: %x %x %x ds %x\n",
|
|
((volatile u_short *)SII_BUF_ADDR(sc))[0],
|
|
((volatile u_short *)SII_BUF_ADDR(sc))[2],
|
|
((volatile u_short *)SII_BUF_ADDR(sc))[4],
|
|
regs->dstat); /* XXX */
|
|
regs->dmaddrl = (u_short)(SII_BUF_ADDR(sc) >> 1);
|
|
regs->dmaddrh = (u_short)(SII_BUF_ADDR(sc) >> 17) & 03;
|
|
regs->dmlotc = 5;
|
|
regs->comm = SII_DMA | SII_INXFER | SII_ATN |
|
|
(regs->cstat & SII_STATE_MSK) | SII_MSG_OUT_PHASE;
|
|
wbflush();
|
|
|
|
/* 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;
|
|
wbflush();
|
|
#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.
|
|
*/
|
|
static void
|
|
sii_StartDMA(regs, phase, dmaAddr, size)
|
|
SIIRegs *regs; /* which SII to use */
|
|
int phase; /* phase to send/receive data */
|
|
u_short *dmaAddr; /* DMA buffer address */
|
|
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 = ((u_long)dmaAddr >> 1);
|
|
regs->dmaddrh = ((u_long)dmaAddr >> 17) & 03;
|
|
regs->dmlotc = size;
|
|
regs->comm = SII_DMA | SII_INXFER | (regs->cstat & SII_STATE_MSK) |
|
|
phase;
|
|
wbflush();
|
|
|
|
#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.
|
|
*/
|
|
static void
|
|
sii_CmdDone(sc, target, error)
|
|
struct siisoftc *sc; /* which SII to use */
|
|
int target; /* which device is done */
|
|
int error; /* error code if any errors */
|
|
{
|
|
ScsiCmd *scsicmd;
|
|
int i;
|
|
|
|
scsicmd = sc->sc_cmd[target];
|
|
#ifdef DIAGNOSTIC
|
|
if (target < 0 || !scsicmd)
|
|
panic("sii_CmdDone");
|
|
#endif
|
|
sc->sc_cmd[target] = (ScsiCmd *)0;
|
|
#ifdef DEBUG
|
|
if (sii_debug > 1) {
|
|
printf("sii_CmdDone: %s target %d cmd %x err %d resid %d\n",
|
|
scsicmd->sd->sd_driver->d_name, 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;
|
|
}
|
|
|
|
(*scsicmd->sd->sd_driver->d_done)(scsicmd->unit, error,
|
|
sc->sc_st[target].buflen, sc->sc_st[target].statusByte);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
sii_DumpLog()
|
|
{
|
|
struct sii_log *lp;
|
|
|
|
printf("sii: cmd %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
|
|
#endif
|
|
|