summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authormycroft <mycroft@NetBSD.org>1993-11-24 04:52:44 +0000
committermycroft <mycroft@NetBSD.org>1993-11-24 04:52:44 +0000
commit37548328a127374ec0fe45b359c28b2234efd451 (patch)
tree677535d657c3fa228d09bf919caf5a362aeb0722 /sys/dev
parent2a9aacbf1cc2963a0155bf997b57e58ca4ebb331 (diff)
Under construction...
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/scsipi/scsi_base.c846
-rw-r--r--sys/dev/scsipi/scsipi_debug.h53
-rw-r--r--sys/dev/scsipi/scsipi_ioctl.c329
-rw-r--r--sys/dev/scsipi/su.c4
-rw-r--r--sys/dev/scsipi/uk.c157
5 files changed, 1389 insertions, 0 deletions
diff --git a/sys/dev/scsipi/scsi_base.c b/sys/dev/scsipi/scsi_base.c
new file mode 100644
index 00000000000..ccc89e65395
--- /dev/null
+++ b/sys/dev/scsipi/scsi_base.c
@@ -0,0 +1,846 @@
+/*
+ * Written by Julian Elischer (julian@dialix.oz.au)
+ * $Id: scsi_base.c,v 1.1 1993/11/24 04:52:46 mycroft Exp $
+ */
+
+#define SPLSD splbio
+#define ESUCCESS 0
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+
+#ifdef __NetBSD__
+#ifdef DDB
+int Debugger();
+#else /* DDB */
+#define Debugger()
+#endif /* DDB */
+#else /* NetBSD */
+#include <ddb.h>
+#if NDDB > 0
+int Debugger();
+#else /* NDDB > 0 */
+#define Debugger()
+#endif /* NDDB > 0 */
+#endif
+
+void sc_print_addr __P((struct scsi_link *sc_link));
+
+struct scsi_xfer *next_free_xs;
+
+/*
+ * Get a scsi transfer structure for the caller. Charge the structure
+ * to the device that is referenced by the sc_link structure. If the
+ * sc_link structure has no 'credits' then the device already has the
+ * maximum number or outstanding operations under way. In this stage,
+ * wait on the structure so that when one is freed, we are awoken again
+ * If the SCSI_NOSLEEP flag is set, then do not wait, but rather, return
+ * a NULL pointer, signifying that no slots were available
+ * Note in the link structure, that we are waiting on it.
+ */
+
+struct scsi_xfer *
+get_xs(sc_link, flags)
+ struct scsi_link *sc_link; /* who to charge the xs to */
+ u_int32 flags; /* if this call can sleep */
+{
+ struct scsi_xfer *xs;
+ u_int32 s;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("get_xs\n"));
+ s = splbio();
+ while (!sc_link->opennings) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("sleeping\n"));
+ if (flags & SCSI_NOSLEEP) {
+ splx(s);
+ return 0;
+ }
+ sc_link->flags |= SDEV_WAITING;
+ sleep(sc_link, PRIBIO);
+ }
+ sc_link->opennings--;
+ if (xs = next_free_xs) {
+ next_free_xs = xs->next;
+ splx(s);
+ } else {
+ splx(s);
+ SC_DEBUG(sc_link, SDEV_DB3, ("making\n"));
+ xs = malloc(sizeof(*xs), M_TEMP,
+ ((flags & SCSI_NOSLEEP) ? M_NOWAIT : M_WAITOK));
+ if (xs == NULL) {
+ sc_print_addr(sc_link);
+ printf("cannot allocate scsi xs\n");
+ return (NULL);
+ }
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("returning\n"));
+ xs->sc_link = sc_link;
+ return (xs);
+}
+
+/*
+ * Given a scsi_xfer struct, and a device (referenced through sc_link)
+ * return the struct to the free pool and credit the device with it
+ * If another process is waiting for an xs, do a wakeup, let it proceed
+ */
+void
+free_xs(xs, sc_link, flags)
+ struct scsi_xfer *xs;
+ struct scsi_link *sc_link; /* who to credit for returning it */
+ u_int32 flags;
+{
+ xs->next = next_free_xs;
+ next_free_xs = xs;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("free_xs\n"));
+ /* if was 0 and someone waits, wake them up */
+ if ((!sc_link->opennings++) && (sc_link->flags & SDEV_WAITING)) {
+ wakeup(sc_link);
+ } else {
+ if (sc_link->device->start) {
+ SC_DEBUG(sc_link, SDEV_DB2, ("calling private start()\n"));
+ (*(sc_link->device->start)) (sc_link->dev_unit);
+ }
+ }
+}
+
+/*
+ * Find out from the device what its capacity is.
+ */
+u_int32
+scsi_size(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_read_cap_data rdcap;
+ struct scsi_read_capacity scsi_cmd;
+ u_int32 size;
+
+ /*
+ * make up a scsi command and ask the scsi driver to do
+ * it for you.
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_CAPACITY;
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks
+ */
+ if (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) & rdcap,
+ sizeof(rdcap),
+ 2,
+ 20000,
+ NULL,
+ flags | SCSI_DATA_IN) != 0) {
+
+ sc_print_addr(sc_link);
+ printf("could not get size\n");
+ return (0);
+ } else {
+ size = rdcap.addr_0 + 1;
+ size += rdcap.addr_1 << 8;
+ size += rdcap.addr_2 << 16;
+ size += rdcap.addr_3 << 24;
+ }
+ return (size);
+}
+
+/*
+ * Get scsi driver to send a "are you ready?" command
+ */
+errval
+scsi_test_unit_ready(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_test_unit_ready scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = TEST_UNIT_READY;
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 100000,
+ NULL,
+ flags));
+}
+
+/*
+ * Do a scsi operation, asking a device to run as SCSI-II if it can.
+ */
+errval
+scsi_change_def(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_changedef scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = CHANGE_DEFINITION;
+ scsi_cmd.how = SC_SCSI_2;
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 100000,
+ NULL,
+ flags));
+}
+
+/*
+ * Do a scsi operation asking a device what it is
+ * Use the scsi_cmd routine in the switch table.
+ */
+errval
+scsi_inquire(sc_link, inqbuf, flags)
+ struct scsi_link *sc_link;
+ struct scsi_inquiry_data *inqbuf;
+ u_int32 flags;
+{
+ struct scsi_inquiry scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = INQUIRY;
+ scsi_cmd.length = sizeof(struct scsi_inquiry_data);
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) inqbuf,
+ sizeof(struct scsi_inquiry_data),
+ 2,
+ 100000,
+ NULL,
+ SCSI_DATA_IN | flags));
+}
+
+/*
+ * Prevent or allow the user to remove the media
+ */
+errval
+scsi_prevent(sc_link, type, flags)
+ struct scsi_link *sc_link;
+ u_int32 type, flags;
+{
+ struct scsi_prevent scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = PREVENT_ALLOW;
+ scsi_cmd.how = type;
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 5000,
+ NULL,
+ flags));
+}
+
+/*
+ * Get scsi driver to send a "start up" command
+ */
+errval
+scsi_start_unit(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_start_stop scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = START_STOP;
+ scsi_cmd.how = SSS_START;
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 2000,
+ NULL,
+ flags));
+}
+
+/*
+ * This routine is called by the scsi interrupt when the transfer is complete.
+ */
+void
+scsi_done(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *sc_link = xs->sc_link;
+ struct buf *bp = xs->bp;
+ errval retval;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("scsi_done\n"));
+#ifdef SCSIDEBUG
+ if (sc_link->flags & SDEV_DB1)
+ {
+ show_scsi_cmd(xs);
+ }
+#endif /*SCSIDEBUG */
+ /*
+ * If it's a user level request, bypass all usual completion processing,
+ * let the user work it out.. We take reponsibility for freeing the
+ * xs when the user returns. (and restarting the device's queue).
+ */
+ if (xs->flags & SCSI_USER) {
+ biodone(xs->bp);
+#ifdef NOTNOW
+ SC_DEBUG(sc_link, SDEV_DB3, ("calling user done()\n"));
+ scsi_user_done(xs); /* to take a copy of the sense etc. */
+ SC_DEBUG(sc_link, SDEV_DB3, ("returned from user done()\n "));
+#endif
+ free_xs(xs, sc_link, SCSI_NOSLEEP); /* restarts queue too */
+ SC_DEBUG(sc_link, SDEV_DB3, ("returning to adapter\n"));
+ return;
+ }
+ /*
+ * If the device has it's own done routine, call it first.
+ * If it returns a legit error value, return that, otherwise
+ * it wants us to continue with normal processing.
+ */
+
+ if (sc_link->device->done) {
+ SC_DEBUG(sc_link, SDEV_DB2, ("calling private done()\n"));
+ retval = (*sc_link->device->done) (xs);
+ if (retval == -1) {
+ free_xs(xs, sc_link, SCSI_NOSLEEP); /*XXX */
+ return; /* it did it all, finish up */
+ }
+ if (retval == -2) {
+ return; /* it did it all, finish up */
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("continuing with generic done()\n"));
+ }
+ if ((bp = xs->bp) == NULL) {
+ /*
+ * if it's a normal upper level request, then ask
+ * the upper level code to handle error checking
+ * rather than doing it here at interrupt time
+ */
+ wakeup(xs);
+ return;
+ }
+ /*
+ * Go and handle errors now.
+ * If it returns -1 then we should RETRY
+ */
+ if ((retval = sc_err1(xs)) == -1) {
+ if ((*(sc_link->adapter->scsi_cmd)) (xs)
+ == SUCCESSFULLY_QUEUED) { /* don't wake the job, ok? */
+ return;
+ }
+ xs->flags |= ITSDONE;
+ }
+ free_xs(xs, sc_link, SCSI_NOSLEEP); /* does a start if needed */
+ biodone(bp);
+}
+
+/*
+ * ask the scsi driver to perform a command for us.
+ * tell it where to read/write the data, and how
+ * long the data is supposed to be. If we have a buf
+ * to associate with the transfer, we need that too.
+ */
+errval
+scsi_scsi_cmd(sc_link, scsi_cmd, cmdlen, data_addr, datalen,
+ retries, timeout, bp, flags)
+ struct scsi_link *sc_link;
+ struct scsi_generic *scsi_cmd;
+ u_int32 cmdlen;
+ u_char *data_addr;
+ u_int32 datalen;
+ u_int32 retries;
+ u_int32 timeout;
+ struct buf *bp;
+ u_int32 flags;
+{
+ struct scsi_xfer *xs;
+ errval retval;
+ u_int32 s;
+
+ if (bp) flags |= SCSI_NOSLEEP;
+ SC_DEBUG(sc_link, SDEV_DB2, ("scsi_cmd\n"));
+
+ xs = get_xs(sc_link, flags); /* should wait unless booting */
+ if (!xs) return (ENOMEM);
+ /*
+ * Fill out the scsi_xfer structure. We don't know whose context
+ * the cmd is in, so copy it.
+ */
+ bcopy(scsi_cmd, &(xs->cmdstore), cmdlen);
+ xs->flags = INUSE | flags;
+ xs->sc_link = sc_link;
+ xs->retries = retries;
+ xs->timeout = timeout;
+ xs->cmd = &xs->cmdstore;
+ xs->cmdlen = cmdlen;
+ xs->data = data_addr;
+ xs->datalen = datalen;
+ xs->resid = datalen;
+ xs->bp = bp;
+/*XXX*/ /*use constant not magic number */
+ if (datalen && ((caddr_t) data_addr < (caddr_t) 0xfe000000)) {
+ if (bp) {
+ printf("Data buffered space not in kernel context\n");
+#ifdef SCSIDEBUG
+ show_scsi_cmd(xs);
+#endif /* SCSIDEBUG */
+ retval = EFAULT;
+ goto bad;
+ }
+ xs->data = malloc(datalen, M_TEMP, M_WAITOK);
+ /* I think waiting is ok *//*XXX */
+ switch (flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
+ case 0:
+ printf("No direction flags, assuming both\n");
+#ifdef SCSIDEBUG
+ show_scsi_cmd(xs);
+#endif /* SCSIDEBUG */
+ case SCSI_DATA_IN | SCSI_DATA_OUT: /* weird */
+ case SCSI_DATA_OUT:
+ bcopy(data_addr, xs->data, datalen);
+ break;
+ case SCSI_DATA_IN:
+ bzero(xs->data, datalen);
+ }
+ }
+retry:
+ xs->error = XS_NOERROR;
+#ifdef PARANOID
+ if (datalen && ((caddr_t) xs->data < (caddr_t) 0xfe000000)) {
+ printf("It's still wrong!\n");
+ }
+#endif /*PARANOID*/
+#ifdef SCSIDEBUG
+ if (sc_link->flags & SDEV_DB3) show_scsi_xs(xs);
+#endif /* SCSIDEBUG */
+ /*
+ * Do the transfer. If we are polling we will return:
+ * COMPLETE, Was poll, and scsi_done has been called
+ * TRY_AGAIN_LATER, Adapter short resources, try again
+ *
+ * if under full steam (interrupts) it will return:
+ * SUCCESSFULLY_QUEUED, will do a wakeup when complete
+ * TRY_AGAIN_LATER, (as for polling)
+ * After the wakeup, we must still check if it succeeded
+ *
+ * If we have a bp however, all the error proccessing
+ * and the buffer code both expect us to return straight
+ * to them, so as soon as the command is queued, return
+ */
+
+ retval = (*(sc_link->adapter->scsi_cmd)) (xs);
+
+ switch (retval) {
+ case SUCCESSFULLY_QUEUED:
+ if (bp)
+ return retval; /* will sleep (or not) elsewhere */
+ s = splbio();
+ while (!(xs->flags & ITSDONE))
+ sleep(xs, PRIBIO + 1);
+ splx(s);
+ /* fall through to check success of completed command */
+ case COMPLETE: /* Polling command completed ok */
+/*XXX*/ case HAD_ERROR: /* Polling command completed with error */
+ SC_DEBUG(sc_link, SDEV_DB3, ("back in cmd()\n"));
+ if ((retval = sc_err1(xs)) == -1)
+ goto retry;
+ break;
+
+ case TRY_AGAIN_LATER: /* adapter resource shortage */
+ SC_DEBUG(sc_link, SDEV_DB3, ("will try again \n"));
+ /* should sleep 1 sec here */
+ if (xs->retries--) {
+ xs->flags &= ~ITSDONE;
+ goto retry;
+ }
+ default:
+ retval = EIO;
+ }
+ /*
+ * If we had to copy the data out of the user's context,
+ * then do the other half (copy it back or whatever)
+ * and free the memory buffer
+ */
+ if (datalen && (xs->data != data_addr)) {
+ switch (flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
+ case 0:
+ case SCSI_DATA_IN | SCSI_DATA_OUT: /* weird */
+ case SCSI_DATA_IN:
+ bcopy(xs->data, data_addr, datalen);
+ break;
+ }
+ free(xs->data, M_TEMP);
+ }
+ /*
+ * we have finished with the xfer stuct, free it and
+ * check if anyone else needs to be started up.
+ */
+bad:
+ free_xs(xs, sc_link, flags); /* includes the 'start' op */
+ if (bp && retval) {
+ bp->b_error = retval;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ }
+ return (retval);
+}
+
+errval
+sc_err1(xs)
+ struct scsi_xfer *xs;
+{
+ struct buf *bp = xs->bp;
+ errval retval;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("sc_err1,err = 0x%x \n", xs->error));
+ /*
+ * If it has a buf, we might be working with
+ * a request from the buffer cache or some other
+ * piece of code that requires us to process
+ * errors at inetrrupt time. We have probably
+ * been called by scsi_done()
+ */
+ switch (xs->error) {
+ case XS_NOERROR: /* nearly always hit this one */
+ retval = ESUCCESS;
+ if (bp) {
+ bp->b_error = 0;
+ bp->b_resid = 0;
+ }
+ break;
+
+ case XS_SENSE:
+ if (bp) {
+ bp->b_error = 0;
+ bp->b_resid = 0;
+ if (retval = (scsi_interpret_sense(xs))) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = retval;
+ bp->b_resid = bp->b_bcount;
+ }
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("scsi_interpret_sense (bp) returned %d\n", retval));
+ } else {
+ retval = (scsi_interpret_sense(xs));
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("scsi_interpret_sense (no bp) returned %d\n", retval));
+ }
+ break;
+
+ case XS_BUSY:
+ /*should somehow arange for a 1 sec delay here (how?) */
+ case XS_TIMEOUT:
+ /*
+ * If we can, resubmit it to the adapter.
+ */
+ if (xs->retries--) {
+ xs->error = XS_NOERROR;
+ xs->flags &= ~ITSDONE;
+ goto retry;
+ }
+ /* fall through */
+ case XS_DRIVER_STUFFUP:
+ if (bp) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ }
+ retval = EIO;
+ break;
+ default:
+ retval = EIO;
+ sc_print_addr(xs->sc_link);
+ printf("unknown error category from scsi driver\n");
+ }
+ return retval;
+retry:
+ return (-1);
+}
+
+/*
+ * Look at the returned sense and act on the error, determining
+ * the unix error number to pass back. (0 = report no error)
+ *
+ * THIS IS THE DEFAULT ERROR HANDLER
+ */
+errval
+scsi_interpret_sense(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *sense;
+ struct scsi_link *sc_link = xs->sc_link;
+ u_int32 key;
+ u_int32 silent;
+ u_int32 info;
+ errval errcode;
+
+ static char *error_mes[] =
+ {"soft error (corrected)",
+ "not ready", "medium error",
+ "non-media hardware failure", "illegal request",
+ "unit attention", "readonly device",
+ "no data found", "vendor unique",
+ "copy aborted", "command aborted",
+ "search returned equal", "volume overflow",
+ "verify miscompare", "unknown error key"
+ };
+
+ /*
+ * If the flags say errs are ok, then always return ok.
+ */
+ if (xs->flags & SCSI_ERR_OK)
+ return (ESUCCESS);
+
+ sense = &(xs->sense);
+#ifdef SCSIDEBUG
+ if (sc_link->flags & SDEV_DB1) {
+ u_int32 count = 0;
+ printf("code%x valid%x ",
+ sense->error_code & SSD_ERRCODE,
+ sense->error_code & SSD_ERRCODE_VALID ? 1 : 0);
+ printf("seg%x key%x ili%x eom%x fmark%x\n",
+ sense->ext.extended.segment,
+ sense->ext.extended.flags & SSD_KEY,
+ sense->ext.extended.flags & SSD_ILI ? 1 : 0,
+ sense->ext.extended.flags & SSD_EOM ? 1 : 0,
+ sense->ext.extended.flags & SSD_FILEMARK ? 1 : 0);
+ printf("info: %x %x %x %x followed by %d extra bytes\n",
+ sense->ext.extended.info[0],
+ sense->ext.extended.info[1],
+ sense->ext.extended.info[2],
+ sense->ext.extended.info[3],
+ sense->ext.extended.extra_len);
+ printf("extra: ");
+ while (count < sense->ext.extended.extra_len) {
+ printf("%x ", sense->ext.extended.extra_bytes[count++]);
+ }
+ printf("\n");
+ }
+#endif /*SCSIDEBUG */
+ /*
+ * If the device has it's own error handler, call it first.
+ * If it returns a legit error value, return that, otherwise
+ * it wants us to continue with normal error processing.
+ */
+ if (sc_link->device->err_handler) {
+ SC_DEBUG(sc_link, SDEV_DB2, ("calling private err_handler()\n"));
+ errcode = (*sc_link->device->err_handler) (xs);
+ if (errcode != -1)
+ return errcode; /* errcode >= 0 better ? */
+ }
+ /* otherwise use the default */
+ silent = (xs->flags & SCSI_SILENT);
+ switch (sense->error_code & SSD_ERRCODE) {
+ /*
+ * If it's code 70, use the extended stuff and interpret the key
+ */
+ case 0x71: /* delayed error */
+ sc_print_addr(sc_link);
+ key = sense->ext.extended.flags & SSD_KEY;
+ printf(" DELAYED ERROR, key = 0x%x\n", key);
+ case 0x70:
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ info = ntohl(*((long *) sense->ext.extended.info));
+ } else {
+ info = 0;
+ }
+ key = sense->ext.extended.flags & SSD_KEY;
+
+ if (key && !silent) {
+ sc_print_addr(sc_link);
+ printf("%s", error_mes[key - 1]);
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ switch (key) {
+ case 0x2: /* NOT READY */
+ case 0x5: /* ILLEGAL REQUEST */
+ case 0x6: /* UNIT ATTENTION */
+ case 0x7: /* DATA PROTECT */
+ break;
+ case 0x8: /* BLANK CHECK */
+ printf(", requested size: %d (decimal)",
+ info);
+ break;
+ default:
+ printf(", info = %d (decimal)", info);
+ }
+ }
+ printf("\n");
+ }
+ switch (key) {
+ case 0x0: /* NO SENSE */
+ case 0x1: /* RECOVERED ERROR */
+ if (xs->resid == xs->datalen)
+ xs->resid = 0; /* not short read */
+ case 0xc: /* EQUAL */
+ return (ESUCCESS);
+ case 0x2: /* NOT READY */
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ return (EBUSY);
+ case 0x5: /* ILLEGAL REQUEST */
+ return (EINVAL);
+ case 0x6: /* UNIT ATTENTION */
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ if (sc_link->flags & SDEV_OPEN) {
+ return (EIO);
+ } else {
+ return 0;
+ }
+ case 0x7: /* DATA PROTECT */
+ return (EACCES);
+ case 0xd: /* VOLUME OVERFLOW */
+ return (ENOSPC);
+ case 0x8: /* BLANK CHECK */
+ return (ESUCCESS);
+ default:
+ return (EIO);
+ }
+ /*
+ * Not code 70, just report it
+ */
+ default:
+ if (!silent) {
+ sc_print_addr(sc_link);
+ printf("error code %d",
+ sense->error_code & SSD_ERRCODE);
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ printf(" at block no. %d (decimal)",
+ (sense->ext.unextended.blockhi << 16) +
+ (sense->ext.unextended.blockmed << 8) +
+ (sense->ext.unextended.blocklow));
+ }
+ printf("\n");
+ }
+ return (EIO);
+ }
+}
+
+/*
+ * Utility routines often used in SCSI stuff
+ */
+
+/*
+ * convert a physical address to 3 bytes,
+ * MSB at the lowest address,
+ * LSB at the highest.
+ */
+void
+lto3b(val, bytes)
+ int val;
+ u_char *bytes;
+{
+ *bytes++ = (val & 0xff0000) >> 16;
+ *bytes++ = (val & 0xff00) >> 8;
+ *bytes = val & 0xff;
+}
+
+/*
+ * The reverse of lto3b
+ */
+int
+_3btol(bytes)
+ u_char *bytes;
+{
+ u_int32 rc;
+ rc = (*bytes++ << 16);
+ rc += (*bytes++ << 8);
+ rc += *bytes;
+ return ((int) rc);
+}
+
+/*
+ * Print out the scsi_link structure's address info.
+ */
+void
+sc_print_addr(sc_link)
+ struct scsi_link *sc_link;
+{
+
+ printf("%s%d(%s:%d:%d): ",
+ sc_link->device->name, sc_link->dev_unit,
+ ((struct device *)sc_link->adapter_softc)->dv_xname,
+ sc_link->target, sc_link->lun);
+}
+
+#ifdef SCSIDEBUG
+/*
+ * Given a scsi_xfer, dump the request, in all it's glory
+ */
+void
+show_scsi_xs(xs)
+ struct scsi_xfer *xs;
+{
+ printf("xs(0x%x): ", xs);
+ printf("flg(0x%x)", xs->flags);
+ printf("sc_link(0x%x)", xs->sc_link);
+ printf("retr(0x%x)", xs->retries);
+ printf("timo(0x%x)", xs->timeout);
+ printf("cmd(0x%x)", xs->cmd);
+ printf("len(0x%x)", xs->cmdlen);
+ printf("data(0x%x)", xs->data);
+ printf("len(0x%x)", xs->datalen);
+ printf("res(0x%x)", xs->resid);
+ printf("err(0x%x)", xs->error);
+ printf("bp(0x%x)", xs->bp);
+ show_scsi_cmd(xs);
+}
+
+void
+show_scsi_cmd(struct scsi_xfer *xs)
+{
+ u_char *b = (u_char *) xs->cmd;
+ int i = 0;
+
+ sc_print_addr(xs->sc_link);
+ printf("command: ");
+
+ if (!(xs->flags & SCSI_RESET)) {
+ while (i < xs->cmdlen) {
+ if (i)
+ printf(",");
+ printf("%x", b[i++]);
+ }
+ printf("-[%d bytes]\n", xs->datalen);
+ if (xs->datalen)
+ show_mem(xs->data, min(64, xs->datalen));
+ } else {
+ printf("-RESET-\n");
+ }
+}
+
+void
+show_mem(address, num)
+ unsigned char *address;
+ u_int32 num;
+{
+ u_int32 x, y;
+ printf("------------------------------");
+ for (y = 0; y < num; y += 1) {
+ if (!(y % 16))
+ printf("\n%03d: ", y);
+ printf("%02x ", *address++);
+ }
+ printf("\n------------------------------\n");
+}
+#endif /*SCSIDEBUG */
diff --git a/sys/dev/scsipi/scsipi_debug.h b/sys/dev/scsipi/scsipi_debug.h
new file mode 100644
index 00000000000..8d9245dc46c
--- /dev/null
+++ b/sys/dev/scsipi/scsipi_debug.h
@@ -0,0 +1,53 @@
+/*#define SCSIDEBUG 1*/
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ *
+ * $Id: scsipi_debug.h,v 1.1 1993/11/24 04:52:47 mycroft Exp $
+ */
+#ifndef _SCSI_SCSI_DEBUG_H
+#define _SCSI_SCSI_DEBUG_H 1
+
+/*
+ * These are the new debug bits. (Sat Oct 2 12:46:46 WST 1993)
+ * the following DEBUG bits are defined to exist in the flags word of
+ * the scsi_link structure.
+ */
+#define SDEV_DB1 0x10 /* scsi commands, errors, data */
+#define SDEV_DB2 0x20 /* routine flow tracking */
+#define SDEV_DB3 0x40 /* internal to routine flows */
+#define SDEV_DB4 0x80 /* level 4 debugging for this dev */
+
+/* target and LUN we want to debug */
+#define DEBUGTARG 9 /*9 = dissable*/
+#define DEBUGLUN 0
+#define DEBUGLEVEL (SDEV_DB1|SDEV_DB2)
+
+/*
+ * This is the usual debug macro for use with the above bits
+ */
+#ifdef SCSIDEBUG
+#define SC_DEBUG(sc_link,Level,Printstuff) \
+ if((sc_link)->flags & (Level)) \
+ { \
+ printf("%s%d(%s%d:%d:%d): ", \
+ sc_link->device->name, \
+ sc_link->dev_unit, \
+ sc_link->adapter->name, \
+ sc_link->adapter_unit, \
+ sc_link->target, \
+ sc_link->lun); \
+ printf Printstuff; \
+ }
+#define SC_DEBUGN(sc_link,Level,Printstuff) \
+ if((sc_link)->flags & (Level)) \
+ { \
+ printf Printstuff; \
+ }
+#else
+#define SC_DEBUG(A,B,C) /* not included */
+#define SC_DEBUGN(A,B,C) /* not included */
+#endif
+
+#endif /*_SCSI_SCSI_DEBUG_H*/
+/* END OF FILE */
+
diff --git a/sys/dev/scsipi/scsipi_ioctl.c b/sys/dev/scsipi/scsipi_ioctl.c
new file mode 100644
index 00000000000..6d37df8afe8
--- /dev/null
+++ b/sys/dev/scsipi/scsipi_ioctl.c
@@ -0,0 +1,329 @@
+/*
+ * Contributed by HD Associates (hd@world.std.com).
+ * Copyright (c) 1992, 1993 HD Associates
+ *
+ * Berkeley style copyright.
+ *
+ *
+ */
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#define b_screq av_forw /* XXX */
+#define b_sc_link av_back /* XXX */
+#include <sys/proc.h>
+#include <sys/device.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <sys/scsiio.h>
+
+
+/*
+ * We let the user interpret his own sense in the generic scsi world.
+ * This routine is called at interrupt time if the SCSI_USER bit was set
+ * in the flags passed to scsi_scsi_cmd(). No other completion processing
+ * takes place, even if we are running over another device driver.
+ * The lower level routines that call us here, will free the xs and restart
+ * the device's queue if such exists.
+ */
+#ifndef min
+#define min(A,B) ((A<B) ? A : B )
+#endif
+
+void scsi_user_done(xs)
+struct scsi_xfer *xs;
+{
+
+ struct buf *bp;
+ scsireq_t *screq;
+
+ bp = xs->bp;
+ if(!bp) { /* ALL user requests must have a buf */
+ sc_print_addr(xs->sc_link);
+ printf("User command with no buf\n");
+ return ;
+ }
+ screq = bp->b_screq;
+ if (!screq) { /* Is it one of ours? (the SCSI_USER bit says it is) */
+ sc_print_addr(xs->sc_link);
+ printf("User command with no request\n");
+ return ;
+ }
+
+ SC_DEBUG(xs->sc_link,SDEV_DB2,("user-done\n"));
+ screq->retsts = 0;
+ screq->status = xs->status;
+ switch(xs->error) {
+ case XS_NOERROR:
+ SC_DEBUG(xs->sc_link,SDEV_DB3,("no error\n"));
+ screq->datalen_used = xs->datalen - xs->resid; /* probably rubbish */
+ screq->retsts = SCCMD_OK;
+ break;
+
+ case XS_SENSE:
+ SC_DEBUG(xs->sc_link,SDEV_DB3,("have sense\n"));
+ screq->senselen_used = min(sizeof(xs->sense),SENSEBUFLEN);
+ bcopy(&xs->sense,screq->sense,screq->senselen);
+ screq->retsts = SCCMD_SENSE;
+ break;
+
+ case XS_DRIVER_STUFFUP:
+ sc_print_addr(xs->sc_link);
+ printf("host adapter code inconsistency\n");
+ screq->retsts = SCCMD_UNKNOWN;
+ break;
+
+ case XS_TIMEOUT:
+ SC_DEBUG(xs->sc_link,SDEV_DB3,("timeout\n"));
+ screq->retsts = SCCMD_TIMEOUT;
+ break;
+
+ case XS_BUSY:
+ SC_DEBUG(xs->sc_link,SDEV_DB3,("busy\n"));
+ screq->retsts = SCCMD_BUSY;
+ break;
+
+ default:
+ sc_print_addr(xs->sc_link);
+ printf("unknown error category from host adapter code\n");
+ screq->retsts = SCCMD_UNKNOWN;
+ break;
+ }
+ biodone(bp); /* we're waiting on it in scsi_strategy() */
+ return; /* it'll free the xs and restart any queue */
+}
+
+
+/* Pseudo strategy function
+ * Called by scsi_do_ioctl() via physio/physstrat if there is to
+ * be data transfered, and directly if there is no data transfer.
+ *
+ * Should I reorganize this so it returns to physio instead
+ * of sleeping in scsiio_scsi_cmd? Is there any advantage, other
+ * than avoiding the probable duplicate wakeup in iodone? [PD]
+ *
+ * No, seems ok to me... [JRE]
+ * (I don't see any duplicate wakeups)
+ *
+ * Can't be used with block devices or raw_read/raw_write directly
+ * from the cdevsw/bdevsw tables because they couldn't have added
+ * the screq structure. [JRE]
+ */
+void scsistrategy(struct buf *bp)
+{
+ errval err;
+ struct scsi_link *sc_link = bp->b_sc_link;
+ scsireq_t *screq;
+ u_int32 flags = 0;
+ int s;
+
+
+ if(!sc_link) {
+ printf("user_strat: No link pointer\n");
+ scsierr(bp,EINVAL);
+ return;
+ }
+ SC_DEBUG(sc_link,SDEV_DB2,("user_strategy\n"));
+ screq = bp->b_screq;
+ if(!screq) {
+ sc_print_addr(sc_link);
+ printf("No request block\n");
+ scsierr(bp,EINVAL);
+ return;
+ }
+
+ /* We're in trouble if physio tried to break up the
+ * transfer:
+ */
+ if (bp->b_bcount != screq->datalen) {
+ sc_print_addr(sc_link);
+ printf("physio split the request.. cannot proceed\n");
+ scsierr(bp, EIO);
+ return;
+ }
+
+ if (screq->timeout == 0) {
+ scsierr(bp, EINVAL);
+ return;
+ }
+
+ if (screq->cmdlen > sizeof(struct scsi_generic)) {
+ sc_print_addr(sc_link);
+ printf("cmdlen too big ");
+ scsierr(bp, EFAULT);
+ return;
+ }
+
+
+ if (screq->flags & SCCMD_READ)
+ flags |= SCSI_DATA_IN;
+
+ if (screq->flags & SCCMD_WRITE)
+ flags |= SCSI_DATA_OUT;
+
+ if (screq->flags & SCCMD_TARGET)
+ flags |= SCSI_TARGET;
+
+ if (screq->flags & SCCMD_ESCAPE)
+ flags |= SCSI_ESCAPE;
+ err = scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *)screq->cmd,
+ screq->cmdlen,
+ (u_char *)bp->b_un.b_addr,
+ screq->datalen,
+ 0, /* user must do the retries *//* ignored */
+ screq->timeout,
+ bp,
+ flags | SCSI_USER);
+
+
+
+ /*because there is a bp, scsi_scsi_cmd will return immediatly*/
+ if (err)
+ {
+ scsierr(bp, err);
+ return;
+ }
+ SC_DEBUG(sc_link,SDEV_DB3,("about to sleep\n"));
+ s = splbio();
+ while(!(bp->b_flags & B_DONE))
+ {
+ sleep(bp,PRIBIO);
+ }
+ splx(s);
+ SC_DEBUG(sc_link,SDEV_DB3,("back from sleep\n"));
+ return;
+}
+
+void scsiminphys(struct buf *bp)
+{
+ /*XXX*//* call the adapter's minphys */
+}
+
+
+/*
+ * Something (e.g. another driver) has called us
+ * with an sc_link for a target/lun/adapter, and a scsi
+ * specific ioctl to perform, better try.
+ * If user-level type command, we must still be running
+ * in the context of the calling process
+ */
+errval scsi_do_ioctl(struct scsi_link *sc_link, int cmd, caddr_t addr, int f)
+{
+ errval ret = 0;
+ int phys;
+
+ SC_DEBUG(sc_link,SDEV_DB2,("scsi_do_ioctl(0x%x)\n",cmd));
+ switch(cmd)
+ {
+#ifndef __NetBSD__
+ case SCIOCCOMMAND:
+ {
+ /*
+ * You won't believe this, but the arg copied in
+ * from the user space, is on the kernel stack
+ * for this process, so we can't write
+ * to it at interrupt time..
+ * we need to copy it in and out!
+ * Make a static copy using malloc!
+ */
+ scsireq_t *screq2 = (scsireq_t *)addr;
+ scsireq_t *screq = (scsireq_t *)addr;
+ int rwflag = (screq->flags & SCCMD_READ) ? B_READ : B_WRITE;
+ struct buf *bp;
+ caddr_t d_addr;
+ int len;
+
+ if((unsigned int)screq < (unsigned int)0xfe000000)
+ {
+ screq = malloc(sizeof(scsireq_t),M_TEMP,M_WAITOK);
+ bcopy(screq2,screq,sizeof(scsireq_t));
+ }
+ bp = malloc(sizeof (struct buf),M_TEMP,M_WAITOK);
+ bzero(bp,sizeof(struct buf));
+ d_addr = screq->databuf;
+ bp->b_bcount = len = screq->datalen;
+ bp->b_screq = screq;
+ bp->b_sc_link = sc_link;
+ if (len) {
+ /* have data, translate it. (physio)*/
+#ifdef __NetBSD__
+#error "dev, mincntfn & uio need defining"
+ ret = physio(scsistrategy, bp, dev, rwflag,
+ mincntfn, uio);
+#else
+ ret = physio(scsistrategy,0,bp,0,rwflag,
+ d_addr,&len,curproc);
+#endif
+ } else {
+ /* if no data, no need to translate it.. */
+ bp->b_un.b_addr = 0;
+ bp->b_dev = -1; /* irrelevant info */
+ bp->b_flags = 0;
+
+ scsistrategy(bp);
+ ret = bp->b_error;
+ }
+ free(bp,M_TEMP);
+ if((unsigned int)screq2 < (unsigned int)0xfe000000)
+ {
+ bcopy(screq,screq2,sizeof(scsireq_t));
+ free(screq,M_TEMP);
+ }
+ break;
+ }
+#endif /* !NetBSD */
+ case SCIOCDEBUG:
+ {
+ int level = *((int *)addr);
+ SC_DEBUG(sc_link,SDEV_DB3,("debug set to %d\n",level));
+ sc_link->flags &= ~SDEV_DBX; /*clear debug bits */
+ if(level & 1) sc_link->flags |= SDEV_DB1;
+ if(level & 2) sc_link->flags |= SDEV_DB2;
+ if(level & 4) sc_link->flags |= SDEV_DB3;
+ if(level & 8) sc_link->flags |= SDEV_DB4;
+ ret = 0;
+ break;
+ }
+ case SCIOCREPROBE:
+ {
+ extern int scsibus;
+ struct scsi_addr *sca = (struct scsi_addr *) addr;
+
+ ret = scsi_probe_busses(sca->scbus,sca->target,sca->lun);
+ break;
+ }
+ case SCIOCRECONFIG:
+ case SCIOCDECONFIG:
+ ret = EINVAL;
+ break;
+ case SCIOCIDENTIFY:
+ {
+ struct scsi_addr *sca = (struct scsi_addr *) addr;
+ sca->scbus = sc_link->scsibus;
+ sca->target = sc_link->target;
+ sca->lun = sc_link->lun;
+ break;
+ }
+
+ default:
+ ret = ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+scsierr(bp,err)
+struct buf *bp;
+int err;
+{
+ bp->b_flags |= B_ERROR;
+ bp->b_error = err;
+ biodone(bp);
+ return;
+}
+
diff --git a/sys/dev/scsipi/su.c b/sys/dev/scsipi/su.c
new file mode 100644
index 00000000000..de4f0172dac
--- /dev/null
+++ b/sys/dev/scsipi/su.c
@@ -0,0 +1,4 @@
+
+/* this will be a special user scsi device */
+/* not written yet */
+
diff --git a/sys/dev/scsipi/uk.c b/sys/dev/scsipi/uk.c
new file mode 100644
index 00000000000..982deb13f9b
--- /dev/null
+++ b/sys/dev/scsipi/uk.c
@@ -0,0 +1,157 @@
+
+/*
+ * Dummy driver for a device we can't identify.
+ * by Julian Elischer (julian@tfs.com)
+ *
+ * $Id: uk.c,v 1.1 1993/11/24 04:52:53 mycroft Exp $
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#define NUK 16
+
+/*
+ * This driver is so simple it uses all the default services
+ */
+struct scsi_device uk_switch =
+{
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "uk",
+ 0,
+ 0, 0
+};
+
+struct uk_data {
+ u_int32 flags;
+ struct scsi_link *sc_link; /* all the inter level info */
+} uk_data[NUK];
+
+#define UK_KNOWN 0x02
+
+static u_int32 next_uk_unit = 0;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * a device suitable for this driver.
+ */
+errval
+ukattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit, i, stat;
+ unsigned char *tbl;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("ukattach: "));
+ /*
+ * Check we have the resources for another drive
+ */
+ unit = next_uk_unit++;
+ if (unit >= NUK) {
+ printf("Too many unknown devices..(%d > %d) reconfigure kernel\n",
+ (unit + 1), NUK);
+ return (0);
+ }
+ /*
+ * Store information needed to contact our base driver
+ */
+ uk_data[unit].sc_link = sc_link;
+ sc_link->device = &uk_switch;
+ sc_link->dev_unit = unit;
+
+ printf("uk%d: unknown device\n", unit);
+ uk_data[unit].flags = UK_KNOWN;
+
+ return;
+
+}
+
+/*
+ * open the device.
+ */
+errval
+ukopen(dev)
+{
+ errval errcode = 0;
+ u_int32 unit, mode;
+ struct scsi_link *sc_link;
+ unit = minor(dev);
+
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= NUK) {
+ printf("uk%d: uk %d > %d\n", unit, unit, NUK);
+ return ENXIO;
+ }
+
+ /*
+ * Make sure the device has been initialised
+ */
+ if((uk_data[unit].flags & UK_KNOWN) == 0) {
+ printf("uk%d: not set up\n", unit);
+ return ENXIO;
+ }
+
+ /*
+ * Only allow one at a time
+ */
+ sc_link = uk_data[unit].sc_link;
+ if (sc_link->flags & SDEV_OPEN) {
+ printf("uk%d: already open\n", unit);
+ return ENXIO;
+ }
+ sc_link->flags |= SDEV_OPEN;
+ SC_DEBUG(sc_link, SDEV_DB1, ("ukopen: dev=0x%x (unit %d (of %d))\n"
+ ,dev, unit, NUK));
+ /*
+ * Catch any unit attention errors.
+ */
+ return 0;
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+errval
+ukclose(dev)
+{
+ unsigned char unit, mode;
+ struct scsi_link *sc_link;
+
+ sc_link = uk_data[unit].sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("Closing device"));
+ sc_link->flags &= ~SDEV_OPEN;
+ return (0);
+}
+
+/*
+ * Perform special action on behalf of the user
+ * Only does generic scsi ioctls.
+ */
+errval
+ukioctl(dev, cmd, arg, mode)
+ dev_t dev;
+ u_int32 cmd;
+ caddr_t arg;
+{
+ unsigned char unit;
+ struct scsi_link *sc_link;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ unit = minor(dev);
+ sc_link = uk_data[unit].sc_link;
+ return(scsi_do_ioctl(sc_link,cmd,arg,mode));
+}
+