summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorbouyer <bouyer@NetBSD.org>2001-12-01 00:00:29 +0000
committerbouyer <bouyer@NetBSD.org>2001-12-01 00:00:29 +0000
commitcd8d64fe2bac92270e186a8ba9abfe017ae1849c (patch)
tree84abccc36f627a16a832f5afc753af52c90f7c0d /sys/dev
parent86e4569262a10afe47cad1ae4f17c72bc7dce62f (diff)
For ATAPI tape drives, poll for DSC (using a callout), to get the real
status of the command, and make sure the drive is ready for the next one.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ata/atavar.h3
-rw-r--r--sys/dev/ic/wdcvar.h3
-rw-r--r--sys/dev/scsipi/atapi_wdc.c139
3 files changed, 104 insertions, 41 deletions
diff --git a/sys/dev/ata/atavar.h b/sys/dev/ata/atavar.h
index bc67aae14e0..c7bfa1bbef6 100644
--- a/sys/dev/ata/atavar.h
+++ b/sys/dev/ata/atavar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: atavar.h,v 1.19 2001/04/25 17:53:27 bouyer Exp $ */
+/* $NetBSD: atavar.h,v 1.20 2001/12/01 00:00:30 bouyer Exp $ */
/*
* Copyright (c) 1998 Manuel Bouyer.
@@ -49,6 +49,7 @@ struct ata_drive_datas {
#define DRIVE_MODE 0x0040 /* the drive reported its mode */
#define DRIVE_RESET 0x0080 /* reset the drive state at next xfer */
#define DRIVE_DMAERR 0x0100 /* Udma transfer had crc error, don't try DMA */
+#define DRIVE_ATAPIST 0x0100 /* device is an ATAPI tape drive */
/*
* Current setting of drive's PIO, DMA and UDMA modes.
* Is initialised by the disks drivers at attach time, and may be
diff --git a/sys/dev/ic/wdcvar.h b/sys/dev/ic/wdcvar.h
index 5dc8b31100d..0f5bbe47b9e 100644
--- a/sys/dev/ic/wdcvar.h
+++ b/sys/dev/ic/wdcvar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: wdcvar.h,v 1.30 2001/06/13 18:17:39 bjh21 Exp $ */
+/* $NetBSD: wdcvar.h,v 1.31 2001/12/01 00:00:30 bouyer Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -160,6 +160,7 @@ struct wdc_xfer {
void *databuf;
int c_bcount; /* byte count left */
int c_skip; /* bytes already transferred */
+ int c_dscpoll; /* counter for dsc polling (ATAPI) */
TAILQ_ENTRY(wdc_xfer) c_xferchain;
void (*c_start) __P((struct channel_softc *, struct wdc_xfer *));
int (*c_intr) __P((struct channel_softc *, struct wdc_xfer *, int));
diff --git a/sys/dev/scsipi/atapi_wdc.c b/sys/dev/scsipi/atapi_wdc.c
index f5d0ec4737f..3575008348a 100644
--- a/sys/dev/scsipi/atapi_wdc.c
+++ b/sys/dev/scsipi/atapi_wdc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: atapi_wdc.c,v 1.44 2001/11/13 06:56:38 lukem Exp $ */
+/* $NetBSD: atapi_wdc.c,v 1.45 2001/12/01 00:00:29 bouyer Exp $ */
/*
* Copyright (c) 1998 Manuel Bouyer.
@@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: atapi_wdc.c,v 1.44 2001/11/13 06:56:38 lukem Exp $");
+__KERNEL_RCSID(0, "$NetBSD: atapi_wdc.c,v 1.45 2001/12/01 00:00:29 bouyer Exp $");
#ifndef WDCDEBUG
#define WDCDEBUG
@@ -91,11 +91,13 @@ void wdc_atapi_start __P((struct channel_softc *,struct wdc_xfer *));
int wdc_atapi_intr __P((struct channel_softc *, struct wdc_xfer *, int));
void wdc_atapi_kill_xfer __P((struct channel_softc *, struct wdc_xfer *));
int wdc_atapi_ctrl __P((struct channel_softc *, struct wdc_xfer *, int));
+void wdc_atapi_phase_complete __P((struct wdc_xfer *));
void wdc_atapi_done __P((struct channel_softc *, struct wdc_xfer *));
void wdc_atapi_reset __P((struct channel_softc *, struct wdc_xfer *));
void wdc_atapi_scsipi_request __P((struct scsipi_channel *,
scsipi_adapter_req_t, void *));
void wdc_atapi_kill_pending __P((struct scsipi_periph *));
+void wdc_atapi_polldsc __P((void *arg));
#define MAX_SIZE MAXPHYS
@@ -291,6 +293,8 @@ wdc_atapi_probe_device(sc, target)
periph->periph_type = ATAPI_CFG_TYPE(id->atap_config);
if (id->atap_config & ATAPI_CFG_REMOV)
periph->periph_flags |= PERIPH_REMOVABLE;
+ if (periph->periph_type == T_SEQUENTIAL)
+ drvp->drive_flags |= DRIVE_ATAPIST;
sa.sa_periph = periph;
sa.sa_inqbuf.type = ATAPI_CFG_TYPE(id->atap_config);
@@ -372,6 +376,7 @@ wdc_atapi_scsipi_request(chan, req, arg)
xfer->c_start = wdc_atapi_start;
xfer->c_intr = wdc_atapi_intr;
xfer->c_kill_xfer = wdc_atapi_kill_xfer;
+ xfer->c_dscpoll = 0;
s = splbio();
wdc_exec_xfer(wdc->channels[channel], xfer);
#ifdef DIAGNOSTIC
@@ -752,47 +757,12 @@ again:
case PHASE_ABORTED:
case PHASE_COMPLETED:
WDCDEBUG_PRINT(("PHASE_COMPLETED\n"), DEBUG_INTR);
- /* turn off DMA channel */
if (xfer->c_flags & C_DMA) {
xfer->c_bcount -= sc_xfer->datalen;
}
sc_xfer->resid = xfer->c_bcount;
- /*
- * Some drive occasionally set WDCS_ERR with
- * "ATA illegal length indication" in the error
- * register. If we read some data the sense is valid
- * anyway, so don't report the error.
- */
- if (chp->ch_status & WDCS_ERR &&
- ((sc_xfer->xs_control & XS_CTL_REQSENSE) == 0 ||
- sc_xfer->resid == sc_xfer->datalen)) {
- /* save the short sense */
- sc_xfer->error = XS_SHORTSENSE;
- sc_xfer->sense.atapi_sense = chp->ch_error;
- if ((sc_xfer->xs_periph->periph_quirks &
- PQUIRK_NOSENSE) == 0) {
- /* ask scsipi to send a REQUEST_SENSE */
- sc_xfer->error = XS_BUSY;
- sc_xfer->status = SCSI_CHECK;
- } else if (chp->wdc->dma_status &
- (WDC_DMAST_NOIRQ | WDC_DMAST_ERR)) {
- ata_dmaerr(drvp);
- sc_xfer->error = XS_RESET;
- wdc_atapi_reset(chp, xfer);
- return (1);
- }
- }
- if (xfer->c_bcount != 0) {
- WDCDEBUG_PRINT(("wdc_atapi_intr: bcount value is "
- "%d after io\n", xfer->c_bcount), DEBUG_XFERS);
- }
-#ifdef DIAGNOSTIC
- if (xfer->c_bcount < 0) {
- printf("wdc_atapi_intr warning: bcount value "
- "is %d after io\n", xfer->c_bcount);
- }
-#endif
- break;
+ wdc_atapi_phase_complete(xfer);
+ return(1);
default:
if (++retries<500) {
@@ -939,6 +909,90 @@ error:
}
void
+wdc_atapi_phase_complete(xfer)
+ struct wdc_xfer *xfer;
+{
+ struct channel_softc *chp = xfer->chp;
+ struct scsipi_xfer *sc_xfer = xfer->cmd;
+ struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
+
+ /* wait for DSC if needed */
+ if (drvp->drive_flags & DRIVE_ATAPIST) {
+ WDCDEBUG_PRINT(("wdc_atapi_phase_complete(%s:%d:%d) "
+ "polldsc %d\n", chp->wdc->sc_dev.dv_xname, chp->channel,
+ xfer->drive, xfer->c_dscpoll), DEBUG_XFERS);
+ if (cold) {
+ if (wdcwait(chp, WDCS_DSC, WDCS_DSC,
+ sc_xfer->timeout)) {
+ printf("%s:%d:%d: wait_for_dsc failed\n",
+ chp->wdc->sc_dev.dv_xname, chp->channel,
+ xfer->drive);
+ sc_xfer->error = XS_TIMEOUT;
+ wdc_atapi_reset(chp, xfer);
+ return;
+ }
+ } else {
+ if (wdcwait(chp, WDCS_DSC, WDCS_DSC, 10)) {
+ /* 10ms not enouth, try again in 1 tick */
+ if (xfer->c_dscpoll++ >
+ sc_xfer->timeout * hz / 1000) {
+ printf("%s:%d:%d: wait_for_dsc "
+ "failed\n",
+ chp->wdc->sc_dev.dv_xname,
+ chp->channel, xfer->drive);
+ sc_xfer->error = XS_TIMEOUT;
+ wdc_atapi_reset(chp, xfer);
+ return;
+ }
+ callout_reset(&chp->ch_callout, 1,
+ wdc_atapi_polldsc, xfer);
+ return;
+ }
+ }
+ }
+
+ /*
+ * Some drive occasionally set WDCS_ERR with
+ * "ATA illegal length indication" in the error
+ * register. If we read some data the sense is valid
+ * anyway, so don't report the error.
+ */
+ if (chp->ch_status & WDCS_ERR &&
+ ((sc_xfer->xs_control & XS_CTL_REQSENSE) == 0 ||
+ sc_xfer->resid == sc_xfer->datalen)) {
+ /* save the short sense */
+ sc_xfer->error = XS_SHORTSENSE;
+ sc_xfer->sense.atapi_sense = chp->ch_error;
+ if ((sc_xfer->xs_periph->periph_quirks &
+ PQUIRK_NOSENSE) == 0) {
+ /* ask scsipi to send a REQUEST_SENSE */
+ sc_xfer->error = XS_BUSY;
+ sc_xfer->status = SCSI_CHECK;
+ } else if (chp->wdc->dma_status &
+ (WDC_DMAST_NOIRQ | WDC_DMAST_ERR)) {
+ ata_dmaerr(drvp);
+ sc_xfer->error = XS_RESET;
+ wdc_atapi_reset(chp, xfer);
+ return;
+ }
+ }
+ if (xfer->c_bcount != 0) {
+ WDCDEBUG_PRINT(("wdc_atapi_intr: bcount value is "
+ "%d after io\n", xfer->c_bcount), DEBUG_XFERS);
+ }
+#ifdef DIAGNOSTIC
+ if (xfer->c_bcount < 0) {
+ printf("wdc_atapi_intr warning: bcount value "
+ "is %d after io\n", xfer->c_bcount);
+ }
+#endif
+ WDCDEBUG_PRINT(("wdc_atapi_phase_complete: wdc_atapi_done(), "
+ "error 0x%x sense 0x%x\n", sc_xfer->error,
+ sc_xfer->sense.atapi_sense), DEBUG_INTR);
+ wdc_atapi_done(chp, xfer);
+}
+
+void
wdc_atapi_done(chp, xfer)
struct channel_softc *chp;
struct wdc_xfer *xfer;
@@ -978,3 +1032,10 @@ wdc_atapi_reset(chp, xfer)
wdc_atapi_done(chp, xfer);
return;
}
+
+void
+wdc_atapi_polldsc(arg)
+ void *arg;
+{
+ wdc_atapi_phase_complete(arg);
+}