diff options
| author | bouyer <bouyer@NetBSD.org> | 2001-12-01 00:00:29 +0000 |
|---|---|---|
| committer | bouyer <bouyer@NetBSD.org> | 2001-12-01 00:00:29 +0000 |
| commit | cd8d64fe2bac92270e186a8ba9abfe017ae1849c (patch) | |
| tree | 84abccc36f627a16a832f5afc753af52c90f7c0d /sys/dev | |
| parent | 86e4569262a10afe47cad1ae4f17c72bc7dce62f (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.h | 3 | ||||
| -rw-r--r-- | sys/dev/ic/wdcvar.h | 3 | ||||
| -rw-r--r-- | sys/dev/scsipi/atapi_wdc.c | 139 |
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); +} |
