diff options
author | stevel <none@none> | 2006-08-08 15:34:50 -0700 |
---|---|---|
committer | stevel <none@none> | 2006-08-08 15:34:50 -0700 |
commit | 9a016c63ca347047a236dff12f0da83aac8981d1 (patch) | |
tree | 86f2711a20a663ed012b7d0bf789d5d253c3cd57 | |
parent | f1710550bd8341486e7494e781335ba875c9b12c (diff) | |
download | illumos-joyent-9a016c63ca347047a236dff12f0da83aac8981d1.tar.gz |
6455550 move sgen to usr/src
-rw-r--r-- | usr/src/uts/common/Makefile.files | 2 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/targets/sgen.c | 2332 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/targets/sgen.conf | 108 | ||||
-rw-r--r-- | usr/src/uts/common/sys/Makefile | 1 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/targets/sgendef.h | 170 | ||||
-rw-r--r-- | usr/src/uts/intel/Makefile.intel.shared | 2 | ||||
-rw-r--r-- | usr/src/uts/intel/sgen/Makefile | 89 | ||||
-rw-r--r-- | usr/src/uts/sparc/Makefile.sparc.shared | 2 | ||||
-rw-r--r-- | usr/src/uts/sparc/sgen/Makefile | 94 |
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 |