summaryrefslogtreecommitdiff
path: root/sys/dev/atapiscsi/atapiscsi.c
diff options
context:
space:
mode:
authorConstantine Sapuntzakis <csapuntz@cvs.openbsd.org>1999-12-11 10:15:04 +0000
committerConstantine Sapuntzakis <csapuntz@cvs.openbsd.org>1999-12-11 10:15:04 +0000
commitcd988a82bb394117dcbba6cdf3dca12f3a5794e9 (patch)
treec07adeb8c05e0192729cc1c042b08af9e4b62f64 /sys/dev/atapiscsi/atapiscsi.c
parent14e60a3375593a9e2c2d617dd07085701f83eb35 (diff)
ATAPI Tape drive support. Should be considered experimental.
Major restructuring of the code path that sends ATAPI packet commands. One set of functions works in polling, process, and interrupt contexts.
Diffstat (limited to 'sys/dev/atapiscsi/atapiscsi.c')
-rw-r--r--sys/dev/atapiscsi/atapiscsi.c910
1 files changed, 543 insertions, 367 deletions
diff --git a/sys/dev/atapiscsi/atapiscsi.c b/sys/dev/atapiscsi/atapiscsi.c
index 5938166e8ee..98d2816ce1e 100644
--- a/sys/dev/atapiscsi/atapiscsi.c
+++ b/sys/dev/atapiscsi/atapiscsi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: atapiscsi.c,v 1.19 1999/11/17 01:22:55 csapuntz Exp $ */
+/* $OpenBSD: atapiscsi.c,v 1.20 1999/12/11 10:15:02 csapuntz Exp $ */
/*
* This code is derived from code with the copyright below.
@@ -77,6 +77,7 @@
#define DEBUG_POLL 0x40
#define DEBUG_ERRORS 0x80 /* Debug error handling code */
+#define WDCDEBUG
#ifdef WDCDEBUG
int wdcdebug_atapi_mask = 0;
#define WDCDEBUG_PRINT(args, level) \
@@ -89,18 +90,46 @@ int wdcdebug_atapi_mask = 0;
/* 10 ms, this is used only before sending a cmd. */
#define ATAPI_DELAY 10
#define ATAPI_RESET_WAIT 2000
+#define ATAPI_CTRL_WAIT 4000
/* When polling, let the exponential backoff max out at 1 second's interval. */
#define ATAPI_POLL_MAXTIC (hz)
void wdc_atapi_minphys __P((struct buf *bp));
void wdc_atapi_start __P((struct channel_softc *,struct wdc_xfer *));
-int wdc_atapi_intr __P((struct channel_softc *, struct wdc_xfer *, int));
+
+void timer_handler __P((void *));
+
+int wdc_atapi_real_start __P((struct channel_softc *, struct wdc_xfer *,
+ int));
+int wdc_atapi_real_start_2 __P((struct channel_softc *, struct wdc_xfer *,
+ int));
+int wdc_atapi_intr_command __P((struct channel_softc *, struct wdc_xfer *,
+ int));
+
+int wdc_atapi_intr_data __P((struct channel_softc *, struct wdc_xfer *,
+ int));
+int wdc_atapi_intr_complete __P((struct channel_softc *, struct wdc_xfer *,
+ int));
+
+int wdc_atapi_intr_for_us __P((struct channel_softc *, struct wdc_xfer *,
+ int));
+
+int wdc_atapi_send_packet __P((struct channel_softc *, struct wdc_xfer *,
+ int));
+int wdc_atapi_dma_flags __P((struct wdc_xfer *));
+
int wdc_atapi_ctrl __P((struct channel_softc *, struct wdc_xfer *, int));
-void wdc_atapi_done __P((struct channel_softc *, struct wdc_xfer *));
-void wdc_atapi_reset __P((struct channel_softc *, struct wdc_xfer *));
+int wdc_atapi_in_data_phase __P((struct wdc_xfer *, int, int));
+
+int wdc_atapi_intr __P((struct channel_softc *, struct wdc_xfer *, int));
+
+int wdc_atapi_done __P((struct channel_softc *, struct wdc_xfer *, int));
+int wdc_atapi_reset __P((struct channel_softc *, struct wdc_xfer *, int));
+int wdc_atapi_reset_2 __P((struct channel_softc *, struct wdc_xfer *, int));
int wdc_atapi_send_cmd __P((struct scsi_xfer *sc_xfer));
+int wdc_atapi_tape_done __P((struct channel_softc *, struct wdc_xfer *, int));
#define MAX_SIZE MAXPHYS
struct atapiscsi_softc;
@@ -113,6 +142,7 @@ int wdc_atapi_get_params __P((struct channel_softc *, u_int8_t, struct ataparams
int atapi_dsc_wait __P((struct ata_drive_datas *, int));
int atapi_dsc_ready __P((void *));
+void atapi_dsc_check __P((void *));
int atapi_dsc_semiready __P((void *));
int atapi_poll_wait __P((int (*) __P((void *)), void *, int, int, char *));
void atapi_to_scsi_sense __P((struct scsi_xfer *, u_int8_t));
@@ -359,11 +389,7 @@ wdc_atapi_send_cmd(sc_xfer)
struct ata_drive_datas *drvp = &chp->ch_drive[drive];
struct wdc_xfer *xfer;
int flags = sc_xfer->flags;
- int s, ret, saved_datalen;
- char saved_len_bytes[3];
-
-restart:
- saved_datalen = 0;
+ int s, ret;
WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drive), DEBUG_XFERS);
@@ -392,31 +418,32 @@ restart:
if (drvp->atapi_cap & ACAP_DSC) {
WDCDEBUG_PRINT(("about to send cmd %x ", sc_xfer->cmd->opcode),
DEBUG_DSC);
- xfer->c_flags |= C_NEEDDONE;
switch (sc_xfer->cmd->opcode) {
case READ:
case WRITE:
+ xfer->c_flags |= C_MEDIA_ACCESS;
+
/* If we are not in buffer availability mode,
we limit the first request to 0 bytes, which
gets us into buffer availability mode without
holding the bus. */
if (!(drvp->drive_flags & DRIVE_DSCBA)) {
- saved_datalen = sc_xfer->datalen;
- xfer->c_flags &= ~C_NEEDDONE;
- sc_xfer->datalen = xfer->c_bcount = 0;
- bcopy(
- ((struct scsi_rw_tape *)sc_xfer->cmd)->len,
- saved_len_bytes, 3);
+ xfer->c_bcount = 0;
+ xfer->transfer_len =
+ _3btol(((struct scsi_rw_tape *)
+ sc_xfer->cmd)->len);
_lto3b(0,
((struct scsi_rw_tape *)
sc_xfer->cmd)->len);
+ xfer->c_done = wdc_atapi_tape_done;
WDCDEBUG_PRINT(
("R/W in completion mode, do 0 blocks\n"),
DEBUG_DSC);
} else
WDCDEBUG_PRINT(("R/W %d blocks %d bytes\n",
- _3btol(((struct scsi_rw_tape *)
- sc_xfer->cmd)->len), sc_xfer->datalen),
+ _3btol(((struct scsi_rw_tape *)
+ sc_xfer->cmd)->len),
+ sc_xfer->datalen),
DEBUG_DSC);
/* DSC will change to buffer availability mode.
@@ -427,15 +454,13 @@ restart:
case LOAD:
case REWIND:
case SPACE:
+ case WRITE_FILEMARKS:
#if 0
case LOCATE:
case READ_POSITION:
- case WRITE_FILEMARK:
#endif
- /* DSC will change to command completion mode.
- We can reflect this early. */
- drvp->drive_flags &= ~DRIVE_DSCBA;
- WDCDEBUG_PRINT(("clear DCSBA\n"), DEBUG_DSC);
+
+ xfer->c_flags |= C_MEDIA_ACCESS;
break;
default:
@@ -445,19 +470,10 @@ restart:
wdc_exec_xfer(chp, xfer);
#ifdef DIAGNOSTIC
- if (((sc_xfer->flags & SCSI_POLL) != 0 ||
- (drvp->atapi_cap & ACAP_DSC) != 0) &&
+ if ((xfer->c_flags & C_POLL) != 0 &&
(sc_xfer->flags & ITSDONE) == 0)
panic("wdc_atapi_send_cmd: polled command not done");
#endif
- if ((drvp->atapi_cap & ACAP_DSC) && saved_datalen != 0) {
- sc_xfer->datalen = saved_datalen;
- bcopy(saved_len_bytes,
- ((struct scsi_rw_tape *)sc_xfer->cmd)->len, 3);
- sc_xfer->flags &= ~ITSDONE;
- splx(s);
- goto restart;
- }
ret = (sc_xfer->flags & ITSDONE) ? COMPLETE : SUCCESSFULLY_QUEUED;
splx(s);
return (ret);
@@ -500,14 +516,215 @@ atapi_to_scsi_sense(xfer, flags)
}
+enum atapi_context {
+ ctxt_process = 0,
+ ctxt_timer = 1,
+ ctxt_interrupt = 2
+};
+
+int wdc_atapi_the_machine __P((struct channel_softc *, struct wdc_xfer *,
+ enum atapi_context));
+
+int wdc_atapi_the_poll_machine __P((struct channel_softc *, struct wdc_xfer *));
+
void
wdc_atapi_start(chp, xfer)
struct channel_softc *chp;
struct wdc_xfer *xfer;
{
+ xfer->next = wdc_atapi_real_start;
+
+ wdc_atapi_the_machine(chp, xfer, ctxt_process);
+}
+
+
+void
+timer_handler(arg)
+ void *arg;
+{
+ struct wdc_xfer *xfer = arg;
+ struct channel_softc *chp = xfer->chp;
+ int s;
+
+ xfer->c_flags &= ~C_POLL_MACHINE;
+
+ /* There is a race here between us and the interrupt */
+ s = splbio();
+ wdc_atapi_the_machine(chp, xfer, ctxt_timer);
+ splx(s);
+}
+
+
+int
+wdc_atapi_intr(chp, xfer, irq)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+ int irq;
+{
+ /* XXX we should consider an alternate signaling regime here */
+ if (xfer->c_flags & C_TIMEOU) {
+ xfer->c_flags &= ~C_TIMEOU;
+ return (wdc_atapi_the_machine(chp, xfer, ctxt_timer));
+ }
+
+ return (wdc_atapi_the_machine(chp, xfer, ctxt_interrupt));
+}
+
+#define CONTINUE_POLL 0
+#define GOTO_NEXT 1
+#define DONE 2
+
+int
+wdc_atapi_the_poll_machine(chp, xfer)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+{
+ int idx = 0, ret;
+ int current_timeout;
+
+ xfer->timeout = -1;
+
+ while (1) {
+ idx++;
+
+ xfer->timeout = -1;
+ xfer->delay = 0;
+ xfer->expect_irq = 0;
+
+ ret = (xfer->next)(chp, xfer, (current_timeout * 1000 <= idx));
+
+ if (xfer->timeout != -1) {
+ current_timeout = xfer->timeout;
+ idx = 0;
+ }
+
+ if (xfer->delay != 0) {
+ while (xfer->delay--)
+ delay(1000);
+ }
+
+ switch (ret) {
+ case GOTO_NEXT:
+ break;
+
+ case CONTINUE_POLL:
+ DELAY(1);
+
+ break;
+
+ case DONE:
+ wdc_free_xfer(chp, xfer);
+ wdcstart(chp);
+ return (0);
+ }
+ }
+}
+
+int
+wdc_atapi_the_machine(chp, xfer, ctxt)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+ enum atapi_context ctxt;
+{
+ int idx = 0, ret;
+ int claim_irq = 0;
+ u_int64_t now = (u_int64_t)time.tv_sec * 1000 + time.tv_usec / 1000;
+ int timeout_delay = hz / 10;
+
+ if (xfer->c_flags & C_POLL) {
+ if (ctxt != ctxt_process)
+ return (0);
+
+ wdc_atapi_the_poll_machine(chp, xfer);
+ return (0);
+ }
+
+ do_op:
+ idx++;
+
+ xfer->timeout = -1;
+ xfer->claim_irq = 0;
+ xfer->delay = 0;
+
+ ret = (xfer->next)(chp, xfer, now >= xfer->endtime);
+
+ if (xfer->timeout != -1) {
+ now = (u_int64_t)time.tv_sec * 1000 +
+ time.tv_usec / 1000;
+
+ xfer->endtime = (u_int64_t)xfer->timeout + now;
+
+ }
+
+ if (xfer->claim_irq) claim_irq = xfer->claim_irq;
+
+ if (xfer->delay) timeout_delay = max(xfer->delay * hz / 1000, 1);
+
+ switch (ret) {
+ case GOTO_NEXT:
+ if (xfer->expect_irq) {
+ chp->ch_flags |= WDCF_IRQ_WAIT;
+ xfer->expect_irq = 0;
+ timeout(wdctimeout, chp,
+ (xfer->endtime - now) * hz / 1000);
+
+ return (claim_irq);
+ }
+
+ if (xfer->delay)
+ break;
+
+ goto do_op;
+
+ case CONTINUE_POLL:
+ if (xfer->delay) break;
+ if (idx >= 50) break;
+
+ DELAY(1);
+ goto do_op;
+
+ case DONE:
+ if (xfer->c_flags & C_POLL_MACHINE)
+ untimeout (timer_handler, xfer);
+
+ wdc_free_xfer(chp, xfer);
+ wdcstart(chp);
+
+ return (claim_irq);
+ }
+
+ timeout(timer_handler, xfer, timeout_delay);
+ xfer->c_flags |= C_POLL_MACHINE;
+ return (claim_irq);
+}
+
+
+void wdc_atapi_update_status __P((struct channel_softc *));
+
+void
+wdc_atapi_update_status(chp)
+ struct channel_softc *chp;
+{
+ chp->ch_status = CHP_READ_REG(chp, wdr_status);
+
+ if (chp->ch_status == 0xff && (chp->ch_flags & WDCF_ONESLAVE)) {
+ CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | 0x10);
+
+ chp->ch_status = CHP_READ_REG(chp, wdr_status);
+ }
+
+ if ((chp->ch_status & (WDCS_BSY | WDCS_ERR)) == WDCS_ERR)
+ chp->ch_error = CHP_READ_REG(chp, wdr_error);
+}
+
+int
+wdc_atapi_real_start(chp, xfer, timeout)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+ int timeout;
+{
struct scsi_xfer *sc_xfer = xfer->cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
- struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc;
WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive,
@@ -515,17 +732,44 @@ wdc_atapi_start(chp, xfer)
/* Adjust C_DMA, it may have changed if we are requesting sense */
if (!(xfer->c_flags & C_POLL) &&
(drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) &&
- (sc_xfer->datalen > 0 || (xfer->c_flags & C_SENSE)))
+ (xfer->c_bcount > 0 || (xfer->c_flags & C_SENSE)))
xfer->c_flags |= C_DMA;
else
xfer->c_flags &= ~C_DMA;
- if (wdc_select_drive(chp, xfer->drive, ATAPI_DELAY) < 0) {
+
+ CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (xfer->drive << 4));
+
+ DELAY(1);
+
+ xfer->next = wdc_atapi_real_start_2;
+ xfer->timeout = ATAPI_DELAY;
+
+ return (GOTO_NEXT);
+}
+
+
+int
+wdc_atapi_real_start_2(chp, xfer, timeout)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+ int timeout;
+{
+ struct scsi_xfer *sc_xfer = xfer->cmd;
+ struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
+
+ if (timeout) {
printf("wdc_atapi_start: not ready, st = %02x\n",
chp->ch_status);
+
sc_xfer->error = XS_TIMEOUT;
- wdc_atapi_reset(chp, xfer);
- return;
+ xfer->next = wdc_atapi_reset;
+ return (GOTO_NEXT);
+ } else {
+ wdc_atapi_update_status(chp);
+
+ if (chp->ch_status & WDCS_BSY)
+ return (CONTINUE_POLL);
}
/* Do control operations specially. */
@@ -536,22 +780,26 @@ wdc_atapi_start(chp, xfer)
xfer->drive, drvp->state);
panic("wdc_atapi_start: bad state");
}
- wdc_atapi_ctrl(chp, xfer, 0);
- return;
- }
-
- if (drvp->atapi_cap & ACAP_DSC) {
- if (atapi_dsc_wait(drvp, sc_xfer->timeout)) {
- sc_xfer->error = XS_TIMEOUT;
- wdc_atapi_reset(chp, xfer);
- return;
- }
- WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, DSC asserted\n",
- chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive),
- DEBUG_DSC);
- drvp->drive_flags &= ~DRIVE_DSCWAIT;
+
+ xfer->next = wdc_atapi_ctrl;
+ return (GOTO_NEXT);
}
+ xfer->next = wdc_atapi_send_packet;
+ return (GOTO_NEXT);
+}
+
+
+int
+wdc_atapi_send_packet(chp, xfer, timeout)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+ int timeout;
+{
+ struct scsi_xfer *sc_xfer = xfer->cmd;
+ struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
+ struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc;
+
/*
* Even with WDCS_ERR, the device should accept a command packet
* Limit length to what can be stuffed into the cylinder register
@@ -569,69 +817,37 @@ wdc_atapi_start(chp, xfer)
as->protocol_phase = as_cmdout;
as->retries = 0;
- /*
- * If there is no interrupt for CMD input, busy-wait for it (done in
- * the interrupt routine. If it is a polled command, call the interrupt
- * routine until command is done.
- */
- if (((drvp->atapi_cap & ATAPI_CFG_DRQ_MASK) != ATAPI_CFG_IRQ_DRQ) ||
- (sc_xfer->flags & SCSI_POLL) || (drvp->atapi_cap & ACAP_DSC)) {
- /* Wait for at last 400ns for status bit to be valid */
- DELAY(1);
- wdc_atapi_intr(chp, xfer, 0);
- } else {
- chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, hz);
- return;
- }
+ DELAY(1);
- if ((sc_xfer->flags & SCSI_POLL) || (drvp->atapi_cap & ACAP_DSC)) {
- while ((sc_xfer->flags & ITSDONE) == 0) {
- if (drvp->atapi_cap & ACAP_DSC) {
- if (atapi_poll_wait(
- (drvp->drive_flags & DRIVE_DSCWAIT) ?
- atapi_dsc_ready : atapi_dsc_semiready,
- drvp, sc_xfer->timeout, PZERO + PCATCH,
- "atapist")) {
- sc_xfer->error = XS_TIMEOUT;
- wdc_atapi_reset(chp, xfer);
- return;
- }
- } else
- /* Wait for at last 400ns for status bit to
- be valid */
- DELAY(1);
+ xfer->next = wdc_atapi_intr_for_us;
+ xfer->timeout = sc_xfer->timeout;
- wdc_atapi_intr(chp, xfer, 0);
- }
+ if ((drvp->atapi_cap & ATAPI_CFG_DRQ_MASK) == ATAPI_CFG_IRQ_DRQ) {
+ /* We expect an IRQ to tell us of the next state */
+ xfer->expect_irq = 1;
}
+ return (GOTO_NEXT);
}
int
-atapi_dsc_semiready(arg)
- void *arg;
+wdc_atapi_dma_flags(xfer)
+ struct wdc_xfer *xfer;
{
- struct ata_drive_datas *drvp = arg;
- struct channel_softc *chp = drvp->chnl_softc;
+ struct scsi_xfer *sc_xfer = xfer->cmd;
+ int dma_flags;
- /* We should really wait_for_unbusy here too before
- switching drives. */
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drvp->drive << 4));
+ dma_flags = ((sc_xfer->flags & SCSI_DATA_IN) ||
+ (xfer->c_flags & C_SENSE)) ? WDC_DMA_READ : 0;
+ dma_flags |= (xfer->c_flags & C_POLL) ? WDC_DMA_POLL : 0;
- return (wait_for_unbusy(chp, 0) == 0);
+ return (dma_flags);
}
-
-int wdc_atapi_intr_command __P((struct channel_softc *, struct wdc_xfer *, int));
-int wdc_atapi_intr_data __P((struct channel_softc *, struct wdc_xfer *, int));
-int wdc_atapi_intr_complete __P((struct channel_softc *, struct wdc_xfer *, int));
-
-
int
-wdc_atapi_intr_command(chp, xfer, dma_flags)
+wdc_atapi_intr_command(chp, xfer, timeout)
struct channel_softc *chp;
struct wdc_xfer *xfer;
- int dma_flags;
+ int timeout;
{
struct scsi_xfer *sc_xfer = xfer->cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
@@ -640,6 +856,10 @@ wdc_atapi_intr_command(chp, xfer, dma_flags)
u_int8_t cmd[16];
struct scsi_sense *cmd_reqsense;
int cmdlen = (drvp->atapi_cap & ACAP_LEN) ? 16 : 12;
+ int dma_flags;
+
+ xfer->claim_irq = 1;
+ dma_flags = wdc_atapi_dma_flags(xfer);
bzero(cmd, sizeof(cmd));
@@ -660,8 +880,9 @@ wdc_atapi_intr_command(chp, xfer, dma_flags)
chp->channel, xfer->drive, xfer->databuf,
xfer->c_bcount, dma_flags) != 0) {
sc_xfer->error = XS_DRIVER_STUFFUP;
- wdc_atapi_done(chp, xfer);
- return (1);
+
+ xfer->next = wdc_atapi_done;
+ return (GOTO_NEXT);
}
}
@@ -676,57 +897,38 @@ wdc_atapi_intr_command(chp, xfer, dma_flags)
dma_flags);
}
- if ((sc_xfer->flags & SCSI_POLL) == 0 &&
- (drvp->atapi_cap & ACAP_DSC) == 0) {
- chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000);
- }
+ xfer->expect_irq = 1;
/* If we read/write to a tape we will get into buffer
availability mode. */
if (drvp->atapi_cap & ACAP_DSC) {
- if (!(drvp->drive_flags & DRIVE_DSCBA) &&
- (sc_xfer->cmd->opcode == READ ||
- sc_xfer->cmd->opcode == WRITE)) {
+ if ((sc_xfer->cmd->opcode == READ ||
+ sc_xfer->cmd->opcode == WRITE)) {
drvp->drive_flags |= DRIVE_DSCBA;
WDCDEBUG_PRINT(("set DSCBA\n"), DEBUG_DSC);
+ } else if ((xfer->c_flags & C_MEDIA_ACCESS) &&
+ (drvp->drive_flags & DRIVE_DSCBA)) {
+ /* Clause 3.2.4 of QIC-157 D.
+
+ Any media access command other than read or
+ write will switch DSC back to completion
+ mode */
+ drvp->drive_flags &= ~DRIVE_DSCBA;
+ WDCDEBUG_PRINT(("clear DCSBA\n"), DEBUG_DSC);
}
- if (sc_xfer->cmd->opcode == READ)
- drvp->drive_flags |= DRIVE_DSCWAIT;
}
- return (1);
+ return (GOTO_NEXT);
}
+
int
-wdc_atapi_intr_data(chp, xfer, dma_flags)
- struct channel_softc *chp;
+wdc_atapi_in_data_phase(xfer, len, ire)
struct wdc_xfer *xfer;
- int dma_flags;
-
+ int len, ire;
{
struct scsi_xfer *sc_xfer = xfer->cmd;
- struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
- struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc;
- int len, ire;
- char *message = 0;
-
- len = (CHP_READ_REG(chp, wdr_cyl_hi) << 8) |
- CHP_READ_REG(chp, wdr_cyl_lo);
- ire = CHP_READ_REG(chp, wdr_ireason);
-
- WDCDEBUG_PRINT(("wdc_atapi_intr: c_bcount %d len %d st 0x%x err 0x%x "
- "ire 0x%x :", xfer->c_bcount,
- len, chp->ch_status, chp->ch_error, ire), DEBUG_INTR);
-
- /* Possibility to explore; what if we get an interrupt
- during DMA ? */
- if ((xfer->c_flags & C_DMA) != 0) {
- if ((xfer->c_flags & C_TIMEOU) == 0)
- message = "unexpected interrupt during DMA mode";
-
- goto unexpected_state;
- }
+ char *message;
if (ire & WDCI_CMD) {
message = "unexpectedly in command phase";
@@ -738,7 +940,7 @@ wdc_atapi_intr_data(chp, xfer, dma_flags)
message = "data phase where none expected";
goto unexpected_state;
}
-
+
/* Make sure polarities match */
if (((ire & WDCI_IN) == WDCI_IN) ==
((sc_xfer->flags & SCSI_DATA_OUT) == SCSI_DATA_OUT)) {
@@ -757,7 +959,36 @@ wdc_atapi_intr_data(chp, xfer, dma_flags)
goto unexpected_state;
}
+
+ return (1);
+
+ unexpected_state:
+
+ return (0);
+}
+
+int
+wdc_atapi_intr_data(chp, xfer, timeout)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+ int timeout;
+{
+ struct scsi_xfer *sc_xfer = xfer->cmd;
+ struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
+ int len, ire;
+
+ len = (CHP_READ_REG(chp, wdr_cyl_hi) << 8) |
+ CHP_READ_REG(chp, wdr_cyl_lo);
+ ire = CHP_READ_REG(chp, wdr_ireason);
+
+ if (!wdc_atapi_in_data_phase(xfer, len, ire))
+ return (CONTINUE_POLL);
+
if (xfer->c_bcount >= len) {
+ WDCDEBUG_PRINT(("wdc_atapi_intr: c_bcount %d len %d st 0x%x err 0x%x "
+ "ire 0x%x\n", xfer->c_bcount,
+ len, chp->ch_status, chp->ch_error, ire), DEBUG_INTR);
+
/* Common case */
if (sc_xfer->flags & SCSI_DATA_OUT)
wdc_output_bytes(drvp, (u_int8_t *)xfer->databuf +
@@ -793,60 +1024,25 @@ wdc_atapi_intr_data(chp, xfer, dma_flags)
xfer->c_bcount = 0;
}
- if ((sc_xfer->flags & SCSI_POLL) == 0 &&
- (drvp->atapi_cap & ACAP_DSC) == 0) {
- chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000);
- } else if (drvp->atapi_cap & ACAP_DSC)
- drvp->drive_flags |= DRIVE_DSCWAIT;
- return (1);
-
- unexpected_state:
+ xfer->expect_irq = 1;
+ xfer->next = wdc_atapi_intr_for_us;
- /* If we're in polling mode, then it's possible we caught
- a drive in some awkward, intermediate state. Of course,
- the drive should have BSY set while it's transitioning
- through awkward states, but this may not always
- be the case. */
-
- /* Spurious interrupts can cause us pain too and we don't deal
- with those nearly as well. We assume the spurious
- interrupts are random and won't affect us on retry */
- if (xfer->c_flags & C_POLL) {
- DELAY(1000);
- as->retries++;
- /* Give the drive up to 2 seconds to fix itself */
- if (as->retries <= 2000)
- return (1);
- }
-
- if (message)
- printf ("wdc_atapi_intr_drq: %s\n", message);
-
- if (xfer->c_flags & C_DMA) {
- (*chp->wdc->dma_finish)(chp->wdc->dma_arg,
- chp->channel, xfer->drive, dma_flags);
- drvp->n_dmaerrs++;
- }
-
- sc_xfer->error = XS_RESET;
- wdc_atapi_reset(chp, xfer);
-
- return (1);
+ return (GOTO_NEXT);
}
int
-wdc_atapi_intr_complete(chp, xfer, dma_flags)
+wdc_atapi_intr_complete(chp, xfer, timeout)
struct channel_softc *chp;
struct wdc_xfer *xfer;
- int dma_flags;
+ int timeout;
{
struct scsi_xfer *sc_xfer = xfer->cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc;
int dma_err = 0;
+ int dma_flags = wdc_atapi_dma_flags(xfer);
WDCDEBUG_PRINT(("PHASE_COMPLETED\n"), DEBUG_INTR);
@@ -856,12 +1052,8 @@ wdc_atapi_intr_complete(chp, xfer, dma_flags)
dma_err = (*chp->wdc->dma_finish)(chp->wdc->dma_arg,
chp->channel, xfer->drive, dma_flags);
- /* Assume everything was transferred
- XXX - maybe we want to check the error register here */
- if (xfer->c_flags & C_SENSE)
- xfer->c_bcount -= sizeof(sc_xfer->sense);
- else
- xfer->c_bcount -= sc_xfer->datalen;
+ /* Assume everything was transferred */
+ xfer->c_bcount = 0;
}
as->protocol_phase = as_none;
@@ -872,8 +1064,8 @@ wdc_atapi_intr_complete(chp, xfer, dma_flags)
WDCDEBUG_PRINT(("wdc_atapi_intr: request_sense aborted, "
"calling wdc_atapi_done()"
), DEBUG_INTR);
- wdc_atapi_done(chp, xfer);
- return (1);
+ xfer->next = wdc_atapi_done;
+ return (GOTO_NEXT);
}
/*
@@ -882,8 +1074,9 @@ wdc_atapi_intr_complete(chp, xfer, dma_flags)
*/
sc_xfer->error = XS_RESET;
- wdc_atapi_reset(chp, xfer);
- return (1);
+ xfer->next = wdc_atapi_reset;
+ return (GOTO_NEXT);
+
} else if (xfer->c_bcount < sizeof(sc_xfer->sense)) {
/* use the sense we just read */
sc_xfer->error = XS_SENSE;
@@ -907,21 +1100,22 @@ wdc_atapi_intr_complete(chp, xfer, dma_flags)
* 'request sense'
*/
xfer->databuf = &sc_xfer->sense;
- xfer->c_bcount =
- sizeof(sc_xfer->sense);
+ xfer->c_bcount = sizeof(sc_xfer->sense);
xfer->c_skip = 0;
+ xfer->c_done = NULL;
xfer->c_flags |= C_SENSE;
- wdc_atapi_start(chp, xfer);
- return (1);
+ xfer->next = wdc_atapi_real_start;
+ return (GOTO_NEXT);
}
- }
+ }
}
if (dma_err < 0) {
drvp->n_dmaerrs++;
sc_xfer->error = XS_RESET;
- wdc_atapi_reset(chp, xfer);
- return (1);
+
+ xfer->next = wdc_atapi_reset;
+ return (GOTO_NEXT);
}
@@ -939,98 +1133,113 @@ wdc_atapi_intr_complete(chp, xfer, dma_flags)
WDCDEBUG_PRINT(("wdc_atapi_intr: wdc_atapi_done() (end), error 0x%x "
"\n", sc_xfer->error),
DEBUG_INTR);
- wdc_atapi_done(chp, xfer);
- return (1);
+
+
+ if (xfer->c_done)
+ xfer->next = xfer->c_done;
+ else
+ xfer->next = wdc_atapi_done;
+
+ return (GOTO_NEXT);
}
int
-wdc_atapi_intr(chp, xfer, irq)
+wdc_atapi_intr_for_us(chp, xfer, timeout)
struct channel_softc *chp;
struct wdc_xfer *xfer;
- int irq;
+ int timeout;
+
{
struct scsi_xfer *sc_xfer = xfer->cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc;
- int dma_flags = 0;
- WDCDEBUG_PRINT(("wdc_atapi_intr %s:%d:%d\n",
- chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive),
- DEBUG_INTR);
+ WDCDEBUG_PRINT(("ATAPI_INTR\n"), DEBUG_INTR);
- /* Is it not a transfer, but a control operation? */
- if (drvp->state < READY) {
- printf("%s:%d:%d: bad state %d in wdc_atapi_intr\n",
- chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
- drvp->state);
- panic("wdc_atapi_intr: bad state\n");
- }
+ /* If we missed an IRQ and were using DMA, flag it as a DMA error */
/* We should really wait_for_unbusy here too before
switching drives. */
CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (xfer->drive << 4));
+ DELAY (1);
- /* Ack interrupt done in wait_for_unbusy */
- if (wait_for_unbusy(chp,
- (irq == 0) ? sc_xfer->timeout : 0) != 0) {
- if (irq && (xfer->c_flags & C_TIMEOU) == 0)
- return (0); /* IRQ was not for us */
- printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d\n",
- chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
- xfer->c_bcount, xfer->c_skip);
- if (xfer->c_flags & C_DMA)
- drvp->n_dmaerrs++;
- sc_xfer->error = XS_TIMEOUT;
- wdc_atapi_reset(chp, xfer);
- return (1);
+ wdc_atapi_update_status(chp);
+
+ if (chp->ch_status & WDCS_BSY) {
+ if (timeout) {
+ printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d\n",
+ chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
+ xfer->c_bcount, xfer->c_skip);
+
+ if (xfer->c_flags & C_DMA)
+ drvp->n_dmaerrs++;
+
+ sc_xfer->error = XS_TIMEOUT;
+ xfer->next = wdc_atapi_reset;
+ return (GOTO_NEXT);
+ } else {
+ return (CONTINUE_POLL);
+ }
}
- /* If we missed an IRQ and were using DMA, flag it as a DMA error */
- if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA))
- drvp->n_dmaerrs++;
- /* DRQ was dropped. This means the command is over.
- Do cleanup, check for errors, etc. */
- if (xfer->c_flags & C_DMA) {
- dma_flags = ((sc_xfer->flags & SCSI_DATA_IN) ||
- (xfer->c_flags & C_SENSE)) ? WDC_DMA_READ : 0;
- dma_flags |= ((sc_xfer->flags & SCSI_POLL) ||
- (drvp->atapi_cap & ACAP_DSC)) ? WDC_DMA_POLL : 0;
+ if (as->protocol_phase != as_cmdout &&
+ (xfer->c_flags & C_MEDIA_ACCESS) &&
+ !(chp->ch_status & WDCS_DSC)) {
+ xfer->delay = 100;
+ return (CONTINUE_POLL);
}
+ xfer->claim_irq = 1;
+
+ if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA))
+ drvp->n_dmaerrs++;
+
if (chp->ch_status & WDCS_DRQ) {
if (as->protocol_phase == as_cmdout)
- return (wdc_atapi_intr_command(chp, xfer, dma_flags));
+ return (wdc_atapi_intr_command(chp, xfer, timeout));
+
+ if (!(xfer->c_flags & C_DMA))
+ return (wdc_atapi_intr_data(chp, xfer, timeout));
else
- return (wdc_atapi_intr_data(chp, xfer, dma_flags));
- } else
- return (wdc_atapi_intr_complete(chp, xfer, dma_flags));
+ return (CONTINUE_POLL);
+ }
+
+ return (wdc_atapi_intr_complete(chp, xfer, timeout));
}
int
-wdc_atapi_ctrl(chp, xfer, irq)
+wdc_atapi_ctrl(chp, xfer, timeout)
struct channel_softc *chp;
struct wdc_xfer *xfer;
- int irq;
+ int timeout;
{
struct scsi_xfer *sc_xfer = xfer->cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
char *errstring = NULL;
- int delay;
/* Ack interrupt done in wait_for_unbusy */
-again:
- delay = (xfer->c_flags & C_POLL) ? 4000 : ((irq == 0) ? ATAPI_DELAY : 1);
+ CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (xfer->drive << 4));
+ delay (1);
+
+ if (timeout) {
+ if (drvp->state == IDENTIFY_WAIT ||
+ drvp->state == PIOMODE ||
+ drvp->state == PIOMODE_WAIT ||
+ drvp->state == DMAMODE_WAIT)
+ goto timeout;
+ } else {
+ wdc_atapi_update_status(chp);
+
+ if (chp->ch_status & WDCS_BSY)
+ return (CONTINUE_POLL);
+ }
+
+ xfer->claim_irq = 1;
+
WDCDEBUG_PRINT(("wdc_atapi_ctrl %s:%d:%d state %d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive, drvp->state),
DEBUG_INTR | DEBUG_FUNCS);
- /* We shouldn't have to select the drive in these states.
- If we do, there are other, more serious problems */
- if (drvp->state != IDENTIFY_WAIT &&
- drvp->state != PIOMODE_WAIT &&
- drvp->state != DMAMODE_WAIT)
- wdc_select_drive(chp, xfer->drive, delay);
-
/* Don't timeout during configuration */
xfer->c_flags &= ~C_TIMEOU;
@@ -1042,17 +1251,13 @@ again:
case IDENTIFY:
wdccommandshort(chp, drvp->drive, ATAPI_IDENTIFY_DEVICE);
drvp->state = IDENTIFY_WAIT;
+ xfer->timeout = ATAPI_CTRL_WAIT;
+ xfer->expect_irq = 1;
break;
case IDENTIFY_WAIT:
errstring = "IDENTIFY";
- /* Some ATAPI devices need to try to read the media
- before responding. Well, let's hope resets while
- polling are few and far between */
- if (wdcwait(chp, 0, 0, delay))
- goto timeout;
-
/* We don't really care if this operation failed.
It's just there to wake the drive from its stupor. */
if (!(chp->ch_status & WDCS_ERR)) {
@@ -1060,11 +1265,13 @@ again:
errstring = "Post IDENTIFY";
- if (wdcwait(chp, WDCS_DRQ, 0, 100))
- goto timeout;
+ xfer->timeout = 100;
+ drvp->state = PIOMODE;
+ break;
}
drvp->state = PIOMODE;
+
case PIOMODE:
piomode:
/* Don't try to set mode if controller can't be adjusted */
@@ -1076,11 +1283,11 @@ piomode:
wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0,
0x08 | drvp->PIO_mode, WDSF_SET_MODE);
drvp->state = PIOMODE_WAIT;
+ xfer->timeout = ATAPI_CTRL_WAIT;
+ xfer->expect_irq = 1;
break;
case PIOMODE_WAIT:
errstring = "piomode";
- if (wait_for_unbusy(chp, delay))
- goto timeout;
if (chp->ch_status & WDCS_ERR) {
if (drvp->PIO_mode < 3) {
drvp->PIO_mode = 3;
@@ -1102,11 +1309,13 @@ piomode:
goto ready;
}
drvp->state = DMAMODE_WAIT;
+
+ xfer->timeout = ATAPI_CTRL_WAIT;
+ xfer->expect_irq = 1;
break;
+
case DMAMODE_WAIT:
errstring = "dmamode";
- if (wait_for_unbusy(chp, delay))
- goto timeout;
if (chp->ch_status & WDCS_ERR)
goto error;
/* fall through */
@@ -1114,29 +1323,18 @@ piomode:
case READY:
ready:
drvp->state = READY;
- xfer->c_intr = wdc_atapi_intr;
- wdc_atapi_start(chp, xfer);
- return (1);
- }
- if ((sc_xfer->flags & SCSI_POLL) == 0 &&
- (drvp->atapi_cap & ACAP_DSC) == 0) {
- chp->ch_flags |= WDCF_IRQ_WAIT;
- xfer->c_intr = wdc_atapi_ctrl;
- timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000);
- } else {
- goto again;
+ xfer->next = wdc_atapi_real_start;
+ break;
}
- return (1);
+ return (GOTO_NEXT);
timeout:
- if (irq && (xfer->c_flags & C_TIMEOU) == 0) {
- return (0); /* IRQ was not for us */
- }
printf("%s:%d:%d: %s timed out\n",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, errstring);
sc_xfer->error = XS_TIMEOUT;
- wdc_atapi_reset(chp, xfer);
- return (1);
+ xfer->next = wdc_atapi_reset;
+ return (GOTO_NEXT);
+
error:
printf("%s:%d:%d: %s ",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
@@ -1144,14 +1342,41 @@ error:
printf("error (0x%x)\n", chp->ch_error);
sc_xfer->error = XS_SHORTSENSE;
atapi_to_scsi_sense(sc_xfer, chp->ch_error);
- wdc_atapi_reset(chp, xfer);
- return (1);
+ xfer->next = wdc_atapi_reset;
+ return (GOTO_NEXT);
}
-void
-wdc_atapi_done(chp, xfer)
+int
+wdc_atapi_tape_done(chp, xfer, timeout)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+ int timeout;
+{
+ struct scsi_xfer *sc_xfer = xfer->cmd;
+
+ if (sc_xfer->error != XS_NOERROR) {
+ xfer->next = wdc_atapi_done;
+ return (GOTO_NEXT);
+ }
+
+ _lto3b(xfer->transfer_len,
+ ((struct scsi_rw_tape *)
+ sc_xfer->cmd)->len);
+
+ xfer->c_bcount = sc_xfer->datalen;
+ xfer->c_done = NULL;
+ xfer->c_skip = 0;
+
+ xfer->next = wdc_atapi_real_start;
+ return (GOTO_NEXT);
+}
+
+
+int
+wdc_atapi_done(chp, xfer, timeout)
struct channel_softc *chp;
struct wdc_xfer *xfer;
+ int timeout;
{
struct scsi_xfer *sc_xfer = xfer->cmd;
int need_done = xfer->c_flags & C_NEEDDONE;
@@ -1161,8 +1386,7 @@ wdc_atapi_done(chp, xfer)
WDCDEBUG_PRINT(("wdc_atapi_done %s:%d:%d: flags 0x%x\n",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
(u_int)xfer->c_flags), DEBUG_XFERS);
- /* remove this command from xfer queue */
- wdc_free_xfer(chp, xfer);
+
sc_xfer->flags |= ITSDONE;
if (drvp->n_dmaerrs ||
(sc_xfer->error != XS_NOERROR && sc_xfer->error != XS_SENSE &&
@@ -1174,111 +1398,63 @@ wdc_atapi_done(chp, xfer)
if (doing_dma)
wdc_downgrade_mode(drvp);
}
-
+
if (need_done) {
WDCDEBUG_PRINT(("wdc_atapi_done: scsi_done\n"), DEBUG_XFERS);
scsi_done(sc_xfer);
}
- WDCDEBUG_PRINT(("wdcstart from wdc_atapi_done, flags 0x%x\n",
- chp->ch_flags), DEBUG_XFERS);
- wdcstart(chp);
-}
-
-/* Wait until DSC gets asserted. */
-int
-atapi_dsc_wait(drvp, timo)
- struct ata_drive_datas *drvp;
- int timo;
-{
- struct channel_softc *chp = drvp->chnl_softc;
- chp->ch_flags &= ~WDCF_ACTIVE;
-#if 0
- /* XXX Something like this may be needed I have not investigated
- close enough yet. If so we may need to put it back after
- the poll wait. */
- TAILQ_REMOVE(&chp->ch_queue->sc_xfer, xfer, c_xferchain);
-#endif
- return (atapi_poll_wait(atapi_dsc_ready, drvp, timo, PZERO + PCATCH,
- "atapidsc"));
+ return (DONE);
}
-int
-atapi_dsc_ready(arg)
- void *arg;
-{
- struct ata_drive_datas *drvp = arg;
- struct channel_softc *chp = drvp->chnl_softc;
-
- if (chp->ch_flags & WDCF_ACTIVE)
- return (0);
- wdc_select_drive(chp, drvp->drive, 0);
- chp->ch_status = CHP_READ_REG(chp, wdr_status);
- return ((chp->ch_status & (WDCS_BSY | WDCS_DSC)) == WDCS_DSC);
-}
int
-atapi_poll_wait(ready, arg, timo, pri, msg)
- int (*ready) __P((void *));
- void *arg;
- int timo;
- int pri;
- char *msg;
-{
- int maxtic, tic = 0, error;
- u_int64_t starttime = time.tv_sec * 1000 + time.tv_usec / 1000;
- u_int64_t endtime = starttime + timo;
-
- while (1) {
- WDCDEBUG_PRINT(("atapi_poll_wait: msg=%s tic=%d\n", msg, tic),
- DEBUG_POLL);
- if (ready(arg))
- return (0);
-
-#if 0
- /* Exponential backoff. */
- tic = tic + tic + 1;
-#else
- tic = min(hz / 100, 1);
-#endif
- maxtic = (int)
- (endtime - (time.tv_sec * 1000 + time.tv_usec / 1000));
- if (maxtic <= 0)
- return (EWOULDBLOCK);
- if (tic > maxtic)
- tic = maxtic;
- if (tic > ATAPI_POLL_MAXTIC)
- tic = ATAPI_POLL_MAXTIC;
- error = tsleep(arg, pri, msg, tic);
- if (error != EWOULDBLOCK)
- return (error);
- }
-}
-
-void
-wdc_atapi_reset(chp, xfer)
+wdc_atapi_reset(chp, xfer, timeout)
struct channel_softc *chp;
struct wdc_xfer *xfer;
+ int timeout;
{
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
- struct scsi_xfer *sc_xfer = xfer->cmd;
wdccommandshort(chp, xfer->drive, ATAPI_SOFT_RESET);
+ drvp->state = 0;
/* Some ATAPI devices need extra time to find their
brains after a reset
*/
- delay(5000);
+ xfer->next = wdc_atapi_reset_2;
+ xfer->delay = 10;
+ xfer->timeout = ATAPI_RESET_WAIT;
+ return (GOTO_NEXT);
+}
- drvp->state = 0;
- if (wdcwait(chp, WDCS_DRQ, 0, ATAPI_RESET_WAIT) != 0) {
+int
+wdc_atapi_reset_2(chp, xfer, timeout)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+ int timeout;
+{
+ struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
+ struct scsi_xfer *sc_xfer = xfer->cmd;
+
+ if (timeout) {
printf("%s:%d:%d: reset failed\n",
chp->wdc->sc_dev.dv_xname, chp->channel,
xfer->drive);
sc_xfer->error = XS_SELTIMEOUT;
wdc_reset_channel(drvp);
+
+ xfer->next = wdc_atapi_done;
+ return (GOTO_NEXT);
}
- wdc_atapi_done(chp, xfer);
- return;
+ wdc_atapi_update_status(chp);
+
+ if (chp->ch_status & (WDCS_BSY | WDCS_DRQ)) {
+ return (CONTINUE_POLL);
+ }
+
+ xfer->next = wdc_atapi_done;
+ return (GOTO_NEXT);
}
+