xref: /onnv-gate/usr/src/uts/common/io/scsi/targets/sgen.c (revision 7656:2621e50fdf4a)
12518Sstevel /*
22518Sstevel  * CDDL HEADER START
32518Sstevel  *
42518Sstevel  * The contents of this file are subject to the terms of the
52518Sstevel  * Common Development and Distribution License (the "License").
62518Sstevel  * You may not use this file except in compliance with the License.
72518Sstevel  *
82518Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92518Sstevel  * or http://www.opensolaris.org/os/licensing.
102518Sstevel  * See the License for the specific language governing permissions
112518Sstevel  * and limitations under the License.
122518Sstevel  *
132518Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
142518Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152518Sstevel  * If applicable, add the following below this CDDL HEADER, with the
162518Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
172518Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
182518Sstevel  *
192518Sstevel  * CDDL HEADER END
202518Sstevel  */
212518Sstevel 
222518Sstevel /*
236316Seschrock  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
242518Sstevel  * Use is subject to license terms.
252518Sstevel  */
262518Sstevel 
272518Sstevel /*
282518Sstevel  * Copyright Siemens 1999
292518Sstevel  * All rights reserved.
302518Sstevel  */
312518Sstevel 
322518Sstevel 
332518Sstevel /*
342518Sstevel  * sgen - SCSI generic device driver
352518Sstevel  *
362518Sstevel  * The sgen driver provides user programs access to SCSI devices that
372518Sstevel  * are not supported by other drivers by providing the USCSI(7I) interface.
382518Sstevel  */
392518Sstevel 
402518Sstevel #include <sys/modctl.h>
412518Sstevel #include <sys/file.h>
422518Sstevel #include <sys/scsi/scsi.h>
432518Sstevel #include <sys/scsi/targets/sgendef.h>
442518Sstevel 
457009Scth /* The name of the driver, established from the module name in _init. */
467009Scth static	char *sgen_label	= NULL;
477009Scth 
482518Sstevel #define	DDI_NT_SGEN		"ddi_generic:scsi"
492518Sstevel 
502518Sstevel static char *sgen_devtypes[] = {
512518Sstevel 	"direct",		/* 0x00 -- disks */
522518Sstevel 	"sequential",		/* 0x01 */
532518Sstevel 	"printer",		/* 0x02 */
542518Sstevel 	"processor",		/* 0x03 */
552518Sstevel 	"worm",			/* 0x04 */
562518Sstevel 	"rodirect",		/* 0x05 */
572518Sstevel 	"scanner",		/* 0x06 */
582518Sstevel 	"optical",		/* 0x07 */
592518Sstevel 	"changer",		/* 0x08 */
602518Sstevel 	"comm",			/* 0x09 */
612518Sstevel 	"prepress1",		/* 0x0a -- reserved for prepress (ASC IT8) */
622518Sstevel 	"prepress2",		/* 0x0b -- reserved for prepress (ASC IT8) */
632518Sstevel 	"array_ctrl",		/* 0x0c -- storage array */
642518Sstevel 	"ses",			/* 0x0d -- enclosure services */
652518Sstevel 	"rbc",			/* 0x0e -- simplified block */
662518Sstevel 	"ocrw",			/* 0x0f -- optical card read/write */
672518Sstevel 	"bridge",		/* 0x10 -- reserved for bridging expanders */
682518Sstevel 	"type_0x11",		/* 0x11 */
692518Sstevel 	"type_0x12",		/* 0x12 */
702518Sstevel 	"type_0x13",		/* 0x13 */
712518Sstevel 	"type_0x14",		/* 0x14 */
722518Sstevel 	"type_0x15",		/* 0x15 */
732518Sstevel 	"type_0x16",		/* 0x16 */
742518Sstevel 	"type_0x17",		/* 0x17 */
752518Sstevel 	"type_0x18",		/* 0x18 */
762518Sstevel 	"type_0x19",		/* 0x19 */
772518Sstevel 	"type_0x1a",		/* 0x1a */
782518Sstevel 	"type_0x1b",		/* 0x1b */
792518Sstevel 	"type_0x1c",		/* 0x1c */
802518Sstevel 	"type_0x1d",		/* 0x1d */
812518Sstevel 	"type_0x1e",		/* 0x1e */
822518Sstevel 	"type_unknown"		/* 0x1f is "no device type" or "unknown" */
832518Sstevel };
842518Sstevel 
852518Sstevel #define	SGEN_NDEVTYPES ((sizeof (sgen_devtypes) / sizeof (char *)))
862518Sstevel 
872518Sstevel #define	SGEN_INQSTRLEN 24
882518Sstevel #define	SGEN_VENDID_MAX 8
892518Sstevel #define	SGEN_PRODID_MAX 16
902518Sstevel 
912518Sstevel #define	FILL_SCSI1_LUN(devp, pkt) 					\
922518Sstevel 	if ((devp)->sd_inq->inq_ansi == 0x1) {				\
932518Sstevel 		int _lun;						\
942518Sstevel 		_lun = ddi_prop_get_int(DDI_DEV_T_ANY, (devp)->sd_dev,	\
952518Sstevel 		    DDI_PROP_DONTPASS, SCSI_ADDR_PROP_LUN, 0);		\
962518Sstevel 		if (_lun > 0) {						\
972518Sstevel 			((union scsi_cdb *)(pkt)->pkt_cdbp)->scc_lun =	\
982518Sstevel 			    _lun;					\
992518Sstevel 		}							\
1002518Sstevel 	}
1012518Sstevel 
1022518Sstevel #define	SGEN_DO_ERRSTATS(sg_state, x)  \
1032518Sstevel 	if (sg_state->sgen_kstats) { \
1042518Sstevel 		struct sgen_errstats *sp; \
1052518Sstevel 		sp = (struct sgen_errstats *)sg_state->sgen_kstats->ks_data; \
1062518Sstevel 		sp->x.value.ui32++; \
1072518Sstevel 	}
1082518Sstevel 
1092518Sstevel #define	SCBP_C(pkt)	((*(pkt)->pkt_scbp) & STATUS_MASK)
1102518Sstevel 
1112518Sstevel /*
1122518Sstevel  * Standard entrypoints
1132518Sstevel  */
1142518Sstevel static int sgen_attach(dev_info_t *, ddi_attach_cmd_t);
1152518Sstevel static int sgen_detach(dev_info_t *, ddi_detach_cmd_t);
1162518Sstevel static int sgen_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1172518Sstevel static int sgen_probe(dev_info_t *);
1182518Sstevel static int sgen_open(dev_t *, int, int, cred_t *);
1192518Sstevel static int sgen_close(dev_t, int, int, cred_t *);
1202518Sstevel static int sgen_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1212518Sstevel 
1222518Sstevel /*
1232518Sstevel  * Configuration routines
1242518Sstevel  */
1252518Sstevel static int sgen_do_attach(dev_info_t *);
1262518Sstevel static int sgen_setup_sense(sgen_state_t *);
1272518Sstevel static void sgen_create_errstats(sgen_state_t *, int);
1282518Sstevel static int sgen_do_suspend(dev_info_t *);
1292518Sstevel static int sgen_do_detach(dev_info_t *);
1302518Sstevel static void sgen_setup_binddb(dev_info_t *);
1312518Sstevel static void sgen_cleanup_binddb();
1322518Sstevel 
1332518Sstevel /*
1342518Sstevel  * Packet transport routines
1352518Sstevel  */
1363368Slh195018 static int  sgen_uscsi_cmd(dev_t, struct uscsi_cmd *, int);
1372518Sstevel static int sgen_start(struct buf *);
1386316Seschrock static int sgen_hold_cmdbuf(sgen_state_t *);
1392518Sstevel static void sgen_rele_cmdbuf(sgen_state_t *);
1402518Sstevel static int sgen_make_uscsi_cmd(sgen_state_t *, struct buf *);
1412518Sstevel static void sgen_restart(void *);
1422518Sstevel static void sgen_callback(struct scsi_pkt *);
1432518Sstevel static int sgen_handle_autosense(sgen_state_t *, struct scsi_pkt *);
1442518Sstevel static int sgen_handle_sense(sgen_state_t *);
1452518Sstevel static int sgen_handle_incomplete(sgen_state_t *, struct scsi_pkt *);
1462518Sstevel static int sgen_check_error(sgen_state_t *, struct buf *);
1476640Scth static int sgen_initiate_sense(sgen_state_t *, int);
1482518Sstevel static int sgen_scsi_transport(struct scsi_pkt *);
1492518Sstevel static int sgen_tur(dev_t);
1502518Sstevel 
1512518Sstevel /*
1522518Sstevel  * Logging/debugging routines
1532518Sstevel  */
1542518Sstevel static void sgen_log(sgen_state_t  *, int,  const char *, ...);
1552518Sstevel static int sgen_diag_ok(sgen_state_t *, int);
1562518Sstevel static void sgen_dump_cdb(sgen_state_t *, const char *, union scsi_cdb *, int);
1572518Sstevel static void sgen_dump_sense(sgen_state_t *, size_t, uchar_t *);
1582518Sstevel 
1592518Sstevel int sgen_diag = 0;
1602518Sstevel int sgen_sporadic_failures = 0;
1612518Sstevel int sgen_force_manual_sense = 0;
1622518Sstevel struct sgen_binddb sgen_binddb;
1632518Sstevel 
1642518Sstevel static struct cb_ops sgen_cb_ops = {
1652518Sstevel 	sgen_open,			/* open */
1662518Sstevel 	sgen_close,			/* close */
1672518Sstevel 	nodev,				/* strategy */
1682518Sstevel 	nodev,				/* print */
1692518Sstevel 	nodev,				/* dump */
1702518Sstevel 	nodev,				/* read */
1712518Sstevel 	nodev,				/* write */
1722518Sstevel 	sgen_ioctl,			/* ioctl */
1732518Sstevel 	nodev,				/* devmap */
1742518Sstevel 	nodev,				/* mmap */
1752518Sstevel 	nodev,				/* segmap */
1762518Sstevel 	nochpoll,			/* poll */
1772518Sstevel 	ddi_prop_op,			/* cb_prop_op */
1782518Sstevel 	0,				/* streamtab  */
1792518Sstevel 	D_MP | D_NEW | D_HOTPLUG	/* Driver compatibility flag */
1802518Sstevel };
1812518Sstevel 
1822518Sstevel static struct dev_ops sgen_dev_ops = {
1832518Sstevel 	DEVO_REV,		/* devo_rev, */
1842518Sstevel 	0,			/* refcnt  */
1852518Sstevel 	sgen_getinfo,		/* info */
1862518Sstevel 	nodev,			/* identify */
1872518Sstevel 	sgen_probe,		/* probe */
1882518Sstevel 	sgen_attach,		/* attach */
1892518Sstevel 	sgen_detach,		/* detach */
1902518Sstevel 	nodev,			/* reset */
1912518Sstevel 	&sgen_cb_ops,		/* driver operations */
1922518Sstevel 	(struct bus_ops *)0,	/* bus operations */
193*7656SSherry.Moore@Sun.COM 	NULL,			/* power */
194*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
1952518Sstevel };
1962518Sstevel 
1972518Sstevel static void *sgen_soft_state = NULL;
1982518Sstevel 
1992518Sstevel static struct modldrv modldrv = {
200*7656SSherry.Moore@Sun.COM 	&mod_driverops, "SCSI generic driver", &sgen_dev_ops
2012518Sstevel };
2022518Sstevel 
2032518Sstevel static struct modlinkage modlinkage = {
2042518Sstevel 	MODREV_1, &modldrv, NULL
2052518Sstevel };
2062518Sstevel 
2072518Sstevel int
_init(void)2082518Sstevel _init(void)
2092518Sstevel {
2102518Sstevel 	int err;
2112518Sstevel 
2127009Scth 	/* establish driver name from module name */
2137009Scth 	sgen_label = (char *)mod_modname(&modlinkage);
2147009Scth 
2152518Sstevel 	sgen_log(NULL, SGEN_DIAG2, "in sgen_init()");
2162518Sstevel 	if ((err = ddi_soft_state_init(&sgen_soft_state,
2175597Slh195018 	    sizeof (sgen_state_t), SGEN_ESTIMATED_NUM_DEVS)) != 0) {
2182518Sstevel 		goto done;
2192518Sstevel 	}
2202518Sstevel 
2212518Sstevel 	if ((err = mod_install(&modlinkage)) != 0) {
2222518Sstevel 		ddi_soft_state_fini(&sgen_soft_state);
2232518Sstevel 		goto done;
2242518Sstevel 	}
2252518Sstevel 
2262518Sstevel done:
2272518Sstevel 	sgen_log(NULL, SGEN_DIAG2, "%s sgen_init()", err ? "failed" : "done");
2282518Sstevel 	return (err);
2292518Sstevel }
2302518Sstevel 
2312518Sstevel int
_fini(void)2322518Sstevel _fini(void)
2332518Sstevel {
2342518Sstevel 	int err;
2352518Sstevel 	sgen_log(NULL, SGEN_DIAG2, "in sgen_fini()");
2362518Sstevel 
2372518Sstevel 	if ((err = mod_remove(&modlinkage)) != 0) {
2382518Sstevel 		goto done;
2392518Sstevel 	}
2402518Sstevel 
2412518Sstevel 	ddi_soft_state_fini(&sgen_soft_state);
2422518Sstevel 	sgen_cleanup_binddb();
2432518Sstevel 
2442518Sstevel done:
2452518Sstevel 	sgen_log(NULL, SGEN_DIAG2, "%s sgen_fini()", err ? "failed" : "done");
2462518Sstevel 	return (err);
2472518Sstevel }
2482518Sstevel 
2492518Sstevel int
_info(struct modinfo * modinfop)2502518Sstevel _info(struct modinfo *modinfop)
2512518Sstevel {
2522518Sstevel 	return (mod_info(&modlinkage, modinfop));
2532518Sstevel }
2542518Sstevel 
2552518Sstevel /*
2562518Sstevel  * sgen_typename()
2572518Sstevel  * 	return a device type's name by looking it up in the sgen_devtypes table.
2582518Sstevel  */
2592518Sstevel static char *
sgen_typename(uchar_t typeno)2602518Sstevel sgen_typename(uchar_t typeno)
2612518Sstevel {
2622518Sstevel 	if (typeno >= SGEN_NDEVTYPES)
2632518Sstevel 		return ("type_unknown");
2642518Sstevel 	return (sgen_devtypes[typeno]);
2652518Sstevel }
2662518Sstevel 
2672518Sstevel /*
2682518Sstevel  * sgen_typenum()
2692518Sstevel  * 	return a device type's number by looking it up in the sgen_devtypes
2702518Sstevel  * 	table.
2712518Sstevel  */
2722518Sstevel static int
sgen_typenum(const char * typename,uchar_t * typenum)2732518Sstevel sgen_typenum(const char *typename, uchar_t *typenum)
2742518Sstevel {
2752518Sstevel 	int i;
2762518Sstevel 	for (i = 0; i < SGEN_NDEVTYPES; i++) {
2772518Sstevel 		if (strcasecmp(sgen_devtypes[i], typename) == 0) {
2782518Sstevel 			*typenum = (uchar_t)i;
2792518Sstevel 			return (0);
2802518Sstevel 		}
2812518Sstevel 	}
2822518Sstevel 	return (-1);
2832518Sstevel }
2842518Sstevel 
2852518Sstevel /*
2862518Sstevel  * sgen_setup_binddb()
2872518Sstevel  * 	initialize a data structure which stores all of the information about
2882518Sstevel  * 	which devices and device types the driver should bind to.
2892518Sstevel  */
2902518Sstevel static void
sgen_setup_binddb(dev_info_t * dip)2912518Sstevel sgen_setup_binddb(dev_info_t *dip)
2922518Sstevel {
2932518Sstevel 	char **strs = NULL, *cp, *pcp, *vcp;
2942518Sstevel 	uint_t nelems, pcplen, vcplen, idx;
2952518Sstevel 
2962518Sstevel 	ASSERT(sgen_binddb.sdb_init == 0);
2972518Sstevel 	ASSERT(MUTEX_HELD(&sgen_binddb.sdb_lock));
2982518Sstevel 
2992518Sstevel 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3002518Sstevel 	    "device-type-config-list", &strs, &nelems) == DDI_PROP_SUCCESS) {
3012518Sstevel 		/*
3022518Sstevel 		 * for each device type specifier make a copy and put it into a
3032518Sstevel 		 * node in the binddb.
3042518Sstevel 		 */
3052518Sstevel 		for (idx = 0; idx < nelems; idx++) {
3062518Sstevel 			sgen_type_node_t *nodep;
3072518Sstevel 			uchar_t devtype;
3082518Sstevel 			cp = strs[idx];
3092518Sstevel 			if (sgen_typenum(cp, &devtype) != 0) {
3102518Sstevel 				sgen_log(NULL, CE_WARN,
3112518Sstevel 				    "unknown device type '%s', "
3122518Sstevel 				    "device unit-address @%s",
3132518Sstevel 				    cp, ddi_get_name_addr(dip));
3142518Sstevel 				continue;
3152518Sstevel 			}
3162518Sstevel 			nodep = kmem_zalloc(sizeof (sgen_type_node_t),
3172518Sstevel 			    KM_SLEEP);
3182518Sstevel 			nodep->node_type = devtype;
3192518Sstevel 			nodep->node_next = sgen_binddb.sdb_type_nodes;
3202518Sstevel 			sgen_binddb.sdb_type_nodes = nodep;
3212518Sstevel 
3222518Sstevel 			sgen_log(NULL, SGEN_DIAG2, "found device type "
3232518Sstevel 			    "'%s' in device-type-config-list, "
3242518Sstevel 			    "device unit-address @%s",
3252518Sstevel 			    cp, ddi_get_name_addr(dip));
3262518Sstevel 		}
3272518Sstevel 		ddi_prop_free(strs);
3282518Sstevel 	}
3292518Sstevel 
3302518Sstevel 	/*
3312518Sstevel 	 * for each Vendor/Product inquiry pair, build a node and put it
3322518Sstevel 	 * into the the binddb.
3332518Sstevel 	 */
3342518Sstevel 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3352518Sstevel 	    "inquiry-config-list", &strs, &nelems) == DDI_PROP_SUCCESS) {
3362518Sstevel 
3372518Sstevel 		if (nelems % 2 == 1) {
3382518Sstevel 			sgen_log(NULL, CE_WARN, "inquiry-config-list must "
3392518Sstevel 			    "contain Vendor/Product pairs, "
3402518Sstevel 			    "device unit-address @%s",
3412518Sstevel 			    ddi_get_name_addr(dip));
3422518Sstevel 			nelems--;
3432518Sstevel 		}
3442518Sstevel 		for (idx = 0; idx < nelems; idx += 2) {
3452518Sstevel 			sgen_inq_node_t *nodep;
3462518Sstevel 			/*
3472518Sstevel 			 * Grab vendor and product ID.
3482518Sstevel 			 */
3492518Sstevel 			vcp = strs[idx];
3502518Sstevel 			vcplen = strlen(vcp);
3512518Sstevel 			if (vcplen == 0 || vcplen > SGEN_VENDID_MAX) {
3522518Sstevel 				sgen_log(NULL, CE_WARN,
3532518Sstevel 				    "Invalid vendor ID '%s', "
3542518Sstevel 				    "device unit-address @%s",
3552518Sstevel 				    vcp, ddi_get_name_addr(dip));
3562518Sstevel 				continue;
3572518Sstevel 			}
3582518Sstevel 
3592518Sstevel 			pcp = strs[idx + 1];
3602518Sstevel 			pcplen = strlen(pcp);
3612518Sstevel 			if (pcplen == 0 || pcplen > SGEN_PRODID_MAX) {
3622518Sstevel 				sgen_log(NULL, CE_WARN,
3632518Sstevel 				    "Invalid product ID '%s', "
3642518Sstevel 				    "device unit-address @%s",
3652518Sstevel 				    pcp, ddi_get_name_addr(dip));
3662518Sstevel 				continue;
3672518Sstevel 			}
3682518Sstevel 
3692518Sstevel 			nodep = kmem_zalloc(sizeof (sgen_inq_node_t),
3702518Sstevel 			    KM_SLEEP);
3712518Sstevel 			nodep->node_vendor = kmem_alloc(vcplen + 1, KM_SLEEP);
3722518Sstevel 			(void) strcpy(nodep->node_vendor, vcp);
3732518Sstevel 			nodep->node_product = kmem_alloc(pcplen + 1, KM_SLEEP);
3742518Sstevel 			(void) strcpy(nodep->node_product, pcp);
3752518Sstevel 
3762518Sstevel 			nodep->node_next = sgen_binddb.sdb_inq_nodes;
3772518Sstevel 			sgen_binddb.sdb_inq_nodes = nodep;
3782518Sstevel 
3792518Sstevel 			sgen_log(NULL, SGEN_DIAG2, "found inquiry string "
3802518Sstevel 			    "'%s' '%s' in device-type-config-list, "
3812518Sstevel 			    "device unit-address @%s",
3822518Sstevel 			    nodep->node_vendor, nodep->node_product,
3832518Sstevel 			    ddi_get_name_addr(dip));
3842518Sstevel 		}
3852518Sstevel 		ddi_prop_free(strs);
3862518Sstevel 	}
3872518Sstevel 
3882518Sstevel 	sgen_binddb.sdb_init = 1;
3892518Sstevel }
3902518Sstevel 
3912518Sstevel /*
3922518Sstevel  * sgen_cleanup_binddb()
3932518Sstevel  * 	deallocate data structures for binding database.
3942518Sstevel  */
3952518Sstevel static void
sgen_cleanup_binddb()3962518Sstevel sgen_cleanup_binddb()
3972518Sstevel {
3982518Sstevel 	sgen_inq_node_t *inqp, *inqnextp;
3992518Sstevel 	sgen_type_node_t *typep, *typenextp;
4002518Sstevel 
4012518Sstevel 	mutex_enter(&sgen_binddb.sdb_lock);
4022518Sstevel 	if (sgen_binddb.sdb_init == 0) {
4032518Sstevel 		mutex_exit(&sgen_binddb.sdb_lock);
4042518Sstevel 		return;
4052518Sstevel 	}
4062518Sstevel 
4072518Sstevel 	for (inqp = sgen_binddb.sdb_inq_nodes; inqp != NULL; inqp = inqnextp) {
4082518Sstevel 		inqnextp = inqp->node_next;
4092518Sstevel 		ASSERT(inqp->node_vendor && inqp->node_product);
4102518Sstevel 		kmem_free(inqp->node_vendor,
4112518Sstevel 		    strlen(inqp->node_vendor) + 1);
4122518Sstevel 		kmem_free(inqp->node_product,
4132518Sstevel 		    strlen(inqp->node_product) + 1);
4142518Sstevel 		kmem_free(inqp, sizeof (sgen_inq_node_t));
4152518Sstevel 	}
4162518Sstevel 
4172518Sstevel 	for (typep = sgen_binddb.sdb_type_nodes; typep != NULL;
4182518Sstevel 	    typep = typenextp) {
4192518Sstevel 		typenextp = typep->node_next;
4202518Sstevel 		kmem_free(typep, sizeof (sgen_type_node_t));
4212518Sstevel 	}
4222518Sstevel 	mutex_exit(&sgen_binddb.sdb_lock);
4232518Sstevel }
4242518Sstevel 
4252518Sstevel /*
4262518Sstevel  * sgen_bind_byinq()
4272518Sstevel  * 	lookup a device in the binding database by its inquiry data.
4282518Sstevel  */
4292518Sstevel static int
sgen_bind_byinq(dev_info_t * dip)4302518Sstevel sgen_bind_byinq(dev_info_t *dip)
4312518Sstevel {
4322518Sstevel 	sgen_inq_node_t *nodep;
4332518Sstevel 	char vend_str[SGEN_VENDID_MAX+1];
4342518Sstevel 	char prod_str[SGEN_PRODID_MAX+1];
4352518Sstevel 	struct scsi_device *scsidevp;
4362518Sstevel 
4372518Sstevel 	scsidevp = ddi_get_driver_private(dip);
4382518Sstevel 
4392518Sstevel 	/*
4402518Sstevel 	 * inq_vid and inq_pid are laid out by the protocol in order in the
4412518Sstevel 	 * inquiry structure, and are not delimited by \0.
4422518Sstevel 	 */
4432518Sstevel 	bcopy(scsidevp->sd_inq->inq_vid, vend_str, SGEN_VENDID_MAX);
4442518Sstevel 	vend_str[SGEN_VENDID_MAX] = '\0';
4452518Sstevel 	bcopy(scsidevp->sd_inq->inq_pid, prod_str, SGEN_PRODID_MAX);
4462518Sstevel 	prod_str[SGEN_PRODID_MAX] = '\0';
4472518Sstevel 
4482518Sstevel 	for (nodep = sgen_binddb.sdb_inq_nodes; nodep != NULL;
4492518Sstevel 	    nodep = nodep->node_next) {
4502518Sstevel 		/*
4512518Sstevel 		 * Allow the "*" wildcard to match all vendor IDs.
4522518Sstevel 		 */
4532518Sstevel 		if (strcmp(nodep->node_vendor, "*") != 0) {
4542518Sstevel 			if (strncasecmp(nodep->node_vendor, vend_str,
4552518Sstevel 			    strlen(nodep->node_vendor)) != 0) {
4562518Sstevel 				continue;
4572518Sstevel 			}
4582518Sstevel 		}
4592518Sstevel 
4602518Sstevel 		/*
4612518Sstevel 		 * Using strncasecmp() with the key length allows substring
4622518Sstevel 		 * matching for product data.
4632518Sstevel 		 */
4642518Sstevel 		if (strncasecmp(nodep->node_product, prod_str,
4652518Sstevel 		    strlen(nodep->node_product)) == 0) {
4662518Sstevel 			return (0);
4672518Sstevel 		}
4682518Sstevel 	}
4692518Sstevel 	return (-1);
4702518Sstevel }
4712518Sstevel 
4722518Sstevel /*
4732518Sstevel  * sgen_bind_bytype()
4742518Sstevel  * 	lookup a device type in the binding database; if found, return a
4752518Sstevel  * 	format string corresponding to the string in the .conf file.
4762518Sstevel  */
4772518Sstevel static int
sgen_bind_bytype(dev_info_t * dip)4782518Sstevel sgen_bind_bytype(dev_info_t *dip)
4792518Sstevel {
4802518Sstevel 	sgen_type_node_t *nodep;
4812518Sstevel 	struct scsi_device *scsidevp;
4822518Sstevel 
4832518Sstevel 	scsidevp = ddi_get_driver_private(dip);
4842518Sstevel 
4852518Sstevel 	for (nodep = sgen_binddb.sdb_type_nodes; nodep != NULL;
4862518Sstevel 	    nodep = nodep->node_next) {
4872518Sstevel 		if (nodep->node_type == scsidevp->sd_inq->inq_dtype) {
4882518Sstevel 			return (0);
4892518Sstevel 		}
4902518Sstevel 	}
4912518Sstevel 	return (-1);
4922518Sstevel }
4932518Sstevel 
4942518Sstevel /*
4952518Sstevel  * sgen_get_binding()
4962518Sstevel  * 	Check to see if the device in question matches the criteria for
4972518Sstevel  * 	sgen to bind.
4982518Sstevel  *
4992518Sstevel  * 	Either the .conf file must specify a device_type entry which
5002518Sstevel  * 	matches the SCSI device type of this device, or the inquiry
5012518Sstevel  * 	string provided by the device must match an inquiry string specified
5022518Sstevel  * 	in the .conf file.  Inquiry data is matched first.
5032518Sstevel  */
5042518Sstevel static int
sgen_get_binding(dev_info_t * dip)5052518Sstevel sgen_get_binding(dev_info_t *dip)
5062518Sstevel {
5072518Sstevel 	int retval = 0;
5082518Sstevel 
5092518Sstevel 	mutex_enter(&sgen_binddb.sdb_lock);
5102518Sstevel 	if (sgen_binddb.sdb_init == 0)
5112518Sstevel 		sgen_setup_binddb(dip);
5122518Sstevel 	mutex_exit(&sgen_binddb.sdb_lock);
5132518Sstevel 
5142518Sstevel 
5152518Sstevel 	/*
5162518Sstevel 	 * Check device-type-config-list for a match by device type.
5172518Sstevel 	 */
5182518Sstevel 	if (sgen_bind_bytype(dip) == 0)
5192518Sstevel 		goto done;
5202518Sstevel 
5212518Sstevel 	/*
5222518Sstevel 	 * Check inquiry-config-list for a match by Vendor/Product ID.
5232518Sstevel 	 */
5242518Sstevel 	if (sgen_bind_byinq(dip) == 0)
5252518Sstevel 		goto done;
5262518Sstevel 
5272518Sstevel 	retval = -1;
5282518Sstevel done:
5292518Sstevel 	return (retval);
5302518Sstevel }
5312518Sstevel 
5322518Sstevel /*
5332518Sstevel  * sgen_attach()
5342518Sstevel  * 	attach(9e) entrypoint.
5352518Sstevel  */
5362518Sstevel static int
sgen_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5372518Sstevel sgen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5382518Sstevel {
5392518Sstevel 	int err;
5402518Sstevel 
5412518Sstevel 	sgen_log(NULL, SGEN_DIAG2, "in sgen_attach(), device unit-address @%s",
5422518Sstevel 	    ddi_get_name_addr(dip));
5432518Sstevel 
5442518Sstevel 	switch (cmd) {
5452518Sstevel 	case DDI_ATTACH:
5462518Sstevel 		err = sgen_do_attach(dip);
5472518Sstevel 		break;
5482518Sstevel 	case DDI_RESUME:
5492518Sstevel 		err = DDI_SUCCESS;
5502518Sstevel 		break;
5512518Sstevel 	case DDI_PM_RESUME:
5522518Sstevel 	default:
5532518Sstevel 		err = DDI_FAILURE;
5542518Sstevel 		break;
5552518Sstevel 	}
5562518Sstevel 
5572518Sstevel done:
5582518Sstevel 	sgen_log(NULL, SGEN_DIAG2, "%s sgen_attach(), device unit-address @%s",
5592518Sstevel 	    err == DDI_SUCCESS ? "done" : "failed", ddi_get_name_addr(dip));
5602518Sstevel 	return (err);
5612518Sstevel }
5622518Sstevel 
5632518Sstevel /*
5642518Sstevel  * sgen_do_attach()
5652518Sstevel  *	handle the nitty details of attach.
5662518Sstevel  */
5672518Sstevel static int
sgen_do_attach(dev_info_t * dip)5682518Sstevel sgen_do_attach(dev_info_t *dip)
5692518Sstevel {
5702518Sstevel 	int instance;
5712518Sstevel 	struct scsi_device *scsidevp;
5722518Sstevel 	sgen_state_t *sg_state;
5732518Sstevel 	uchar_t devtype;
5742518Sstevel 	struct scsi_inquiry *inq;
5752518Sstevel 
5762518Sstevel 	instance = ddi_get_instance(dip);
5772518Sstevel 
5782518Sstevel 	scsidevp = ddi_get_driver_private(dip);
5792518Sstevel 	ASSERT(scsidevp);
5802518Sstevel 
5812518Sstevel 	sgen_log(NULL, SGEN_DIAG2, "sgen_do_attach: instance = %d, "
5822518Sstevel 	    "device unit-address @%s", instance, ddi_get_name_addr(dip));
5832518Sstevel 
5842518Sstevel 	/*
5852518Sstevel 	 * Probe the device in order to get its device type to name the minor
5862518Sstevel 	 * node.
5872518Sstevel 	 */
5882518Sstevel 	if (scsi_probe(scsidevp, NULL_FUNC) != SCSIPROBE_EXISTS) {
5892518Sstevel 		scsi_unprobe(scsidevp);
5902518Sstevel 		return (DDI_FAILURE);
5912518Sstevel 	}
5922518Sstevel 
5932518Sstevel 	if (ddi_soft_state_zalloc(sgen_soft_state, instance) != DDI_SUCCESS) {
5942518Sstevel 		sgen_log(NULL, SGEN_DIAG1,
5952518Sstevel 		    "sgen_do_attach: failed to allocate softstate, "
5962518Sstevel 		    "device unit-address @%s", ddi_get_name_addr(dip));
5972518Sstevel 		scsi_unprobe(scsidevp);
5982518Sstevel 		return (DDI_FAILURE);
5992518Sstevel 	}
6002518Sstevel 
6012518Sstevel 	inq = scsidevp->sd_inq;		/* valid while device is probed... */
6022518Sstevel 	devtype = inq->inq_dtype;
6032518Sstevel 
6042518Sstevel 	sg_state = ddi_get_soft_state(sgen_soft_state, instance);
6052518Sstevel 	sg_state->sgen_scsidev = scsidevp;
6062518Sstevel 	scsidevp->sd_dev = dip;
6072518Sstevel 
6082518Sstevel 	/*
6092518Sstevel 	 * Now that sg_state->sgen_scsidev is initialized, it's ok to
6102518Sstevel 	 * call sgen_log with sg_state instead of NULL.
6112518Sstevel 	 */
6122518Sstevel 
6132518Sstevel 	/*
6142518Sstevel 	 * If the user specified the sgen_diag property, override the global
6152518Sstevel 	 * sgen_diag setting by setting sg_state's sgen_diag value.  If the
6162518Sstevel 	 * user gave a value out of range, default to '0'.
6172518Sstevel 	 */
6182518Sstevel 	sg_state->sgen_diag = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
6192518Sstevel 	    "sgen-diag", -1);
6202518Sstevel 
6212518Sstevel 	if (sg_state->sgen_diag != -1) {
6222518Sstevel 		if (sg_state->sgen_diag < 0 || sg_state->sgen_diag > 3)
6232518Sstevel 			sg_state->sgen_diag = 0;
6242518Sstevel 	}
6252518Sstevel 
6262518Sstevel 	sgen_log(sg_state, SGEN_DIAG2,
6272518Sstevel 	    "sgen_do_attach: sgen_soft_state=0x%p, instance=%d, "
6282518Sstevel 	    "device unit-address @%s",
6292518Sstevel 	    sgen_soft_state, instance, ddi_get_name_addr(dip));
6302518Sstevel 
6312518Sstevel 	/*
6322518Sstevel 	 * For simplicity, the minor number == the instance number
6332518Sstevel 	 */
6342518Sstevel 	if (ddi_create_minor_node(dip, sgen_typename(devtype), S_IFCHR,
6352518Sstevel 	    instance, DDI_NT_SGEN, NULL) == DDI_FAILURE) {
6362518Sstevel 		scsi_unprobe(scsidevp);
6372518Sstevel 		ddi_prop_remove_all(dip);
6382518Sstevel 		sgen_log(sg_state, SGEN_DIAG1,
6395597Slh195018 		    "sgen_do_attach: minor node creation failed, "
6405597Slh195018 		    "device unit-address @%s", ddi_get_name_addr(dip));
6412518Sstevel 		ddi_soft_state_free(sgen_soft_state, instance);
6422518Sstevel 		return (DDI_FAILURE);
6432518Sstevel 	}
6442518Sstevel 
6452518Sstevel 	/*
6462518Sstevel 	 * Allocate the command buffer, then create a condition variable for
6472518Sstevel 	 * managing it; mark the command buffer as free.
6482518Sstevel 	 */
6492518Sstevel 	sg_state->sgen_cmdbuf = getrbuf(KM_SLEEP);
6502518Sstevel 	cv_init(&sg_state->sgen_cmdbuf_cv, NULL, CV_DRIVER, NULL);
6512518Sstevel 
6522518Sstevel 	SGEN_CLR_BUSY(sg_state);
6532518Sstevel 	SGEN_CLR_OPEN(sg_state);
6542518Sstevel 	SGEN_CLR_SUSP(sg_state);
6552518Sstevel 
6562518Sstevel 	/*
6572518Sstevel 	 * If the hba and the target both support wide xfers, enable them.
6582518Sstevel 	 */
6592518Sstevel 	if (scsi_ifgetcap(&sg_state->sgen_scsiaddr, "wide-xfer", 1) != -1) {
6602518Sstevel 		int wide = 0;
6612518Sstevel 		if ((inq->inq_rdf == RDF_SCSI2) &&
6622518Sstevel 		    (inq->inq_wbus16 || inq->inq_wbus32))
6632518Sstevel 			wide = 1;
6642518Sstevel 		if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "wide-xfer",
6652518Sstevel 		    wide, 1) == 1) {
6662518Sstevel 			sgen_log(sg_state, SGEN_DIAG1,
6672518Sstevel 			    "sgen_attach: wide xfer %s, "
6682518Sstevel 			    "device unit-address @%s",
6692518Sstevel 			    wide ? "enabled" : "disabled",
6702518Sstevel 			    ddi_get_name_addr(dip));
6712518Sstevel 		}
6722518Sstevel 	}
6732518Sstevel 
6742518Sstevel 	/*
6752518Sstevel 	 * This is a little debugging code-- since the codepath for auto-sense
6762518Sstevel 	 * and 'manual' sense is split, toggling this variable will make
6772518Sstevel 	 * sgen act as though the adapter in question can't do auto-sense.
6782518Sstevel 	 */
6792518Sstevel 	if (sgen_force_manual_sense) {
6802518Sstevel 		if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "auto-rqsense",
6812518Sstevel 		    0, 1) == 1) {
6822518Sstevel 			sg_state->sgen_arq_enabled = 0;
6832518Sstevel 		} else {
6842518Sstevel 			sg_state->sgen_arq_enabled = 1;
6852518Sstevel 		}
6862518Sstevel 	} else {
6872518Sstevel 		/*
6882518Sstevel 		 * Enable autorequest sense, if supported
6892518Sstevel 		 */
6902518Sstevel 		if (scsi_ifgetcap(&sg_state->sgen_scsiaddr,
6912518Sstevel 		    "auto-rqsense", 1) != 1) {
6922518Sstevel 			if (scsi_ifsetcap(&sg_state->sgen_scsiaddr,
6932518Sstevel 			    "auto-rqsense", 1, 1) == 1) {
6942518Sstevel 				sg_state->sgen_arq_enabled = 1;
6952518Sstevel 				sgen_log(sg_state, SGEN_DIAG1,
6962518Sstevel 				    "sgen_attach: auto-request-sense enabled, "
6972518Sstevel 				    "device unit-address @%s",
6982518Sstevel 				    ddi_get_name_addr(dip));
6992518Sstevel 			} else {
7002518Sstevel 				sg_state->sgen_arq_enabled = 0;
7012518Sstevel 				sgen_log(sg_state, SGEN_DIAG1,
7022518Sstevel 				    "sgen_attach: auto-request-sense disabled, "
7032518Sstevel 				    "device unit-address @%s",
7042518Sstevel 				    ddi_get_name_addr(dip));
7052518Sstevel 			}
7062518Sstevel 		} else {
7072518Sstevel 			sg_state->sgen_arq_enabled = 1;	/* already enabled */
7082518Sstevel 			sgen_log(sg_state, SGEN_DIAG1,
7092518Sstevel 			    "sgen_attach: auto-request-sense enabled, "
7102518Sstevel 			    "device unit-address @%s", ddi_get_name_addr(dip));
7112518Sstevel 		}
7122518Sstevel 	}
7132518Sstevel 
7142518Sstevel 	/*
7152518Sstevel 	 * Allocate plumbing for manually fetching sense.
7162518Sstevel 	 */
7172518Sstevel 	if (sgen_setup_sense(sg_state) != 0) {
7182518Sstevel 		freerbuf(sg_state->sgen_cmdbuf);
7192518Sstevel 		ddi_prop_remove_all(dip);
7202518Sstevel 		ddi_remove_minor_node(dip, NULL);
7212518Sstevel 		scsi_unprobe(scsidevp);
7222518Sstevel 		sgen_log(sg_state, SGEN_DIAG1,
7232518Sstevel 		    "sgen_do_attach: failed to setup request-sense, "
7242518Sstevel 		    "device unit-address @%s", ddi_get_name_addr(dip));
7252518Sstevel 		ddi_soft_state_free(sgen_soft_state, instance);
7262518Sstevel 		return (DDI_FAILURE);
7272518Sstevel 	}
7282518Sstevel 
7292518Sstevel 	sgen_create_errstats(sg_state, instance);
7302518Sstevel 
7312518Sstevel 	ddi_report_dev(dip);
7322518Sstevel 
7332518Sstevel 	return (DDI_SUCCESS);
7342518Sstevel }
7352518Sstevel 
7362518Sstevel /*
7372518Sstevel  * sgen_setup_sense()
7382518Sstevel  * 	Allocate a request sense packet so that if sgen needs to fetch sense
7392518Sstevel  * 	data for the user, it will have a pkt ready to send.
7402518Sstevel  */
7412518Sstevel static int
sgen_setup_sense(sgen_state_t * sg_state)7422518Sstevel sgen_setup_sense(sgen_state_t *sg_state)
7432518Sstevel {
7442518Sstevel 	struct buf *bp;
7452518Sstevel 	struct scsi_pkt *rqpkt;
7462518Sstevel 
7472518Sstevel 	if ((bp = scsi_alloc_consistent_buf(&sg_state->sgen_scsiaddr, NULL,
7485597Slh195018 	    MAX_SENSE_LENGTH, B_READ, SLEEP_FUNC, NULL)) == NULL) {
7492518Sstevel 		return (-1);
7502518Sstevel 	}
7512518Sstevel 
7522518Sstevel 	if ((rqpkt = scsi_init_pkt(&sg_state->sgen_scsiaddr, NULL, bp,
7532518Sstevel 	    CDB_GROUP0, 1, 0, PKT_CONSISTENT, SLEEP_FUNC, NULL)) == NULL) {
7542518Sstevel 		scsi_free_consistent_buf(bp);
7552518Sstevel 		return (-1);
7562518Sstevel 	}
7572518Sstevel 
7582518Sstevel 	/*
7592518Sstevel 	 * Make the results of running a SENSE available by filling out the
7602518Sstevel 	 * sd_sense field of the scsi device (sgen_sense is just an alias).
7612518Sstevel 	 */
7622518Sstevel 	sg_state->sgen_sense = (struct scsi_extended_sense *)bp->b_un.b_addr;
7632518Sstevel 
7642518Sstevel 	(void) scsi_setup_cdb((union scsi_cdb *)rqpkt->pkt_cdbp,
7655597Slh195018 	    SCMD_REQUEST_SENSE, 0, MAX_SENSE_LENGTH, 0);
7662518Sstevel 	FILL_SCSI1_LUN(sg_state->sgen_scsidev, rqpkt);
7672518Sstevel 
7682518Sstevel 	rqpkt->pkt_comp = sgen_callback;
7692518Sstevel 	rqpkt->pkt_time = SGEN_IO_TIME;
7702518Sstevel 	rqpkt->pkt_flags |= FLAG_SENSING;
7712518Sstevel 	rqpkt->pkt_private = sg_state;
7722518Sstevel 
7732518Sstevel 	sg_state->sgen_rqspkt = rqpkt;
7742518Sstevel 	sg_state->sgen_rqsbuf = bp;
7752518Sstevel 
7762518Sstevel 	return (0);
7772518Sstevel }
7782518Sstevel 
7792518Sstevel /*
7802518Sstevel  * sgen_create_errstats()
7816316Seschrock  * 	create named kstats for tracking occurrence of errors.
7822518Sstevel  */
7832518Sstevel static void
sgen_create_errstats(sgen_state_t * sg_state,int instance)7842518Sstevel sgen_create_errstats(sgen_state_t *sg_state, int instance)
7852518Sstevel {
7862518Sstevel 	char kstatname[KSTAT_STRLEN];
7872518Sstevel 	struct sgen_errstats *stp;
7882518Sstevel 
7897009Scth 	(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,err",
7907009Scth 	    sgen_label, instance);
7912518Sstevel 	sg_state->sgen_kstats = kstat_create("sgenerr", instance,
7922518Sstevel 	    kstatname, "device_error", KSTAT_TYPE_NAMED,
7932518Sstevel 	    sizeof (struct sgen_errstats) / sizeof (kstat_named_t),
7942518Sstevel 	    KSTAT_FLAG_PERSISTENT);
7952518Sstevel 
7962518Sstevel 	if (sg_state->sgen_kstats == NULL)
7972518Sstevel 		return;
7982518Sstevel 
7992518Sstevel 	stp = (struct sgen_errstats *)sg_state->sgen_kstats->ks_data;
8002518Sstevel 	kstat_named_init(&stp->sgen_trans_err, "transport_errors",
8012518Sstevel 	    KSTAT_DATA_UINT32);
8022518Sstevel 	kstat_named_init(&stp->sgen_restart, "command_restarts",
8032518Sstevel 	    KSTAT_DATA_UINT32);
8042518Sstevel 	kstat_named_init(&stp->sgen_incmp_err, "incomplete_commands",
8052518Sstevel 	    KSTAT_DATA_UINT32);
8062518Sstevel 	kstat_named_init(&stp->sgen_autosen_rcv, "autosense_occurred",
8072518Sstevel 	    KSTAT_DATA_UINT32);
8082518Sstevel 	kstat_named_init(&stp->sgen_autosen_bad, "autosense_undecipherable",
8092518Sstevel 	    KSTAT_DATA_UINT32);
8102518Sstevel 	kstat_named_init(&stp->sgen_sense_rcv, "sense_fetches",
8112518Sstevel 	    KSTAT_DATA_UINT32);
8122518Sstevel 	kstat_named_init(&stp->sgen_sense_bad, "sense_data_undecipherable",
8132518Sstevel 	    KSTAT_DATA_UINT32);
8142518Sstevel 	kstat_named_init(&stp->sgen_recov_err, "recoverable_error",
8152518Sstevel 	    KSTAT_DATA_UINT32);
8162518Sstevel 	kstat_named_init(&stp->sgen_nosen_err, "NO_SENSE_sense_key",
8172518Sstevel 	    KSTAT_DATA_UINT32);
8182518Sstevel 	kstat_named_init(&stp->sgen_unrecov_err, "unrecoverable_sense_error",
8192518Sstevel 	    KSTAT_DATA_UINT32);
8202518Sstevel 	sg_state->sgen_kstats->ks_private = sg_state;
8212518Sstevel 	sg_state->sgen_kstats->ks_update = nulldev;
8222518Sstevel 	kstat_install(sg_state->sgen_kstats);
8232518Sstevel }
8242518Sstevel 
8252518Sstevel /*
8262518Sstevel  * sgen_detach()
8272518Sstevel  * 	detach(9E) entrypoint
8282518Sstevel  */
8292518Sstevel static int
sgen_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)8302518Sstevel sgen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
8312518Sstevel {
8322518Sstevel 	int instance;
8332518Sstevel 	sgen_state_t *sg_state;
8342518Sstevel 
8352518Sstevel 	instance = ddi_get_instance(dip);
8362518Sstevel 	sg_state = ddi_get_soft_state(sgen_soft_state, instance);
8372518Sstevel 
8382518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_detach(), "
8392518Sstevel 	    "device unit-address @%s", ddi_get_name_addr(dip));
8402518Sstevel 
8412518Sstevel 	if (sg_state == NULL) {
8422518Sstevel 		sgen_log(NULL, SGEN_DIAG1,
8432518Sstevel 		    "sgen_detach: failed, no softstate found (%d), "
8442518Sstevel 		    "device unit-address @%s",
8452518Sstevel 		    instance, ddi_get_name_addr(dip));
8462518Sstevel 		return (DDI_FAILURE);
8472518Sstevel 	}
8482518Sstevel 
8492518Sstevel 	switch (cmd) {
8502518Sstevel 	case DDI_DETACH:
8512518Sstevel 		return (sgen_do_detach(dip));
8522518Sstevel 	case DDI_SUSPEND:
8532518Sstevel 		return (sgen_do_suspend(dip));
8542518Sstevel 	case DDI_PM_SUSPEND:
8552518Sstevel 	default:
8562518Sstevel 		return (DDI_FAILURE);
8572518Sstevel 	}
8582518Sstevel }
8592518Sstevel 
8602518Sstevel /*
8612518Sstevel  * sgen_do_detach()
8622518Sstevel  * 	detach the driver, tearing down resources.
8632518Sstevel  */
8642518Sstevel static int
sgen_do_detach(dev_info_t * dip)8652518Sstevel sgen_do_detach(dev_info_t *dip)
8662518Sstevel {
8672518Sstevel 	int instance;
8682518Sstevel 	sgen_state_t *sg_state;
8692518Sstevel 	struct scsi_device *devp;
8702518Sstevel 
8712518Sstevel 	instance = ddi_get_instance(dip);
8722518Sstevel 	sg_state = ddi_get_soft_state(sgen_soft_state, instance);
8732518Sstevel 	ASSERT(sg_state);
8742518Sstevel 
8752518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_do_detach(), "
8762518Sstevel 	    "device unit-address @%s", ddi_get_name_addr(dip));
8772518Sstevel 	devp = ddi_get_driver_private(dip);
8782518Sstevel 
8792518Sstevel 	mutex_enter(&sg_state->sgen_mutex);
8802518Sstevel 	if (SGEN_IS_BUSY(sg_state)) {
8812518Sstevel 		mutex_exit(&sg_state->sgen_mutex);
8822518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_do_detach: failed because "
8832518Sstevel 		    "device is busy, device unit-address @%s",
8842518Sstevel 		    ddi_get_name_addr(dip));
8852518Sstevel 		return (DDI_FAILURE);
8862518Sstevel 	}
8872518Sstevel 	mutex_exit(&sg_state->sgen_mutex);
8882518Sstevel 
8892518Sstevel 	/*
8902518Sstevel 	 * Final approach for detach.  Free data allocated by scsi_probe()
8912518Sstevel 	 * in attach.
8922518Sstevel 	 */
8932518Sstevel 	if (sg_state->sgen_restart_timeid)
8942518Sstevel 		(void) untimeout(sg_state->sgen_restart_timeid);
8952518Sstevel 	sg_state->sgen_restart_timeid = 0;
8962518Sstevel 	scsi_unprobe(devp);
8972518Sstevel 
8982518Sstevel 	/*
8992518Sstevel 	 * Free auto-request plumbing.
9002518Sstevel 	 */
9012518Sstevel 	scsi_free_consistent_buf(sg_state->sgen_rqsbuf);
9022518Sstevel 	scsi_destroy_pkt(sg_state->sgen_rqspkt);
9032518Sstevel 
9042518Sstevel 	if (sg_state->sgen_kstats) {
9052518Sstevel 		kstat_delete(sg_state->sgen_kstats);
9062518Sstevel 		sg_state->sgen_kstats = NULL;
9072518Sstevel 	}
9082518Sstevel 
9092518Sstevel 	/*
9102518Sstevel 	 * Free command buffer and clean up
9112518Sstevel 	 */
9122518Sstevel 	freerbuf(sg_state->sgen_cmdbuf);
9132518Sstevel 	cv_destroy(&sg_state->sgen_cmdbuf_cv);
9142518Sstevel 
9152518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_do_detach(), "
9162518Sstevel 	    "device unit-address @%s", ddi_get_name_addr(dip));
9172518Sstevel 
9182518Sstevel 	ddi_soft_state_free(sgen_soft_state, instance);
9192518Sstevel 	ddi_prop_remove_all(dip);
9202518Sstevel 	ddi_remove_minor_node(dip, NULL);
9212518Sstevel 	return (DDI_SUCCESS);
9222518Sstevel }
9232518Sstevel 
9242518Sstevel /*
9252518Sstevel  * sgen_do_suspend()
9262518Sstevel  * 	suspend the driver.  This sets the "suspend" bit for this target if it
9272518Sstevel  * 	is currently open; once resumed, the suspend bit will cause
9282518Sstevel  * 	subsequent I/Os to fail.  We want user programs to close and
9292518Sstevel  * 	reopen the device to acknowledge that they need to reexamine its
9302518Sstevel  * 	state and do the right thing.
9312518Sstevel  */
9322518Sstevel static int
sgen_do_suspend(dev_info_t * dip)9332518Sstevel sgen_do_suspend(dev_info_t *dip)
9342518Sstevel {
9352518Sstevel 	int instance;
9362518Sstevel 	sgen_state_t *sg_state;
9372518Sstevel 
9382518Sstevel 	instance = ddi_get_instance(dip);
9392518Sstevel 	sg_state = ddi_get_soft_state(sgen_soft_state, instance);
9402518Sstevel 	ASSERT(sg_state);
9412518Sstevel 
9422518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_do_suspend(), "
9432518Sstevel 	    "device unit-address @%s", ddi_get_name_addr(dip));
9442518Sstevel 
9452518Sstevel 	if (sg_state->sgen_restart_timeid) {
9462518Sstevel 		(void) untimeout(sg_state->sgen_restart_timeid);
9472518Sstevel 	}
9482518Sstevel 	sg_state->sgen_restart_timeid = 0;
9492518Sstevel 
9502518Sstevel 	mutex_enter(&sg_state->sgen_mutex);
9512518Sstevel 	if (SGEN_IS_OPEN(sg_state))
9522518Sstevel 		SGEN_SET_SUSP(sg_state);
9532518Sstevel 	mutex_exit(&sg_state->sgen_mutex);
9542518Sstevel 
9552518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_do_suspend(), "
9562518Sstevel 	    "device unit-address @%s", ddi_get_name_addr(dip));
9572518Sstevel 	return (DDI_SUCCESS);
9582518Sstevel }
9592518Sstevel 
9602518Sstevel /*
9612518Sstevel  * sgen_getinfo()
9622518Sstevel  *	getinfo(9e) entrypoint.
9632518Sstevel  */
9642518Sstevel /*ARGSUSED*/
9652518Sstevel static int
sgen_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)9662518Sstevel sgen_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
9672518Sstevel {
9682518Sstevel 	dev_t dev;
9692518Sstevel 	sgen_state_t *sg_state;
9702518Sstevel 	int instance, error;
9712518Sstevel 	switch (infocmd) {
9722518Sstevel 	case DDI_INFO_DEVT2DEVINFO:
9732518Sstevel 		dev = (dev_t)arg;
9742518Sstevel 		instance = getminor(dev);
9752518Sstevel 		if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance))
9762518Sstevel 		    == NULL)
9772518Sstevel 			return (DDI_FAILURE);
9782518Sstevel 		*result = (void *) sg_state->sgen_scsidev->sd_dev;
9792518Sstevel 		error = DDI_SUCCESS;
9802518Sstevel 		break;
9812518Sstevel 	case DDI_INFO_DEVT2INSTANCE:
9822518Sstevel 		dev = (dev_t)arg;
9832518Sstevel 		instance = getminor(dev);
9842518Sstevel 		*result = (void *)(uintptr_t)instance;
9852518Sstevel 		error = DDI_SUCCESS;
9862518Sstevel 		break;
9872518Sstevel 	default:
9882518Sstevel 		error = DDI_FAILURE;
9892518Sstevel 	}
9902518Sstevel 	return (error);
9912518Sstevel }
9922518Sstevel 
9932518Sstevel /*
9942518Sstevel  * sgen_probe()
9952518Sstevel  * 	probe(9e) entrypoint.  sgen *never* returns DDI_PROBE_PARTIAL, in
9962518Sstevel  * 	order to avoid leaving around extra devinfos.  If sgen's binding
9972518Sstevel  * 	rules indicate that it should bind, it returns DDI_PROBE_SUCCESS.
9982518Sstevel  */
9992518Sstevel static int
sgen_probe(dev_info_t * dip)10002518Sstevel sgen_probe(dev_info_t *dip)
10012518Sstevel {
10022518Sstevel 	struct scsi_device *scsidevp;
10032518Sstevel 	int instance;
10042518Sstevel 	int rval;
10052518Sstevel 
10062518Sstevel 	scsidevp = ddi_get_driver_private(dip);
10072518Sstevel 	instance = ddi_get_instance(dip);
10082518Sstevel 	sgen_log(NULL, SGEN_DIAG2, "in sgen_probe(): instance = %d, "
10092518Sstevel 	    "device unit-address @%s", instance, ddi_get_name_addr(dip));
10102518Sstevel 
10112518Sstevel 	if (ddi_dev_is_sid(dip) == DDI_SUCCESS)
10122518Sstevel 		return (DDI_PROBE_DONTCARE);
10132518Sstevel 
10142518Sstevel 	if (ddi_get_soft_state(sgen_soft_state, instance) != NULL)
10152518Sstevel 		return (DDI_PROBE_FAILURE);
10162518Sstevel 
10172518Sstevel 	mutex_enter(&sgen_binddb.sdb_lock);
10182518Sstevel 	if (sgen_binddb.sdb_init == 0) {
10192518Sstevel 		sgen_setup_binddb(dip);
10202518Sstevel 	}
10212518Sstevel 	mutex_exit(&sgen_binddb.sdb_lock);
10222518Sstevel 
10232518Sstevel 	/*
10242518Sstevel 	 * A small optimization: if it's impossible for sgen to bind to
10252518Sstevel 	 * any devices, don't bother probing, just fail.
10262518Sstevel 	 */
10272518Sstevel 	if ((sgen_binddb.sdb_inq_nodes == NULL) &&
10282518Sstevel 	    (sgen_binddb.sdb_type_nodes == NULL)) {
10292518Sstevel 		return (DDI_PROBE_FAILURE);
10302518Sstevel 	}
10312518Sstevel 
10322518Sstevel 	if (scsi_probe(scsidevp, NULL_FUNC) == SCSIPROBE_EXISTS) {
10332518Sstevel 		if (sgen_get_binding(dip) == 0) {
10342518Sstevel 			rval = DDI_PROBE_SUCCESS;
10352518Sstevel 		}
10362518Sstevel 	} else {
10372518Sstevel 		rval = DDI_PROBE_FAILURE;
10382518Sstevel 	}
10392518Sstevel 	scsi_unprobe(scsidevp);
10402518Sstevel 
10412518Sstevel 	sgen_log(NULL, SGEN_DIAG2, "sgen_probe() %s, device unit-address @%s",
10422518Sstevel 	    rval == DDI_PROBE_SUCCESS ? "succeeded" : "failed",
10432518Sstevel 	    ddi_get_name_addr(dip));
10442518Sstevel 	return (rval);
10452518Sstevel }
10462518Sstevel 
10472518Sstevel /*
10482518Sstevel  * sgen_open()
10492518Sstevel  * 	open(9e) entrypoint.  sgen enforces a strict exclusive open policy per
10502518Sstevel  * 	target.
10512518Sstevel  */
10522518Sstevel /*ARGSUSED1*/
10532518Sstevel static int
sgen_open(dev_t * dev_p,int flag,int otyp,cred_t * cred_p)10542518Sstevel sgen_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
10552518Sstevel {
10562518Sstevel 	dev_t dev = *dev_p;
10572518Sstevel 	sgen_state_t *sg_state;
10582518Sstevel 	int instance;
10592518Sstevel 
10602518Sstevel 	instance = getminor(dev);
10612518Sstevel 
10622518Sstevel 	if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
10632518Sstevel 		return (ENXIO);
10642518Sstevel 
10652518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_open(): instance = %d",
10662518Sstevel 	    instance);
10672518Sstevel 
10682518Sstevel 	mutex_enter(&sg_state->sgen_mutex);
10692518Sstevel 
10706316Seschrock 	/*
10716316Seschrock 	 * Don't allow new opens of a suspended device until the last close has
10726316Seschrock 	 * happened.  This is rather simplistic, but keeps the implementation
10736316Seschrock 	 * straightforward.
10746316Seschrock 	 */
10756316Seschrock 	if (SGEN_IS_SUSP(sg_state)) {
10766316Seschrock 		mutex_exit(&sg_state->sgen_mutex);
10776316Seschrock 		return (EIO);
10786316Seschrock 	}
10796316Seschrock 
10806316Seschrock 	/*
10816316Seschrock 	 * Enforce exclusive access.
10826316Seschrock 	 */
10836316Seschrock 	if (SGEN_IS_EXCL(sg_state) ||
10846316Seschrock 	    (SGEN_IS_OPEN(sg_state) && (flag & FEXCL))) {
10852518Sstevel 		mutex_exit(&sg_state->sgen_mutex);
10862518Sstevel 		return (EBUSY);
10872518Sstevel 	}
10882518Sstevel 
10896316Seschrock 	if (flag & FEXCL)
10906316Seschrock 		SGEN_SET_EXCL(sg_state);
10916316Seschrock 
10922518Sstevel 	SGEN_SET_OPEN(sg_state);
10932518Sstevel 
10942518Sstevel 	mutex_exit(&sg_state->sgen_mutex);
10952518Sstevel 
10962518Sstevel 	return (0);
10972518Sstevel }
10982518Sstevel 
10992518Sstevel /*
11002518Sstevel  * sgen_close()
11012518Sstevel  * 	close(9e) entrypoint.
11022518Sstevel  */
11032518Sstevel /*ARGSUSED1*/
11042518Sstevel static int
sgen_close(dev_t dev,int flag,int otyp,cred_t * cred_p)11052518Sstevel sgen_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
11062518Sstevel {
11072518Sstevel 	sgen_state_t *sg_state;
11082518Sstevel 	int instance;
11092518Sstevel 
11102518Sstevel 	instance = getminor(dev);
11112518Sstevel 
11122518Sstevel 	if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
11132518Sstevel 		return (ENXIO);
11142518Sstevel 
11152518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_close(): instance = %d",
11162518Sstevel 	    instance);
11172518Sstevel 
11182518Sstevel 	mutex_enter(&sg_state->sgen_mutex);
11192518Sstevel 	SGEN_CLR_OPEN(sg_state);
11206316Seschrock 	SGEN_CLR_EXCL(sg_state);
11212518Sstevel 	SGEN_CLR_SUSP(sg_state); /* closing clears the 'I was suspended' bit */
11222518Sstevel 	mutex_exit(&sg_state->sgen_mutex);
11232518Sstevel 
11242518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_close()");
11252518Sstevel 
11262518Sstevel 	return (0);
11272518Sstevel }
11282518Sstevel 
11292518Sstevel /*
11302518Sstevel  * sgen_ioctl()
11312518Sstevel  * 	sgen supports the USCSI(7I) ioctl interface.
11322518Sstevel  */
11332518Sstevel /*ARGSUSED4*/
11342518Sstevel static int
sgen_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)11352518Sstevel sgen_ioctl(dev_t dev,
11362518Sstevel     int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p)
11372518Sstevel {
11382518Sstevel 	int retval = 0;
11392518Sstevel 	sgen_state_t *sg_state;
11402518Sstevel 	int instance;
11412518Sstevel 
11422518Sstevel 	instance = getminor(dev);
11432518Sstevel 
11442518Sstevel 	if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
11452518Sstevel 		return (ENXIO);
11462518Sstevel 
11472518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_ioctl(): instance = %d",
11482518Sstevel 	    instance);
11492518Sstevel 
11502518Sstevel 	/*
11512518Sstevel 	 * If the driver has been suspended since the last open, fail all
11522518Sstevel 	 * subsequent IO's so that the userland consumer reinitializes state.
11532518Sstevel 	 */
11542518Sstevel 	mutex_enter(&sg_state->sgen_mutex);
11552518Sstevel 	if (SGEN_IS_SUSP(sg_state)) {
11562518Sstevel 		mutex_exit(&sg_state->sgen_mutex);
11572518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_ioctl: returning EIO: "
11582518Sstevel 		    "driver instance %d was previously suspended", instance);
11592518Sstevel 		return (EIO);
11602518Sstevel 	}
11612518Sstevel 	mutex_exit(&sg_state->sgen_mutex);
11622518Sstevel 
11632518Sstevel 	switch (cmd) {
11642518Sstevel 	case SGEN_IOC_DIAG: {
11652518Sstevel 		if (arg > 3) {
11662518Sstevel 			arg = 0;
11672518Sstevel 		}
11682518Sstevel 		sg_state->sgen_diag = (int)arg;
11692518Sstevel 		retval = 0;
11702518Sstevel 		break;
11712518Sstevel 	}
11722518Sstevel 
11732518Sstevel 	case SGEN_IOC_READY: {
11742518Sstevel 		if (sgen_tur(dev) != 0) {
11752518Sstevel 			retval = EIO;
11762518Sstevel 		} else {
11772518Sstevel 			retval = 0;
11782518Sstevel 		}
11792518Sstevel 		break;
11802518Sstevel 	}
11812518Sstevel 
11823368Slh195018 	case USCSICMD:
11833368Slh195018 		retval = sgen_uscsi_cmd(dev, (struct uscsi_cmd *)arg, flag);
11843368Slh195018 		break;
11852518Sstevel 
11862518Sstevel 	default:
11872518Sstevel 		retval = ENOTTY;
11882518Sstevel 	}
11892518Sstevel 
11902518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_ioctl(), returning %d",
11912518Sstevel 	    retval);
11922518Sstevel 
11932518Sstevel 	return (retval);
11942518Sstevel }
11952518Sstevel 
11962518Sstevel /*
11972518Sstevel  * sgen_uscsi_cmd()
11982518Sstevel  * 	Setup, configuration and teardown for a uscsi(7I) command
11992518Sstevel  */
12002518Sstevel /*ARGSUSED*/
12012518Sstevel static int
sgen_uscsi_cmd(dev_t dev,struct uscsi_cmd * ucmd,int flag)12023368Slh195018 sgen_uscsi_cmd(dev_t dev, struct uscsi_cmd *ucmd, int flag)
12032518Sstevel {
12043368Slh195018 	struct uscsi_cmd	*uscmd;
12053368Slh195018 	struct buf	*bp;
12063368Slh195018 	sgen_state_t	*sg_state;
12073368Slh195018 	enum uio_seg	uioseg;
12083368Slh195018 	int	instance;
12093368Slh195018 	int	flags;
12103368Slh195018 	int	err;
12112518Sstevel 
12122518Sstevel 	instance = getminor(dev);
12132518Sstevel 
12142518Sstevel 	sg_state = ddi_get_soft_state(sgen_soft_state, instance);
12152518Sstevel 	ASSERT(sg_state);
12162518Sstevel 
12172518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_uscsi_cmd(): instance = %d",
12182518Sstevel 	    instance);
12192518Sstevel 
12206316Seschrock 	/*
12216316Seschrock 	 * At this point, we start affecting state relevant to the target,
12226316Seschrock 	 * so access needs to be serialized.
12236316Seschrock 	 */
12246316Seschrock 	if (sgen_hold_cmdbuf(sg_state) != 0) {
12256316Seschrock 		sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: interrupted");
12266316Seschrock 		return (EINTR);
12276316Seschrock 	}
12286316Seschrock 
12293368Slh195018 	err = scsi_uscsi_alloc_and_copyin((intptr_t)ucmd, flag,
12303368Slh195018 	    &sg_state->sgen_scsiaddr, &uscmd);
12313368Slh195018 	if (err != 0) {
12326316Seschrock 		sgen_rele_cmdbuf(sg_state);
12333368Slh195018 		sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: "
12343368Slh195018 		    "scsi_uscsi_alloc_and_copyin failed\n");
12353368Slh195018 		return (err);
12363368Slh195018 	}
12372518Sstevel 
12382518Sstevel 	/*
12392518Sstevel 	 * Clear out undesirable command flags
12402518Sstevel 	 */
12413368Slh195018 	flags = (uscmd->uscsi_flags & ~(USCSI_NOINTR | USCSI_NOPARITY |
12422518Sstevel 	    USCSI_OTAG | USCSI_HTAG | USCSI_HEAD));
12433368Slh195018 	if (flags != uscmd->uscsi_flags) {
12442518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: cleared "
12453455Slh195018 		    "unsafe uscsi_flags 0x%x", uscmd->uscsi_flags & ~flags);
12463368Slh195018 		uscmd->uscsi_flags = flags;
12472518Sstevel 	}
12482518Sstevel 
12493368Slh195018 	if (uscmd->uscsi_cdb != NULL) {
12503368Slh195018 		sgen_dump_cdb(sg_state, "sgen_uscsi_cmd: ",
12513368Slh195018 		    (union scsi_cdb *)uscmd->uscsi_cdb, uscmd->uscsi_cdblen);
12522518Sstevel 	}
12532518Sstevel 
12542518Sstevel 	/*
12552518Sstevel 	 * Stash the sense buffer into sgen_rqs_sen for convenience.
12562518Sstevel 	 */
12573368Slh195018 	sg_state->sgen_rqs_sen = uscmd->uscsi_rqbuf;
12582518Sstevel 
12593368Slh195018 	bp = sg_state->sgen_cmdbuf;
12602518Sstevel 	bp->av_back = NULL;
12612518Sstevel 	bp->av_forw = NULL;
12623368Slh195018 	bp->b_private = (struct buf *)uscmd;
12633368Slh195018 	uioseg = (flag & FKIOCTL) ? UIO_SYSSPACE : UIO_USERSPACE;
12642518Sstevel 
12653368Slh195018 	err = scsi_uscsi_handle_cmd(dev, uioseg, uscmd, sgen_start, bp, NULL);
12662518Sstevel 
12672518Sstevel 	if (sg_state->sgen_cmdpkt != NULL) {
12683368Slh195018 		uscmd->uscsi_status = SCBP_C(sg_state->sgen_cmdpkt);
12692518Sstevel 	} else {
12703368Slh195018 		uscmd->uscsi_status = 0;
12712518Sstevel 	}
12722518Sstevel 
12732518Sstevel 	sgen_log(sg_state, SGEN_DIAG3, "sgen_uscsi_cmd: awake from waiting "
12743368Slh195018 	    "for command.  Status is 0x%x", uscmd->uscsi_status);
12752518Sstevel 
12763368Slh195018 	if (uscmd->uscsi_rqbuf != NULL) {
12773368Slh195018 		int rqlen = uscmd->uscsi_rqlen - uscmd->uscsi_rqresid;
12783368Slh195018 		sgen_dump_sense(sg_state, rqlen,
12793368Slh195018 		    (uchar_t *)uscmd->uscsi_rqbuf);
12802518Sstevel 	}
12812518Sstevel 
12823368Slh195018 	(void) scsi_uscsi_copyout_and_free((intptr_t)ucmd, uscmd);
12833368Slh195018 
12842518Sstevel 	if (sg_state->sgen_cmdpkt != NULL) {
12852518Sstevel 		scsi_destroy_pkt(sg_state->sgen_cmdpkt);
12862518Sstevel 		sg_state->sgen_cmdpkt = NULL;
12872518Sstevel 	}
12882518Sstevel 
12892518Sstevel 	/*
12902518Sstevel 	 * After this point, we can't touch per-target state.
12912518Sstevel 	 */
12926316Seschrock 	sgen_rele_cmdbuf(sg_state);
12932518Sstevel 
12942518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_uscsi_cmd()");
12952518Sstevel 
12962518Sstevel 	return (err);
12972518Sstevel }
12982518Sstevel 
12992518Sstevel /*
13002518Sstevel  * sgen_hold_cmdbuf()
13016316Seschrock  * 	Acquire a lock on the command buffer for the given target.  Returns
13026316Seschrock  * 	non-zero if interrupted.
13032518Sstevel  */
13046316Seschrock static int
sgen_hold_cmdbuf(sgen_state_t * sg_state)13052518Sstevel sgen_hold_cmdbuf(sgen_state_t *sg_state)
13062518Sstevel {
13072518Sstevel 	mutex_enter(&sg_state->sgen_mutex);
13086316Seschrock 	while (SGEN_IS_BUSY(sg_state)) {
13096316Seschrock 		if (!cv_wait_sig(&sg_state->sgen_cmdbuf_cv,
13106316Seschrock 		    &sg_state->sgen_mutex)) {
13116316Seschrock 			mutex_exit(&sg_state->sgen_mutex);
13126316Seschrock 			return (-1);
13136316Seschrock 		}
13146316Seschrock 	}
13152518Sstevel 	SGEN_SET_BUSY(sg_state);
13162518Sstevel 	mutex_exit(&sg_state->sgen_mutex);
13176316Seschrock 	return (0);
13182518Sstevel }
13192518Sstevel 
13202518Sstevel /*
13212518Sstevel  * sgen_rele_cmdbuf()
13222518Sstevel  * 	release the command buffer for a particular target.
13232518Sstevel  */
13242518Sstevel static void
sgen_rele_cmdbuf(sgen_state_t * sg_state)13252518Sstevel sgen_rele_cmdbuf(sgen_state_t *sg_state)
13262518Sstevel {
13272518Sstevel 	mutex_enter(&sg_state->sgen_mutex);
13282518Sstevel 	SGEN_CLR_BUSY(sg_state);
13292518Sstevel 	cv_signal(&sg_state->sgen_cmdbuf_cv);
13302518Sstevel 	mutex_exit(&sg_state->sgen_mutex);
13312518Sstevel }
13322518Sstevel 
13332518Sstevel /*
13342518Sstevel  * sgen_start()
13352518Sstevel  * 	Transport a uscsi command; this is invoked by physio() or directly
13362518Sstevel  * 	by sgen_uscsi_cmd().
13372518Sstevel  */
13382518Sstevel static int
sgen_start(struct buf * bp)13392518Sstevel sgen_start(struct buf *bp)
13402518Sstevel {
13412518Sstevel 	sgen_state_t *sg_state;
13422518Sstevel 	dev_t dev = bp->b_edev;
13432518Sstevel 	int trans_err;
13442518Sstevel 
13452518Sstevel 	if ((sg_state = ddi_get_soft_state(sgen_soft_state,
13462518Sstevel 	    getminor(dev))) == NULL) {
13472518Sstevel 		bp->b_resid = bp->b_bcount;
13482518Sstevel 		bioerror(bp, ENXIO);
13492518Sstevel 		biodone(bp);
13502518Sstevel 		return (ENXIO);
13512518Sstevel 	}
13522518Sstevel 
13532518Sstevel 	/*
13542518Sstevel 	 * Sanity checks - command should not be complete, no packet should
13552518Sstevel 	 * be allocated, and there ought to be a uscsi cmd in b_private
13562518Sstevel 	 */
13572518Sstevel 	ASSERT(bp == sg_state->sgen_cmdbuf && sg_state->sgen_cmdpkt == NULL);
13582518Sstevel 	ASSERT((bp->b_flags & B_DONE) == 0);
13592518Sstevel 	ASSERT(bp->b_private);
13602518Sstevel 	if (sgen_make_uscsi_cmd(sg_state, bp) != 0) {
13612518Sstevel 		bp->b_resid = bp->b_bcount;
13622518Sstevel 		bioerror(bp, EFAULT);
13632518Sstevel 		biodone(bp);
13642518Sstevel 		return (EFAULT);
13652518Sstevel 	}
13662518Sstevel 
13672518Sstevel 	ASSERT(sg_state->sgen_cmdpkt != NULL);
13682518Sstevel 
13692518Sstevel 	/*
13702518Sstevel 	 * Clear out the residual and error fields
13712518Sstevel 	 */
13722518Sstevel 	bp->b_resid = 0;
13732518Sstevel 	bp->b_error = 0;
13742518Sstevel 
13752518Sstevel 	trans_err = sgen_scsi_transport(sg_state->sgen_cmdpkt);
13762518Sstevel 	switch (trans_err) {
13772518Sstevel 	case TRAN_ACCEPT:
13782518Sstevel 		break;
13792518Sstevel 	case TRAN_BUSY:
13802518Sstevel 		sgen_log(sg_state, SGEN_DIAG2,
13812518Sstevel 		    "sgen_start: scsi_transport() returned TRAN_BUSY");
13822518Sstevel 		sg_state->sgen_restart_timeid = timeout(sgen_restart, sg_state,
13832518Sstevel 		    SGEN_BSY_TIMEOUT);
13842518Sstevel 		break;
13852518Sstevel 	default:
13862518Sstevel 		/*
13872518Sstevel 		 * Indicate there has been an I/O transfer error.
13882518Sstevel 		 * Be done with the command.
13892518Sstevel 		 */
13902518Sstevel 		mutex_enter(&sg_state->sgen_mutex);
13912518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_trans_err);
13922518Sstevel 		mutex_exit(&sg_state->sgen_mutex);
13932518Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "sgen_start: scsi_transport() "
13942518Sstevel 		    "returned %d", trans_err);
13952518Sstevel 		bioerror(bp, EIO);
13962518Sstevel 		biodone(bp);
13972518Sstevel 		return (EIO);
13982518Sstevel 	}
13992518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "sgen_start: b_flags 0x%x", bp->b_flags);
14002518Sstevel 	return (0);
14012518Sstevel }
14022518Sstevel 
14032518Sstevel /*
14042518Sstevel  * sgen_scsi_transport()
14052518Sstevel  * 	a simple scsi_transport() wrapper which can be configured to inject
14062518Sstevel  * 	sporadic errors for testing.
14072518Sstevel  */
14082518Sstevel static int
sgen_scsi_transport(struct scsi_pkt * pkt)14092518Sstevel sgen_scsi_transport(struct scsi_pkt *pkt)
14102518Sstevel {
14112518Sstevel 	int trans_err;
14122518Sstevel 	static int cnt = 0;
14132518Sstevel 	sgen_state_t *sg_state = pkt->pkt_private;
14142518Sstevel 
14152518Sstevel 	if (sgen_sporadic_failures == 0) {
14162518Sstevel 		return (scsi_transport(pkt));
14172518Sstevel 	}
14182518Sstevel 
14192518Sstevel 	cnt = (cnt * 2416 + 374441) % 1771875;	/* borrowed from kmem.c */
14202518Sstevel 	if (cnt % 40 == 1) {
14212518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_scsi_transport: "
14222518Sstevel 		    "injecting sporadic BUSY");
14232518Sstevel 		trans_err = TRAN_BUSY;
14242518Sstevel 	} else if (cnt % 40 == 2) {
14252518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_scsi_transport: "
14262518Sstevel 		    "injecting sporadic BADPKT");
14272518Sstevel 		trans_err = TRAN_BADPKT;
14282518Sstevel 	} else {
14292518Sstevel 		/*
14302518Sstevel 		 * Most of the time we take the normal path
14312518Sstevel 		 */
14322518Sstevel 		trans_err = scsi_transport(pkt);
14332518Sstevel 	}
14342518Sstevel 	return (trans_err);
14352518Sstevel }
14362518Sstevel 
14372518Sstevel /*
14382518Sstevel  * sgen_make_uscsi_cmd()
14392518Sstevel  * 	Initialize a SCSI packet usable for USCSI.
14402518Sstevel  */
14412518Sstevel static int
sgen_make_uscsi_cmd(sgen_state_t * sg_state,struct buf * bp)14422518Sstevel sgen_make_uscsi_cmd(sgen_state_t *sg_state, struct buf *bp)
14432518Sstevel {
14442518Sstevel 	struct scsi_pkt	*pkt;
14452518Sstevel 	struct uscsi_cmd *ucmd;
14465597Slh195018 	int stat_size = 1;
14475597Slh195018 	int flags = 0;
14482518Sstevel 
14492518Sstevel 	ASSERT(bp);
14502518Sstevel 
14512518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_make_uscsi_cmd()");
14522518Sstevel 
14532518Sstevel 	ucmd = (struct uscsi_cmd *)bp->b_private;
14542518Sstevel 
14552518Sstevel 	if (ucmd->uscsi_flags & USCSI_RQENABLE) {
14565597Slh195018 		if (ucmd->uscsi_rqlen > SENSE_LENGTH) {
14575597Slh195018 			stat_size = (int)(ucmd->uscsi_rqlen) +
14585597Slh195018 			    sizeof (struct scsi_arq_status) -
14595597Slh195018 			    sizeof (struct scsi_extended_sense);
14605597Slh195018 			flags = PKT_XARQ;
14615597Slh195018 		} else {
14625597Slh195018 			stat_size = sizeof (struct scsi_arq_status);
14635597Slh195018 		}
14642518Sstevel 	}
14652518Sstevel 
14662518Sstevel 	sgen_log(sg_state, SGEN_DIAG3, "sgen_make_uscsi_cmd: b_bcount = %ld",
14672518Sstevel 	    bp->b_bcount);
14682518Sstevel 	pkt = scsi_init_pkt(&sg_state->sgen_scsiaddr,
14692518Sstevel 	    NULL,			/* in_pkt - null so it'll be alloc'd */
14702518Sstevel 	    bp->b_bcount ? bp : NULL,	/* buf structure for data xfer */
14712518Sstevel 	    ucmd->uscsi_cdblen,		/* cmdlen */
14722518Sstevel 	    stat_size,			/* statuslen */
14732518Sstevel 	    0,				/* privatelen */
14745597Slh195018 	    flags,			/* flags */
14752518Sstevel 	    SLEEP_FUNC,			/* callback */
14762518Sstevel 	    (caddr_t)sg_state);		/* callback_arg */
14772518Sstevel 
14782518Sstevel 	if (pkt == NULL) {
14792518Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "failed sgen_make_uscsi_cmd()");
14802518Sstevel 		return (-1);
14812518Sstevel 	}
14822518Sstevel 
14832518Sstevel 	pkt->pkt_comp = sgen_callback;
14842518Sstevel 	pkt->pkt_private = sg_state;
14852518Sstevel 	sg_state->sgen_cmdpkt = pkt;
14862518Sstevel 
14872518Sstevel 	/*
14882518Sstevel 	 * We *don't* call scsi_setup_cdb here, as is customary, since the
14892518Sstevel 	 * user could specify a command from one group, but pass cdblen
14902518Sstevel 	 * as something totally different.  If cdblen is smaller than expected,
14912518Sstevel 	 * this results in scsi_setup_cdb writing past the end of the cdb.
14922518Sstevel 	 */
14932518Sstevel 	bcopy(ucmd->uscsi_cdb, pkt->pkt_cdbp, ucmd->uscsi_cdblen);
14942518Sstevel 	if (ucmd->uscsi_cdblen >= CDB_GROUP0) {
14952518Sstevel 		FILL_SCSI1_LUN(sg_state->sgen_scsidev, pkt);
14962518Sstevel 	}
14972518Sstevel 
14982518Sstevel 	if (ucmd->uscsi_timeout > 0)
14992518Sstevel 		pkt->pkt_time = ucmd->uscsi_timeout;
15002518Sstevel 	else
15012518Sstevel 		pkt->pkt_time = SGEN_IO_TIME;
15022518Sstevel 
15032518Sstevel 	/*
15042518Sstevel 	 * Set packet options
15052518Sstevel 	 */
15062518Sstevel 	if (ucmd->uscsi_flags & USCSI_SILENT)
15072518Sstevel 		pkt->pkt_flags |= FLAG_SILENT;
15082518Sstevel 	if (ucmd->uscsi_flags & USCSI_ISOLATE)
15092518Sstevel 		pkt->pkt_flags |= FLAG_ISOLATE;
15102518Sstevel 	if (ucmd->uscsi_flags & USCSI_DIAGNOSE)
15112518Sstevel 		pkt->pkt_flags |= FLAG_DIAGNOSE;
15122518Sstevel 	if (ucmd->uscsi_flags & USCSI_RENEGOT) {
15132518Sstevel 		pkt->pkt_flags |= FLAG_RENEGOTIATE_WIDE_SYNC;
15142518Sstevel 	}
15152518Sstevel 
15166640Scth 	/* Transfer uscsi information to scsi_pkt */
15176640Scth 	(void) scsi_uscsi_pktinit(ucmd, pkt);
15186640Scth 
15192518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_make_uscsi_cmd()");
15202518Sstevel 	return (0);
15212518Sstevel }
15222518Sstevel 
15232518Sstevel 
15242518Sstevel /*
15252518Sstevel  * sgen_restart()
15262518Sstevel  * 	sgen_restart() is called after a timeout, when a command has been
15272518Sstevel  * 	postponed due to a TRAN_BUSY response from the HBA.
15282518Sstevel  */
15292518Sstevel static void
sgen_restart(void * arg)15302518Sstevel sgen_restart(void *arg)
15312518Sstevel {
15322518Sstevel 	sgen_state_t *sg_state = (sgen_state_t *)arg;
15332518Sstevel 	struct scsi_pkt *pkt;
15342518Sstevel 	struct buf *bp;
15352518Sstevel 
15362518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_restart()");
15372518Sstevel 
15382518Sstevel 	bp = sg_state->sgen_cmdbuf;
15392518Sstevel 	pkt = sg_state->sgen_cmdpkt;
15402518Sstevel 	ASSERT(bp && pkt);
15412518Sstevel 
15422518Sstevel 	SGEN_DO_ERRSTATS(sg_state, sgen_restart);
15432518Sstevel 
15442518Sstevel 	/*
15452518Sstevel 	 * If the packet is marked with the sensing flag, sgen is off running
15462518Sstevel 	 * a request sense, and *that packet* is what needs to be restarted.
15472518Sstevel 	 */
15482518Sstevel 	if (pkt->pkt_flags & FLAG_SENSING) {
15492518Sstevel 		sgen_log(sg_state, SGEN_DIAG3,
15502518Sstevel 		    "sgen_restart: restarting REQUEST SENSE");
15512518Sstevel 		pkt = sg_state->sgen_rqspkt;
15522518Sstevel 	}
15532518Sstevel 
15542518Sstevel 	if (sgen_scsi_transport(pkt) != TRAN_ACCEPT) {
15552518Sstevel 		bp->b_resid = bp->b_bcount;
15562518Sstevel 		bioerror(bp, EIO);
15572518Sstevel 		biodone(bp);
15582518Sstevel 	}
15592518Sstevel }
15602518Sstevel 
15612518Sstevel /*
15622518Sstevel  * sgen_callback()
15632518Sstevel  * 	Command completion processing
15642518Sstevel  *
15652518Sstevel  * 	sgen's completion processing is very pessimistic-- it does not retry
15662518Sstevel  * 	failed commands; instead, it allows the user application to make
15672518Sstevel  * 	decisions about what has gone wrong.
15682518Sstevel  */
15692518Sstevel static void
sgen_callback(struct scsi_pkt * pkt)15702518Sstevel sgen_callback(struct scsi_pkt *pkt)
15712518Sstevel {
15722518Sstevel 	sgen_state_t *sg_state;
15736640Scth 	struct uscsi_cmd *ucmd;
15742518Sstevel 	struct buf *bp;
15752518Sstevel 	int action;
15762518Sstevel 
15772518Sstevel 	sg_state = pkt->pkt_private;
15782518Sstevel 	/*
15792518Sstevel 	 * bp should always be the command buffer regardless of whether
15802518Sstevel 	 * this is a command completion or a request-sense completion.
15812518Sstevel 	 * This is because there is no need to biodone() the sense buf
15822518Sstevel 	 * when it completes-- we want to biodone() the actual command buffer!
15832518Sstevel 	 */
15842518Sstevel 	bp = sg_state->sgen_cmdbuf;
15852518Sstevel 	if (pkt->pkt_flags & FLAG_SENSING) {
15862518Sstevel 		ASSERT(pkt == sg_state->sgen_rqspkt);
15872518Sstevel 		sgen_log(sg_state, SGEN_DIAG2,
15882518Sstevel 		    "in sgen_callback() (SENSE completion callback)");
15892518Sstevel 	} else {
15902518Sstevel 		ASSERT(pkt == sg_state->sgen_cmdpkt);
15912518Sstevel 		sgen_log(sg_state, SGEN_DIAG2,
15922518Sstevel 		    "in sgen_callback() (command completion callback)");
15932518Sstevel 	}
15946640Scth 	ucmd = (struct uscsi_cmd *)bp->b_private;
15952518Sstevel 
15962518Sstevel 	sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: reason=0x%x resid=%ld "
15972518Sstevel 	    "state=0x%x", pkt->pkt_reason, pkt->pkt_resid, pkt->pkt_state);
15982518Sstevel 
15996640Scth 	/* Transfer scsi_pkt information to uscsi */
16006640Scth 	(void) scsi_uscsi_pktfini(pkt, ucmd);
16016640Scth 
16022518Sstevel 	if (pkt->pkt_reason != CMD_CMPLT) {
16032518Sstevel 		/*
16042518Sstevel 		 * The command did not complete.
16052518Sstevel 		 */
16062518Sstevel 		sgen_log(sg_state, SGEN_DIAG3,
16072518Sstevel 		    "sgen_callback: command did not complete");
16082518Sstevel 		action = sgen_handle_incomplete(sg_state, pkt);
16092518Sstevel 	} else if (sg_state->sgen_arq_enabled &&
16102518Sstevel 	    (pkt->pkt_state & STATE_ARQ_DONE)) {
16112518Sstevel 		/*
16122518Sstevel 		 * The auto-rqsense happened, and the packet has a filled-in
16132518Sstevel 		 * scsi_arq_status structure, pointed to by pkt_scbp.
16142518Sstevel 		 */
16152518Sstevel 		sgen_log(sg_state, SGEN_DIAG3,
16162518Sstevel 		    "sgen_callback: received auto-requested sense");
16172518Sstevel 		action = sgen_handle_autosense(sg_state, pkt);
16182518Sstevel 		ASSERT(action != FETCH_SENSE);
16192518Sstevel 	} else if (pkt->pkt_flags & FLAG_SENSING) {
16202518Sstevel 		/*
16212518Sstevel 		 * sgen was running a REQUEST SENSE. Decode the sense data and
16222518Sstevel 		 * decide what to do next.
16232518Sstevel 		 *
16242518Sstevel 		 * Clear FLAG_SENSING on the original packet for completeness.
16252518Sstevel 		 */
16262518Sstevel 		sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: received sense");
16272518Sstevel 		sg_state->sgen_cmdpkt->pkt_flags &= ~FLAG_SENSING;
16282518Sstevel 		action = sgen_handle_sense(sg_state);
16292518Sstevel 		ASSERT(action != FETCH_SENSE);
16302518Sstevel 	} else {
16312518Sstevel 		/*
16322518Sstevel 		 * Command completed and we're not getting sense. Check for
16332518Sstevel 		 * errors and decide what to do next.
16342518Sstevel 		 */
16352518Sstevel 		sgen_log(sg_state, SGEN_DIAG3,
16362518Sstevel 		    "sgen_callback: command appears complete");
16372518Sstevel 		action = sgen_check_error(sg_state, bp);
16382518Sstevel 	}
16392518Sstevel 
16402518Sstevel 	switch (action) {
16412518Sstevel 	case FETCH_SENSE:
16422518Sstevel 		/*
16432518Sstevel 		 * If there is sense to fetch, break out to prevent biodone'ing
16442518Sstevel 		 * until the sense fetch is complete.
16452518Sstevel 		 */
16466640Scth 		if (sgen_initiate_sense(sg_state,
16476640Scth 		    scsi_pkt_allocated_correctly(pkt) ?
16486640Scth 		    pkt->pkt_path_instance : 0) == 0)
16492518Sstevel 			break;
16502518Sstevel 		/*FALLTHROUGH*/
16512518Sstevel 	case COMMAND_DONE_ERROR:
16522518Sstevel 		bp->b_resid = bp->b_bcount;
16532518Sstevel 		bioerror(bp, EIO);
16542518Sstevel 		/*FALLTHROUGH*/
16552518Sstevel 	case COMMAND_DONE:
16562518Sstevel 		biodone(bp);
16572518Sstevel 		break;
16582518Sstevel 	default:
16592518Sstevel 		ASSERT(0);
16602518Sstevel 		break;
16612518Sstevel 	}
16622518Sstevel 
16632518Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_callback()");
16642518Sstevel }
16652518Sstevel 
16662518Sstevel /*
16672518Sstevel  * sgen_initiate_sense()
16682518Sstevel  *	Send the sgen_rqspkt to the target, thereby requesting sense data.
16692518Sstevel  */
16702518Sstevel static int
sgen_initiate_sense(sgen_state_t * sg_state,int path_instance)16716640Scth sgen_initiate_sense(sgen_state_t *sg_state, int path_instance)
16722518Sstevel {
16736640Scth 	/* use same path_instance as command */
16746640Scth 	if (scsi_pkt_allocated_correctly(sg_state->sgen_rqspkt))
16756640Scth 		sg_state->sgen_rqspkt->pkt_path_instance = path_instance;
16766640Scth 
16772518Sstevel 	switch (sgen_scsi_transport(sg_state->sgen_rqspkt)) {
16782518Sstevel 	case TRAN_ACCEPT:
16792518Sstevel 		sgen_log(sg_state, SGEN_DIAG3, "sgen_initiate_sense: "
16802518Sstevel 		    "sense fetch transport accepted.");
16812518Sstevel 		return (0);
16822518Sstevel 	case TRAN_BUSY:
16832518Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "sgen_initiate_sense: "
16842518Sstevel 		    "sense fetch transport busy, setting timeout.");
16852518Sstevel 		sg_state->sgen_restart_timeid = timeout(sgen_restart, sg_state,
16862518Sstevel 		    SGEN_BSY_TIMEOUT);
16872518Sstevel 		return (0);
16882518Sstevel 	default:
16892518Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "sgen_initiate_sense: "
16902518Sstevel 		    "sense fetch transport failed or busy.");
16912518Sstevel 		return (-1);
16922518Sstevel 	}
16932518Sstevel }
16942518Sstevel 
16952518Sstevel /*
16962518Sstevel  * sgen_handle_incomplete()
16972518Sstevel  * 	sgen is pessimistic, but also careful-- it doesn't try to retry
16982518Sstevel  * 	incomplete commands, but it also doesn't go resetting devices;
16992518Sstevel  * 	it is hard to tell if the device will be tolerant of that sort
17002518Sstevel  * 	of prodding.
17012518Sstevel  *
17022518Sstevel  * 	This routine has been left as a guide for the future--- the
17032518Sstevel  * 	current administration's hands-off policy may need modification.
17042518Sstevel  */
17052518Sstevel /*ARGSUSED*/
17062518Sstevel static int
sgen_handle_incomplete(sgen_state_t * sg_state,struct scsi_pkt * pkt)17072518Sstevel sgen_handle_incomplete(sgen_state_t *sg_state, struct scsi_pkt *pkt)
17082518Sstevel {
17092518Sstevel 	SGEN_DO_ERRSTATS(sg_state, sgen_incmp_err);
17102518Sstevel 	return (COMMAND_DONE_ERROR);
17112518Sstevel }
17122518Sstevel 
17132518Sstevel /*
17142518Sstevel  * sgen_handle_autosense()
17152518Sstevel  * 	Deal with SENSE data acquired automatically via the auto-request-sense
17162518Sstevel  * 	facility.
17172518Sstevel  *
17182518Sstevel  * 	Sgen takes a pessimistic view of things-- it doesn't retry commands,
17192518Sstevel  * 	and unless the device recovered from the problem, this routine returns
17202518Sstevel  * 	COMMAND_DONE_ERROR.
17212518Sstevel  */
17222518Sstevel static int
sgen_handle_autosense(sgen_state_t * sg_state,struct scsi_pkt * pkt)17232518Sstevel sgen_handle_autosense(sgen_state_t *sg_state, struct scsi_pkt *pkt)
17242518Sstevel {
17252518Sstevel 	struct scsi_arq_status *arqstat;
17262518Sstevel 	struct uscsi_cmd *ucmd =
17272518Sstevel 	    (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
17282518Sstevel 	int amt;
17292518Sstevel 
17302518Sstevel 	arqstat = (struct scsi_arq_status *)(pkt->pkt_scbp);
17312518Sstevel 
17322518Sstevel 	SGEN_DO_ERRSTATS(sg_state, sgen_autosen_rcv);
17332518Sstevel 
17342518Sstevel 	if (arqstat->sts_rqpkt_reason != CMD_CMPLT) {
17352518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: ARQ"
17362518Sstevel 		    "failed to complete.");
17372518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
17382518Sstevel 		return (COMMAND_DONE_ERROR);
17392518Sstevel 	}
17402518Sstevel 
17415597Slh195018 	if (pkt->pkt_state & STATE_XARQ_DONE) {
17425597Slh195018 		amt = MAX_SENSE_LENGTH - arqstat->sts_rqpkt_resid;
17435597Slh195018 	} else {
17445597Slh195018 		if (arqstat->sts_rqpkt_resid > SENSE_LENGTH) {
17455597Slh195018 			amt = MAX_SENSE_LENGTH - arqstat->sts_rqpkt_resid;
17465597Slh195018 		} else {
17475597Slh195018 			amt = SENSE_LENGTH - arqstat->sts_rqpkt_resid;
17485597Slh195018 		}
17495597Slh195018 	}
17505597Slh195018 
17512518Sstevel 	if (ucmd->uscsi_flags & USCSI_RQENABLE) {
17522518Sstevel 		ucmd->uscsi_rqstatus = *((char *)&arqstat->sts_rqpkt_status);
17535597Slh195018 		uchar_t rqlen = min((uchar_t)amt, ucmd->uscsi_rqlen);
17545597Slh195018 		ucmd->uscsi_rqresid = ucmd->uscsi_rqlen - rqlen;
17552518Sstevel 		ASSERT(ucmd->uscsi_rqlen && sg_state->sgen_rqs_sen);
17565597Slh195018 		bcopy(&(arqstat->sts_sensedata), sg_state->sgen_rqs_sen, rqlen);
17572518Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "sgen_handle_autosense: "
17582518Sstevel 		    "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
17592518Sstevel 		    ucmd->uscsi_rqstatus, ucmd->uscsi_rqresid);
17602518Sstevel 	}
17612518Sstevel 
17622518Sstevel 	if (arqstat->sts_rqpkt_status.sts_chk) {
17632518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: got "
17642518Sstevel 		    "check condition on auto request sense!");
17652518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
17662518Sstevel 		return (COMMAND_DONE_ERROR);
17672518Sstevel 	}
17682518Sstevel 
17692518Sstevel 	if (((arqstat->sts_rqpkt_state & STATE_XFERRED_DATA) == 0) ||
17702518Sstevel 	    (amt == 0)) {
17712518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: got "
17722518Sstevel 		    "auto-sense, but it contains no data!");
17732518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
17742518Sstevel 		return (COMMAND_DONE_ERROR);
17752518Sstevel 	}
17762518Sstevel 
17772518Sstevel 	/*
17782518Sstevel 	 * Stuff the sense data pointer into sgen_sense for later retrieval
17792518Sstevel 	 */
17802518Sstevel 	sg_state->sgen_sense = &arqstat->sts_sensedata;
17812518Sstevel 
17822518Sstevel 	/*
17832518Sstevel 	 * Now, check to see whether we got enough sense data to make any
17842518Sstevel 	 * sense out if it (heh-heh).
17852518Sstevel 	 */
17862518Sstevel 	if (amt < SUN_MIN_SENSE_LENGTH) {
17872518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: not "
17882518Sstevel 		    "enough auto sense data");
17892518Sstevel 		return (COMMAND_DONE_ERROR);
17902518Sstevel 	}
17912518Sstevel 
17922518Sstevel 	switch (arqstat->sts_sensedata.es_key) {
17932518Sstevel 	case KEY_RECOVERABLE_ERROR:
17942518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_recov_err);
17952518Sstevel 		break;
17962518Sstevel 	case KEY_NO_SENSE:
17972518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_nosen_err);
17982518Sstevel 		break;
17992518Sstevel 	default:
18002518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_unrecov_err);
18012518Sstevel 		break;
18022518Sstevel 	}
18032518Sstevel 
18042518Sstevel 	return (COMMAND_DONE);
18052518Sstevel }
18062518Sstevel 
18072518Sstevel /*
18082518Sstevel  * sgen_handle_sense()
18092518Sstevel  * 	Examine sense data that was manually fetched from the target.
18102518Sstevel  */
18112518Sstevel static int
sgen_handle_sense(sgen_state_t * sg_state)18122518Sstevel sgen_handle_sense(sgen_state_t *sg_state)
18132518Sstevel {
18142518Sstevel 	struct scsi_pkt *rqpkt = sg_state->sgen_rqspkt;
18152518Sstevel 	struct scsi_status *rqstatus = (struct scsi_status *)rqpkt->pkt_scbp;
18162518Sstevel 	struct uscsi_cmd *ucmd =
18172518Sstevel 	    (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
18182518Sstevel 	int amt;
18192518Sstevel 
18202518Sstevel 	SGEN_DO_ERRSTATS(sg_state, sgen_sense_rcv);
18212518Sstevel 
18225597Slh195018 	amt = MAX_SENSE_LENGTH - rqpkt->pkt_resid;
18235597Slh195018 
18242518Sstevel 	if (ucmd->uscsi_flags & USCSI_RQENABLE) {
18252518Sstevel 		ucmd->uscsi_rqstatus = *((char *)rqstatus);
18265597Slh195018 		uchar_t rqlen = min((uchar_t)amt, ucmd->uscsi_rqlen);
18275597Slh195018 		ucmd->uscsi_rqresid = ucmd->uscsi_rqlen - rqlen;
18282518Sstevel 		ASSERT(ucmd->uscsi_rqlen && sg_state->sgen_rqs_sen);
18295597Slh195018 		bcopy(sg_state->sgen_sense, sg_state->sgen_rqs_sen, rqlen);
18302518Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "sgen_handle_sense: "
18312518Sstevel 		    "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
18322518Sstevel 		    ucmd->uscsi_rqstatus, ucmd->uscsi_rqresid);
18332518Sstevel 	}
18342518Sstevel 
18352518Sstevel 	if (rqstatus->sts_busy) {
18362518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got busy "
18372518Sstevel 		    "on request sense");
18382518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18392518Sstevel 		return (COMMAND_DONE_ERROR);
18402518Sstevel 	}
18412518Sstevel 
18422518Sstevel 	if (rqstatus->sts_chk) {
18432518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got check "
18442518Sstevel 		    "condition on request sense!");
18452518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18462518Sstevel 		return (COMMAND_DONE_ERROR);
18472518Sstevel 	}
18482518Sstevel 
18492518Sstevel 	if ((rqpkt->pkt_state & STATE_XFERRED_DATA) == 0 || amt == 0) {
18502518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got "
18512518Sstevel 		    "sense, but it contains no data");
18522518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18532518Sstevel 		return (COMMAND_DONE_ERROR);
18542518Sstevel 	}
18552518Sstevel 
18562518Sstevel 	/*
18572518Sstevel 	 * Now, check to see whether we got enough sense data to make any
18582518Sstevel 	 * sense out if it (heh-heh).
18592518Sstevel 	 */
18602518Sstevel 	if (amt < SUN_MIN_SENSE_LENGTH) {
18612518Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: not "
18622518Sstevel 		    "enough sense data");
18632518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18642518Sstevel 		return (COMMAND_DONE_ERROR);
18652518Sstevel 	}
18662518Sstevel 
18672518Sstevel 	/*
18682518Sstevel 	 * Decode the sense data-- this was deposited here for us by the
18692518Sstevel 	 * setup in sgen_do_attach(). (note that sgen_sense is an alias for
18702518Sstevel 	 * the sd_sense field in the scsi_device).
18712518Sstevel 	 */
18722518Sstevel 	sgen_log(sg_state, SGEN_DIAG1, "Sense key is %s [0x%x]",
18732518Sstevel 	    scsi_sname(sg_state->sgen_sense->es_key),
18742518Sstevel 	    sg_state->sgen_sense->es_key);
18752518Sstevel 	switch (sg_state->sgen_sense->es_key) {
18762518Sstevel 	case KEY_RECOVERABLE_ERROR:
18772518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_recov_err);
18782518Sstevel 		break;
18792518Sstevel 	case KEY_NO_SENSE:
18802518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_nosen_err);
18812518Sstevel 		break;
18822518Sstevel 	default:
18832518Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_unrecov_err);
18842518Sstevel 		break;
18852518Sstevel 	}
18862518Sstevel 
18872518Sstevel 	return (COMMAND_DONE);
18882518Sstevel }
18892518Sstevel 
18902518Sstevel /*
18912518Sstevel  * sgen_check_error()
18922518Sstevel  * 	examine the command packet for abnormal completion.
18932518Sstevel  *
18942518Sstevel  *	sgen_check_error should only be called at the completion of the
18952518Sstevel  *	command packet.
18962518Sstevel  */
18972518Sstevel static int
sgen_check_error(sgen_state_t * sg_state,struct buf * bp)18982518Sstevel sgen_check_error(sgen_state_t *sg_state, struct buf *bp)
18992518Sstevel {
19002518Sstevel 	struct scsi_pkt *pkt = sg_state->sgen_cmdpkt;
19012518Sstevel 	struct scsi_status *status = (struct scsi_status *)pkt->pkt_scbp;
19022518Sstevel 	struct uscsi_cmd *ucmd =
19032518Sstevel 	    (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
19042518Sstevel 
19052518Sstevel 	if (status->sts_busy) {
19062518Sstevel 		sgen_log(sg_state, SGEN_DIAG1,
19072518Sstevel 		    "sgen_check_error: target is busy");
19082518Sstevel 		return (COMMAND_DONE_ERROR);
19092518Sstevel 	}
19102518Sstevel 
19112518Sstevel 	/*
19122518Sstevel 	 * pkt_resid will reflect, at this point, a residual of how many bytes
19132518Sstevel 	 * were not transferred; a non-zero pkt_resid is an error.
19142518Sstevel 	 */
19152518Sstevel 	if (pkt->pkt_resid) {
19162518Sstevel 		bp->b_resid += pkt->pkt_resid;
19172518Sstevel 	}
19182518Sstevel 
19192518Sstevel 	if (status->sts_chk) {
19202518Sstevel 		if (ucmd->uscsi_flags & USCSI_RQENABLE) {
19212518Sstevel 			if (sg_state->sgen_arq_enabled) {
19222518Sstevel 				sgen_log(sg_state, SGEN_DIAG1,
19232518Sstevel 				    "sgen_check_error: strange: target "
19242518Sstevel 				    "indicates CHECK CONDITION with auto-sense "
19252518Sstevel 				    "enabled.");
19262518Sstevel 			}
19272518Sstevel 			sgen_log(sg_state, SGEN_DIAG2, "sgen_check_error: "
19282518Sstevel 			    "target ready for sense fetch");
19292518Sstevel 			return (FETCH_SENSE);
19302518Sstevel 		} else {
19312518Sstevel 			sgen_log(sg_state, SGEN_DIAG2, "sgen_check_error: "
19322518Sstevel 			    "target indicates CHECK CONDITION");
19332518Sstevel 		}
19342518Sstevel 	}
19352518Sstevel 
19362518Sstevel 	return (COMMAND_DONE);
19372518Sstevel }
19382518Sstevel 
19392518Sstevel /*
19402518Sstevel  * sgen_tur()
19412518Sstevel  * 	test if a target is ready to operate by sending it a TUR command.
19422518Sstevel  */
19432518Sstevel static int
sgen_tur(dev_t dev)19442518Sstevel sgen_tur(dev_t dev)
19452518Sstevel {
19462518Sstevel 	char cmdblk[CDB_GROUP0];
19472518Sstevel 	struct uscsi_cmd scmd;
19482518Sstevel 
19492518Sstevel 	bzero(&scmd, sizeof (scmd));
19502518Sstevel 	scmd.uscsi_bufaddr = 0;
19512518Sstevel 	scmd.uscsi_buflen = 0;
19522518Sstevel 	bzero(cmdblk, CDB_GROUP0);
19532518Sstevel 	cmdblk[0] = (char)SCMD_TEST_UNIT_READY;
19542518Sstevel 	scmd.uscsi_flags = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_WRITE;
19552518Sstevel 	scmd.uscsi_cdb = cmdblk;
19562518Sstevel 	scmd.uscsi_cdblen = CDB_GROUP0;
19572518Sstevel 
19583368Slh195018 	return (sgen_uscsi_cmd(dev, &scmd, FKIOCTL));
19592518Sstevel }
19602518Sstevel 
19612518Sstevel /*
19622518Sstevel  * sgen_diag_ok()
19632518Sstevel  * 	given an sg_state and a desired diagnostic level, return true if
19642518Sstevel  * 	it is acceptable to output a message.
19652518Sstevel  */
19662518Sstevel /*ARGSUSED*/
19672518Sstevel static int
sgen_diag_ok(sgen_state_t * sg_state,int level)19682518Sstevel sgen_diag_ok(sgen_state_t *sg_state, int level)
19692518Sstevel {
19702518Sstevel 	int diag_lvl;
19712518Sstevel 
19722518Sstevel 	switch (level) {
19732518Sstevel 	case CE_WARN:
19742518Sstevel 	case CE_NOTE:
19752518Sstevel 	case CE_CONT:
19762518Sstevel 	case CE_PANIC:
19772518Sstevel 		return (1);
19782518Sstevel 	case SGEN_DIAG1:
19792518Sstevel 	case SGEN_DIAG2:
19802518Sstevel 	case SGEN_DIAG3:
19812518Sstevel 		if (sg_state) {
19822518Sstevel 			/*
19832518Sstevel 			 * Check to see if user overrode the diagnostics level
19842518Sstevel 			 * for this instance (either via SGEN_IOC_DIAG or via
19852518Sstevel 			 * .conf file).  If not, fall back to the global diag
19862518Sstevel 			 * level.
19872518Sstevel 			 */
19882518Sstevel 			if (sg_state->sgen_diag != -1)
19892518Sstevel 				diag_lvl = sg_state->sgen_diag;
19902518Sstevel 			else
19912518Sstevel 				diag_lvl = sgen_diag;
19922518Sstevel 		} else {
19932518Sstevel 			diag_lvl = sgen_diag;
19942518Sstevel 		}
19952518Sstevel 		if (((diag_lvl << 8) | CE_CONT) >= level) {
19962518Sstevel 			return (1);
19972518Sstevel 		} else {
19982518Sstevel 			return (0);
19992518Sstevel 		}
20002518Sstevel 	default:
20012518Sstevel 		return (1);
20022518Sstevel 	}
20032518Sstevel }
20042518Sstevel 
20052518Sstevel /*PRINTFLIKE3*/
20062518Sstevel static void
sgen_log(sgen_state_t * sg_state,int level,const char * fmt,...)20072518Sstevel sgen_log(sgen_state_t *sg_state, int level, const char *fmt, ...)
20082518Sstevel {
20092518Sstevel 	va_list	ap;
20102518Sstevel 	char buf[256];
20112518Sstevel 
20122518Sstevel 	if (!sgen_diag_ok(sg_state, level))
20132518Sstevel 		return;
20142518Sstevel 
20152518Sstevel 	va_start(ap, fmt);
20162518Sstevel 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
20172518Sstevel 	va_end(ap);
20182518Sstevel 
20192518Sstevel 	switch (level) {
20202518Sstevel 	case CE_NOTE:
20212518Sstevel 	case CE_CONT:
20222518Sstevel 	case CE_WARN:
20232518Sstevel 	case CE_PANIC:
20242518Sstevel 		if (sg_state == (sgen_state_t *)NULL) {
20252518Sstevel 			cmn_err(level, "%s", buf);
20262518Sstevel 		} else {
20277009Scth 			scsi_log(sg_state->sgen_devinfo, sgen_label, level,
20282518Sstevel 			    "%s", buf);
20292518Sstevel 		}
20302518Sstevel 		break;
20312518Sstevel 	case SGEN_DIAG1:
20322518Sstevel 	case SGEN_DIAG2:
20332518Sstevel 	case SGEN_DIAG3:
20342518Sstevel 	default:
20352518Sstevel 		if (sg_state == (sgen_state_t *)NULL) {
20367009Scth 			scsi_log(NULL, sgen_label, CE_CONT, "%s", buf);
20372518Sstevel 		} else {
20387009Scth 			scsi_log(sg_state->sgen_devinfo, sgen_label, CE_CONT,
20392518Sstevel 			    "%s", buf);
20402518Sstevel 		}
20412518Sstevel 	}
20422518Sstevel }
20432518Sstevel 
20442518Sstevel /*
20452518Sstevel  * sgen_dump_cdb()
20462518Sstevel  * 	dump out the contents of a cdb.  Take care that 'label' is not too
20472518Sstevel  * 	large, or 'buf' could overflow.
20482518Sstevel  */
20492518Sstevel static void
sgen_dump_cdb(sgen_state_t * sg_state,const char * label,union scsi_cdb * cdb,int cdblen)20502518Sstevel sgen_dump_cdb(sgen_state_t *sg_state, const char *label,
20512518Sstevel     union scsi_cdb *cdb, int cdblen)
20522518Sstevel {
20532518Sstevel 	static char hex[] = "0123456789abcdef";
20542518Sstevel 	char *buf, *p;
20552518Sstevel 	size_t nbytes;
20562518Sstevel 	int i;
20572518Sstevel 	uchar_t	*cdbp = (uchar_t *)cdb;
20582518Sstevel 
20592518Sstevel 	/*
20602518Sstevel 	 * fastpath-- if we're not able to print out, don't do all of this
20612518Sstevel 	 * extra work.
20622518Sstevel 	 */
20632518Sstevel 	if (!sgen_diag_ok(sg_state, SGEN_DIAG3))
20642518Sstevel 		return;
20652518Sstevel 
20662518Sstevel 	/*
20672518Sstevel 	 * 3 characters for each byte (because of the ' '), plus the size of
20682518Sstevel 	 * the label, plus the trailing ']' and the null character.
20692518Sstevel 	 */
20702518Sstevel 	nbytes = 3 * cdblen + strlen(label) + strlen(" CDB = [") + 2;
20712518Sstevel 	buf = kmem_alloc(nbytes, KM_SLEEP);
20722518Sstevel 	(void) sprintf(buf, "%s CDB = [", label);
20732518Sstevel 	p = &buf[strlen(buf)];
20742518Sstevel 	for (i = 0; i < cdblen; i++, cdbp++) {
20752518Sstevel 		if (i > 0)
20762518Sstevel 			*p++ = ' ';
20772518Sstevel 		*p++ = hex[(*cdbp >> 4) & 0x0f];
20782518Sstevel 		*p++ = hex[*cdbp & 0x0f];
20792518Sstevel 	}
20802518Sstevel 	*p++ = ']';
20812518Sstevel 	*p = 0;
20822518Sstevel 	sgen_log(sg_state, SGEN_DIAG3, buf);
20832518Sstevel 	kmem_free(buf, nbytes);
20842518Sstevel }
20852518Sstevel 
20862518Sstevel static void
sgen_dump_sense(sgen_state_t * sg_state,size_t rqlen,uchar_t * rqbuf)20872518Sstevel sgen_dump_sense(sgen_state_t *sg_state, size_t rqlen, uchar_t *rqbuf)
20882518Sstevel {
20892518Sstevel 	static char hex[] = "0123456789abcdef";
20902518Sstevel 	char *buf, *p;
20912518Sstevel 	size_t nbytes;
20922518Sstevel 	int i;
20932518Sstevel 
20942518Sstevel 	/*
20952518Sstevel 	 * fastpath-- if we're not able to print out, don't do all of this
20962518Sstevel 	 * extra work.
20972518Sstevel 	 */
20982518Sstevel 	if (!sgen_diag_ok(sg_state, SGEN_DIAG3))
20992518Sstevel 		return;
21002518Sstevel 
21012518Sstevel 	/*
21022518Sstevel 	 * 3 characters for each byte (because of the ' '), plus the size of
21032518Sstevel 	 * the label, plus the trailing ']' and the null character.
21042518Sstevel 	 */
21052518Sstevel 	nbytes = 3 * rqlen + strlen(" SENSE = [") + 2;
21062518Sstevel 	buf = kmem_alloc(nbytes, KM_SLEEP);
21072518Sstevel 	(void) sprintf(buf, "SENSE = [");
21082518Sstevel 	p = &buf[strlen(buf)];
21092518Sstevel 	for (i = 0; i < rqlen; i++, rqbuf++) {
21102518Sstevel 		if (i > 0)
21112518Sstevel 			*p++ = ' ';
21122518Sstevel 		*p++ = hex[(*rqbuf >> 4) & 0x0f];
21132518Sstevel 		*p++ = hex[*rqbuf & 0x0f];
21142518Sstevel 	}
21152518Sstevel 	*p++ = ']';
21162518Sstevel 	*p = 0;
21172518Sstevel 	sgen_log(sg_state, SGEN_DIAG3, buf);
21182518Sstevel 	kmem_free(buf, nbytes);
21192518Sstevel }
2120