* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Van Jacobson of Lawrence Berkeley Laboratory.
* %sccs.include.redist.c%
* @(#)scsi.c 7.7 (Berkeley) %G%
* HP9000/3xx 98658 SCSI host adaptor driver.
static char rcsid
[] = "$Header: /usr/src/sys/hp300/dev/RCS/scsi.c,v 1.2 92/04/10 20:48:29 mike Exp $";
#include "hp/dev/device.h"
#include "../include/cpu.h"
#include "../hp300/isr.h"
* In u-seconds, primarily for state changes on the SPC.
#define SCSI_CMD_WAIT 1000 /* wait per step of 'immediate' cmds */
#define SCSI_DATA_WAIT 1000 /* wait per data in/out step */
#define SCSI_INIT_WAIT 50000 /* wait per step (both) during init */
int scsiinit(), scsigo(), scsiintr(), scsixfer();
void scsistart(), scsidone(), scsifree(), scsireset();
struct driver scsidriver
= {
scsiinit
, "scsi", (int (*)())scsistart
, scsigo
, scsiintr
,
struct scsi_softc scsi_softc
[NSCSI
];
struct isr scsi_isr
[NSCSI
];
int scsi_cmd_wait
= SCSI_CMD_WAIT
;
int scsi_data_wait
= SCSI_DATA_WAIT
;
int scsi_init_wait
= SCSI_INIT_WAIT
;
int scsi_nosync
= 1; /* inhibit sync xfers if 1 */
int scsi_pridma
= 0; /* use "priority" dma */
u_int ixstart_wait
[MAXWAIT
+2];
u_int ixin_wait
[MAXWAIT
+2];
u_int ixout_wait
[MAXWAIT
+2];
u_int mxin_wait
[MAXWAIT
+2];
u_int mxin2_wait
[MAXWAIT
+2];
u_int cxin_wait
[MAXWAIT
+2];
u_int fxfr_wait
[MAXWAIT
+2];
u_int sgo_wait
[MAXWAIT
+2];
#define HIST(h,w) (++h[((w)>MAXWAIT? MAXWAIT : ((w) < 0 ? -1 : (w))) + 1]);
register struct scsi_softc
*hs
;
volatile register struct scsidevice
*hd
;
int maxtries
; /* XXX - kludge till I understand whats *supposed* to happen */
int startlen
; /* XXX - kludge till I understand whats *supposed* to happen */
printf("scsi%d: abort from %s: phase=0x%x, ssts=0x%x, ints=0x%x\n",
hs
->sc_hc
->hp_unit
, where
, hd
->scsi_psns
, hd
->scsi_ssts
,
hd
->scsi_ints
= hd
->scsi_ints
;
if (hd
->scsi_psns
== 0 || (hd
->scsi_ssts
& SSTS_INITIATOR
) == 0)
/* no longer connected to scsi target */
/* get the number of bytes remaining in current xfer + fudge */
len
= (hd
->scsi_tch
<< 16) | (hd
->scsi_tcm
<< 8) | hd
->scsi_tcl
;
/* for that many bus cycles, try to send an abort msg */
for (startlen
= (len
+= 1024); (hd
->scsi_ssts
& SSTS_INITIATOR
) && --len
>= 0; ) {
hd
->scsi_scmd
= SCMD_SET_ATN
;
while ((hd
->scsi_psns
& PSNS_REQ
) == 0) {
if (! (hd
->scsi_ssts
& SSTS_INITIATOR
))
printf("-- scsiabort gave up after 1000 tries (startlen = %d len = %d)\n",
if ((hd
->scsi_psns
& PHASE
) == MESG_OUT_PHASE
)
hd
->scsi_scmd
= SCMD_RST_ATN
;
hd
->scsi_pctl
= hd
->scsi_psns
& PHASE
;
if (hd
->scsi_psns
& PHASE_IO
) {
/* one of the input phases - read & discard a byte */
hd
->scsi_scmd
= SCMD_SET_ACK
;
while (hd
->scsi_psns
& PSNS_REQ
)
/* one of the output phases - send an abort msg */
hd
->scsi_temp
= MSG_ABORT
;
hd
->scsi_scmd
= SCMD_SET_ACK
;
while (hd
->scsi_psns
& PSNS_REQ
)
hd
->scsi_scmd
= SCMD_RST_ACK
;
* Either the abort was successful & the bus is disconnected or
* the device didn't listen. If the latter, announce the problem.
* Either way, reset the card & the SPC.
printf("scsi%d: abort failed. phase=0x%x, ssts=0x%x\n",
hs
->sc_hc
->hp_unit
, hd
->scsi_psns
, hd
->scsi_ssts
);
if (! ((junk
= hd
->scsi_ints
) & INTS_RESEL
)) {
hd
->scsi_sctl
|= SCTL_CTRLRST
;
hd
->scsi_sctl
&=~ SCTL_CTRLRST
;
hd
->scsi_ints
= hd
->scsi_ints
;
* XXX Set/reset long delays.
* if delay == 0, reset default delays
* if delay < 0, set both delays to default long initialization values
* if delay > 0, set both delays to this value
* Used when a devices is expected to respond slowly (e.g. during
static int saved_cmd_wait
, saved_data_wait
;
saved_cmd_wait
= scsi_cmd_wait
;
saved_data_wait
= scsi_data_wait
;
scsi_cmd_wait
= scsi_data_wait
= delay
;
scsi_cmd_wait
= scsi_data_wait
= scsi_init_wait
;
scsi_cmd_wait
= saved_cmd_wait
;
scsi_data_wait
= saved_data_wait
;
register struct hp_ctlr
*hc
;
register struct scsi_softc
*hs
= &scsi_softc
[hc
->hp_unit
];
register struct scsidevice
*hd
= (struct scsidevice
*)hc
->hp_addr
;
if ((hd
->scsi_id
& ID_MASK
) != SCSI_ID
)
hc
->hp_ipl
= SCSI_IPL(hd
->scsi_csr
);
hs
->sc_dq
.dq_unit
= hc
->hp_unit
;
hs
->sc_dq
.dq_driver
= &scsidriver
;
hs
->sc_sq
.dq_forw
= hs
->sc_sq
.dq_back
= &hs
->sc_sq
;
scsi_isr
[hc
->hp_unit
].isr_intr
= scsiintr
;
scsi_isr
[hc
->hp_unit
].isr_ipl
= hc
->hp_ipl
;
scsi_isr
[hc
->hp_unit
].isr_arg
= hc
->hp_unit
;
isrlink(&scsi_isr
[hc
->hp_unit
]);
* XXX scale initialization wait according to CPU speed.
* Should we do this for all wait? Should we do this at all?
scsi_init_wait
*= cpuspeed
;
register struct scsi_softc
*hs
= &scsi_softc
[unit
];
volatile register struct scsidevice
*hd
=
(struct scsidevice
*)hs
->sc_hc
->hp_addr
;
if (hs
->sc_flags
& SCSI_ALIVE
)
scsiabort(hs
, hd
, "reset");
printf("scsi%d: ", unit
);
* Disable interrupts then reset the FUJI chip.
hd
->scsi_sctl
= SCTL_DISABLE
| SCTL_CTRLRST
;
if ((hd
->scsi_id
& ID_WORD_DMA
) == 0) {
hs
->sc_flags
|= SCSI_DMA32
;
/* Determine Max Synchronous Transfer Rate */
i
= SCSI_SYNC_XFER(hd
->scsi_hconf
);
hs
->sc_sync
= TMOD_SYNC
| 0x3e; /* 250 nsecs */
hs
->sc_sync
= TMOD_SYNC
| 0x5e; /* 375 nsecs */
hs
->sc_sync
= TMOD_SYNC
| 0x7d; /* 500 nsecs */
* Configure the FUJI chip with its SCSI address, all
* interrupts enabled & appropriate parity.
i
= (~hd
->scsi_hconf
) & 0x7;
hs
->sc_scsi_addr
= 1 << i
;
if (hd
->scsi_hconf
& HCONF_PARITY
)
hd
->scsi_sctl
= SCTL_DISABLE
| SCTL_ABRT_ENAB
|
SCTL_SEL_ENAB
| SCTL_RESEL_ENAB
|
SCTL_INTR_ENAB
| SCTL_PARITY_ENAB
;
hd
->scsi_sctl
= SCTL_DISABLE
| SCTL_ABRT_ENAB
|
SCTL_SEL_ENAB
| SCTL_RESEL_ENAB
|
hd
->scsi_sctl
&=~ SCTL_DISABLE
;
printf(", scsi id %d\n", i
);
hs
->sc_flags
|= SCSI_ALIVE
;
register struct scsi_softc
*hs
;
volatile register struct scsidevice
*hd
;
int unit
= hs
->sc_hc
->hp_unit
;
printf("scsi%d: ", unit
);
if (hd
->scsi_hconf
& HCONF_SD
)
printf("spurious RST interrupt");
printf("hardware error - check fuse");
if ((ints
& INTS_HARD_ERR
) || hd
->scsi_serr
) {
if (hd
->scsi_serr
& SERR_SCSI_PAR
) {
printf("%sparity err", sep
);
if (hd
->scsi_serr
& SERR_SPC_PAR
) {
printf("%sSPC parity err", sep
);
if (hd
->scsi_serr
& SERR_TC_PAR
) {
printf("%sTC parity err", sep
);
if (hd
->scsi_serr
& SERR_PHASE_ERR
) {
printf("%sphase err", sep
);
if (hd
->scsi_serr
& SERR_SHORT_XFR
) {
printf("%ssync short transfer err", sep
);
if (hd
->scsi_serr
& SERR_OFFSET
) {
printf("%ssync offset error", sep
);
printf("%sSPC select timeout error", sep
);
printf("%sspurious SRV_REQ interrupt", sep
);
if (ints
& INTS_CMD_DONE
)
printf("%sspurious CMD_DONE interrupt", sep
);
printf("%sspurious disconnect interrupt", sep
);
printf("%sspurious reselect interrupt", sep
);
printf("%sspurious select interrupt", sep
);
issue_select(hd
, target
, our_addr
)
volatile register struct scsidevice
*hd
;
if (hd
->scsi_ssts
& (SSTS_INITIATOR
|SSTS_TARGET
|SSTS_BUSY
))
if (hd
->scsi_ints
& INTS_DISCON
)
hd
->scsi_ints
= INTS_DISCON
;
hd
->scsi_temp
= (1 << target
) | our_addr
;
/* select timeout is hardcoded to 2ms */
hd
->scsi_scmd
= SCMD_SELECT
;
volatile register struct scsidevice
*hd
;
while ((ints
= hd
->scsi_ints
) == 0)
return (!(hd
->scsi_ssts
& SSTS_INITIATOR
));
ixfer_start(hd
, len
, phase
, wait
)
volatile register struct scsidevice
*hd
;
hd
->scsi_tch
= len
>> 16;
hd
->scsi_tmod
= 0; /*XXX*/
hd
->scsi_scmd
= SCMD_XFR
| SCMD_PROG_XFR
;
/* wait for xfer to start or svc_req interrupt */
while ((hd
->scsi_ssts
& SSTS_BUSY
) == 0) {
if (hd
->scsi_ints
|| --wait
< 0) {
printf("ixfer_start fail: i%x, w%d\n",
volatile register struct scsidevice
*hd
;
register int wait
= scsi_data_wait
;
while (hd
->scsi_ssts
& SSTS_DREG_FULL
) {
if (hd
->scsi_ints
|| --wait
< 0) {
printf("ixfer_out fail: l%d i%x w%d\n",
len
, hd
->scsi_ints
, wait
);
volatile register struct scsidevice
*hd
;
register int wait
= scsi_data_wait
;
while (hd
->scsi_ssts
& SSTS_DREG_EMPTY
) {
if (hd
->scsi_ints
|| --wait
< 0) {
while (! (hd
->scsi_ssts
& SSTS_DREG_EMPTY
)) {
printf("ixfer_in fail: l%d i%x w%d\n",
len
, hd
->scsi_ints
, wait
);
mxfer_in(hd
, len
, buf
, phase
)
volatile register struct scsidevice
*hd
;
register int wait
= scsi_cmd_wait
;
for (i
= 0; i
< len
; ++i
) {
* manual sez: reset ATN before ACK is sent.
if (hd
->scsi_psns
& PSNS_ATN
)
hd
->scsi_scmd
= SCMD_RST_ATN
;
* wait for the request line (which says the target
* wants to give us data). If the phase changes while
* we're waiting, we're done.
while ((hd
->scsi_psns
& PSNS_REQ
) == 0) {
if ((hd
->scsi_psns
& PHASE
) != phase
||
(hd
->scsi_ssts
& SSTS_INITIATOR
) == 0)
* set ack (which says we're ready for the data, wait for
* req to go away (target says data is available), grab the
* data, then reset ack (say we've got the data).
hd
->scsi_scmd
= SCMD_SET_ACK
;
while (hd
->scsi_psns
& PSNS_REQ
) {
hd
->scsi_scmd
= SCMD_RST_ACK
;
* Wait for manual transfer to finish.
* Avoids occasional "unexpected phase" errors in finishxfer
* formerly addressed by per-slave delays.
while ((hd
->scsi_ssts
& SSTS_ACTIVE
) == SSTS_INITIATOR
) {
* SCSI 'immediate' command: issue a command to some SCSI device
* and get back an 'immediate' response (i.e., do programmed xfer
* to get the response data). 'cbuf' is a buffer containing a scsi
* command of length clen bytes. 'buf' is a buffer of length 'len'
* bytes for data. The transfer direction is determined by the device
* (i.e., by the scsi bus data xfer phase). If 'len' is zero, the
* command must supply no data. 'xferphase' is the bus phase the
* caller expects to happen after the command is issued. It should
* be one of DATA_IN_PHASE, DATA_OUT_PHASE or STATUS_PHASE.
scsiicmd(hs
, target
, cbuf
, clen
, buf
, len
, xferphase
)
volatile register struct scsidevice
*hd
=
(struct scsidevice
*)hs
->sc_hc
->hp_addr
;
/* select the SCSI bus (it's an error if bus isn't free) */
if (issue_select(hd
, target
, hs
->sc_scsi_addr
))
* Wait for a phase change (or error) then let the device
* sequence us through the various SCSI phases.
if (ixfer_start(hd
, clen
, phase
, wait
))
if (ixfer_out(hd
, clen
, cbuf
))
if (ixfer_start(hd
, len
, phase
, wait
) ||
!(hd
->scsi_ssts
& SSTS_DREG_EMPTY
))
if (ixfer_start(hd
, len
, phase
, wait
)) {
if (ixfer_out(hd
, len
, buf
))
if (ixfer_start(hd
, sizeof(hs
->sc_stat
), phase
, wait
) ||
!(hd
->scsi_ssts
& SSTS_DREG_EMPTY
))
ixfer_in(hd
, sizeof(hs
->sc_stat
), hs
->sc_stat
);
if (ixfer_start(hd
, sizeof(hs
->sc_msg
), phase
, wait
) ||
!(hd
->scsi_ssts
& SSTS_DREG_EMPTY
)) {
ixfer_in(hd
, sizeof(hs
->sc_msg
), hs
->sc_msg
);
hd
->scsi_scmd
= SCMD_RST_ACK
;
printf("scsi%d: unexpected phase %d in icmd from %d\n",
hs
->sc_hc
->hp_unit
, phase
, target
);
/* wait for last command to complete */
while ((ints
= hd
->scsi_ints
) == 0) {
phase
= hd
->scsi_psns
& PHASE
;
else if (ints
& INTS_DISCON
)
else if ((ints
& INTS_CMD_DONE
) == 0) {
scsiabort(hs
, hd
, "icmd");
* Finish SCSI xfer command: After the completion interrupt from
* a read/write operation, sequence through the final phases in
* programmed i/o. This routine is a lot like scsiicmd except we
* skip (and don't allow) the select, cmd out and data in/out phases.
finishxfer(hs
, hd
, target
)
volatile register struct scsidevice
*hd
;
* We specified padding xfer so we ended with either a phase
* change interrupt (normal case) or an error interrupt (handled
* elsewhere). Reset the board dma logic then try to get the
* completion status & command done msg. The reset confuses
* the SPC REQ/ACK logic so we have to do any status/msg input
* operations via 'manual xfer'.
if (hd
->scsi_ssts
& SSTS_BUSY
) {
int wait
= scsi_cmd_wait
;
/* wait for dma operation to finish */
while (hd
->scsi_ssts
& SSTS_BUSY
) {
printf("finishxfer fail: ssts %x\n",
hd
->scsi_scmd
|= SCMD_PROG_XFR
;
hd
->scsi_sctl
|= SCTL_CTRLRST
;
hd
->scsi_sctl
&=~ SCTL_CTRLRST
;
* The following delay is definitely needed when trying to
* write on a write protected disk (in the optical jukebox anyways),
* but we shall see if other unexplained machine freezeups
* also stop occuring... A value of 5 seems to work but
* 10 seems safer considering the potential consequences.
hd
->scsi_ints
= ints
= hd
->scsi_ints
;
phase
= hd
->scsi_psns
& PHASE
;
if (mxfer_in(hd
, sizeof(hs
->sc_stat
), hs
->sc_stat
,
if (mxfer_in(hd
, sizeof(hs
->sc_msg
), hs
->sc_msg
,
printf("scsi%d: unexpected phase %d in finishxfer from %d\n",
hs
->sc_hc
->hp_unit
, phase
, target
);
if (ints
= hd
->scsi_ints
) {
else if (ints
& ~(INTS_SRV_REQ
|INTS_CMD_DONE
)) {
if ((hd
->scsi_ssts
& SSTS_INITIATOR
) == 0)
scsiabort(hs
, hd
, "finishxfer");
scsi_test_unit_rdy(ctlr
, slave
, unit
)
register struct scsi_softc
*hs
= &scsi_softc
[ctlr
];
static struct scsi_cdb6 cdb
= { CMD_TEST_UNIT_READY
};
return (scsiicmd(hs
, slave
, &cdb
, sizeof(cdb
), (u_char
*)0, 0,
scsi_request_sense(ctlr
, slave
, unit
, buf
, len
)
register struct scsi_softc
*hs
= &scsi_softc
[ctlr
];
static struct scsi_cdb6 cdb
= { CMD_REQUEST_SENSE
};
return (scsiicmd(hs
, slave
, &cdb
, sizeof(cdb
), buf
, len
, DATA_IN_PHASE
));
scsi_immed_command(ctlr
, slave
, unit
, cdb
, buf
, len
, rd
)
struct scsi_fmt_cdb
*cdb
;
register struct scsi_softc
*hs
= &scsi_softc
[ctlr
];
cdb
->cdb
[1] |= unit
<< 5;
return (scsiicmd(hs
, slave
, cdb
->cdb
, cdb
->len
, buf
, len
,
rd
!= 0? DATA_IN_PHASE
: DATA_OUT_PHASE
));
* The following routines are test-and-transfer i/o versions of read/write
* for things like reading disk labels and writing core dumps. The
* routine scsigo should be used for normal data transfers, NOT these
scsi_tt_read(ctlr
, slave
, unit
, buf
, len
, blk
, bshift
)
register struct scsi_softc
*hs
= &scsi_softc
[ctlr
];
int old_wait
= scsi_data_wait
;
bzero(&cdb
, sizeof(cdb
));
cdb
.lenh
= len
>> (8 + DEV_BSHIFT
+ bshift
);
cdb
.lenl
= len
>> (DEV_BSHIFT
+ bshift
);
stat
= scsiicmd(hs
, slave
, &cdb
, sizeof(cdb
), buf
, len
, DATA_IN_PHASE
);
scsi_data_wait
= old_wait
;
scsi_tt_write(ctlr
, slave
, unit
, buf
, len
, blk
, bshift
)
register struct scsi_softc
*hs
= &scsi_softc
[ctlr
];
int old_wait
= scsi_data_wait
;
bzero(&cdb
, sizeof(cdb
));
cdb
.lenh
= len
>> (8 + DEV_BSHIFT
+ bshift
);
cdb
.lenl
= len
>> (DEV_BSHIFT
+ bshift
);
stat
= scsiicmd(hs
, slave
, &cdb
, sizeof(cdb
), buf
, len
, DATA_OUT_PHASE
);
scsi_data_wait
= old_wait
;
register struct devqueue
*dq
;
register struct devqueue
*hq
;
hq
= &scsi_softc
[dq
->dq_ctlr
].sc_sq
;
register struct scsi_softc
*hs
= &scsi_softc
[unit
];
hs
->sc_dq
.dq_ctlr
= DMA0
| DMA1
;
hs
->sc_flags
|= SCSI_HAVEDMA
;
register struct devqueue
*dq
;
dq
= scsi_softc
[unit
].sc_sq
.dq_forw
;
(dq
->dq_driver
->d_go
)(dq
->dq_unit
);
scsigo(ctlr
, slave
, unit
, bp
, cdb
, pad
)
struct scsi_fmt_cdb
*cdb
;
register struct scsi_softc
*hs
= &scsi_softc
[ctlr
];
volatile register struct scsidevice
*hd
=
(struct scsidevice
*)hs
->sc_hc
->hp_addr
;
cdb
->cdb
[1] |= unit
<< 5;
/* select the SCSI bus (it's an error if bus isn't free) */
if (issue_select(hd
, slave
, hs
->sc_scsi_addr
) || wait_for_select(hd
)) {
if (hs
->sc_flags
& SCSI_HAVEDMA
) {
hs
->sc_flags
&=~ SCSI_HAVEDMA
;
* Wait for a phase change (or error) then let the device
* sequence us through command phase (we may have to take
* a msg in/out before doing the command). If the disk has
* to do a seek, it may be a long time until we get a change
* to data phase so, in the absense of an explicit phase
* change, we assume data phase will be coming up and tell
* the SPC to start a transfer whenever it does. We'll get
* a service required interrupt later if this assumption is
* wrong. Otherwise we'll get a service required int when
* the transfer changes to status phase.
register int wait
= scsi_cmd_wait
;
if (ixfer_start(hd
, cdb
->len
, phase
, wait
))
if (ixfer_out(hd
, cdb
->len
, cdb
->cdb
))
if (ixfer_start(hd
, sizeof(hs
->sc_msg
), phase
, wait
)||
!(hd
->scsi_ssts
& SSTS_DREG_EMPTY
)) {
ixfer_in(hd
, sizeof(hs
->sc_msg
), hs
->sc_msg
);
hd
->scsi_scmd
= SCMD_RST_ACK
;
printf("scsi%d: unexpected phase %d in go from %d\n",
hs
->sc_hc
->hp_unit
, phase
, slave
);
while ((ints
= hd
->scsi_ints
) == 0) {
phase
= hd
->scsi_psns
& PHASE
;
else if (ints
& INTS_CMD_DONE
)
* Reset the card dma logic, setup the dma channel then
* get the dio part of the card set for a dma xfer.
if (bp
->b_flags
& B_READ
)
if ((hs
->sc_flags
& SCSI_DMA32
) &&
((int)bp
->b_un
.b_addr
& 3) == 0 && (bp
->b_bcount
& 3) == 0) {
dmago(hs
->sc_dq
.dq_ctlr
, bp
->b_un
.b_addr
, bp
->b_bcount
, dmaflags
);
if (bp
->b_flags
& B_READ
) {
* DMA enable bits must be set after size and direction bits.
hd
->scsi_csr
|= (CSR_DE0
<< hs
->sc_dq
.dq_ctlr
);
* Setup the SPC for the transfer. We don't want to take
* first a command complete then a service required interrupt
* at the end of the transfer so we try to disable the cmd
* complete by setting the transfer counter to more bytes
* than we expect. (XXX - This strategy may have to be
* modified to deal with devices that return variable length
* blocks, e.g., some tape drives.)
i
= (unsigned)bp
->b_bcount
;
* XXX - If we don't do this, the last 2 or 4 bytes
* (depending on word/lword DMA) of a read get trashed.
* It looks like it is necessary for the DMA to complete
* before the SPC goes into "pad mode"??? Note: if we
* also do this on a write, the request never completes.
if (bp
->b_flags
& B_READ
)
hs
->sc_flags
|= SCSI_PAD
;
printf("scsi%d: odd byte count: %d bytes @ %d\n",
hs
->sc_flags
&=~ SCSI_HAVEDMA
;
volatile register struct scsidevice
*hd
=
(struct scsidevice
*)scsi_softc
[unit
].sc_hc
->hp_addr
;
printf("scsi%d: done called!\n");
/* dma operation is done -- turn off card dma */
hd
->scsi_csr
&=~ (CSR_DE1
|CSR_DE0
);
register struct scsi_softc
*hs
= &scsi_softc
[unit
];
volatile register struct scsidevice
*hd
=
(struct scsidevice
*)hs
->sc_hc
->hp_addr
;
register struct devqueue
*dq
;
if ((hd
->scsi_csr
& (CSR_IE
|CSR_IR
)) != (CSR_IE
|CSR_IR
))
if ((ints
& INTS_SRV_REQ
) && (hs
->sc_flags
& SCSI_IO
)) {
* this should be the normal i/o completion case.
* get the status & cmd complete msg then let the
* device driver look at what happened.
int len
= (hd
->scsi_tch
<< 16) | (hd
->scsi_tcm
<< 8) |
if (!(hs
->sc_flags
& SCSI_PAD
))
hs
->sc_flags
&=~ SCSI_PAD
;
finishxfer(hs
, hd
, dq
->dq_slave
);
hs
->sc_flags
&=~ (SCSI_IO
|SCSI_HAVEDMA
);
(dq
->dq_driver
->d_intr
)(dq
->dq_unit
, hs
->sc_stat
[0]);
/* Something unexpected happened -- deal with it. */
scsiabort(hs
, hd
, "intr");
if (hs
->sc_flags
& SCSI_IO
) {
hs
->sc_flags
&=~ (SCSI_IO
|SCSI_HAVEDMA
);
(dq
->dq_driver
->d_intr
)(dq
->dq_unit
, -1);
register struct devqueue
*dq
;
register struct devqueue
*hq
;
hq
= &scsi_softc
[dq
->dq_ctlr
].sc_sq
;
if ((dq
= hq
->dq_forw
) != hq
)
(dq
->dq_driver
->d_start
)(dq
->dq_unit
);
* (XXX) The following routine is needed for the SCSI tape driver
* to read odd-size records.
scsi_tt_oddio(ctlr
, slave
, unit
, buf
, len
, b_flags
, freedma
)
int ctlr
, slave
, unit
, b_flags
;
register struct scsi_softc
*hs
= &scsi_softc
[ctlr
];
if (freedma
&& (hs
->sc_flags
& SCSI_HAVEDMA
) == 0 ||
!freedma
&& (hs
->sc_flags
& SCSI_HAVEDMA
))
printf("oddio: freedma (%d) inconsistency (flags=%x)\n",
* First free any DMA channel that was allocated.
* We can't use DMA to do this transfer.
hs
->sc_flags
&=~ SCSI_HAVEDMA
;
* Initialize command block
bzero(&cdb
, sizeof(cdb
));
cdb
.lbam
= (len
>> 16) & 0xff;
cdb
.lbal
= (len
>> 8) & 0xff;
} else if (b_flags
& B_READ
) {
* Perform command (with very long delays)
stat
= scsiicmd(hs
, slave
, &cdb
, sizeof(cdb
), buf
, len
, iphase
);