summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstevel <none@none>2006-08-08 15:34:50 -0700
committerstevel <none@none>2006-08-08 15:34:50 -0700
commit9a016c63ca347047a236dff12f0da83aac8981d1 (patch)
tree86f2711a20a663ed012b7d0bf789d5d253c3cd57
parentf1710550bd8341486e7494e781335ba875c9b12c (diff)
downloadillumos-joyent-9a016c63ca347047a236dff12f0da83aac8981d1.tar.gz
6455550 move sgen to usr/src
-rw-r--r--usr/src/uts/common/Makefile.files2
-rw-r--r--usr/src/uts/common/io/scsi/targets/sgen.c2332
-rw-r--r--usr/src/uts/common/io/scsi/targets/sgen.conf108
-rw-r--r--usr/src/uts/common/sys/Makefile1
-rw-r--r--usr/src/uts/common/sys/scsi/targets/sgendef.h170
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared2
-rw-r--r--usr/src/uts/intel/sgen/Makefile89
-rw-r--r--usr/src/uts/sparc/Makefile.sparc.shared2
-rw-r--r--usr/src/uts/sparc/sgen/Makefile94
9 files changed, 2798 insertions, 2 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index d029353751..7712d7dcda 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -602,6 +602,8 @@ SCSI_OBJS += scsi_capabilities.o scsi_control.o scsi_watch.o \
scsi_hba.o scsi_transport.o scsi_confsubr.o \
scsi_reset_notify.o
+SGEN_OBJS += sgen.o
+
SATA_OBJS += sata.o
USBA_OBJS += hcdi.o usba.o usbai.o hubdi.o parser.o genconsole.o \
diff --git a/usr/src/uts/common/io/scsi/targets/sgen.c b/usr/src/uts/common/io/scsi/targets/sgen.c
new file mode 100644
index 0000000000..d0ccc74af5
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/targets/sgen.c
@@ -0,0 +1,2332 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright Siemens 1999
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * sgen - SCSI generic device driver
+ *
+ * The sgen driver provides user programs access to SCSI devices that
+ * are not supported by other drivers by providing the USCSI(7I) interface.
+ */
+
+#include <sys/modctl.h>
+#include <sys/file.h>
+#include <sys/scsi/scsi.h>
+#include <sys/stat.h>
+#include <sys/scsi/targets/sgendef.h>
+
+#define DDI_NT_SGEN "ddi_generic:scsi"
+
+static char *sgen_devtypes[] = {
+ "direct", /* 0x00 -- disks */
+ "sequential", /* 0x01 */
+ "printer", /* 0x02 */
+ "processor", /* 0x03 */
+ "worm", /* 0x04 */
+ "rodirect", /* 0x05 */
+ "scanner", /* 0x06 */
+ "optical", /* 0x07 */
+ "changer", /* 0x08 */
+ "comm", /* 0x09 */
+ "prepress1", /* 0x0a -- reserved for prepress (ASC IT8) */
+ "prepress2", /* 0x0b -- reserved for prepress (ASC IT8) */
+ "array_ctrl", /* 0x0c -- storage array */
+ "ses", /* 0x0d -- enclosure services */
+ "rbc", /* 0x0e -- simplified block */
+ "ocrw", /* 0x0f -- optical card read/write */
+ "bridge", /* 0x10 -- reserved for bridging expanders */
+ "type_0x11", /* 0x11 */
+ "type_0x12", /* 0x12 */
+ "type_0x13", /* 0x13 */
+ "type_0x14", /* 0x14 */
+ "type_0x15", /* 0x15 */
+ "type_0x16", /* 0x16 */
+ "type_0x17", /* 0x17 */
+ "type_0x18", /* 0x18 */
+ "type_0x19", /* 0x19 */
+ "type_0x1a", /* 0x1a */
+ "type_0x1b", /* 0x1b */
+ "type_0x1c", /* 0x1c */
+ "type_0x1d", /* 0x1d */
+ "type_0x1e", /* 0x1e */
+ "type_unknown" /* 0x1f is "no device type" or "unknown" */
+};
+
+#define SGEN_NDEVTYPES ((sizeof (sgen_devtypes) / sizeof (char *)))
+
+#define SGEN_INQSTRLEN 24
+#define SGEN_VENDID_MAX 8
+#define SGEN_PRODID_MAX 16
+
+#define FILL_SCSI1_LUN(devp, pkt) \
+ if ((devp)->sd_inq->inq_ansi == 0x1) { \
+ int _lun; \
+ _lun = ddi_prop_get_int(DDI_DEV_T_ANY, (devp)->sd_dev, \
+ DDI_PROP_DONTPASS, SCSI_ADDR_PROP_LUN, 0); \
+ if (_lun > 0) { \
+ ((union scsi_cdb *)(pkt)->pkt_cdbp)->scc_lun = \
+ _lun; \
+ } \
+ }
+
+#define SGEN_DO_ERRSTATS(sg_state, x) \
+ if (sg_state->sgen_kstats) { \
+ struct sgen_errstats *sp; \
+ sp = (struct sgen_errstats *)sg_state->sgen_kstats->ks_data; \
+ sp->x.value.ui32++; \
+ }
+
+#define SCBP_C(pkt) ((*(pkt)->pkt_scbp) & STATUS_MASK)
+
+/*
+ * Standard entrypoints
+ */
+static int sgen_attach(dev_info_t *, ddi_attach_cmd_t);
+static int sgen_detach(dev_info_t *, ddi_detach_cmd_t);
+static int sgen_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int sgen_probe(dev_info_t *);
+static int sgen_open(dev_t *, int, int, cred_t *);
+static int sgen_close(dev_t, int, int, cred_t *);
+static int sgen_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+
+/*
+ * Configuration routines
+ */
+static int sgen_do_attach(dev_info_t *);
+static int sgen_setup_sense(sgen_state_t *);
+static void sgen_create_errstats(sgen_state_t *, int);
+static int sgen_do_suspend(dev_info_t *);
+static int sgen_do_detach(dev_info_t *);
+static void sgen_setup_binddb(dev_info_t *);
+static void sgen_cleanup_binddb();
+
+/*
+ * Packet transport routines
+ */
+static int sgen_uscsi_cmd(dev_t, struct uscsi_cmd *,
+ enum uio_seg, enum uio_seg, enum uio_seg);
+static int sgen_start(struct buf *);
+static void sgen_hold_cmdbuf(sgen_state_t *);
+static void sgen_rele_cmdbuf(sgen_state_t *);
+static int sgen_make_uscsi_cmd(sgen_state_t *, struct buf *);
+static void sgen_restart(void *);
+static void sgen_callback(struct scsi_pkt *);
+static int sgen_handle_autosense(sgen_state_t *, struct scsi_pkt *);
+static int sgen_handle_sense(sgen_state_t *);
+static int sgen_handle_incomplete(sgen_state_t *, struct scsi_pkt *);
+static int sgen_check_error(sgen_state_t *, struct buf *);
+static int sgen_initiate_sense(sgen_state_t *);
+static int sgen_scsi_transport(struct scsi_pkt *);
+static int sgen_tur(dev_t);
+
+/*
+ * Logging/debugging routines
+ */
+static void sgen_log(sgen_state_t *, int, const char *, ...);
+static int sgen_diag_ok(sgen_state_t *, int);
+static void sgen_dump_cdb(sgen_state_t *, const char *, union scsi_cdb *, int);
+static void sgen_dump_sense(sgen_state_t *, size_t, uchar_t *);
+
+int sgen_diag = 0;
+int sgen_sporadic_failures = 0;
+int sgen_force_manual_sense = 0;
+struct sgen_binddb sgen_binddb;
+
+static struct cb_ops sgen_cb_ops = {
+ sgen_open, /* open */
+ sgen_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ sgen_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_MP | D_NEW | D_HOTPLUG /* Driver compatibility flag */
+};
+
+static struct dev_ops sgen_dev_ops = {
+ DEVO_REV, /* devo_rev, */
+ 0, /* refcnt */
+ sgen_getinfo, /* info */
+ nodev, /* identify */
+ sgen_probe, /* probe */
+ sgen_attach, /* attach */
+ sgen_detach, /* detach */
+ nodev, /* reset */
+ &sgen_cb_ops, /* driver operations */
+ (struct bus_ops *)0, /* bus operations */
+ NULL /* power */
+};
+
+static void *sgen_soft_state = NULL;
+
+static struct modldrv modldrv = {
+ &mod_driverops, "SCSI generic driver %I%", &sgen_dev_ops
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modldrv, NULL
+};
+
+int
+_init(void)
+{
+ int err;
+
+ sgen_log(NULL, SGEN_DIAG2, "in sgen_init()");
+ if ((err = ddi_soft_state_init(&sgen_soft_state,
+ sizeof (sgen_state_t), SGEN_ESTIMATED_NUM_DEVS)) != 0) {
+ goto done;
+ }
+
+ if ((err = mod_install(&modlinkage)) != 0) {
+ ddi_soft_state_fini(&sgen_soft_state);
+ goto done;
+ }
+
+done:
+ sgen_log(NULL, SGEN_DIAG2, "%s sgen_init()", err ? "failed" : "done");
+ return (err);
+}
+
+int
+_fini(void)
+{
+ int err;
+ sgen_log(NULL, SGEN_DIAG2, "in sgen_fini()");
+
+ if ((err = mod_remove(&modlinkage)) != 0) {
+ goto done;
+ }
+
+ ddi_soft_state_fini(&sgen_soft_state);
+ sgen_cleanup_binddb();
+
+done:
+ sgen_log(NULL, SGEN_DIAG2, "%s sgen_fini()", err ? "failed" : "done");
+ return (err);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * sgen_typename()
+ * return a device type's name by looking it up in the sgen_devtypes table.
+ */
+static char *
+sgen_typename(uchar_t typeno)
+{
+ if (typeno >= SGEN_NDEVTYPES)
+ return ("type_unknown");
+ return (sgen_devtypes[typeno]);
+}
+
+/*
+ * sgen_typenum()
+ * return a device type's number by looking it up in the sgen_devtypes
+ * table.
+ */
+static int
+sgen_typenum(const char *typename, uchar_t *typenum)
+{
+ int i;
+ for (i = 0; i < SGEN_NDEVTYPES; i++) {
+ if (strcasecmp(sgen_devtypes[i], typename) == 0) {
+ *typenum = (uchar_t)i;
+ return (0);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * sgen_setup_binddb()
+ * initialize a data structure which stores all of the information about
+ * which devices and device types the driver should bind to.
+ */
+static void
+sgen_setup_binddb(dev_info_t *dip)
+{
+ char **strs = NULL, *cp, *pcp, *vcp;
+ uint_t nelems, pcplen, vcplen, idx;
+
+ ASSERT(sgen_binddb.sdb_init == 0);
+ ASSERT(MUTEX_HELD(&sgen_binddb.sdb_lock));
+
+ if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "device-type-config-list", &strs, &nelems) == DDI_PROP_SUCCESS) {
+ /*
+ * for each device type specifier make a copy and put it into a
+ * node in the binddb.
+ */
+ for (idx = 0; idx < nelems; idx++) {
+ sgen_type_node_t *nodep;
+ uchar_t devtype;
+ cp = strs[idx];
+ if (sgen_typenum(cp, &devtype) != 0) {
+ sgen_log(NULL, CE_WARN,
+ "unknown device type '%s', "
+ "device unit-address @%s",
+ cp, ddi_get_name_addr(dip));
+ continue;
+ }
+ nodep = kmem_zalloc(sizeof (sgen_type_node_t),
+ KM_SLEEP);
+ nodep->node_type = devtype;
+ nodep->node_next = sgen_binddb.sdb_type_nodes;
+ sgen_binddb.sdb_type_nodes = nodep;
+
+ sgen_log(NULL, SGEN_DIAG2, "found device type "
+ "'%s' in device-type-config-list, "
+ "device unit-address @%s",
+ cp, ddi_get_name_addr(dip));
+ }
+ ddi_prop_free(strs);
+ }
+
+ /*
+ * for each Vendor/Product inquiry pair, build a node and put it
+ * into the the binddb.
+ */
+ if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "inquiry-config-list", &strs, &nelems) == DDI_PROP_SUCCESS) {
+
+ if (nelems % 2 == 1) {
+ sgen_log(NULL, CE_WARN, "inquiry-config-list must "
+ "contain Vendor/Product pairs, "
+ "device unit-address @%s",
+ ddi_get_name_addr(dip));
+ nelems--;
+ }
+ for (idx = 0; idx < nelems; idx += 2) {
+ sgen_inq_node_t *nodep;
+ /*
+ * Grab vendor and product ID.
+ */
+ vcp = strs[idx];
+ vcplen = strlen(vcp);
+ if (vcplen == 0 || vcplen > SGEN_VENDID_MAX) {
+ sgen_log(NULL, CE_WARN,
+ "Invalid vendor ID '%s', "
+ "device unit-address @%s",
+ vcp, ddi_get_name_addr(dip));
+ continue;
+ }
+
+ pcp = strs[idx + 1];
+ pcplen = strlen(pcp);
+ if (pcplen == 0 || pcplen > SGEN_PRODID_MAX) {
+ sgen_log(NULL, CE_WARN,
+ "Invalid product ID '%s', "
+ "device unit-address @%s",
+ pcp, ddi_get_name_addr(dip));
+ continue;
+ }
+
+ nodep = kmem_zalloc(sizeof (sgen_inq_node_t),
+ KM_SLEEP);
+ nodep->node_vendor = kmem_alloc(vcplen + 1, KM_SLEEP);
+ (void) strcpy(nodep->node_vendor, vcp);
+ nodep->node_product = kmem_alloc(pcplen + 1, KM_SLEEP);
+ (void) strcpy(nodep->node_product, pcp);
+
+ nodep->node_next = sgen_binddb.sdb_inq_nodes;
+ sgen_binddb.sdb_inq_nodes = nodep;
+
+ sgen_log(NULL, SGEN_DIAG2, "found inquiry string "
+ "'%s' '%s' in device-type-config-list, "
+ "device unit-address @%s",
+ nodep->node_vendor, nodep->node_product,
+ ddi_get_name_addr(dip));
+ }
+ ddi_prop_free(strs);
+ }
+
+ sgen_binddb.sdb_init = 1;
+}
+
+/*
+ * sgen_cleanup_binddb()
+ * deallocate data structures for binding database.
+ */
+static void
+sgen_cleanup_binddb()
+{
+ sgen_inq_node_t *inqp, *inqnextp;
+ sgen_type_node_t *typep, *typenextp;
+
+ mutex_enter(&sgen_binddb.sdb_lock);
+ if (sgen_binddb.sdb_init == 0) {
+ mutex_exit(&sgen_binddb.sdb_lock);
+ return;
+ }
+
+ for (inqp = sgen_binddb.sdb_inq_nodes; inqp != NULL; inqp = inqnextp) {
+ inqnextp = inqp->node_next;
+ ASSERT(inqp->node_vendor && inqp->node_product);
+ kmem_free(inqp->node_vendor,
+ strlen(inqp->node_vendor) + 1);
+ kmem_free(inqp->node_product,
+ strlen(inqp->node_product) + 1);
+ kmem_free(inqp, sizeof (sgen_inq_node_t));
+ }
+
+ for (typep = sgen_binddb.sdb_type_nodes; typep != NULL;
+ typep = typenextp) {
+ typenextp = typep->node_next;
+ kmem_free(typep, sizeof (sgen_type_node_t));
+ }
+ mutex_exit(&sgen_binddb.sdb_lock);
+}
+
+/*
+ * sgen_bind_byinq()
+ * lookup a device in the binding database by its inquiry data.
+ */
+static int
+sgen_bind_byinq(dev_info_t *dip)
+{
+ sgen_inq_node_t *nodep;
+ char vend_str[SGEN_VENDID_MAX+1];
+ char prod_str[SGEN_PRODID_MAX+1];
+ struct scsi_device *scsidevp;
+
+ scsidevp = ddi_get_driver_private(dip);
+
+ /*
+ * inq_vid and inq_pid are laid out by the protocol in order in the
+ * inquiry structure, and are not delimited by \0.
+ */
+ bcopy(scsidevp->sd_inq->inq_vid, vend_str, SGEN_VENDID_MAX);
+ vend_str[SGEN_VENDID_MAX] = '\0';
+ bcopy(scsidevp->sd_inq->inq_pid, prod_str, SGEN_PRODID_MAX);
+ prod_str[SGEN_PRODID_MAX] = '\0';
+
+ for (nodep = sgen_binddb.sdb_inq_nodes; nodep != NULL;
+ nodep = nodep->node_next) {
+ /*
+ * Allow the "*" wildcard to match all vendor IDs.
+ */
+ if (strcmp(nodep->node_vendor, "*") != 0) {
+ if (strncasecmp(nodep->node_vendor, vend_str,
+ strlen(nodep->node_vendor)) != 0) {
+ continue;
+ }
+ }
+
+ /*
+ * Using strncasecmp() with the key length allows substring
+ * matching for product data.
+ */
+ if (strncasecmp(nodep->node_product, prod_str,
+ strlen(nodep->node_product)) == 0) {
+ return (0);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * sgen_bind_bytype()
+ * lookup a device type in the binding database; if found, return a
+ * format string corresponding to the string in the .conf file.
+ */
+static int
+sgen_bind_bytype(dev_info_t *dip)
+{
+ sgen_type_node_t *nodep;
+ struct scsi_device *scsidevp;
+
+ scsidevp = ddi_get_driver_private(dip);
+
+ for (nodep = sgen_binddb.sdb_type_nodes; nodep != NULL;
+ nodep = nodep->node_next) {
+ if (nodep->node_type == scsidevp->sd_inq->inq_dtype) {
+ return (0);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * sgen_get_binding()
+ * Check to see if the device in question matches the criteria for
+ * sgen to bind.
+ *
+ * Either the .conf file must specify a device_type entry which
+ * matches the SCSI device type of this device, or the inquiry
+ * string provided by the device must match an inquiry string specified
+ * in the .conf file. Inquiry data is matched first.
+ */
+static int
+sgen_get_binding(dev_info_t *dip)
+{
+ int retval = 0;
+
+ mutex_enter(&sgen_binddb.sdb_lock);
+ if (sgen_binddb.sdb_init == 0)
+ sgen_setup_binddb(dip);
+ mutex_exit(&sgen_binddb.sdb_lock);
+
+
+ /*
+ * Check device-type-config-list for a match by device type.
+ */
+ if (sgen_bind_bytype(dip) == 0)
+ goto done;
+
+ /*
+ * Check inquiry-config-list for a match by Vendor/Product ID.
+ */
+ if (sgen_bind_byinq(dip) == 0)
+ goto done;
+
+ retval = -1;
+done:
+ return (retval);
+}
+
+/*
+ * sgen_attach()
+ * attach(9e) entrypoint.
+ */
+static int
+sgen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int err;
+
+ sgen_log(NULL, SGEN_DIAG2, "in sgen_attach(), device unit-address @%s",
+ ddi_get_name_addr(dip));
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ err = sgen_do_attach(dip);
+ break;
+ case DDI_RESUME:
+ err = DDI_SUCCESS;
+ break;
+ case DDI_PM_RESUME:
+ default:
+ err = DDI_FAILURE;
+ break;
+ }
+
+done:
+ sgen_log(NULL, SGEN_DIAG2, "%s sgen_attach(), device unit-address @%s",
+ err == DDI_SUCCESS ? "done" : "failed", ddi_get_name_addr(dip));
+ return (err);
+}
+
+/*
+ * sgen_do_attach()
+ * handle the nitty details of attach.
+ */
+static int
+sgen_do_attach(dev_info_t *dip)
+{
+ int instance;
+ struct scsi_device *scsidevp;
+ sgen_state_t *sg_state;
+ uchar_t devtype;
+ struct scsi_inquiry *inq;
+
+ instance = ddi_get_instance(dip);
+
+ scsidevp = ddi_get_driver_private(dip);
+ ASSERT(scsidevp);
+
+ sgen_log(NULL, SGEN_DIAG2, "sgen_do_attach: instance = %d, "
+ "device unit-address @%s", instance, ddi_get_name_addr(dip));
+
+ /*
+ * Probe the device in order to get its device type to name the minor
+ * node.
+ */
+ if (scsi_probe(scsidevp, NULL_FUNC) != SCSIPROBE_EXISTS) {
+ scsi_unprobe(scsidevp);
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_soft_state_zalloc(sgen_soft_state, instance) != DDI_SUCCESS) {
+ sgen_log(NULL, SGEN_DIAG1,
+ "sgen_do_attach: failed to allocate softstate, "
+ "device unit-address @%s", ddi_get_name_addr(dip));
+ scsi_unprobe(scsidevp);
+ return (DDI_FAILURE);
+ }
+
+ inq = scsidevp->sd_inq; /* valid while device is probed... */
+ devtype = inq->inq_dtype;
+
+ sg_state = ddi_get_soft_state(sgen_soft_state, instance);
+ sg_state->sgen_scsidev = scsidevp;
+ scsidevp->sd_dev = dip;
+
+ /*
+ * Now that sg_state->sgen_scsidev is initialized, it's ok to
+ * call sgen_log with sg_state instead of NULL.
+ */
+
+ /*
+ * If the user specified the sgen_diag property, override the global
+ * sgen_diag setting by setting sg_state's sgen_diag value. If the
+ * user gave a value out of range, default to '0'.
+ */
+ sg_state->sgen_diag = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "sgen-diag", -1);
+
+ if (sg_state->sgen_diag != -1) {
+ if (sg_state->sgen_diag < 0 || sg_state->sgen_diag > 3)
+ sg_state->sgen_diag = 0;
+ }
+
+ sgen_log(sg_state, SGEN_DIAG2,
+ "sgen_do_attach: sgen_soft_state=0x%p, instance=%d, "
+ "device unit-address @%s",
+ sgen_soft_state, instance, ddi_get_name_addr(dip));
+
+ /*
+ * For simplicity, the minor number == the instance number
+ */
+ if (ddi_create_minor_node(dip, sgen_typename(devtype), S_IFCHR,
+ instance, DDI_NT_SGEN, NULL) == DDI_FAILURE) {
+ scsi_unprobe(scsidevp);
+ ddi_prop_remove_all(dip);
+ sgen_log(sg_state, SGEN_DIAG1,
+ "sgen_do_attach: minor node creation failed, "
+ "device unit-address @%s", ddi_get_name_addr(dip));
+ ddi_soft_state_free(sgen_soft_state, instance);
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Allocate the command buffer, then create a condition variable for
+ * managing it; mark the command buffer as free.
+ */
+ sg_state->sgen_cmdbuf = getrbuf(KM_SLEEP);
+ cv_init(&sg_state->sgen_cmdbuf_cv, NULL, CV_DRIVER, NULL);
+
+ SGEN_CLR_BUSY(sg_state);
+ SGEN_CLR_OPEN(sg_state);
+ SGEN_CLR_SUSP(sg_state);
+
+ /*
+ * If the hba and the target both support wide xfers, enable them.
+ */
+ if (scsi_ifgetcap(&sg_state->sgen_scsiaddr, "wide-xfer", 1) != -1) {
+ int wide = 0;
+ if ((inq->inq_rdf == RDF_SCSI2) &&
+ (inq->inq_wbus16 || inq->inq_wbus32))
+ wide = 1;
+ if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "wide-xfer",
+ wide, 1) == 1) {
+ sgen_log(sg_state, SGEN_DIAG1,
+ "sgen_attach: wide xfer %s, "
+ "device unit-address @%s",
+ wide ? "enabled" : "disabled",
+ ddi_get_name_addr(dip));
+ }
+ }
+
+ /*
+ * This is a little debugging code-- since the codepath for auto-sense
+ * and 'manual' sense is split, toggling this variable will make
+ * sgen act as though the adapter in question can't do auto-sense.
+ */
+ if (sgen_force_manual_sense) {
+ if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "auto-rqsense",
+ 0, 1) == 1) {
+ sg_state->sgen_arq_enabled = 0;
+ } else {
+ sg_state->sgen_arq_enabled = 1;
+ }
+ } else {
+ /*
+ * Enable autorequest sense, if supported
+ */
+ if (scsi_ifgetcap(&sg_state->sgen_scsiaddr,
+ "auto-rqsense", 1) != 1) {
+ if (scsi_ifsetcap(&sg_state->sgen_scsiaddr,
+ "auto-rqsense", 1, 1) == 1) {
+ sg_state->sgen_arq_enabled = 1;
+ sgen_log(sg_state, SGEN_DIAG1,
+ "sgen_attach: auto-request-sense enabled, "
+ "device unit-address @%s",
+ ddi_get_name_addr(dip));
+ } else {
+ sg_state->sgen_arq_enabled = 0;
+ sgen_log(sg_state, SGEN_DIAG1,
+ "sgen_attach: auto-request-sense disabled, "
+ "device unit-address @%s",
+ ddi_get_name_addr(dip));
+ }
+ } else {
+ sg_state->sgen_arq_enabled = 1; /* already enabled */
+ sgen_log(sg_state, SGEN_DIAG1,
+ "sgen_attach: auto-request-sense enabled, "
+ "device unit-address @%s", ddi_get_name_addr(dip));
+ }
+ }
+
+ /*
+ * Allocate plumbing for manually fetching sense.
+ */
+ if (sgen_setup_sense(sg_state) != 0) {
+ freerbuf(sg_state->sgen_cmdbuf);
+ ddi_prop_remove_all(dip);
+ ddi_remove_minor_node(dip, NULL);
+ scsi_unprobe(scsidevp);
+ sgen_log(sg_state, SGEN_DIAG1,
+ "sgen_do_attach: failed to setup request-sense, "
+ "device unit-address @%s", ddi_get_name_addr(dip));
+ ddi_soft_state_free(sgen_soft_state, instance);
+ return (DDI_FAILURE);
+ }
+
+ sgen_create_errstats(sg_state, instance);
+
+ ddi_report_dev(dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * sgen_setup_sense()
+ * Allocate a request sense packet so that if sgen needs to fetch sense
+ * data for the user, it will have a pkt ready to send.
+ */
+static int
+sgen_setup_sense(sgen_state_t *sg_state)
+{
+ struct buf *bp;
+ struct scsi_pkt *rqpkt;
+
+ if ((bp = scsi_alloc_consistent_buf(&sg_state->sgen_scsiaddr, NULL,
+ SENSE_LENGTH, B_READ, SLEEP_FUNC, NULL)) == NULL) {
+ return (-1);
+ }
+
+ if ((rqpkt = scsi_init_pkt(&sg_state->sgen_scsiaddr, NULL, bp,
+ CDB_GROUP0, 1, 0, PKT_CONSISTENT, SLEEP_FUNC, NULL)) == NULL) {
+ scsi_free_consistent_buf(bp);
+ return (-1);
+ }
+
+ /*
+ * Make the results of running a SENSE available by filling out the
+ * sd_sense field of the scsi device (sgen_sense is just an alias).
+ */
+ sg_state->sgen_sense = (struct scsi_extended_sense *)bp->b_un.b_addr;
+
+ (void) scsi_setup_cdb((union scsi_cdb *)rqpkt->pkt_cdbp,
+ SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0);
+ FILL_SCSI1_LUN(sg_state->sgen_scsidev, rqpkt);
+
+ rqpkt->pkt_comp = sgen_callback;
+ rqpkt->pkt_time = SGEN_IO_TIME;
+ rqpkt->pkt_flags |= FLAG_SENSING;
+ rqpkt->pkt_private = sg_state;
+
+ sg_state->sgen_rqspkt = rqpkt;
+ sg_state->sgen_rqsbuf = bp;
+
+ return (0);
+}
+
+/*
+ * sgen_create_errstats()
+ * create named kstats for tracking occurence of errors.
+ */
+static void
+sgen_create_errstats(sgen_state_t *sg_state, int instance)
+{
+ char kstatname[KSTAT_STRLEN];
+ struct sgen_errstats *stp;
+
+ (void) snprintf(kstatname, KSTAT_STRLEN, "sgen%d,err", instance);
+ sg_state->sgen_kstats = kstat_create("sgenerr", instance,
+ kstatname, "device_error", KSTAT_TYPE_NAMED,
+ sizeof (struct sgen_errstats) / sizeof (kstat_named_t),
+ KSTAT_FLAG_PERSISTENT);
+
+ if (sg_state->sgen_kstats == NULL)
+ return;
+
+ stp = (struct sgen_errstats *)sg_state->sgen_kstats->ks_data;
+ kstat_named_init(&stp->sgen_trans_err, "transport_errors",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&stp->sgen_restart, "command_restarts",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&stp->sgen_incmp_err, "incomplete_commands",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&stp->sgen_autosen_rcv, "autosense_occurred",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&stp->sgen_autosen_bad, "autosense_undecipherable",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&stp->sgen_sense_rcv, "sense_fetches",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&stp->sgen_sense_bad, "sense_data_undecipherable",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&stp->sgen_recov_err, "recoverable_error",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&stp->sgen_nosen_err, "NO_SENSE_sense_key",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&stp->sgen_unrecov_err, "unrecoverable_sense_error",
+ KSTAT_DATA_UINT32);
+ sg_state->sgen_kstats->ks_private = sg_state;
+ sg_state->sgen_kstats->ks_update = nulldev;
+ kstat_install(sg_state->sgen_kstats);
+}
+
+/*
+ * sgen_detach()
+ * detach(9E) entrypoint
+ */
+static int
+sgen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int instance;
+ sgen_state_t *sg_state;
+
+ instance = ddi_get_instance(dip);
+ sg_state = ddi_get_soft_state(sgen_soft_state, instance);
+
+ sgen_log(sg_state, SGEN_DIAG2, "in sgen_detach(), "
+ "device unit-address @%s", ddi_get_name_addr(dip));
+
+ if (sg_state == NULL) {
+ sgen_log(NULL, SGEN_DIAG1,
+ "sgen_detach: failed, no softstate found (%d), "
+ "device unit-address @%s",
+ instance, ddi_get_name_addr(dip));
+ return (DDI_FAILURE);
+ }
+
+ switch (cmd) {
+ case DDI_DETACH:
+ return (sgen_do_detach(dip));
+ case DDI_SUSPEND:
+ return (sgen_do_suspend(dip));
+ case DDI_PM_SUSPEND:
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+/*
+ * sgen_do_detach()
+ * detach the driver, tearing down resources.
+ */
+static int
+sgen_do_detach(dev_info_t *dip)
+{
+ int instance;
+ sgen_state_t *sg_state;
+ struct scsi_device *devp;
+
+ instance = ddi_get_instance(dip);
+ sg_state = ddi_get_soft_state(sgen_soft_state, instance);
+ ASSERT(sg_state);
+
+ sgen_log(sg_state, SGEN_DIAG2, "in sgen_do_detach(), "
+ "device unit-address @%s", ddi_get_name_addr(dip));
+ devp = ddi_get_driver_private(dip);
+
+ mutex_enter(&sg_state->sgen_mutex);
+ if (SGEN_IS_BUSY(sg_state)) {
+ mutex_exit(&sg_state->sgen_mutex);
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_do_detach: failed because "
+ "device is busy, device unit-address @%s",
+ ddi_get_name_addr(dip));
+ return (DDI_FAILURE);
+ }
+ mutex_exit(&sg_state->sgen_mutex);
+
+ /*
+ * Final approach for detach. Free data allocated by scsi_probe()
+ * in attach.
+ */
+ if (sg_state->sgen_restart_timeid)
+ (void) untimeout(sg_state->sgen_restart_timeid);
+ sg_state->sgen_restart_timeid = 0;
+ scsi_unprobe(devp);
+
+ /*
+ * Free auto-request plumbing.
+ */
+ scsi_free_consistent_buf(sg_state->sgen_rqsbuf);
+ scsi_destroy_pkt(sg_state->sgen_rqspkt);
+
+ if (sg_state->sgen_kstats) {
+ kstat_delete(sg_state->sgen_kstats);
+ sg_state->sgen_kstats = NULL;
+ }
+
+ /*
+ * Free command buffer and clean up
+ */
+ freerbuf(sg_state->sgen_cmdbuf);
+ cv_destroy(&sg_state->sgen_cmdbuf_cv);
+
+ sgen_log(sg_state, SGEN_DIAG2, "done sgen_do_detach(), "
+ "device unit-address @%s", ddi_get_name_addr(dip));
+
+ ddi_soft_state_free(sgen_soft_state, instance);
+ ddi_prop_remove_all(dip);
+ ddi_remove_minor_node(dip, NULL);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * sgen_do_suspend()
+ * suspend the driver. This sets the "suspend" bit for this target if it
+ * is currently open; once resumed, the suspend bit will cause
+ * subsequent I/Os to fail. We want user programs to close and
+ * reopen the device to acknowledge that they need to reexamine its
+ * state and do the right thing.
+ */
+static int
+sgen_do_suspend(dev_info_t *dip)
+{
+ int instance;
+ sgen_state_t *sg_state;
+
+ instance = ddi_get_instance(dip);
+ sg_state = ddi_get_soft_state(sgen_soft_state, instance);
+ ASSERT(sg_state);
+
+ sgen_log(sg_state, SGEN_DIAG2, "in sgen_do_suspend(), "
+ "device unit-address @%s", ddi_get_name_addr(dip));
+
+ if (sg_state->sgen_restart_timeid) {
+ (void) untimeout(sg_state->sgen_restart_timeid);
+ }
+ sg_state->sgen_restart_timeid = 0;
+
+ mutex_enter(&sg_state->sgen_mutex);
+ if (SGEN_IS_OPEN(sg_state))
+ SGEN_SET_SUSP(sg_state);
+ mutex_exit(&sg_state->sgen_mutex);
+
+ sgen_log(sg_state, SGEN_DIAG2, "done sgen_do_suspend(), "
+ "device unit-address @%s", ddi_get_name_addr(dip));
+ return (DDI_SUCCESS);
+}
+
+/*
+ * sgen_getinfo()
+ * getinfo(9e) entrypoint.
+ */
+/*ARGSUSED*/
+static int
+sgen_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ dev_t dev;
+ sgen_state_t *sg_state;
+ int instance, error;
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ dev = (dev_t)arg;
+ instance = getminor(dev);
+ if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance))
+ == NULL)
+ return (DDI_FAILURE);
+ *result = (void *) sg_state->sgen_scsidev->sd_dev;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ dev = (dev_t)arg;
+ instance = getminor(dev);
+ *result = (void *)(uintptr_t)instance;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return (error);
+}
+
+/*
+ * sgen_probe()
+ * probe(9e) entrypoint. sgen *never* returns DDI_PROBE_PARTIAL, in
+ * order to avoid leaving around extra devinfos. If sgen's binding
+ * rules indicate that it should bind, it returns DDI_PROBE_SUCCESS.
+ */
+static int
+sgen_probe(dev_info_t *dip)
+{
+ struct scsi_device *scsidevp;
+ int instance;
+ int rval;
+
+ scsidevp = ddi_get_driver_private(dip);
+ instance = ddi_get_instance(dip);
+ sgen_log(NULL, SGEN_DIAG2, "in sgen_probe(): instance = %d, "
+ "device unit-address @%s", instance, ddi_get_name_addr(dip));
+
+ if (ddi_dev_is_sid(dip) == DDI_SUCCESS)
+ return (DDI_PROBE_DONTCARE);
+
+ if (ddi_get_soft_state(sgen_soft_state, instance) != NULL)
+ return (DDI_PROBE_FAILURE);
+
+ mutex_enter(&sgen_binddb.sdb_lock);
+ if (sgen_binddb.sdb_init == 0) {
+ sgen_setup_binddb(dip);
+ }
+ mutex_exit(&sgen_binddb.sdb_lock);
+
+ /*
+ * A small optimization: if it's impossible for sgen to bind to
+ * any devices, don't bother probing, just fail.
+ */
+ if ((sgen_binddb.sdb_inq_nodes == NULL) &&
+ (sgen_binddb.sdb_type_nodes == NULL)) {
+ return (DDI_PROBE_FAILURE);
+ }
+
+ if (scsi_probe(scsidevp, NULL_FUNC) == SCSIPROBE_EXISTS) {
+ if (sgen_get_binding(dip) == 0) {
+ rval = DDI_PROBE_SUCCESS;
+ }
+ } else {
+ rval = DDI_PROBE_FAILURE;
+ }
+ scsi_unprobe(scsidevp);
+
+ sgen_log(NULL, SGEN_DIAG2, "sgen_probe() %s, device unit-address @%s",
+ rval == DDI_PROBE_SUCCESS ? "succeeded" : "failed",
+ ddi_get_name_addr(dip));
+ return (rval);
+}
+
+/*
+ * sgen_open()
+ * open(9e) entrypoint. sgen enforces a strict exclusive open policy per
+ * target.
+ */
+/*ARGSUSED1*/
+static int
+sgen_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
+{
+ dev_t dev = *dev_p;
+ sgen_state_t *sg_state;
+ int instance;
+
+ instance = getminor(dev);
+
+ if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
+ return (ENXIO);
+
+ sgen_log(sg_state, SGEN_DIAG2, "in sgen_open(): instance = %d",
+ instance);
+
+ mutex_enter(&sg_state->sgen_mutex);
+
+ if (SGEN_IS_OPEN(sg_state)) {
+ mutex_exit(&sg_state->sgen_mutex);
+ return (EBUSY);
+ }
+
+ SGEN_SET_OPEN(sg_state);
+
+ /*
+ * At this point, sgen cannot have the suspended bit set,
+ * since each target is exclusive-access; when the previous
+ * close occurred, it cleared the flag.
+ */
+ ASSERT(!SGEN_IS_SUSP(sg_state));
+ mutex_exit(&sg_state->sgen_mutex);
+
+ return (0);
+}
+
+/*
+ * sgen_close()
+ * close(9e) entrypoint.
+ */
+/*ARGSUSED1*/
+static int
+sgen_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
+{
+ sgen_state_t *sg_state;
+ int instance;
+
+ instance = getminor(dev);
+
+ if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
+ return (ENXIO);
+
+ sgen_log(sg_state, SGEN_DIAG2, "in sgen_close(): instance = %d",
+ instance);
+
+ mutex_enter(&sg_state->sgen_mutex);
+ SGEN_CLR_OPEN(sg_state);
+ SGEN_CLR_SUSP(sg_state); /* closing clears the 'I was suspended' bit */
+ mutex_exit(&sg_state->sgen_mutex);
+
+ sgen_log(sg_state, SGEN_DIAG2, "done sgen_close()");
+
+ return (0);
+}
+
+/*
+ * sgen_ioctl()
+ * sgen supports the USCSI(7I) ioctl interface.
+ */
+/*ARGSUSED4*/
+static int
+sgen_ioctl(dev_t dev,
+ int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p)
+{
+ int retval = 0;
+ enum uio_seg uioseg;
+ sgen_state_t *sg_state;
+ int instance;
+
+ instance = getminor(dev);
+
+ if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
+ return (ENXIO);
+
+ sgen_log(sg_state, SGEN_DIAG2, "in sgen_ioctl(): instance = %d",
+ instance);
+
+ /*
+ * If the driver has been suspended since the last open, fail all
+ * subsequent IO's so that the userland consumer reinitializes state.
+ */
+ mutex_enter(&sg_state->sgen_mutex);
+ if (SGEN_IS_SUSP(sg_state)) {
+ mutex_exit(&sg_state->sgen_mutex);
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_ioctl: returning EIO: "
+ "driver instance %d was previously suspended", instance);
+ return (EIO);
+ }
+ mutex_exit(&sg_state->sgen_mutex);
+
+ switch (cmd) {
+ case SGEN_IOC_DIAG: {
+ if (arg > 3) {
+ arg = 0;
+ }
+ sg_state->sgen_diag = (int)arg;
+ retval = 0;
+ break;
+ }
+
+ case SGEN_IOC_READY: {
+ if (sgen_tur(dev) != 0) {
+ retval = EIO;
+ } else {
+ retval = 0;
+ }
+ break;
+ }
+
+ case USCSICMD: {
+ struct uscsi_cmd scmd;
+#ifdef _MULTI_DATAMODEL
+ struct uscsi_cmd32 ucmd32;
+ model_t model;
+
+ switch (model = ddi_model_convert_from(flag & FMODELS)) {
+ case DDI_MODEL_ILP32:
+ if (ddi_copyin((void *)arg, &ucmd32,
+ sizeof (ucmd32), flag)) {
+ retval = EFAULT;
+ break;
+ }
+ /*
+ * Convert the ILP32 uscsi data from the application
+ * to LP64 for internal use.
+ */
+ uscsi_cmd32touscsi_cmd((&ucmd32), (&scmd));
+ break;
+ case DDI_MODEL_NONE:
+ if (ddi_copyin((void *)arg, &scmd, sizeof (scmd),
+ flag)) {
+ retval = EFAULT;
+ }
+ break;
+ }
+
+#else /* ! _MULTI_DATAMODEL */
+ if (ddi_copyin((void *)arg, &scmd, sizeof (scmd), flag)) {
+ retval = EFAULT;
+ }
+#endif /* _MULTI_DATAMODEL */
+ if (retval != 0) {
+ break;
+ }
+
+
+ uioseg = (flag & FKIOCTL) ? UIO_SYSSPACE : UIO_USERSPACE;
+ retval = sgen_uscsi_cmd(dev, &scmd, uioseg, uioseg, uioseg);
+
+#ifdef _MULTI_DATAMODEL
+ switch (model) {
+ case DDI_MODEL_ILP32:
+ /*
+ * Convert back to ILP32 before copyout to the
+ * application
+ */
+ uscsi_cmdtouscsi_cmd32((&scmd), (&ucmd32));
+ if (ddi_copyout(&ucmd32, (void *)arg,
+ sizeof (ucmd32), flag)) {
+ if (retval == 0) {
+ retval = EFAULT;
+ }
+ }
+ break;
+ case DDI_MODEL_NONE:
+ if (ddi_copyout(&scmd, (void *)arg, sizeof (scmd),
+ flag)) {
+ if (retval == 0) {
+ retval = EFAULT;
+ }
+ }
+ break;
+ }
+
+#else /* ! _MULTI_DATAMODEL */
+ if (ddi_copyout(&scmd, (void *)arg, sizeof (scmd), flag)) {
+ if (retval == 0) {
+ retval = EFAULT;
+ }
+ }
+#endif /* _MULTI_DATAMODEL */
+ break;
+ }
+ default:
+ retval = ENOTTY;
+ }
+
+ sgen_log(sg_state, SGEN_DIAG2, "done sgen_ioctl(), returning %d",
+ retval);
+
+ return (retval);
+}
+
+/*ARGSUSED*/
+static void
+sgen_minphys(struct buf *bp)
+{
+ /*
+ * Do not break up the CDB; using normal minphys() causes breakup of
+ * large uscsi requests and headaches. This matches the implementation
+ * found in sd and st.
+ */
+}
+
+/*
+ * sgen_uscsi_cmd()
+ * Setup, configuration and teardown for a uscsi(7I) command
+ */
+/*ARGSUSED*/
+static int
+sgen_uscsi_cmd(dev_t dev, struct uscsi_cmd *ucmd,
+ enum uio_seg cdbspace, enum uio_seg dataspace, enum uio_seg rqbufspace)
+{
+ caddr_t cdb = NULL;
+ caddr_t saved_rqbuf = NULL;
+ caddr_t saved_cdb = NULL;
+ uchar_t saved_rqlen = 0;
+ int instance, err, rw, rqlen, flag, newflags;
+ struct buf *bp;
+ sgen_state_t *sg_state;
+ int cmdbufhold = 0;
+
+ instance = getminor(dev);
+
+ sg_state = ddi_get_soft_state(sgen_soft_state, instance);
+ ASSERT(sg_state);
+
+ sgen_log(sg_state, SGEN_DIAG2, "in sgen_uscsi_cmd(): instance = %d",
+ instance);
+
+ /*
+ * Stash values from the user's command. They are restored on exit
+ * from this routine.
+ */
+ saved_cdb = ucmd->uscsi_cdb;
+ saved_rqbuf = ucmd->uscsi_rqbuf;
+ saved_rqlen = ucmd->uscsi_rqlen;
+
+ /*
+ * Clear out undesirable command flags
+ */
+ newflags = (ucmd->uscsi_flags & ~(USCSI_NOINTR | USCSI_NOPARITY |
+ USCSI_OTAG | USCSI_HTAG | USCSI_HEAD));
+ if (newflags != ucmd->uscsi_flags) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: cleared "
+ "unsafe uscsi_flags 0x%x", ucmd->uscsi_flags & ~newflags);
+ ucmd->uscsi_flags = newflags;
+ }
+
+ /*
+ * Skip sanity checks for RESET commands
+ */
+ if ((ucmd->uscsi_flags & (USCSI_RESET|USCSI_RESET_ALL)) == 0) {
+ /*
+ * Do some sanity checks -- these seem to catch 90% of the
+ * coding mistakes people make when using uscsi.
+ *
+ * 1. cdb's must be at least 6 bytes long. After the
+ * copyin we will check for CDBs that are greater
+ * than 16 bytes long which don't have the opcode set
+ * correctly.
+ * 2. If no buffer is specified, neither the read nor write
+ * flag should be turned on.
+ * 3. If auto request sense is enabled, then the rqlen and
+ * the rqbuf must be non-zero and non-null respectively.
+ */
+ if (ucmd->uscsi_cdblen < 6) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: "
+ "rejected command because uscsi_cdblen less "
+ "than 6");
+ err = EINVAL;
+ goto exit;
+ }
+
+ if (ucmd->uscsi_flags & USCSI_RQENABLE) {
+ if ((ucmd->uscsi_rqlen == 0) ||
+ (ucmd->uscsi_rqbuf == NULL)) {
+ sgen_log(sg_state, SGEN_DIAG1,
+ "sgen_uscsi_cmd: rejected command because "
+ "USCSI_RQENABLE is set and uscsi_rqlen is "
+ "0 or uscsi_rqbuf is NULL");
+ err = EINVAL;
+ goto exit;
+ }
+ }
+ }
+
+ /*
+ * At this point, we start affecting state relevant to the target,
+ * so access needs to be serialized.
+ */
+ sgen_hold_cmdbuf(sg_state); /* lock command buf for this target */
+ cmdbufhold = 1;
+ bp = sg_state->sgen_cmdbuf;
+
+ /*
+ * A reset? Go no further.
+ */
+ if (ucmd->uscsi_flags & (USCSI_RESET|USCSI_RESET_ALL)) {
+ flag = (ucmd->uscsi_flags & USCSI_RESET_ALL) ?
+ RESET_ALL : RESET_TARGET;
+ err = (scsi_reset(&sg_state->sgen_scsiaddr, flag)) ? 0 : EIO;
+ goto exit;
+ }
+
+ /*
+ * Enable asynchronous mode if requested.
+ */
+ if ((ucmd->uscsi_flags & USCSI_ASYNC) &&
+ (scsi_ifgetcap(&sg_state->sgen_scsiaddr, "synchronous", 1) == 1)) {
+ if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "synchronous",
+ 0, 1) == 1) {
+ sgen_log(sg_state, SGEN_DIAG3,
+ "sgen_uscsi_cmd: set target asynchronous");
+ } else {
+ sgen_log(sg_state, SGEN_DIAG3, "sgen_uscsi_cmd: "
+ "failed to set target asynchronous");
+ err = EINVAL;
+ goto exit;
+ }
+ }
+
+ /*
+ * Enable synchronous mode if requested.
+ */
+ if ((ucmd->uscsi_flags & USCSI_SYNC) &&
+ (scsi_ifgetcap(&sg_state->sgen_scsiaddr, "synchronous", 1) == 0)) {
+ if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "synchronous",
+ 1, 1) == 1) {
+ sgen_log(sg_state, SGEN_DIAG3,
+ "sgen_uscsi_cmd: set target synchronous");
+ } else {
+ sgen_log(sg_state, SGEN_DIAG3,
+ "sgen_uscsi_cmd: failed to set target synchronous");
+ err = EINVAL;
+ goto exit;
+ }
+ }
+
+ sgen_log(sg_state, SGEN_DIAG2, "sgen_uscsi_cmd: uscsi_cmd copied-in");
+
+ /*
+ * copyin from kernel space if the cdb's origin is inside the kernel.
+ */
+ cdb = kmem_zalloc((size_t)ucmd->uscsi_cdblen, KM_SLEEP);
+ if (ddi_copyin(ucmd->uscsi_cdb, cdb, (size_t)ucmd->uscsi_cdblen,
+ (cdbspace == UIO_SYSSPACE) ? FKIOCTL : 0)) {
+ err = EFAULT;
+ goto exit;
+ }
+ /*
+ * if the length of the CDB is greater than 16 bytes, it must be
+ * a variable length CDB (i.e. the opcode must be 0x7f)
+ */
+ if ((ucmd->uscsi_cdblen > SCSI_CDB_SIZE) &&
+ (cdb[0] != SCMD_VAR_LEN)) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: "
+ "rejected command because uscsi_cdblen is "
+ "0x%x, but opcode is 0x%x",
+ ucmd->uscsi_cdblen, cdb[0]);
+ err = EINVAL;
+ goto exit;
+ }
+ ucmd->uscsi_cdb = cdb;
+
+ sgen_log(sg_state, SGEN_DIAG2, "sgen_uscsi_cmd: cdb copied-in");
+ sgen_dump_cdb(sg_state, "sgen_uscsi_cmd: ",
+ (union scsi_cdb *)ucmd->uscsi_cdb, ucmd->uscsi_cdblen);
+
+ rw = (ucmd->uscsi_flags & USCSI_READ) ? B_READ : B_WRITE;
+
+ /*
+ * Initialize Request Sense buffering. Allocate a kernel copy of
+ * the sense buffer, if sense is requested and supported by the HBA.
+ * Stash the sense buffer into sgen_rqs_sen for convenience.
+ */
+ if (ucmd->uscsi_flags & USCSI_RQENABLE) {
+ sgen_log(sg_state, SGEN_DIAG3, "sgen_uscsi_cmd: setting up "
+ "sense buffer");
+ ucmd->uscsi_rqlen = SENSE_LENGTH;
+ ucmd->uscsi_rqresid = SENSE_LENGTH;
+ ucmd->uscsi_rqbuf = kmem_zalloc(SENSE_LENGTH, KM_SLEEP);
+ sg_state->sgen_rqs_sen = ucmd->uscsi_rqbuf;
+ } else {
+ ucmd->uscsi_rqlen = 0;
+ ucmd->uscsi_rqresid = 0;
+ ucmd->uscsi_rqbuf = NULL;
+ sg_state->sgen_rqs_sen = NULL;
+ }
+
+
+ bp->av_back = NULL;
+ bp->av_forw = NULL;
+ bp->b_private = (struct buf *)ucmd;
+
+ if (ucmd->uscsi_buflen) {
+ struct iovec aiov;
+ struct uio auio;
+ struct uio *uio = &auio;
+
+ bzero(&auio, sizeof (struct uio));
+ bzero(&aiov, sizeof (struct iovec));
+ aiov.iov_base = ucmd->uscsi_bufaddr;
+ aiov.iov_len = ucmd->uscsi_buflen;
+
+ uio->uio_iov = &aiov;
+ uio->uio_iovcnt = 1;
+ uio->uio_resid = aiov.iov_len;
+ uio->uio_segflg = dataspace;
+
+ /*
+ * Call physio, waiting here until the command is completed.
+ */
+ err = physio(sgen_start, bp, dev, rw, sgen_minphys, uio);
+ } else {
+ /*
+ * Since we're not actually moving data (uscsi_buflen == 0)
+ * call sgen_start directly, mimicing physio().
+ */
+ sgen_log(sg_state, SGEN_DIAG3, "sgen_uscsi_cmd: taking "
+ "non-physio path because uscsi_buflen == 0");
+ bp->b_flags = B_BUSY | rw;
+ bp->b_edev = dev;
+ bp->b_bcount = bp->b_blkno = 0;
+ (void) sgen_start(bp);
+ err = biowait(bp);
+ }
+
+ if (sg_state->sgen_cmdpkt != NULL) {
+ ucmd->uscsi_status = SCBP_C(sg_state->sgen_cmdpkt);
+ ucmd->uscsi_resid = bp->b_resid;
+ } else {
+ ucmd->uscsi_status = 0;
+ }
+
+ sgen_log(sg_state, SGEN_DIAG3, "sgen_uscsi_cmd: awake from waiting "
+ "for command. Status is 0x%x", ucmd->uscsi_status);
+
+ /*
+ * If sense was requested, copy available sense data into ucmd.
+ */
+ if (ucmd->uscsi_rqbuf != NULL) {
+ sgen_log(sg_state, SGEN_DIAG3, "sgen_uscsi_cmd: checking for "
+ "sense. ucmd->uscsi_rqlen=%d ucmd->uscsi_rqresid=%d",
+ ucmd->uscsi_rqlen, ucmd->uscsi_rqresid);
+
+ rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid;
+ rqlen = MIN(saved_rqlen, rqlen);
+ ucmd->uscsi_rqresid = ucmd->uscsi_rqlen - rqlen;
+ rqlen = MIN((int)ucmd->uscsi_rqlen, rqlen);
+
+ if (rqlen) {
+ sgen_dump_sense(sg_state, rqlen,
+ (uchar_t *)ucmd->uscsi_rqbuf);
+ sgen_log(sg_state, SGEN_DIAG3, "sgen_uscsi_cmd: "
+ "copyout of rqlen=0x%x sense bytes to buffer at "
+ "0x%p", rqlen, (void *)saved_rqbuf);
+ if (ddi_copyout(ucmd->uscsi_rqbuf, saved_rqbuf, rqlen,
+ (rqbufspace == UIO_SYSSPACE) ? FKIOCTL : 0)) {
+ err = EFAULT;
+ }
+ }
+ kmem_free(ucmd->uscsi_rqbuf, ucmd->uscsi_rqlen);
+ }
+
+exit:
+ if (sg_state->sgen_cmdpkt != NULL) {
+ scsi_destroy_pkt(sg_state->sgen_cmdpkt);
+ sg_state->sgen_cmdpkt = NULL;
+ }
+
+ /*
+ * After this point, we can't touch per-target state.
+ */
+ if (cmdbufhold) {
+ sgen_rele_cmdbuf(sg_state);
+ }
+
+ ucmd->uscsi_rqbuf = saved_rqbuf;
+ ucmd->uscsi_cdb = saved_cdb;
+ ucmd->uscsi_rqlen = saved_rqlen;
+
+ if (cdb) {
+ kmem_free(cdb, ucmd->uscsi_cdblen);
+ }
+
+ sgen_log(sg_state, SGEN_DIAG2, "done sgen_uscsi_cmd()");
+
+ return (err);
+}
+
+/*
+ * sgen_hold_cmdbuf()
+ * Aquire a lock on the command buffer for the given target.
+ */
+static void
+sgen_hold_cmdbuf(sgen_state_t *sg_state)
+{
+ mutex_enter(&sg_state->sgen_mutex);
+ while (SGEN_IS_BUSY(sg_state))
+ cv_wait(&sg_state->sgen_cmdbuf_cv, &sg_state->sgen_mutex);
+ SGEN_SET_BUSY(sg_state);
+ mutex_exit(&sg_state->sgen_mutex);
+}
+
+/*
+ * sgen_rele_cmdbuf()
+ * release the command buffer for a particular target.
+ */
+static void
+sgen_rele_cmdbuf(sgen_state_t *sg_state)
+{
+ mutex_enter(&sg_state->sgen_mutex);
+ SGEN_CLR_BUSY(sg_state);
+ cv_signal(&sg_state->sgen_cmdbuf_cv);
+ mutex_exit(&sg_state->sgen_mutex);
+}
+
+/*
+ * sgen_start()
+ * Transport a uscsi command; this is invoked by physio() or directly
+ * by sgen_uscsi_cmd().
+ */
+static int
+sgen_start(struct buf *bp)
+{
+ sgen_state_t *sg_state;
+ dev_t dev = bp->b_edev;
+ int trans_err;
+
+ if ((sg_state = ddi_get_soft_state(sgen_soft_state,
+ getminor(dev))) == NULL) {
+ bp->b_resid = bp->b_bcount;
+ bioerror(bp, ENXIO);
+ biodone(bp);
+ return (ENXIO);
+ }
+
+ /*
+ * Sanity checks - command should not be complete, no packet should
+ * be allocated, and there ought to be a uscsi cmd in b_private
+ */
+ ASSERT(bp == sg_state->sgen_cmdbuf && sg_state->sgen_cmdpkt == NULL);
+ ASSERT((bp->b_flags & B_DONE) == 0);
+ ASSERT(bp->b_private);
+ if (sgen_make_uscsi_cmd(sg_state, bp) != 0) {
+ bp->b_resid = bp->b_bcount;
+ bioerror(bp, EFAULT);
+ biodone(bp);
+ return (EFAULT);
+ }
+
+ ASSERT(sg_state->sgen_cmdpkt != NULL);
+
+ /*
+ * Clear out the residual and error fields
+ */
+ bp->b_resid = 0;
+ bp->b_error = 0;
+
+ trans_err = sgen_scsi_transport(sg_state->sgen_cmdpkt);
+ switch (trans_err) {
+ case TRAN_ACCEPT:
+ break;
+ case TRAN_BUSY:
+ sgen_log(sg_state, SGEN_DIAG2,
+ "sgen_start: scsi_transport() returned TRAN_BUSY");
+ sg_state->sgen_restart_timeid = timeout(sgen_restart, sg_state,
+ SGEN_BSY_TIMEOUT);
+ break;
+ default:
+ /*
+ * Indicate there has been an I/O transfer error.
+ * Be done with the command.
+ */
+ mutex_enter(&sg_state->sgen_mutex);
+ SGEN_DO_ERRSTATS(sg_state, sgen_trans_err);
+ mutex_exit(&sg_state->sgen_mutex);
+ sgen_log(sg_state, SGEN_DIAG2, "sgen_start: scsi_transport() "
+ "returned %d", trans_err);
+ bioerror(bp, EIO);
+ biodone(bp);
+ return (EIO);
+ }
+ sgen_log(sg_state, SGEN_DIAG2, "sgen_start: b_flags 0x%x", bp->b_flags);
+ return (0);
+}
+
+/*
+ * sgen_scsi_transport()
+ * a simple scsi_transport() wrapper which can be configured to inject
+ * sporadic errors for testing.
+ */
+static int
+sgen_scsi_transport(struct scsi_pkt *pkt)
+{
+ int trans_err;
+ static int cnt = 0;
+ sgen_state_t *sg_state = pkt->pkt_private;
+
+ if (sgen_sporadic_failures == 0) {
+ return (scsi_transport(pkt));
+ }
+
+ cnt = (cnt * 2416 + 374441) % 1771875; /* borrowed from kmem.c */
+ if (cnt % 40 == 1) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_scsi_transport: "
+ "injecting sporadic BUSY");
+ trans_err = TRAN_BUSY;
+ } else if (cnt % 40 == 2) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_scsi_transport: "
+ "injecting sporadic BADPKT");
+ trans_err = TRAN_BADPKT;
+ } else {
+ /*
+ * Most of the time we take the normal path
+ */
+ trans_err = scsi_transport(pkt);
+ }
+ return (trans_err);
+}
+
+/*
+ * sgen_make_uscsi_cmd()
+ * Initialize a SCSI packet usable for USCSI.
+ */
+static int
+sgen_make_uscsi_cmd(sgen_state_t *sg_state, struct buf *bp)
+{
+ struct scsi_pkt *pkt;
+ struct uscsi_cmd *ucmd;
+ int stat_size;
+
+ ASSERT(bp);
+
+ sgen_log(sg_state, SGEN_DIAG2, "in sgen_make_uscsi_cmd()");
+
+ ucmd = (struct uscsi_cmd *)bp->b_private;
+
+ if (ucmd->uscsi_flags & USCSI_RQENABLE) {
+ stat_size = sizeof (struct scsi_arq_status);
+ } else {
+ stat_size = 1;
+ }
+
+ sgen_log(sg_state, SGEN_DIAG3, "sgen_make_uscsi_cmd: b_bcount = %ld",
+ bp->b_bcount);
+ pkt = scsi_init_pkt(&sg_state->sgen_scsiaddr,
+ NULL, /* in_pkt - null so it'll be alloc'd */
+ bp->b_bcount ? bp : NULL, /* buf structure for data xfer */
+ ucmd->uscsi_cdblen, /* cmdlen */
+ stat_size, /* statuslen */
+ 0, /* privatelen */
+ 0, /* flags */
+ SLEEP_FUNC, /* callback */
+ (caddr_t)sg_state); /* callback_arg */
+
+ if (pkt == NULL) {
+ sgen_log(sg_state, SGEN_DIAG2, "failed sgen_make_uscsi_cmd()");
+ return (-1);
+ }
+
+ pkt->pkt_comp = sgen_callback;
+ pkt->pkt_private = sg_state;
+ sg_state->sgen_cmdpkt = pkt;
+
+ /*
+ * We *don't* call scsi_setup_cdb here, as is customary, since the
+ * user could specify a command from one group, but pass cdblen
+ * as something totally different. If cdblen is smaller than expected,
+ * this results in scsi_setup_cdb writing past the end of the cdb.
+ */
+ bcopy(ucmd->uscsi_cdb, pkt->pkt_cdbp, ucmd->uscsi_cdblen);
+ if (ucmd->uscsi_cdblen >= CDB_GROUP0) {
+ FILL_SCSI1_LUN(sg_state->sgen_scsidev, pkt);
+ }
+
+ if (ucmd->uscsi_timeout > 0)
+ pkt->pkt_time = ucmd->uscsi_timeout;
+ else
+ pkt->pkt_time = SGEN_IO_TIME;
+
+ /*
+ * Set packet options
+ */
+ if (ucmd->uscsi_flags & USCSI_SILENT)
+ pkt->pkt_flags |= FLAG_SILENT;
+ if (ucmd->uscsi_flags & USCSI_ISOLATE)
+ pkt->pkt_flags |= FLAG_ISOLATE;
+ if (ucmd->uscsi_flags & USCSI_DIAGNOSE)
+ pkt->pkt_flags |= FLAG_DIAGNOSE;
+ if (ucmd->uscsi_flags & USCSI_RENEGOT) {
+ pkt->pkt_flags |= FLAG_RENEGOTIATE_WIDE_SYNC;
+ }
+
+ sgen_log(sg_state, SGEN_DIAG2, "done sgen_make_uscsi_cmd()");
+ return (0);
+}
+
+
+/*
+ * sgen_restart()
+ * sgen_restart() is called after a timeout, when a command has been
+ * postponed due to a TRAN_BUSY response from the HBA.
+ */
+static void
+sgen_restart(void *arg)
+{
+ sgen_state_t *sg_state = (sgen_state_t *)arg;
+ struct scsi_pkt *pkt;
+ struct buf *bp;
+
+ sgen_log(sg_state, SGEN_DIAG2, "in sgen_restart()");
+
+ bp = sg_state->sgen_cmdbuf;
+ pkt = sg_state->sgen_cmdpkt;
+ ASSERT(bp && pkt);
+
+ SGEN_DO_ERRSTATS(sg_state, sgen_restart);
+
+ /*
+ * If the packet is marked with the sensing flag, sgen is off running
+ * a request sense, and *that packet* is what needs to be restarted.
+ */
+ if (pkt->pkt_flags & FLAG_SENSING) {
+ sgen_log(sg_state, SGEN_DIAG3,
+ "sgen_restart: restarting REQUEST SENSE");
+ pkt = sg_state->sgen_rqspkt;
+ }
+
+ if (sgen_scsi_transport(pkt) != TRAN_ACCEPT) {
+ bp->b_resid = bp->b_bcount;
+ bioerror(bp, EIO);
+ biodone(bp);
+ }
+}
+
+/*
+ * sgen_callback()
+ * Command completion processing
+ *
+ * sgen's completion processing is very pessimistic-- it does not retry
+ * failed commands; instead, it allows the user application to make
+ * decisions about what has gone wrong.
+ */
+static void
+sgen_callback(struct scsi_pkt *pkt)
+{
+ sgen_state_t *sg_state;
+ struct buf *bp;
+ int action;
+
+ sg_state = pkt->pkt_private;
+ /*
+ * bp should always be the command buffer regardless of whether
+ * this is a command completion or a request-sense completion.
+ * This is because there is no need to biodone() the sense buf
+ * when it completes-- we want to biodone() the actual command buffer!
+ */
+ bp = sg_state->sgen_cmdbuf;
+ if (pkt->pkt_flags & FLAG_SENSING) {
+ ASSERT(pkt == sg_state->sgen_rqspkt);
+ sgen_log(sg_state, SGEN_DIAG2,
+ "in sgen_callback() (SENSE completion callback)");
+ } else {
+ ASSERT(pkt == sg_state->sgen_cmdpkt);
+ sgen_log(sg_state, SGEN_DIAG2,
+ "in sgen_callback() (command completion callback)");
+ }
+
+ sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: reason=0x%x resid=%ld "
+ "state=0x%x", pkt->pkt_reason, pkt->pkt_resid, pkt->pkt_state);
+
+ if (pkt->pkt_reason != CMD_CMPLT) {
+ /*
+ * The command did not complete.
+ */
+ sgen_log(sg_state, SGEN_DIAG3,
+ "sgen_callback: command did not complete");
+ action = sgen_handle_incomplete(sg_state, pkt);
+ } else if (sg_state->sgen_arq_enabled &&
+ (pkt->pkt_state & STATE_ARQ_DONE)) {
+ /*
+ * The auto-rqsense happened, and the packet has a filled-in
+ * scsi_arq_status structure, pointed to by pkt_scbp.
+ */
+ sgen_log(sg_state, SGEN_DIAG3,
+ "sgen_callback: received auto-requested sense");
+ action = sgen_handle_autosense(sg_state, pkt);
+ ASSERT(action != FETCH_SENSE);
+ } else if (pkt->pkt_flags & FLAG_SENSING) {
+ /*
+ * sgen was running a REQUEST SENSE. Decode the sense data and
+ * decide what to do next.
+ *
+ * Clear FLAG_SENSING on the original packet for completeness.
+ */
+ sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: received sense");
+ sg_state->sgen_cmdpkt->pkt_flags &= ~FLAG_SENSING;
+ action = sgen_handle_sense(sg_state);
+ ASSERT(action != FETCH_SENSE);
+ } else {
+ /*
+ * Command completed and we're not getting sense. Check for
+ * errors and decide what to do next.
+ */
+ sgen_log(sg_state, SGEN_DIAG3,
+ "sgen_callback: command appears complete");
+ action = sgen_check_error(sg_state, bp);
+ }
+
+ switch (action) {
+ case FETCH_SENSE:
+ /*
+ * If there is sense to fetch, break out to prevent biodone'ing
+ * until the sense fetch is complete.
+ */
+ if (sgen_initiate_sense(sg_state) == 0)
+ break;
+ /*FALLTHROUGH*/
+ case COMMAND_DONE_ERROR:
+ bp->b_resid = bp->b_bcount;
+ bioerror(bp, EIO);
+ /*FALLTHROUGH*/
+ case COMMAND_DONE:
+ biodone(bp);
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ sgen_log(sg_state, SGEN_DIAG2, "done sgen_callback()");
+}
+
+/*
+ * sgen_initiate_sense()
+ * Send the sgen_rqspkt to the target, thereby requesting sense data.
+ */
+static int
+sgen_initiate_sense(sgen_state_t *sg_state)
+{
+ switch (sgen_scsi_transport(sg_state->sgen_rqspkt)) {
+ case TRAN_ACCEPT:
+ sgen_log(sg_state, SGEN_DIAG3, "sgen_initiate_sense: "
+ "sense fetch transport accepted.");
+ return (0);
+ case TRAN_BUSY:
+ sgen_log(sg_state, SGEN_DIAG2, "sgen_initiate_sense: "
+ "sense fetch transport busy, setting timeout.");
+ sg_state->sgen_restart_timeid = timeout(sgen_restart, sg_state,
+ SGEN_BSY_TIMEOUT);
+ return (0);
+ default:
+ sgen_log(sg_state, SGEN_DIAG2, "sgen_initiate_sense: "
+ "sense fetch transport failed or busy.");
+ return (-1);
+ }
+}
+
+/*
+ * sgen_handle_incomplete()
+ * sgen is pessimistic, but also careful-- it doesn't try to retry
+ * incomplete commands, but it also doesn't go resetting devices;
+ * it is hard to tell if the device will be tolerant of that sort
+ * of prodding.
+ *
+ * This routine has been left as a guide for the future--- the
+ * current administration's hands-off policy may need modification.
+ */
+/*ARGSUSED*/
+static int
+sgen_handle_incomplete(sgen_state_t *sg_state, struct scsi_pkt *pkt)
+{
+ SGEN_DO_ERRSTATS(sg_state, sgen_incmp_err);
+ return (COMMAND_DONE_ERROR);
+}
+
+/*
+ * sgen_handle_autosense()
+ * Deal with SENSE data acquired automatically via the auto-request-sense
+ * facility.
+ *
+ * Sgen takes a pessimistic view of things-- it doesn't retry commands,
+ * and unless the device recovered from the problem, this routine returns
+ * COMMAND_DONE_ERROR.
+ */
+static int
+sgen_handle_autosense(sgen_state_t *sg_state, struct scsi_pkt *pkt)
+{
+ struct scsi_arq_status *arqstat;
+ struct uscsi_cmd *ucmd =
+ (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
+ int amt;
+
+ arqstat = (struct scsi_arq_status *)(pkt->pkt_scbp);
+
+ SGEN_DO_ERRSTATS(sg_state, sgen_autosen_rcv);
+
+ if (arqstat->sts_rqpkt_reason != CMD_CMPLT) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: ARQ"
+ "failed to complete.");
+ SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
+ return (COMMAND_DONE_ERROR);
+ }
+
+ if (ucmd->uscsi_flags & USCSI_RQENABLE) {
+ ucmd->uscsi_rqstatus = *((char *)&arqstat->sts_rqpkt_status);
+ ucmd->uscsi_rqresid = arqstat->sts_rqpkt_resid;
+ ASSERT(ucmd->uscsi_rqlen && sg_state->sgen_rqs_sen);
+ bcopy(&(arqstat->sts_sensedata), sg_state->sgen_rqs_sen,
+ ucmd->uscsi_rqlen);
+ sgen_log(sg_state, SGEN_DIAG2, "sgen_handle_autosense: "
+ "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
+ ucmd->uscsi_rqstatus, ucmd->uscsi_rqresid);
+ }
+
+ if (arqstat->sts_rqpkt_status.sts_chk) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: got "
+ "check condition on auto request sense!");
+ SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
+ return (COMMAND_DONE_ERROR);
+ }
+
+ amt = SENSE_LENGTH - arqstat->sts_rqpkt_resid;
+ if (((arqstat->sts_rqpkt_state & STATE_XFERRED_DATA) == 0) ||
+ (amt == 0)) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: got "
+ "auto-sense, but it contains no data!");
+ SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
+ return (COMMAND_DONE_ERROR);
+ }
+
+ /*
+ * Stuff the sense data pointer into sgen_sense for later retrieval
+ */
+ sg_state->sgen_sense = &arqstat->sts_sensedata;
+
+ /*
+ * Now, check to see whether we got enough sense data to make any
+ * sense out if it (heh-heh).
+ */
+ if (amt < SUN_MIN_SENSE_LENGTH) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: not "
+ "enough auto sense data");
+ return (COMMAND_DONE_ERROR);
+ }
+
+ switch (arqstat->sts_sensedata.es_key) {
+ case KEY_RECOVERABLE_ERROR:
+ SGEN_DO_ERRSTATS(sg_state, sgen_recov_err);
+ break;
+ case KEY_NO_SENSE:
+ SGEN_DO_ERRSTATS(sg_state, sgen_nosen_err);
+ break;
+ default:
+ SGEN_DO_ERRSTATS(sg_state, sgen_unrecov_err);
+ break;
+ }
+
+ return (COMMAND_DONE);
+}
+
+/*
+ * sgen_handle_sense()
+ * Examine sense data that was manually fetched from the target.
+ */
+static int
+sgen_handle_sense(sgen_state_t *sg_state)
+{
+ struct scsi_pkt *rqpkt = sg_state->sgen_rqspkt;
+ struct scsi_status *rqstatus = (struct scsi_status *)rqpkt->pkt_scbp;
+ struct uscsi_cmd *ucmd =
+ (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
+ int amt;
+
+ SGEN_DO_ERRSTATS(sg_state, sgen_sense_rcv);
+
+ if (ucmd->uscsi_flags & USCSI_RQENABLE) {
+ ucmd->uscsi_rqstatus = *((char *)rqstatus);
+ ucmd->uscsi_rqresid = rqpkt->pkt_resid;
+ ASSERT(ucmd->uscsi_rqlen && sg_state->sgen_rqs_sen);
+ bcopy(sg_state->sgen_sense, sg_state->sgen_rqs_sen,
+ ucmd->uscsi_rqlen);
+ sgen_log(sg_state, SGEN_DIAG2, "sgen_handle_sense: "
+ "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
+ ucmd->uscsi_rqstatus, ucmd->uscsi_rqresid);
+ }
+
+ if (rqstatus->sts_busy) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got busy "
+ "on request sense");
+ SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
+ return (COMMAND_DONE_ERROR);
+ }
+
+ if (rqstatus->sts_chk) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got check "
+ "condition on request sense!");
+ SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
+ return (COMMAND_DONE_ERROR);
+ }
+
+ amt = SENSE_LENGTH - rqpkt->pkt_resid;
+ if ((rqpkt->pkt_state & STATE_XFERRED_DATA) == 0 || amt == 0) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got "
+ "sense, but it contains no data");
+ SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
+ return (COMMAND_DONE_ERROR);
+ }
+
+ /*
+ * Now, check to see whether we got enough sense data to make any
+ * sense out if it (heh-heh).
+ */
+ if (amt < SUN_MIN_SENSE_LENGTH) {
+ sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: not "
+ "enough sense data");
+ SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
+ return (COMMAND_DONE_ERROR);
+ }
+
+ /*
+ * Decode the sense data-- this was deposited here for us by the
+ * setup in sgen_do_attach(). (note that sgen_sense is an alias for
+ * the sd_sense field in the scsi_device).
+ */
+ sgen_log(sg_state, SGEN_DIAG1, "Sense key is %s [0x%x]",
+ scsi_sname(sg_state->sgen_sense->es_key),
+ sg_state->sgen_sense->es_key);
+ switch (sg_state->sgen_sense->es_key) {
+ case KEY_RECOVERABLE_ERROR:
+ SGEN_DO_ERRSTATS(sg_state, sgen_recov_err);
+ break;
+ case KEY_NO_SENSE:
+ SGEN_DO_ERRSTATS(sg_state, sgen_nosen_err);
+ break;
+ default:
+ SGEN_DO_ERRSTATS(sg_state, sgen_unrecov_err);
+ break;
+ }
+
+ return (COMMAND_DONE);
+}
+
+/*
+ * sgen_check_error()
+ * examine the command packet for abnormal completion.
+ *
+ * sgen_check_error should only be called at the completion of the
+ * command packet.
+ */
+static int
+sgen_check_error(sgen_state_t *sg_state, struct buf *bp)
+{
+ struct scsi_pkt *pkt = sg_state->sgen_cmdpkt;
+ struct scsi_status *status = (struct scsi_status *)pkt->pkt_scbp;
+ struct uscsi_cmd *ucmd =
+ (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
+
+ if (status->sts_busy) {
+ sgen_log(sg_state, SGEN_DIAG1,
+ "sgen_check_error: target is busy");
+ return (COMMAND_DONE_ERROR);
+ }
+
+ /*
+ * pkt_resid will reflect, at this point, a residual of how many bytes
+ * were not transferred; a non-zero pkt_resid is an error.
+ */
+ if (pkt->pkt_resid) {
+ bp->b_resid += pkt->pkt_resid;
+ }
+
+ if (status->sts_chk) {
+ if (ucmd->uscsi_flags & USCSI_RQENABLE) {
+ if (sg_state->sgen_arq_enabled) {
+ sgen_log(sg_state, SGEN_DIAG1,
+ "sgen_check_error: strange: target "
+ "indicates CHECK CONDITION with auto-sense "
+ "enabled.");
+ }
+ sgen_log(sg_state, SGEN_DIAG2, "sgen_check_error: "
+ "target ready for sense fetch");
+ return (FETCH_SENSE);
+ } else {
+ sgen_log(sg_state, SGEN_DIAG2, "sgen_check_error: "
+ "target indicates CHECK CONDITION");
+ }
+ }
+
+ return (COMMAND_DONE);
+}
+
+/*
+ * sgen_tur()
+ * test if a target is ready to operate by sending it a TUR command.
+ */
+static int
+sgen_tur(dev_t dev)
+{
+ char cmdblk[CDB_GROUP0];
+ struct uscsi_cmd scmd;
+
+ bzero(&scmd, sizeof (scmd));
+ scmd.uscsi_bufaddr = 0;
+ scmd.uscsi_buflen = 0;
+ bzero(cmdblk, CDB_GROUP0);
+ cmdblk[0] = (char)SCMD_TEST_UNIT_READY;
+ scmd.uscsi_flags = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_WRITE;
+ scmd.uscsi_cdb = cmdblk;
+ scmd.uscsi_cdblen = CDB_GROUP0;
+
+ return (sgen_uscsi_cmd(dev, &scmd, UIO_SYSSPACE, UIO_SYSSPACE,
+ UIO_SYSSPACE));
+}
+
+/*
+ * sgen_diag_ok()
+ * given an sg_state and a desired diagnostic level, return true if
+ * it is acceptable to output a message.
+ */
+/*ARGSUSED*/
+static int
+sgen_diag_ok(sgen_state_t *sg_state, int level)
+{
+ int diag_lvl;
+
+ switch (level) {
+ case CE_WARN:
+ case CE_NOTE:
+ case CE_CONT:
+ case CE_PANIC:
+ return (1);
+ case SGEN_DIAG1:
+ case SGEN_DIAG2:
+ case SGEN_DIAG3:
+ if (sg_state) {
+ /*
+ * Check to see if user overrode the diagnostics level
+ * for this instance (either via SGEN_IOC_DIAG or via
+ * .conf file). If not, fall back to the global diag
+ * level.
+ */
+ if (sg_state->sgen_diag != -1)
+ diag_lvl = sg_state->sgen_diag;
+ else
+ diag_lvl = sgen_diag;
+ } else {
+ diag_lvl = sgen_diag;
+ }
+ if (((diag_lvl << 8) | CE_CONT) >= level) {
+ return (1);
+ } else {
+ return (0);
+ }
+ default:
+ return (1);
+ }
+}
+
+/*PRINTFLIKE3*/
+static void
+sgen_log(sgen_state_t *sg_state, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+
+ if (!sgen_diag_ok(sg_state, level))
+ return;
+
+ va_start(ap, fmt);
+ (void) vsnprintf(buf, sizeof (buf), fmt, ap);
+ va_end(ap);
+
+ switch (level) {
+ case CE_NOTE:
+ case CE_CONT:
+ case CE_WARN:
+ case CE_PANIC:
+ if (sg_state == (sgen_state_t *)NULL) {
+ cmn_err(level, "%s", buf);
+ } else {
+ scsi_log(sg_state->sgen_devinfo, "sgen", level,
+ "%s", buf);
+ }
+ break;
+ case SGEN_DIAG1:
+ case SGEN_DIAG2:
+ case SGEN_DIAG3:
+ default:
+ if (sg_state == (sgen_state_t *)NULL) {
+ scsi_log(NULL, "sgen", CE_CONT, "%s", buf);
+ } else {
+ scsi_log(sg_state->sgen_devinfo, "sgen", CE_CONT,
+ "%s", buf);
+ }
+ }
+}
+
+/*
+ * sgen_dump_cdb()
+ * dump out the contents of a cdb. Take care that 'label' is not too
+ * large, or 'buf' could overflow.
+ */
+static void
+sgen_dump_cdb(sgen_state_t *sg_state, const char *label,
+ union scsi_cdb *cdb, int cdblen)
+{
+ static char hex[] = "0123456789abcdef";
+ char *buf, *p;
+ size_t nbytes;
+ int i;
+ uchar_t *cdbp = (uchar_t *)cdb;
+
+ /*
+ * fastpath-- if we're not able to print out, don't do all of this
+ * extra work.
+ */
+ if (!sgen_diag_ok(sg_state, SGEN_DIAG3))
+ return;
+
+ /*
+ * 3 characters for each byte (because of the ' '), plus the size of
+ * the label, plus the trailing ']' and the null character.
+ */
+ nbytes = 3 * cdblen + strlen(label) + strlen(" CDB = [") + 2;
+ buf = kmem_alloc(nbytes, KM_SLEEP);
+ (void) sprintf(buf, "%s CDB = [", label);
+ p = &buf[strlen(buf)];
+ for (i = 0; i < cdblen; i++, cdbp++) {
+ if (i > 0)
+ *p++ = ' ';
+ *p++ = hex[(*cdbp >> 4) & 0x0f];
+ *p++ = hex[*cdbp & 0x0f];
+ }
+ *p++ = ']';
+ *p = 0;
+ sgen_log(sg_state, SGEN_DIAG3, buf);
+ kmem_free(buf, nbytes);
+}
+
+static void
+sgen_dump_sense(sgen_state_t *sg_state, size_t rqlen, uchar_t *rqbuf)
+{
+ static char hex[] = "0123456789abcdef";
+ char *buf, *p;
+ size_t nbytes;
+ int i;
+
+ /*
+ * fastpath-- if we're not able to print out, don't do all of this
+ * extra work.
+ */
+ if (!sgen_diag_ok(sg_state, SGEN_DIAG3))
+ return;
+
+ /*
+ * 3 characters for each byte (because of the ' '), plus the size of
+ * the label, plus the trailing ']' and the null character.
+ */
+ nbytes = 3 * rqlen + strlen(" SENSE = [") + 2;
+ buf = kmem_alloc(nbytes, KM_SLEEP);
+ (void) sprintf(buf, "SENSE = [");
+ p = &buf[strlen(buf)];
+ for (i = 0; i < rqlen; i++, rqbuf++) {
+ if (i > 0)
+ *p++ = ' ';
+ *p++ = hex[(*rqbuf >> 4) & 0x0f];
+ *p++ = hex[*rqbuf & 0x0f];
+ }
+ *p++ = ']';
+ *p = 0;
+ sgen_log(sg_state, SGEN_DIAG3, buf);
+ kmem_free(buf, nbytes);
+}
diff --git a/usr/src/uts/common/io/scsi/targets/sgen.conf b/usr/src/uts/common/io/scsi/targets/sgen.conf
new file mode 100644
index 0000000000..8f6a886e0b
--- /dev/null
+++ b/usr/src/uts/common/io/scsi/targets/sgen.conf
@@ -0,0 +1,108 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 1999 by Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Portions Copyright (c) Siemens 1999
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# WARNING: enabling this driver may impact the security and data integrity of
+# devices on your system. Please refer to sgen(7d) for details.
+
+#
+# sgen may be configured to bind to SCSI devices exporting a particular device
+# type, using the device-type-config-list, which is a ',' delimited list of
+# strings.
+#
+#device-type-config-list=
+# "direct" (type 0x00)
+# "sequential" (type 0x01)
+# "printer" (type 0x02)
+# "processor" (type 0x03)
+# "worm" (type 0x04)
+# "rodirect" (type 0x05)
+# "scanner" (type 0x06)
+# "optical" (type 0x07)
+# "changer" (type 0x08)
+# "comm" (type 0x09)
+# "prepress1" (type 0x0A)
+# "prepress2" (type 0x0B)
+# "array_ctrl" (type 0x0C)
+# "ses" (type 0x0D)
+# "rbc" (type 0x0E)
+# "ocrw" (type 0x0F)
+# "bridge" (type 0x10)
+# "type_0x<typenum>" (types 0x11-0x1e are undefined by SCSI-3)
+# "type_unknown" (type 0x1f)
+
+#
+# In addition to binding to device types, sgen can be configured to bind to one
+# or more particular devices. The latter is accomplished by specifying the
+# Vendor and Product IDs returned by the device in response to the SCSI INQUIRY
+# command. This is accomplished by specifying pairs of Vendor ID and Product ID
+# strings in the inquiry-config-list property, below. "*" may be substituted
+# for the vendor ID as a wildcard. See sgen(7D) for details and extended usage
+# examples.
+
+#
+# USAGE EXAMPLE
+#
+# In this example, sgen is configured to bind to all scanner and ocrw devices in
+# the system, as well as the UltraToast 4000 from ACME, and the PowerToast
+# series of devices, regardless of vendor.
+#
+#device-type-config-list="scanner", "ocrw";
+#
+#inquiry-config-list= "ACME", "UltraToast 4000",
+# "*", "PowerToast";
+
+
+#
+# After configuring the device-type-config-list and/or the inquiry-config-list,
+# the administrator must uncomment those target/lun pairs at which there are
+# devices for sgen to control. If it is expected that devices controlled by
+# sgen will be hotplugged or added into the system later, it is recommended
+# that all of the following lines be uncommented.
+
+#name="sgen" class="scsi" target=0 lun=0;
+#name="sgen" class="scsi" target=1 lun=0;
+#name="sgen" class="scsi" target=2 lun=0;
+#name="sgen" class="scsi" target=3 lun=0;
+#name="sgen" class="scsi" target=4 lun=0;
+#name="sgen" class="scsi" target=5 lun=0;
+#name="sgen" class="scsi" target=6 lun=0;
+#name="sgen" class="scsi" target=7 lun=0;
+#name="sgen" class="scsi" target=8 lun=0;
+#name="sgen" class="scsi" target=9 lun=0;
+#name="sgen" class="scsi" target=10 lun=0;
+#name="sgen" class="scsi" target=11 lun=0;
+#name="sgen" class="scsi" target=12 lun=0;
+#name="sgen" class="scsi" target=13 lun=0;
+#name="sgen" class="scsi" target=14 lun=0;
+#name="sgen" class="scsi" target=15 lun=0;
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 60f5eef05a..a784680fe6 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -768,6 +768,7 @@ SCSIIMPLHDRS= \
SCSITARGETSHDRS= \
ses.h \
sesio.h \
+ sgendef.h \
stdef.h \
sddef.h
diff --git a/usr/src/uts/common/sys/scsi/targets/sgendef.h b/usr/src/uts/common/sys/scsi/targets/sgendef.h
new file mode 100644
index 0000000000..23e29c0265
--- /dev/null
+++ b/usr/src/uts/common/sys/scsi/targets/sgendef.h
@@ -0,0 +1,170 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2001 by Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright Siemens 1999
+ * All rights reserved.
+ */
+
+#ifndef _SYS_SCSI_TARGETS_SGENDEF_H
+#define _SYS_SCSI_TARGETS_SGENDEF_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/kstat.h>
+#include <sys/condvar.h>
+#include <sys/mutex.h>
+#include <sys/buf.h>
+#include <sys/scsi/scsi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SGEN_IOC (('S' << 16) | ('G' << 8))
+#define SGEN_IOC_READY (SGEN_IOC | 0x01)
+#define SGEN_IOC_DIAG (SGEN_IOC | 0x02)
+
+#if defined(_KERNEL)
+
+#define SGEN_DIAG1 ((1 << 8) | CE_CONT)
+#define SGEN_DIAG2 ((2 << 8) | CE_CONT)
+#define SGEN_DIAG3 ((3 << 8) | CE_CONT)
+
+struct sgen_errstats {
+ kstat_named_t sgen_trans_err; /* error trying to transport pkt */
+ kstat_named_t sgen_restart; /* command restart attempted */
+ kstat_named_t sgen_incmp_err; /* command failed to complete */
+ kstat_named_t sgen_autosen_rcv; /* autosense occurred */
+ kstat_named_t sgen_autosen_bad; /* autosense data looks malformed */
+ kstat_named_t sgen_sense_rcv; /* sense fetch occurred */
+ kstat_named_t sgen_sense_bad; /* sense data looks malformed */
+ kstat_named_t sgen_recov_err; /* sense key is KEY_RECOVERABLE */
+ kstat_named_t sgen_nosen_err; /* sense key is KEY_NO_SENSE */
+ kstat_named_t sgen_unrecov_err; /* sense key indicates other err */
+};
+
+typedef struct sgen_state {
+ struct scsi_device *sgen_scsidev; /* pointer to scsi_device */
+ struct uscsi_cmd *sgen_ucmd; /* uscsi command struct */
+ struct buf *sgen_cmdbuf; /* xfer buffer */
+ struct scsi_pkt *sgen_cmdpkt; /* scsi packet for command */
+ kcondvar_t sgen_cmdbuf_cv; /* cv for cmdbuf */
+ int sgen_flags; /* see SGEN_FL_* */
+ struct scsi_pkt *sgen_rqspkt; /* request sense packet */
+ struct buf *sgen_rqsbuf; /* request sense xfer buffer */
+ char *sgen_rqs_sen; /* sense buffer */
+ int sgen_arq_enabled; /* auto request sense enabled */
+ int sgen_diag; /* diagnostic output level */
+ timeout_id_t sgen_restart_timeid; /* timeout for sgen_restart */
+ kstat_t *sgen_kstats; /* for error statistics */
+} sgen_state_t;
+
+/*
+ * Convenience accessors for sgen_state_t.
+ */
+#define sgen_mutex sgen_scsidev->sd_mutex
+#define sgen_devinfo sgen_scsidev->sd_dev
+#define sgen_scsiaddr sgen_scsidev->sd_address
+#define sgen_sense sgen_scsidev->sd_sense
+
+/*
+ * sgen_flags accessors/mutators
+ */
+#define SGEN_FL_OPEN 0x01 /* instance is open */
+#define SGEN_FL_SUSP 0x02 /* instance suspended */
+#define SGEN_FL_BUSY 0x04 /* command buffer busy */
+
+#define SGEN_SET_OPEN(stp) \
+ (((sgen_state_t *)(stp))->sgen_flags |= SGEN_FL_OPEN)
+#define SGEN_CLR_OPEN(stp) \
+ (((sgen_state_t *)(stp))->sgen_flags &= ~SGEN_FL_OPEN)
+#define SGEN_IS_OPEN(stp) \
+ ((((sgen_state_t *)(stp))->sgen_flags & SGEN_FL_OPEN) == SGEN_FL_OPEN)
+
+#define SGEN_SET_SUSP(stp) \
+ (((sgen_state_t *)(stp))->sgen_flags |= SGEN_FL_SUSP)
+#define SGEN_CLR_SUSP(stp) \
+ (((sgen_state_t *)(stp))->sgen_flags &= ~SGEN_FL_SUSP)
+#define SGEN_IS_SUSP(stp) \
+ ((((sgen_state_t *)(stp))->sgen_flags & SGEN_FL_SUSP) == SGEN_FL_SUSP)
+
+#define SGEN_SET_BUSY(stp) \
+ (((sgen_state_t *)(stp))->sgen_flags |= SGEN_FL_BUSY)
+#define SGEN_CLR_BUSY(stp) \
+ (((sgen_state_t *)(stp))->sgen_flags &= ~SGEN_FL_BUSY)
+#define SGEN_IS_BUSY(stp) \
+ ((((sgen_state_t *)(stp))->sgen_flags & SGEN_FL_BUSY) == SGEN_FL_BUSY)
+
+/*
+ * These structures form the driver's database of binding information.
+ * Inquiry strings and device types from the inquiry-config-list and
+ * device-type-config-list properties are stored.
+ */
+typedef struct sgen_inq_node {
+ char *node_vendor; /* up to 8 character vendor */
+ char *node_product; /* up to 16 character product */
+ struct sgen_inq_node *node_next;
+} sgen_inq_node_t;
+
+typedef struct sgen_type_node {
+ uchar_t node_type; /* SCSI device type */
+ struct sgen_type_node *node_next;
+} sgen_type_node_t;
+
+struct sgen_binddb {
+ int sdb_init; /* has this been initialized? */
+ kmutex_t sdb_lock; /* protects this structure */
+ sgen_inq_node_t *sdb_inq_nodes; /* inquiry binding nodes */
+ sgen_type_node_t *sdb_type_nodes; /* dev-type binding nodes */
+};
+
+#define SGEN_ESTIMATED_NUM_DEVS 4 /* for soft-state allocation */
+
+/*
+ * Time to wait before a retry for commands returning Busy Status
+ */
+#define SGEN_BSY_TIMEOUT (drv_usectohz(5 * 1000000))
+#define SGEN_IO_TIME 60 /* seconds */
+
+/*
+ * sgen_callback action codes
+ */
+#define COMMAND_DONE 0 /* command completed, biodone it */
+#define COMMAND_DONE_ERROR 1 /* command completed, indicate error */
+#define FETCH_SENSE 2 /* CHECK CONDITION, so initiate sense */
+ /* fetch */
+
+#define SET_BP_ERROR(bp, err) bioerror(bp, err);
+
+#endif /* defined(_KERNEL) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_SCSI_TARGETS_SGENDEF_H */
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index b243229c6c..cc4c7b8c9c 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -272,6 +272,7 @@ DRV_KMODS += sad
DRV_KMODS += sctp
DRV_KMODS += sctp6
DRV_KMODS += sd
+DRV_KMODS += sgen
DRV_KMODS += si3124
DRV_KMODS += spdsock
DRV_KMODS += smbios
@@ -306,7 +307,6 @@ $(CLOSED_BUILD)CLOSED_DRV_KMODS += glm
$(CLOSED_BUILD)CLOSED_DRV_KMODS += llc2
$(CLOSED_BUILD)CLOSED_DRV_KMODS += mpt
$(CLOSED_BUILD)CLOSED_DRV_KMODS += marvell88sx
-$(CLOSED_BUILD)CLOSED_DRV_KMODS += sgen
#
# Common code drivers
diff --git a/usr/src/uts/intel/sgen/Makefile b/usr/src/uts/intel/sgen/Makefile
new file mode 100644
index 0000000000..7c25cab5f1
--- /dev/null
+++ b/usr/src/uts/intel/sgen/Makefile
@@ -0,0 +1,89 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the sgen driver.
+# intel architecture dependent
+#
+
+#
+# Paths to the base of the uts directory trees
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = sgen
+OBJECTS = $(SGEN_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(SGEN_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io/scsi/targets
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets.
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Note dependancy on misc/scsi.
+#
+LDFLAGS += -dy -N"misc/scsi"
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared
index b7353edd3c..c7375d31bd 100644
--- a/usr/src/uts/sparc/Makefile.sparc.shared
+++ b/usr/src/uts/sparc/Makefile.sparc.shared
@@ -276,6 +276,7 @@ DRV_KMODS += pci_pci px_pci pxb_bcm pcie
DRV_KMODS += i8042 kb8042 mouse8042
DRV_KMODS += fcode
DRV_KMODS += socal
+DRV_KMODS += sgen
$(CLOSED_BUILD)CLOSED_DRV_KMODS += audioens
$(CLOSED_BUILD)CLOSED_DRV_KMODS += audioixp
@@ -284,7 +285,6 @@ $(CLOSED_BUILD)CLOSED_DRV_KMODS += chxge
$(CLOSED_BUILD)CLOSED_DRV_KMODS += dad
$(CLOSED_BUILD)CLOSED_DRV_KMODS += ifp
$(CLOSED_BUILD)CLOSED_DRV_KMODS += scsi_vhci
-$(CLOSED_BUILD)CLOSED_DRV_KMODS += sgen
$(CLOSED_BUILD)CLOSED_DRV_KMODS += uata
$(CLOSED_BUILD)CLOSED_DRV_KMODS += usbser_edge
diff --git a/usr/src/uts/sparc/sgen/Makefile b/usr/src/uts/sparc/sgen/Makefile
new file mode 100644
index 0000000000..c41632c9b2
--- /dev/null
+++ b/usr/src/uts/sparc/sgen/Makefile
@@ -0,0 +1,94 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the sgen driver.
+# sparc architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = sgen
+OBJECTS = $(SGEN_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(SGEN_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io/scsi/targets
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+# Define targets.
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Note dependancy on misc/scsi.
+#
+LDFLAGS += -dy -N"misc/scsi"
+
+#
+# Overrides.
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+lint: $(LINT_DEPS)
+
+modlintlib: $(MODLINTLIB_DEPS)
+
+clean.lint: $(CLEAN_LINT_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ