summaryrefslogtreecommitdiff
path: root/sys/dev/ic
diff options
context:
space:
mode:
authorNiklas Hallqvist <niklas@cvs.openbsd.org>2000-11-10 09:42:16 +0000
committerNiklas Hallqvist <niklas@cvs.openbsd.org>2000-11-10 09:42:16 +0000
commitf51858efd4992b548e0fe55a5bf82ab802fbe44d (patch)
treed0d967bebc4852547e1e0bb97a2e0b868adce917 /sys/dev/ic
parent901a2870a712cf0a705a50206332b672c79889ce (diff)
improved queue handling + some small bug fixes
Diffstat (limited to 'sys/dev/ic')
-rw-r--r--sys/dev/ic/gdt_common.c374
-rw-r--r--sys/dev/ic/gdtvar.h4
2 files changed, 202 insertions, 176 deletions
diff --git a/sys/dev/ic/gdt_common.c b/sys/dev/ic/gdt_common.c
index d3ff2542dec..5b3368858e0 100644
--- a/sys/dev/ic/gdt_common.c
+++ b/sys/dev/ic/gdt_common.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: gdt_common.c,v 1.8 2000/08/19 14:25:15 nate Exp $ */
+/* $OpenBSD: gdt_common.c,v 1.9 2000/11/10 09:42:14 niklas Exp $ */
/*
* Copyright (c) 1999, 2000 Niklas Hallqvist. All rights reserved.
@@ -53,6 +53,12 @@
#include <dev/ic/gdtreg.h>
#include <dev/ic/gdtvar.h>
+#ifdef GDT_DEBUG
+int gdt_maxcmds = GDT_MAXCMDS;
+#undef GDT_MAXCMDS
+#define GDT_MAXCMDS gdt_maxcmds
+#endif
+
int gdt_async_event __P((struct gdt_softc *, int));
void gdt_chain __P((struct gdt_softc *));
void gdt_clear_events __P((struct gdt_softc *));
@@ -136,11 +142,12 @@ gdt_attach(gdt)
/* Fill in the prototype scsi_link. */
gdt->sc_link.adapter_softc = gdt;
gdt->sc_link.adapter = &gdt_switch;
- gdt->sc_link.adapter_target = 7;
gdt->sc_link.device = &gdt_dev;
- gdt->sc_link.openings = GDT_MAXCMDS; /* XXX what is optimal? */
+ /* XXX what is optimal? */
+ gdt->sc_link.openings = GDT_MAXCMDS;
gdt->sc_link.adapter_buswidth =
(gdt->sc_class & GDT_FC) ? GDT_MAXID : GDT_MAX_HDRIVES;
+ gdt->sc_link.adapter_target = gdt->sc_link.adapter_buswidth;
if (!gdt_internal_cmd(gdt, GDT_SCREENSERVICE, GDT_INIT, 0, 0, 0)) {
printf("screen service initialization error %d\n",
@@ -295,12 +302,12 @@ gdt_attach(gdt)
gdt->sc_more_proc = 1;
}
} else {
- /* XXX Not implemented yet */
+ /* XXX Not implemented yet */
}
/* Read more information */
if (gdt->sc_more_proc) {
- /* XXX Not implemented yet */
+ /* XXX Not implemented yet */
}
if (!gdt_internal_cmd(gdt, GDT_SCSIRAWSERVICE, GDT_INIT, 0, 0, 0)) {
@@ -447,6 +454,8 @@ gdt_dequeue(gdt)
struct scsi_xfer *xs;
xs = LIST_FIRST(&gdt->sc_queue);
+ if (xs == NULL)
+ return (NULL);
LIST_REMOVE(xs, free_list);
if (LIST_FIRST(&gdt->sc_queue) == NULL)
@@ -455,7 +464,10 @@ gdt_dequeue(gdt)
return (xs);
}
-/* Start a SCSI operation on a cache device */
+/*
+ * Start a SCSI operation on a cache device.
+ * XXX Polled operation is not yet complete. What kind of locking do we need?
+ */
int
gdt_scsi_cmd(xs)
struct scsi_xfer *xs;
@@ -464,203 +476,206 @@ gdt_scsi_cmd(xs)
struct gdt_softc *gdt = link->adapter_softc;
u_int8_t target = link->target;
struct gdt_ccb *ccb;
- int dontqueue = 0;
u_int32_t blockno, blockcnt;
struct scsi_rw *rw;
struct scsi_rw_big *rwb;
bus_dmamap_t xfer;
- int error;
+ int error, retval = SUCCESSFULLY_QUEUED;
gdt_lock_t lock;
GDT_DPRINTF(GDT_D_CMD, ("gdt_scsi_cmd "));
+ xs->error = XS_NOERROR;
+
if (target >= GDT_MAX_HDRIVES || !gdt->sc_hdr[target].hd_present ||
link->lun != 0) {
+ /*
+ * XXX Should be XS_SENSE but that would require setting up a
+ * faked sense too.
+ */
xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
return (COMPLETE);
}
- xs->error = XS_NOERROR;
- ccb = NULL;
-
- lock = GDT_LOCK_GDT(gdt);
- if (!gdt_polling && !(xs->flags & SCSI_POLL) &&
- gdt->sc_test_busy(gdt)) {
- /* Don't double enqueue if we came from gdt_chain. */
- if (xs != LIST_FIRST(&gdt->sc_queue))
- gdt_enqueue(gdt, xs, 0);
- GDT_UNLOCK_GDT(gdt, lock);
- return (SUCCESSFULLY_QUEUED);
- }
- GDT_UNLOCK_GDT(gdt, lock);
+ lock = GDT_LOCK_GDT(lock);
- switch (xs->cmd->opcode) {
- case TEST_UNIT_READY:
- case REQUEST_SENSE:
- case INQUIRY:
- case MODE_SENSE:
- case START_STOP:
- case READ_CAPACITY:
-#if 0
- case VERIFY:
-#endif
- return (gdt_internal_cache_cmd(xs) ? COMPLETE :
- TRY_AGAIN_LATER);
+ /* Don't double enqueue if we came from gdt_chain. */
+ if (xs != LIST_FIRST(&gdt->sc_queue))
+ gdt_enqueue(gdt, xs, 0);
- case PREVENT_ALLOW:
- GDT_DPRINTF(GDT_D_CMD, ("PREVENT/ALLOW "));
- /* XXX Not yet implemented */
+ while ((xs = gdt_dequeue(gdt))) {
xs->error = XS_NOERROR;
- return (COMPLETE);
-
- case READ_COMMAND:
- case READ_BIG:
- case WRITE_COMMAND:
- case WRITE_BIG:
- case SYNCHRONIZE_CACHE:
- lock = GDT_LOCK_GDT(gdt);
-
- /*
- * When chaining commands we may get called with the
- * first command in the queue, recognize this case
- * easily.
- */
- if (xs == LIST_FIRST(&gdt->sc_queue))
- xs = gdt_dequeue(gdt);
- else {
- /* A new command chain, start from the beginning. */
- gdt->sc_cmd_off = 0;
-
- /* Don't resort to queuing if we are polling. */
- dontqueue = xs->flags & SCSI_POLL;
+ ccb = NULL;
+ if (!gdt_polling && !(xs->flags & SCSI_POLL) &&
+ gdt->sc_test_busy(gdt)) {
/*
- * The queue, if existent, must be processed first,
- * before the new command can be run.
+ * Put it back in front. XXX Should we instead
+ * set xs->error to XS_BUSY?
*/
- if (LIST_FIRST(&gdt->sc_queue) != NULL) {
- /* If order cannot be preserved, punt. */
- if (dontqueue) {
- GDT_UNLOCK_GDT(gdt, lock);
- xs->error = XS_DRIVER_STUFFUP;
- return (TRY_AGAIN_LATER);
- }
-
- /*
- * Enqueue the new command, ponder on the front
- * command of the queue instead.
- */
- gdt_enqueue(gdt, xs, 0);
- xs = gdt_dequeue(gdt);
- }
+ gdt_enqueue(gdt, xs, 1);
+ break;
}
- if (xs->cmd->opcode != SYNCHRONIZE_CACHE) {
- /* A read or write operation. */
- if (xs->cmdlen == 6) {
- rw = (struct scsi_rw *)xs->cmd;
- blockno = _3btol(rw->addr) &
- (SRW_TOPADDR << 16 | 0xffff);
- blockcnt = rw->length ? rw->length : 0x100;
- } else {
- rwb = (struct scsi_rw_big *)xs->cmd;
- blockno = _4btol(rwb->addr);
- blockcnt = _2btol(rwb->length);
- }
- if (blockno >= gdt->sc_hdr[target].hd_size ||
- blockno + blockcnt > gdt->sc_hdr[target].hd_size) {
+ switch (xs->cmd->opcode) {
+ case TEST_UNIT_READY:
+ case REQUEST_SENSE:
+ case INQUIRY:
+ case MODE_SENSE:
+ case START_STOP:
+ case READ_CAPACITY:
+#if 0
+ case VERIFY:
+#endif
+ if (!gdt_internal_cache_cmd(xs)) {
GDT_UNLOCK_GDT(gdt, lock);
- printf("%s: out of bounds %u-%u >= %u\n",
- gdt->sc_dev.dv_xname, blockno, blockcnt,
- gdt->sc_hdr[target].hd_size);
- scsi_done(xs);
- xs->error = XS_DRIVER_STUFFUP;
- return (COMPLETE);
+ return (TRY_AGAIN_LATER);
}
- }
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+
+ case PREVENT_ALLOW:
+ GDT_DPRINTF(GDT_D_CMD, ("PREVENT/ALLOW "));
+ /* XXX Not yet implemented */
+ xs->error = XS_NOERROR;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
- ccb = gdt_get_ccb(gdt, xs->flags);
+ default:
+ GDT_DPRINTF(GDT_D_CMD,
+ ("unknown opc %d ", xs->cmd->opcode));
+ /* XXX Not yet implemented */
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+
+ case READ_COMMAND:
+ case READ_BIG:
+ case WRITE_COMMAND:
+ case WRITE_BIG:
+ case SYNCHRONIZE_CACHE:
+ /*
+ * A new command chain, start from the beginning.
+ */
+ gdt->sc_cmd_off = 0;
- /*
- * Are we out of commands, then queue. If we cannot queue,
- * then punt.
- */
- if (ccb == NULL) {
- if (dontqueue) {
- GDT_UNLOCK_GDT(gdt, lock);
- scsi_done(xs);
- xs->error = XS_DRIVER_STUFFUP;
- return (COMPLETE);
+ if (xs->cmd->opcode != SYNCHRONIZE_CACHE) {
+ /* A read or write operation. */
+ if (xs->cmdlen == 6) {
+ rw = (struct scsi_rw *)xs->cmd;
+ blockno = _3btol(rw->addr) &
+ (SRW_TOPADDR << 16 | 0xffff);
+ blockcnt =
+ rw->length ? rw->length : 0x100;
+ } else {
+ rwb = (struct scsi_rw_big *)xs->cmd;
+ blockno = _4btol(rwb->addr);
+ blockcnt = _2btol(rwb->length);
+ }
+ if (blockno >= gdt->sc_hdr[target].hd_size ||
+ blockno + blockcnt >
+ gdt->sc_hdr[target].hd_size) {
+ printf(
+ "%s: out of bounds %u-%u >= %u\n",
+ gdt->sc_dev.dv_xname, blockno,
+ blockcnt,
+ gdt->sc_hdr[target].hd_size);
+ /*
+ * XXX Should be XS_SENSE but that
+ * would require setting up a faked
+ * sense too.
+ */
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+ }
}
- if (xs->error) {
- GDT_UNLOCK_GDT(gdt, lock);
+
+ ccb = gdt_get_ccb(gdt, xs->flags);
+ /*
+ * Are we out of commands, something is wrong.
+ *
+ */
+ if (ccb == NULL) {
+ printf("%s: no ccb in gdt_scsi_cmd",
+ gdt->sc_dev.dv_xname);
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
scsi_done(xs);
- return (COMPLETE);
+ goto ready;
}
- /* Put back on the queue, in the front. */
- gdt_enqueue(gdt, xs, 1);
- GDT_UNLOCK_GDT(gdt, lock);
- return (SUCCESSFULLY_QUEUED);
- }
-
- ccb->gc_blockno = blockno;
- ccb->gc_blockcnt = blockcnt;
- ccb->gc_xs = xs;
- ccb->gc_timeout = xs->timeout;
- ccb->gc_service = GDT_CACHESERVICE;
- gdt_ccb_set_cmd(ccb, GDT_GCF_SCSI);
-
- if (xs->cmd->opcode != SYNCHRONIZE_CACHE) {
- xfer = ccb->gc_dmamap_xfer;
- error = bus_dmamap_load(gdt->sc_dmat, xfer, xs->data,
- xs->datalen, NULL, (xs->flags & SCSI_NOSLEEP) ?
- BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
- if (error) {
- printf("%s: gdt_scsi_cmd: ",
- gdt->sc_dev.dv_xname);
- if (error == EFBIG)
- printf("more than %d dma segs\n",
- GDT_MAXOFFSETS);
- else
- printf("error %d loading dma map\n",
- error);
-
- xs->error = XS_DRIVER_STUFFUP;
- gdt_free_ccb(gdt, ccb);
- return (COMPLETE);
+ ccb->gc_blockno = blockno;
+ ccb->gc_blockcnt = blockcnt;
+ ccb->gc_xs = xs;
+ ccb->gc_timeout = xs->timeout;
+ ccb->gc_service = GDT_CACHESERVICE;
+ gdt_ccb_set_cmd(ccb, GDT_GCF_SCSI);
+
+ if (xs->cmd->opcode != SYNCHRONIZE_CACHE) {
+ xfer = ccb->gc_dmamap_xfer;
+ error = bus_dmamap_load(gdt->sc_dmat, xfer,
+ xs->data, xs->datalen, NULL,
+ (xs->flags & SCSI_NOSLEEP) ?
+ BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
+ if (error) {
+ printf("%s: gdt_scsi_cmd: ",
+ gdt->sc_dev.dv_xname);
+ if (error == EFBIG)
+ printf(
+ "more than %d dma segs\n",
+ GDT_MAXOFFSETS);
+ else
+ printf("error %d "
+ "loading dma map\n",
+ error);
+
+ gdt_free_ccb(gdt, ccb);
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+ }
+ bus_dmamap_sync(gdt->sc_dmat, xfer,
+ (xs->flags & SCSI_DATA_IN) ?
+ BUS_DMASYNC_PREREAD :
+ BUS_DMASYNC_PREWRITE);
}
- bus_dmamap_sync(gdt->sc_dmat, xfer,
- (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD :
- BUS_DMASYNC_PREWRITE);
- }
- gdt_enqueue_ccb(gdt, ccb);
- /* XXX what if enqueue did not start a transfer? */
- if (gdt_polling || (xs->flags & SCSI_POLL)) {
- if (!gdt_wait(gdt, ccb, ccb->gc_timeout)) {
- GDT_UNLOCK_GDT(gdt, lock);
- printf("%s: command %d timed out\n",
- gdt->sc_dev.dv_xname, ccb->gc_cmd_index);
- xs->error = XS_TIMEOUT;
- return (TRY_AGAIN_LATER);
+ gdt_enqueue_ccb(gdt, ccb);
+ /* XXX what if enqueue did not start a transfer? */
+ if (gdt_polling || (xs->flags & SCSI_POLL)) {
+ if (!gdt_wait(gdt, ccb, ccb->gc_timeout)) {
+ GDT_UNLOCK_GDT(gdt, lock);
+ printf("%s: command %d timed out\n",
+ gdt->sc_dev.dv_xname,
+ ccb->gc_cmd_index);
+ xs->error = XS_TIMEOUT;
+ return (TRY_AGAIN_LATER);
+ }
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
}
}
- GDT_UNLOCK_GDT(gdt, lock);
-
- if (gdt_polling || (xs->flags & SCSI_POLL)) {
- scsi_done(xs);
- return (COMPLETE);
+ ready:
+ /*
+ * Don't process the queue if we are polling.
+ */
+ if (xs->flags & SCSI_POLL) {
+ retval = COMPLETE;
+ break;
}
- return (SUCCESSFULLY_QUEUED);
-
- default:
- GDT_DPRINTF(GDT_D_CMD, ("unknown opc %d ", xs->cmd->opcode));
- /* XXX Not yet implemented */
- xs->error = XS_DRIVER_STUFFUP;
- return (COMPLETE);
}
+
+ GDT_UNLOCK_GDT(gdt, lock);
+ return (retval);
}
/* XXX Currently only for cacheservice, returns 0 if busy */
@@ -679,8 +694,9 @@ gdt_exec_ccb(ccb)
GDT_DPRINTF(GDT_D_CMD, ("gdt_exec_ccb(%p, %p) ", xs, ccb));
gdt->sc_cmd_cnt = 0;
+
/*
- * XXX Yeah I know it's a always-true condition, but that may change
+ * XXX Yeah I know it's an always-true condition, but that may change
* later.
*/
if (gdt->sc_cmd_cnt == 0)
@@ -795,8 +811,6 @@ gdt_copy_internal_data(xs, data, size)
copy_cnt = MIN(size, xs->datalen);
bcopy(data, xs->data, copy_cnt);
}
-
-
}
/* Emulated SCSI operation on cache device */
@@ -886,7 +900,7 @@ gdt_internal_cache_cmd(xs)
default:
printf("%s: mode sense page %d not simulated\n",
gdt->sc_dev.dv_xname,
- ((struct scsi_mode_sense *)xs->cmd)->page);
+ ((struct scsi_mode_sense *)xs->cmd)->page);
xs->error = XS_DRIVER_STUFFUP;
return (0);
}
@@ -959,12 +973,22 @@ gdt_sync_event(gdt, service, index, xs)
("gdt_sync_event(%p, %d, %d, %p) ", gdt, service, index, xs));
if (service == GDT_SCREENSERVICE) {
+ GDT_DPRINTF(GDT_D_INTR, ("service == GDT_SCREENSERVICE "));
/* XXX To be implemented */
return (0);
} else {
- if (gdt->sc_status == GDT_S_OK) {
+ switch (gdt->sc_status) {
+ case GDT_S_OK:
+ GDT_DPRINTF(GDT_D_INTR, ("sc_status == GDT_S_OK "));
/* XXX To be implemented */
- } else {
+ break;
+ case GDT_S_BSY:
+ GDT_DPRINTF(GDT_D_INTR, ("sc_status == GDT_S_BSY "));
+ /* XXX To be implemented */
+ return (2);
+ default:
+ GDT_DPRINTF(GDT_D_INTR, ("sc_status is %d ",
+ gdt->sc_status));
/* XXX To be implemented */
return (0);
}
@@ -979,7 +1003,8 @@ gdt_intr(arg)
{
struct gdt_softc *gdt = arg;
struct gdt_intr_ctx ctx;
- int chain = 1, sync_val = 0;
+ int chain = 1;
+ int sync_val = 0;
struct scsi_xfer *xs;
int prev_cmd;
struct gdt_ccb *ccb;
@@ -1022,7 +1047,8 @@ gdt_intr(arg)
goto finish;
case GDT_SPEZINDEX:
- /* XXX Not yet implemented */
+ printf("%s: uninitialized or unknown service (%d %d)\n",
+ gdt->sc_dev.dv_xname, ctx.info, ctx.info2);
chain = 0;
goto finish;
}
diff --git a/sys/dev/ic/gdtvar.h b/sys/dev/ic/gdtvar.h
index 16aebe9d1bd..f436b15a9a4 100644
--- a/sys/dev/ic/gdtvar.h
+++ b/sys/dev/ic/gdtvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: gdtvar.h,v 1.4 2000/08/05 18:52:34 niklas Exp $ */
+/* $OpenBSD: gdtvar.h,v 1.5 2000/11/10 09:42:14 niklas Exp $ */
/*
* Copyright (c) 1999, 2000 Niklas Hallqvist. All rights reserved.
@@ -51,7 +51,7 @@ extern int gdt_debug;
#define GDT_RETRIES 100000000 /* 100000 * 1us = 100s */
#define GDT_TIMEOUT 100000000 /* 100000 * 1us = 100s */
#define GDT_POLL_TIMEOUT 10000000 /* 10000 * 1us = 10s */
-#define GDT_WATCH_TIMEOUT 10000000 /* 10000 * 1us = 10s */
+#define GDT_WATCH_TIMEOUT 10000 /* 10000 * 1ms = 10s */
#define GDT_SCRATCH_SZ 4096 /* 4KB scratch buffer */
/* Context structure for interrupt services */