summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiklas Hallqvist <niklas@cvs.openbsd.org>1999-09-05 21:45:24 +0000
committerNiklas Hallqvist <niklas@cvs.openbsd.org>1999-09-05 21:45:24 +0000
commit53b92427f5d03f17632bf2e858f84e997efe34bb (patch)
tree9d1cdce63c122c00d3be2806a14d3c535adba075
parentd8ac3c9e54a89f58d4bff30473488e2b4ca95596 (diff)
Support for ATAPI tapes, works but still has some rough edges. Some style.
-rw-r--r--sys/dev/ata/atareg.h37
-rw-r--r--sys/dev/ata/atavar.h7
-rw-r--r--sys/dev/atapiscsi/atapiscsi.c509
3 files changed, 403 insertions, 150 deletions
diff --git a/sys/dev/ata/atareg.h b/sys/dev/ata/atareg.h
index 18fbff35cc6..d8d1a82c813 100644
--- a/sys/dev/ata/atareg.h
+++ b/sys/dev/ata/atareg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: atareg.h,v 1.1 1999/07/18 21:25:17 csapuntz Exp $ */
+/* $OpenBSD: atareg.h,v 1.2 1999/09/05 21:45:22 niklas Exp $ */
/* $NetBSD: atareg.h,v 1.5 1999/01/18 20:06:24 bouyer Exp $ */
/*
@@ -10,20 +10,25 @@
struct ataparams {
/* drive info */
u_int16_t atap_config; /* 0: general configuration */
-#define WDC_CFG_ATAPI_MASK 0xc000
-#define WDC_CFG_ATAPI 0x8000
-#define ATA_CFG_REMOVABLE 0x0080
-#define ATA_CFG_FIXED 0x0040
-#define ATAPI_CFG_TYPE_MASK 0x1f00
-#define ATAPI_CFG_TYPE(x) (((x) & ATAPI_CFG_TYPE_MASK) >> 8)
-#define ATAPI_CFG_REMOV 0x0080
-#define ATAPI_CFG_DRQ_MASK 0x0060
-#define ATAPI_CFG_STD_DRQ 0x0000
-#define ATAPI_CFG_IRQ_DRQ 0x0020
-#define ATAPI_CFG_ACCEL_DRQ 0x0040
-#define ATAPI_CFG_CMD_MASK 0x0003
-#define ATAPI_CFG_CMD_12 0x0000
-#define ATAPI_CFG_CMD_16 0x0001
+#define WDC_CFG_ATAPI_MASK 0xc000
+#define WDC_CFG_ATAPI 0x8000
+#define ATA_CFG_REMOVABLE 0x0080
+#define ATA_CFG_FIXED 0x0040
+#define ATAPI_CFG_TYPE_MASK 0x1f00
+#define ATAPI_CFG_TYPE(x) (((x) & ATAPI_CFG_TYPE_MASK) >> 8)
+#define ATAPI_CFG_TYPE_DIRECT 0x00
+#define ATAPI_CFG_TYPE_SEQUENTIAL 0x01
+#define ATAPI_CFG_TYPE_CDROM 0x05
+#define ATAPI_CFG_TYPE_OPTICAL 0x07
+#define ATAPI_CFG_TYPE_NODEVICE 0x1F
+#define ATAPI_CFG_REMOV 0x0080
+#define ATAPI_CFG_DRQ_MASK 0x0060
+#define ATAPI_CFG_STD_DRQ 0x0000
+#define ATAPI_CFG_IRQ_DRQ 0x0020
+#define ATAPI_CFG_ACCEL_DRQ 0x0040
+#define ATAPI_CFG_CMD_MASK 0x0003
+#define ATAPI_CFG_CMD_12 0x0000
+#define ATAPI_CFG_CMD_16 0x0001
/* words 1-9 are ATA only */
u_int16_t atap_cylinders; /* 1: # of non-removable cylinders */
u_int16_t __reserved1;
@@ -47,7 +52,7 @@ struct ataparams {
#define ATA_CAP_STBY 0x2000
#define ATAPI_CAP_INTERL_DMA 0x8000
#define ATAPI_CAP_CMD_QUEUE 0x4000
-#define ATAPI_CAP_OVERLP 0X2000
+#define ATAPI_CAP_OVERLP 0x2000
#define ATAPI_CAP_ATA_RST 0x1000
u_int16_t atap_capabilities2; /* 50: capability flags (ATA) */
#if BYTE_ORDER == LITTLE_ENDIAN
diff --git a/sys/dev/ata/atavar.h b/sys/dev/ata/atavar.h
index e536d55e41d..15c521093ad 100644
--- a/sys/dev/ata/atavar.h
+++ b/sys/dev/ata/atavar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: atavar.h,v 1.2 1999/07/22 02:51:15 csapuntz Exp $ */
+/* $OpenBSD: atavar.h,v 1.3 1999/09/05 21:45:22 niklas Exp $ */
/* $NetBSD: atavar.h,v 1.13 1999/03/10 13:11:43 bouyer Exp $ */
/*
@@ -51,6 +51,8 @@ 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_DSCBA 0x0200 /* DSC in buffer availability mode */
+#define DRIVE_DSCWAIT 0x0400 /* In wait for DSC to be asserted */
/*
* Current setting of drive's PIO, DMA and UDMA modes.
* Is initialised by the disks drivers at attach time, and may be
@@ -70,6 +72,7 @@ struct ata_drive_datas {
u_int8_t state;
#define ACAP_LEN 0x01 /* 16 byte commands */
+#define ACAP_DSC 0x02 /* use DSC signalling */
/* 0x20-0x40 reserved for ATAPI_CFG_DRQ_MASK */
u_int8_t atapi_cap;
@@ -79,7 +82,7 @@ struct ata_drive_datas {
#define NERRS_MAX 2
struct device *drv_softc; /* ATA drives softc, if any */
- void* chnl_softc; /* channel softc */
+ void *chnl_softc; /* channel softc */
};
/* ATA/ATAPI common attachement datas */
diff --git a/sys/dev/atapiscsi/atapiscsi.c b/sys/dev/atapiscsi/atapiscsi.c
index 12a09ade3fb..9e7c9654d5b 100644
--- a/sys/dev/atapiscsi/atapiscsi.c
+++ b/sys/dev/atapiscsi/atapiscsi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: atapiscsi.c,v 1.9 1999/08/12 13:01:13 niklas Exp $ */
+/* $OpenBSD: atapiscsi.c,v 1.10 1999/09/05 21:45:23 niklas Exp $ */
/*
* This code is derived from code with the copyright below.
@@ -52,6 +52,7 @@
#include <sys/ioctl.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_disk.h>
+#include <scsi/scsi_tape.h>
#include <scsi/scsiconf.h>
#include <vm/vm.h>
@@ -60,6 +61,7 @@
#include <machine/cpu.h>
#include <machine/intr.h>
+/* XXX OpenBSD actually has the "raw" API for "stream" functionality. */
#ifndef __BUS_SPACE_HAS_STREAM_METHODS
#define bus_space_write_multi_stream_2 bus_space_write_multi_2
#define bus_space_write_multi_stream_4 bus_space_write_multi_4
@@ -80,6 +82,8 @@
#define DEBUG_STATUS 0x04
#define DEBUG_FUNCS 0x08
#define DEBUG_PROBE 0x10
+#define DEBUG_DSC 0x20
+#define DEBUG_POLL 0x40
#ifdef WDCDEBUG
int wdcdebug_atapi_mask = 0x0;
#define WDCDEBUG_PRINT(args, level) \
@@ -89,14 +93,18 @@ int wdcdebug_atapi_mask = 0x0;
#define WDCDEBUG_PRINT(args, level)
#endif
-#define ATAPI_DELAY 10 /* 10 ms, this is used only before sending a cmd */
+/* 10 ms, this is used only before sending a cmd. */
+#define ATAPI_DELAY 10
-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));
-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 *));
+/* 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));
+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_send_cmd __P((struct scsi_xfer *sc_xfer));
#define MAX_SIZE MAXPHYS
@@ -104,12 +112,17 @@ int wdc_atapi_send_cmd __P((struct scsi_xfer *sc_xfer));
struct atapiscsi_softc;
struct atapiscsi_xfer;
-static int atapiscsi_match __P((struct device *, void *, void *));
-static void atapiscsi_attach __P((struct device *, struct device *, void *));
+int atapiscsi_match __P((struct device *, void *, void *));
+void atapiscsi_attach __P((struct device *, struct device *, void *));
int wdc_atapi_get_params __P((struct atapiscsi_softc *, u_int8_t, int,
struct ataparams *));
+int atapi_dsc_wait __P((struct ata_drive_datas *, int));
+int atapi_dsc_ready __P((void *));
+int atapi_dsc_semiready __P((void *));
+int atapi_poll_wait __P((int (*) __P((void *)), void *, int, int, char *));
+
#define ATAPI_TO_SCSI_SENSE(sc, atapi_error) \
(sc)->error_code = XS_SHORTSENSE; (sc)->flags = (atapi_error) >> 4;
@@ -215,11 +228,25 @@ atapiscsi_attach(parent, self, aux)
drvp->drv_softc = (struct device *)as;
wdc_probe_caps(drvp);
- if ((id->atap_config & ATAPI_CFG_CMD_MASK)
- == ATAPI_CFG_CMD_16)
+ WDCDEBUG_PRINT(
+ ("general config %04x capabilities %04x ",
+ id->atap_config, id->atap_capabilities1),
+ DEBUG_PROBE);
+
+ /* Tape drives do funny DSC stuff */
+ if (ATAPI_CFG_TYPE(id->atap_config) ==
+ ATAPI_CFG_TYPE_SEQUENTIAL)
+ drvp->atapi_cap |= ACAP_DSC;
+
+ if ((id->atap_config & ATAPI_CFG_CMD_MASK) ==
+ ATAPI_CFG_CMD_16)
drvp->atapi_cap |= ACAP_LEN;
- drvp->atapi_cap |= (id->atap_config & ATAPI_CFG_DRQ_MASK);
+ drvp->atapi_cap |=
+ (id->atap_config & ATAPI_CFG_DRQ_MASK);
+
+ WDCDEBUG_PRINT(("driver caps %04x\n", drvp->atapi_cap),
+ DEBUG_PROBE);
}
}
}
@@ -290,7 +317,7 @@ wdc_atapi_get_params(as, drive, flags, id)
if ((chp->ch_drive[drive].drive_flags & DRIVE_ATAPI) == 0) {
WDCDEBUG_PRINT(("wdc_atapi_get_params: drive %d not present\n",
drive), DEBUG_PROBE);
- return -1;
+ return (-1);
}
bzero(&wdc_c, sizeof(struct wdc_command));
wdc_c.r_command = ATAPI_SOFT_RESET;
@@ -309,7 +336,7 @@ wdc_atapi_get_params(as, drive, flags, id)
"failed for drive %s:%d:%d: error 0x%x\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drive,
wdc_c.r_error), DEBUG_PROBE);
- return -1;
+ return (-1);
}
chp->ch_drive[drive].state = 0;
@@ -322,9 +349,9 @@ wdc_atapi_get_params(as, drive, flags, id)
"failed for drive %s:%d:%d: error 0x%x\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drive,
wdc_c.r_error), DEBUG_PROBE);
- return -1;
+ return (-1);
}
- return COMPLETE;
+ return (COMPLETE);
}
int
@@ -337,19 +364,24 @@ wdc_atapi_send_cmd(sc_xfer)
int flags = sc_xfer->flags;
int channel = as->sc_channel;
int drive = sc_xfer->sc_link->target;
- int s, ret;
+ struct ata_drive_datas *drvp = &as->sc_drvs[drive];
+ int s, ret, saved_datalen;
+ char saved_len_bytes[3];
+
+restart:
+ saved_datalen = 0;
WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d\n",
wdc->sc_dev.dv_xname, channel, drive), DEBUG_XFERS);
if (drive > 1 || !as->valid[drive]) {
sc_xfer->error = XS_DRIVER_STUFFUP;
- return COMPLETE;
+ return (COMPLETE);
}
xfer = wdc_get_xfer(flags & SCSI_NOSLEEP ? WDC_NOSLEEP : WDC_CANSLEEP);
if (xfer == NULL) {
- return TRY_AGAIN_LATER;
+ return (TRY_AGAIN_LATER);
}
if (sc_xfer->flags & SCSI_POLL)
xfer->c_flags |= C_POLL;
@@ -360,16 +392,81 @@ wdc_atapi_send_cmd(sc_xfer)
xfer->c_bcount = sc_xfer->datalen;
xfer->c_start = wdc_atapi_start;
xfer->c_intr = wdc_atapi_intr;
+
s = splbio();
+
+ 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:
+ /* 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);
+ _lto3b(0,
+ ((struct scsi_rw_tape *)
+ sc_xfer->cmd)->len);
+ 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),
+ DEBUG_DSC);
+
+ /* DSC will change to buffer availability mode.
+ We reflect this in wdc_atapi_intr. */
+ break;
+
+ case ERASE: /* Media access commands */
+ case LOAD:
+ case REWIND:
+ case SPACE:
+#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);
+ break;
+
+ default:
+ WDCDEBUG_PRINT(("no media access\n"), DEBUG_DSC);
+ }
+ }
+
wdc_exec_xfer(wdc->channels[channel], xfer);
#ifdef DIAGNOSTIC
- if ((sc_xfer->flags & SCSI_POLL) != 0 &&
+ if (((sc_xfer->flags & SCSI_POLL) != 0 ||
+ (drvp->atapi_cap & ACAP_DSC) != 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;
+ return (ret);
}
void
@@ -380,7 +477,7 @@ wdc_atapi_start(chp, xfer)
struct scsi_xfer *sc_xfer = xfer->cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
- WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x \n",
+ WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive,
sc_xfer->flags), DEBUG_XFERS);
/* Adjust C_DMA, it may have changed if we are requesting sense */
@@ -409,6 +506,18 @@ wdc_atapi_start(chp, xfer)
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;
+ }
+
/*
* Even with WDCS_ERR, the device should accept a command packet
* Limit length to what can be stuffed into the cylinder register
@@ -428,26 +537,54 @@ wdc_atapi_start(chp, xfer)
* 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)) {
+ 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;
}
- if (sc_xfer->flags & SCSI_POLL) {
+ if ((sc_xfer->flags & SCSI_POLL) || (drvp->atapi_cap & ACAP_DSC)) {
while ((sc_xfer->flags & ITSDONE) == 0) {
- /* Wait for at last 400ns for status bit to be valid */
- DELAY(1);
+ 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);
wdc_atapi_intr(chp, xfer, 0);
}
}
}
int
+atapi_dsc_semiready(arg)
+ void *arg;
+{
+ struct ata_drive_datas *drvp = arg;
+ struct channel_softc *chp = drvp->chnl_softc;
+
+ /* We should really wait_for_unbusy here too before
+ switching drives. */
+ bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
+ WDSD_IBM | (drvp->drive << 4));
+
+ return (wait_for_unbusy(chp, 0) == 0);
+}
+
+int
wdc_atapi_intr(chp, xfer, irq)
struct channel_softc *chp;
struct wdc_xfer *xfer;
@@ -462,7 +599,7 @@ wdc_atapi_intr(chp, xfer, irq)
struct scsi_sense *cmd_reqsense =
(struct scsi_sense *)&_cmd_reqsense;
u_int32_t cmd[4];
- int cmdlen = (drvp->atapi_cap & ACAP_LEN) ? 16 : 12;
+ int cmdlen = (drvp->atapi_cap & ACAP_LEN) ? 16 : 12;
bzero(cmd, sizeof(cmd));
@@ -486,7 +623,7 @@ wdc_atapi_intr(chp, xfer, irq)
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 */
+ 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);
@@ -494,7 +631,7 @@ wdc_atapi_intr(chp, xfer, irq)
drvp->n_dmaerrs++;
sc_xfer->error = XS_TIMEOUT;
wdc_atapi_reset(chp, xfer);
- return 1;
+ return (1);
}
/* 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))
@@ -511,13 +648,14 @@ wdc_atapi_intr(chp, xfer, irq)
"calling wdc_atapi_done()"
), DEBUG_INTR);
wdc_atapi_done(chp, xfer);
- return 1;
+ return (1);
}
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 ? WDC_DMA_POLL : 0;
+ dma_flags |= ((sc_xfer->flags & SCSI_POLL) ||
+ (drvp->atapi_cap & ACAP_DSC)) ? WDC_DMA_POLL : 0;
}
again:
len = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo) +
@@ -582,11 +720,25 @@ again:
chp->channel, xfer->drive, dma_flags);
}
- if ((sc_xfer->flags & SCSI_POLL) == 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);
}
- return 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)) {
+ drvp->drive_flags |= DRIVE_DSCBA;
+ WDCDEBUG_PRINT(("set DSCBA\n"), DEBUG_DSC);
+ }
+ if (sc_xfer->cmd->opcode == READ)
+ drvp->drive_flags |= DRIVE_DSCWAIT;
+ }
+ return (1);
case PHASE_DATAOUT:
/* write data */
@@ -601,7 +753,7 @@ again:
}
sc_xfer->error = XS_TIMEOUT;
wdc_atapi_reset(chp, xfer);
- return 1;
+ return (1);
}
if (xfer->c_bcount < len) {
printf("wdc_atapi_intr: warning: write only "
@@ -609,14 +761,14 @@ again:
if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) {
bus_space_write_multi_2(chp->cmd_iot,
chp->cmd_ioh, wd_data,
- (u_int16_t *)((char *)xfer->databuf +
- xfer->c_skip),
+ (u_int16_t *)
+ ((char *)xfer->databuf + xfer->c_skip),
xfer->c_bcount >> 1);
} else {
bus_space_write_multi_stream_2(chp->cmd_iot,
chp->cmd_ioh, wd_data,
- (u_int16_t *)((char *)xfer->databuf +
- xfer->c_skip),
+ (u_int16_t *)
+ ((char *)xfer->databuf + xfer->c_skip),
xfer->c_bcount >> 1);
}
for (i = xfer->c_bcount; i < len; i += 2)
@@ -626,46 +778,56 @@ again:
xfer->c_bcount = 0;
} else {
if (drvp->drive_flags & DRIVE_CAP32) {
- if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM))
- bus_space_write_multi_4(chp->data32iot,
- chp->data32ioh, 0,
- (u_int32_t *)((char *)xfer->databuf +
- xfer->c_skip),
- len >> 2);
- else
- bus_space_write_multi_stream_4(chp->data32iot,
- chp->data32ioh, wd_data,
- (u_int32_t *)((char *)xfer->databuf +
- xfer->c_skip),
- len >> 2);
-
- xfer->c_skip += len & 0xfffffffc;
- xfer->c_bcount -= len & 0xfffffffc;
- len = len & 0x03;
+ if ((chp->wdc->cap &
+ WDC_CAPABILITY_ATAPI_NOSTREAM))
+ bus_space_write_multi_4(chp->data32iot,
+ chp->data32ioh, 0,
+ (u_int32_t *)
+ ((char *)xfer->databuf +
+ xfer->c_skip),
+ len >> 2);
+ else
+ bus_space_write_multi_stream_4(
+ chp->data32iot, chp->data32ioh,
+ wd_data,
+ (u_int32_t *)
+ ((char *)xfer->databuf +
+ xfer->c_skip),
+ len >> 2);
+
+ xfer->c_skip += len & 0xfffffffc;
+ xfer->c_bcount -= len & 0xfffffffc;
+ len = len & 0x03;
}
if (len > 0) {
- if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM))
- bus_space_write_multi_2(chp->cmd_iot,
- chp->cmd_ioh, wd_data,
- (u_int16_t *)((char *)xfer->databuf +
- xfer->c_skip),
- len >> 1);
- else
- bus_space_write_multi_stream_2(chp->cmd_iot,
- chp->cmd_ioh, wd_data,
- (u_int16_t *)((char *)xfer->databuf +
- xfer->c_skip),
- len >> 1);
- xfer->c_skip += len;
- xfer->c_bcount -= len;
+ if ((chp->wdc->cap &
+ WDC_CAPABILITY_ATAPI_NOSTREAM))
+ bus_space_write_multi_2(chp->cmd_iot,
+ chp->cmd_ioh, wd_data,
+ (u_int16_t *)
+ ((char *)xfer->databuf +
+ xfer->c_skip),
+ len >> 1);
+ else
+ bus_space_write_multi_stream_2(
+ chp->cmd_iot, chp->cmd_ioh,
+ wd_data,
+ (u_int16_t *)
+ ((char *)xfer->databuf +
+ xfer->c_skip),
+ len >> 1);
+ xfer->c_skip += len;
+ xfer->c_bcount -= len;
}
}
- if ((sc_xfer->flags & SCSI_POLL) == 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);
- }
- return 1;
+ } else if (drvp->atapi_cap & ACAP_DSC)
+ drvp->drive_flags |= DRIVE_DSCWAIT;
+ return (1);
case PHASE_DATAIN:
/* Read data */
@@ -681,69 +843,78 @@ again:
}
sc_xfer->error = XS_TIMEOUT;
wdc_atapi_reset(chp, xfer);
- return 1;
+ return (1);
}
if (xfer->c_bcount < len) {
printf("wdc_atapi_intr: warning: reading only "
"%d of %d bytes\n", xfer->c_bcount, len);
if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) {
- bus_space_read_multi_2(chp->cmd_iot,
- chp->cmd_ioh, wd_data,
- (u_int16_t *)((char *)xfer->databuf +
- xfer->c_skip),
- xfer->c_bcount >> 1);
+ bus_space_read_multi_2(chp->cmd_iot,
+ chp->cmd_ioh, wd_data,
+ (u_int16_t *)
+ ((char *)xfer->databuf + xfer->c_skip),
+ xfer->c_bcount >> 1);
} else {
- bus_space_read_multi_stream_2(chp->cmd_iot,
- chp->cmd_ioh, wd_data,
- (u_int16_t *)((char *)xfer->databuf +
- xfer->c_skip),
- xfer->c_bcount >> 1);
+ bus_space_read_multi_stream_2(chp->cmd_iot,
+ chp->cmd_ioh, wd_data,
+ (u_int16_t *)
+ ((char *)xfer->databuf + xfer->c_skip),
+ xfer->c_bcount >> 1);
}
wdcbit_bucket(chp, len - xfer->c_bcount);
xfer->c_skip += xfer->c_bcount;
xfer->c_bcount = 0;
} else {
if (drvp->drive_flags & DRIVE_CAP32) {
- if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM))
- bus_space_read_multi_4(chp->data32iot,
- chp->data32ioh, 0,
- (u_int32_t *)((char *)xfer->databuf +
- xfer->c_skip),
- len >> 2);
- else
- bus_space_read_multi_stream_4(chp->data32iot,
- chp->data32ioh, wd_data,
- (u_int32_t *)((char *)xfer->databuf +
- xfer->c_skip),
- len >> 2);
+ if ((chp->wdc->cap &
+ WDC_CAPABILITY_ATAPI_NOSTREAM))
+ bus_space_read_multi_4(chp->data32iot,
+ chp->data32ioh, 0,
+ (u_int32_t *)
+ ((char *)xfer->databuf +
+ xfer->c_skip),
+ len >> 2);
+ else
+ bus_space_read_multi_stream_4(
+ chp->data32iot, chp->data32ioh,
+ wd_data,
+ (u_int32_t *)
+ ((char *)xfer->databuf +
+ xfer->c_skip),
+ len >> 2);
- xfer->c_skip += len & 0xfffffffc;
- xfer->c_bcount -= len & 0xfffffffc;
- len = len & 0x03;
+ xfer->c_skip += len & 0xfffffffc;
+ xfer->c_bcount -= len & 0xfffffffc;
+ len = len & 0x03;
}
if (len > 0) {
- if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM))
- bus_space_read_multi_2(chp->cmd_iot,
- chp->cmd_ioh, wd_data,
- (u_int16_t *)((char *)xfer->databuf +
- xfer->c_skip),
- len >> 1);
- else
- bus_space_read_multi_stream_2(chp->cmd_iot,
- chp->cmd_ioh, wd_data,
- (u_int16_t *)((char *)xfer->databuf +
- xfer->c_skip),
- len >> 1);
- xfer->c_skip += len;
- xfer->c_bcount -=len;
+ if ((chp->wdc->cap &
+ WDC_CAPABILITY_ATAPI_NOSTREAM))
+ bus_space_read_multi_2(chp->cmd_iot,
+ chp->cmd_ioh, wd_data,
+ (u_int16_t *)
+ ((char *)xfer->databuf +
+ xfer->c_skip),
+ len >> 1);
+ else
+ bus_space_read_multi_stream_2(
+ chp->cmd_iot, chp->cmd_ioh,
+ wd_data,
+ (u_int16_t *)
+ ((char *)xfer->databuf +
+ xfer->c_skip),
+ len >> 1);
+ xfer->c_skip += len;
+ xfer->c_bcount -=len;
}
}
- if ((sc_xfer->flags & SCSI_POLL) == 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);
}
- return 1;
+ return (1);
case PHASE_ABORTED:
case PHASE_COMPLETED:
@@ -785,7 +956,8 @@ again:
if (chp->ch_status & WDCS_ERR) {
/* save the short sense */
sc_xfer->error = XS_SHORTSENSE;
- ATAPI_TO_SCSI_SENSE(&sc_xfer->sense, chp->ch_error);
+ ATAPI_TO_SCSI_SENSE(&sc_xfer->sense,
+ chp->ch_error);
if ((sc_xfer->sc_link->quirks &
ADEV_NOSENSE) == 0) {
/*
@@ -798,7 +970,7 @@ again:
xfer->c_skip = 0;
xfer->c_flags |= C_SENSE;
wdc_atapi_start(chp, xfer);
- return 1;
+ return (1);
}
} else if (dma_err < 0) {
drvp->n_dmaerrs++;
@@ -908,7 +1080,7 @@ piomode:
goto ready;
/* Also don't try if the drive didn't report its mode */
if ((drvp->drive_flags & DRIVE_MODE) == 0)
- goto ready;;
+ goto ready;
wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0,
0x08 | drvp->PIO_mode, WDSF_SET_MODE);
drvp->state = PIOMODE_WAIT;
@@ -952,26 +1124,27 @@ piomode:
drvp->state = READY;
xfer->c_intr = wdc_atapi_intr;
wdc_atapi_start(chp, xfer);
- return 1;
+ return (1);
}
- if ((sc_xfer->flags & SCSI_POLL) == 0) {
+ 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;
}
- return 1;
+ return (1);
timeout:
if (irq && (xfer->c_flags & C_TIMEOU) == 0) {
- return 0; /* IRQ was not for us */
+ 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;
+ return (1);
error:
printf("%s:%d:%d: %s ",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
@@ -980,7 +1153,7 @@ error:
sc_xfer->error = XS_SHORTSENSE;
ATAPI_TO_SCSI_SENSE(&sc_xfer->sense, chp->ch_error);
wdc_atapi_reset(chp, xfer);
- return 1;
+ return (1);
}
void
@@ -989,7 +1162,7 @@ wdc_atapi_done(chp, xfer)
struct wdc_xfer *xfer;
{
struct scsi_xfer *sc_xfer = xfer->cmd;
- int need_done = xfer->c_flags & C_NEEDDONE;
+ int need_done = xfer->c_flags & C_NEEDDONE;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
WDCDEBUG_PRINT(("wdc_atapi_done %s:%d:%d: flags 0x%x\n",
@@ -999,8 +1172,9 @@ wdc_atapi_done(chp, xfer)
wdc_free_xfer(chp, xfer);
sc_xfer->flags |= ITSDONE;
if (drvp->n_dmaerrs ||
- (sc_xfer->error != XS_NOERROR && sc_xfer->error != XS_SENSE &&
- sc_xfer->error != XS_SHORTSENSE)) {
+ (sc_xfer->error != XS_NOERROR && sc_xfer->error != XS_SENSE &&
+ sc_xfer->error != XS_SHORTSENSE)) {
+ printf("wdc_atapi_done: sc_xfer->error %d\n", sc_xfer->error);
drvp->n_dmaerrs = 0;
wdc_downgrade_mode(drvp);
}
@@ -1014,6 +1188,78 @@ wdc_atapi_done(chp, xfer)
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"));
+}
+
+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 =
+ bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_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)
struct channel_softc *chp;
@@ -1022,12 +1268,12 @@ wdc_atapi_reset(chp, xfer)
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
struct scsi_xfer *sc_xfer = xfer->cmd;
- chp->ch_status = bus_space_read_1(chp->cmd_iot,
- chp->cmd_ioh,
- wd_status);
+ chp->ch_status =
+ bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status);
if (chp->ch_status & WDCS_DRQ) {
- printf ("wdc_atapi_reset: DRQ is asserted. We've really messed up\n");
+ printf ("wdc_atapi_reset: "
+ "DRQ is asserted. We've really messed up\n");
wdc_reset_channel(drvp);
}
@@ -1049,4 +1295,3 @@ wdc_atapi_reset(chp, xfer)
wdc_atapi_done(chp, xfer);
return;
}
-