xref: /titanic_52/usr/src/uts/common/io/scsi/targets/sgen.c (revision 193974072f41a843678abf5f61979c748687e66b)
19a016c63Sstevel /*
29a016c63Sstevel  * CDDL HEADER START
39a016c63Sstevel  *
49a016c63Sstevel  * The contents of this file are subject to the terms of the
59a016c63Sstevel  * Common Development and Distribution License (the "License").
69a016c63Sstevel  * You may not use this file except in compliance with the License.
79a016c63Sstevel  *
89a016c63Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99a016c63Sstevel  * or http://www.opensolaris.org/os/licensing.
109a016c63Sstevel  * See the License for the specific language governing permissions
119a016c63Sstevel  * and limitations under the License.
129a016c63Sstevel  *
139a016c63Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
149a016c63Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159a016c63Sstevel  * If applicable, add the following below this CDDL HEADER, with the
169a016c63Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
179a016c63Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
189a016c63Sstevel  *
199a016c63Sstevel  * CDDL HEADER END
209a016c63Sstevel  */
219a016c63Sstevel 
229a016c63Sstevel /*
23275c9da8Seschrock  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
249a016c63Sstevel  * Use is subject to license terms.
259a016c63Sstevel  */
269a016c63Sstevel 
279a016c63Sstevel /*
289a016c63Sstevel  * Copyright Siemens 1999
299a016c63Sstevel  * All rights reserved.
309a016c63Sstevel  */
319a016c63Sstevel 
329a016c63Sstevel 
339a016c63Sstevel /*
349a016c63Sstevel  * sgen - SCSI generic device driver
359a016c63Sstevel  *
369a016c63Sstevel  * The sgen driver provides user programs access to SCSI devices that
379a016c63Sstevel  * are not supported by other drivers by providing the USCSI(7I) interface.
389a016c63Sstevel  */
399a016c63Sstevel 
409a016c63Sstevel #include <sys/modctl.h>
419a016c63Sstevel #include <sys/file.h>
429a016c63Sstevel #include <sys/scsi/scsi.h>
439a016c63Sstevel #include <sys/scsi/targets/sgendef.h>
449a016c63Sstevel 
45a204de77Scth /* The name of the driver, established from the module name in _init. */
46a204de77Scth static	char *sgen_label	= NULL;
47a204de77Scth 
489a016c63Sstevel #define	DDI_NT_SGEN		"ddi_generic:scsi"
499a016c63Sstevel 
509a016c63Sstevel static char *sgen_devtypes[] = {
519a016c63Sstevel 	"direct",		/* 0x00 -- disks */
529a016c63Sstevel 	"sequential",		/* 0x01 */
539a016c63Sstevel 	"printer",		/* 0x02 */
549a016c63Sstevel 	"processor",		/* 0x03 */
559a016c63Sstevel 	"worm",			/* 0x04 */
569a016c63Sstevel 	"rodirect",		/* 0x05 */
579a016c63Sstevel 	"scanner",		/* 0x06 */
589a016c63Sstevel 	"optical",		/* 0x07 */
599a016c63Sstevel 	"changer",		/* 0x08 */
609a016c63Sstevel 	"comm",			/* 0x09 */
619a016c63Sstevel 	"prepress1",		/* 0x0a -- reserved for prepress (ASC IT8) */
629a016c63Sstevel 	"prepress2",		/* 0x0b -- reserved for prepress (ASC IT8) */
639a016c63Sstevel 	"array_ctrl",		/* 0x0c -- storage array */
649a016c63Sstevel 	"ses",			/* 0x0d -- enclosure services */
659a016c63Sstevel 	"rbc",			/* 0x0e -- simplified block */
669a016c63Sstevel 	"ocrw",			/* 0x0f -- optical card read/write */
679a016c63Sstevel 	"bridge",		/* 0x10 -- reserved for bridging expanders */
689a016c63Sstevel 	"type_0x11",		/* 0x11 */
699a016c63Sstevel 	"type_0x12",		/* 0x12 */
709a016c63Sstevel 	"type_0x13",		/* 0x13 */
719a016c63Sstevel 	"type_0x14",		/* 0x14 */
729a016c63Sstevel 	"type_0x15",		/* 0x15 */
739a016c63Sstevel 	"type_0x16",		/* 0x16 */
749a016c63Sstevel 	"type_0x17",		/* 0x17 */
759a016c63Sstevel 	"type_0x18",		/* 0x18 */
769a016c63Sstevel 	"type_0x19",		/* 0x19 */
779a016c63Sstevel 	"type_0x1a",		/* 0x1a */
789a016c63Sstevel 	"type_0x1b",		/* 0x1b */
799a016c63Sstevel 	"type_0x1c",		/* 0x1c */
809a016c63Sstevel 	"type_0x1d",		/* 0x1d */
819a016c63Sstevel 	"type_0x1e",		/* 0x1e */
829a016c63Sstevel 	"type_unknown"		/* 0x1f is "no device type" or "unknown" */
839a016c63Sstevel };
849a016c63Sstevel 
859a016c63Sstevel #define	SGEN_NDEVTYPES ((sizeof (sgen_devtypes) / sizeof (char *)))
869a016c63Sstevel 
879a016c63Sstevel #define	SGEN_INQSTRLEN 24
889a016c63Sstevel #define	SGEN_VENDID_MAX 8
899a016c63Sstevel #define	SGEN_PRODID_MAX 16
909a016c63Sstevel 
919a016c63Sstevel #define	FILL_SCSI1_LUN(devp, pkt) 					\
929a016c63Sstevel 	if ((devp)->sd_inq->inq_ansi == 0x1) {				\
939a016c63Sstevel 		int _lun;						\
949a016c63Sstevel 		_lun = ddi_prop_get_int(DDI_DEV_T_ANY, (devp)->sd_dev,	\
959a016c63Sstevel 		    DDI_PROP_DONTPASS, SCSI_ADDR_PROP_LUN, 0);		\
969a016c63Sstevel 		if (_lun > 0) {						\
979a016c63Sstevel 			((union scsi_cdb *)(pkt)->pkt_cdbp)->scc_lun =	\
989a016c63Sstevel 			    _lun;					\
999a016c63Sstevel 		}							\
1009a016c63Sstevel 	}
1019a016c63Sstevel 
1029a016c63Sstevel #define	SGEN_DO_ERRSTATS(sg_state, x)  \
1039a016c63Sstevel 	if (sg_state->sgen_kstats) { \
1049a016c63Sstevel 		struct sgen_errstats *sp; \
1059a016c63Sstevel 		sp = (struct sgen_errstats *)sg_state->sgen_kstats->ks_data; \
1069a016c63Sstevel 		sp->x.value.ui32++; \
1079a016c63Sstevel 	}
1089a016c63Sstevel 
1099a016c63Sstevel #define	SCBP_C(pkt)	((*(pkt)->pkt_scbp) & STATUS_MASK)
1109a016c63Sstevel 
1119a016c63Sstevel /*
1129a016c63Sstevel  * Standard entrypoints
1139a016c63Sstevel  */
1149a016c63Sstevel static int sgen_attach(dev_info_t *, ddi_attach_cmd_t);
1159a016c63Sstevel static int sgen_detach(dev_info_t *, ddi_detach_cmd_t);
1169a016c63Sstevel static int sgen_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1179a016c63Sstevel static int sgen_probe(dev_info_t *);
1189a016c63Sstevel static int sgen_open(dev_t *, int, int, cred_t *);
1199a016c63Sstevel static int sgen_close(dev_t, int, int, cred_t *);
1209a016c63Sstevel static int sgen_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1219a016c63Sstevel 
1229a016c63Sstevel /*
1239a016c63Sstevel  * Configuration routines
1249a016c63Sstevel  */
1259a016c63Sstevel static int sgen_do_attach(dev_info_t *);
1269a016c63Sstevel static int sgen_setup_sense(sgen_state_t *);
1279a016c63Sstevel static void sgen_create_errstats(sgen_state_t *, int);
1289a016c63Sstevel static int sgen_do_suspend(dev_info_t *);
1299a016c63Sstevel static int sgen_do_detach(dev_info_t *);
1309a016c63Sstevel static void sgen_setup_binddb(dev_info_t *);
1319a016c63Sstevel static void sgen_cleanup_binddb();
1329a016c63Sstevel 
1339a016c63Sstevel /*
1349a016c63Sstevel  * Packet transport routines
1359a016c63Sstevel  */
1366567eb0aSlh195018 static int  sgen_uscsi_cmd(dev_t, struct uscsi_cmd *, int);
1379a016c63Sstevel static int sgen_start(struct buf *);
138275c9da8Seschrock static int sgen_hold_cmdbuf(sgen_state_t *);
1399a016c63Sstevel static void sgen_rele_cmdbuf(sgen_state_t *);
1409a016c63Sstevel static int sgen_make_uscsi_cmd(sgen_state_t *, struct buf *);
1419a016c63Sstevel static void sgen_restart(void *);
1429a016c63Sstevel static void sgen_callback(struct scsi_pkt *);
1439a016c63Sstevel static int sgen_handle_autosense(sgen_state_t *, struct scsi_pkt *);
1449a016c63Sstevel static int sgen_handle_sense(sgen_state_t *);
1459a016c63Sstevel static int sgen_handle_incomplete(sgen_state_t *, struct scsi_pkt *);
1469a016c63Sstevel static int sgen_check_error(sgen_state_t *, struct buf *);
147602ca9eaScth static int sgen_initiate_sense(sgen_state_t *, int);
1489a016c63Sstevel static int sgen_scsi_transport(struct scsi_pkt *);
1499a016c63Sstevel static int sgen_tur(dev_t);
1509a016c63Sstevel 
1519a016c63Sstevel /*
1529a016c63Sstevel  * Logging/debugging routines
1539a016c63Sstevel  */
1549a016c63Sstevel static void sgen_log(sgen_state_t  *, int,  const char *, ...);
1559a016c63Sstevel static int sgen_diag_ok(sgen_state_t *, int);
1569a016c63Sstevel static void sgen_dump_cdb(sgen_state_t *, const char *, union scsi_cdb *, int);
1579a016c63Sstevel static void sgen_dump_sense(sgen_state_t *, size_t, uchar_t *);
1589a016c63Sstevel 
1599a016c63Sstevel int sgen_diag = 0;
1609a016c63Sstevel int sgen_sporadic_failures = 0;
1619a016c63Sstevel int sgen_force_manual_sense = 0;
1629a016c63Sstevel struct sgen_binddb sgen_binddb;
1639a016c63Sstevel 
1649a016c63Sstevel static struct cb_ops sgen_cb_ops = {
1659a016c63Sstevel 	sgen_open,			/* open */
1669a016c63Sstevel 	sgen_close,			/* close */
1679a016c63Sstevel 	nodev,				/* strategy */
1689a016c63Sstevel 	nodev,				/* print */
1699a016c63Sstevel 	nodev,				/* dump */
1709a016c63Sstevel 	nodev,				/* read */
1719a016c63Sstevel 	nodev,				/* write */
1729a016c63Sstevel 	sgen_ioctl,			/* ioctl */
1739a016c63Sstevel 	nodev,				/* devmap */
1749a016c63Sstevel 	nodev,				/* mmap */
1759a016c63Sstevel 	nodev,				/* segmap */
1769a016c63Sstevel 	nochpoll,			/* poll */
1779a016c63Sstevel 	ddi_prop_op,			/* cb_prop_op */
1789a016c63Sstevel 	0,				/* streamtab  */
1799a016c63Sstevel 	D_MP | D_NEW | D_HOTPLUG	/* Driver compatibility flag */
1809a016c63Sstevel };
1819a016c63Sstevel 
1829a016c63Sstevel static struct dev_ops sgen_dev_ops = {
1839a016c63Sstevel 	DEVO_REV,		/* devo_rev, */
1849a016c63Sstevel 	0,			/* refcnt  */
1859a016c63Sstevel 	sgen_getinfo,		/* info */
1869a016c63Sstevel 	nodev,			/* identify */
1879a016c63Sstevel 	sgen_probe,		/* probe */
1889a016c63Sstevel 	sgen_attach,		/* attach */
1899a016c63Sstevel 	sgen_detach,		/* detach */
1909a016c63Sstevel 	nodev,			/* reset */
1919a016c63Sstevel 	&sgen_cb_ops,		/* driver operations */
1929a016c63Sstevel 	(struct bus_ops *)0,	/* bus operations */
193*19397407SSherry Moore 	NULL,			/* power */
194*19397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
1959a016c63Sstevel };
1969a016c63Sstevel 
1979a016c63Sstevel static void *sgen_soft_state = NULL;
1989a016c63Sstevel 
1999a016c63Sstevel static struct modldrv modldrv = {
200*19397407SSherry Moore 	&mod_driverops, "SCSI generic driver", &sgen_dev_ops
2019a016c63Sstevel };
2029a016c63Sstevel 
2039a016c63Sstevel static struct modlinkage modlinkage = {
2049a016c63Sstevel 	MODREV_1, &modldrv, NULL
2059a016c63Sstevel };
2069a016c63Sstevel 
2079a016c63Sstevel int
2089a016c63Sstevel _init(void)
2099a016c63Sstevel {
2109a016c63Sstevel 	int err;
2119a016c63Sstevel 
212a204de77Scth 	/* establish driver name from module name */
213a204de77Scth 	sgen_label = (char *)mod_modname(&modlinkage);
214a204de77Scth 
2159a016c63Sstevel 	sgen_log(NULL, SGEN_DIAG2, "in sgen_init()");
2169a016c63Sstevel 	if ((err = ddi_soft_state_init(&sgen_soft_state,
2179a016c63Sstevel 	    sizeof (sgen_state_t), SGEN_ESTIMATED_NUM_DEVS)) != 0) {
2189a016c63Sstevel 		goto done;
2199a016c63Sstevel 	}
2209a016c63Sstevel 
2219a016c63Sstevel 	if ((err = mod_install(&modlinkage)) != 0) {
2229a016c63Sstevel 		ddi_soft_state_fini(&sgen_soft_state);
2239a016c63Sstevel 		goto done;
2249a016c63Sstevel 	}
2259a016c63Sstevel 
2269a016c63Sstevel done:
2279a016c63Sstevel 	sgen_log(NULL, SGEN_DIAG2, "%s sgen_init()", err ? "failed" : "done");
2289a016c63Sstevel 	return (err);
2299a016c63Sstevel }
2309a016c63Sstevel 
2319a016c63Sstevel int
2329a016c63Sstevel _fini(void)
2339a016c63Sstevel {
2349a016c63Sstevel 	int err;
2359a016c63Sstevel 	sgen_log(NULL, SGEN_DIAG2, "in sgen_fini()");
2369a016c63Sstevel 
2379a016c63Sstevel 	if ((err = mod_remove(&modlinkage)) != 0) {
2389a016c63Sstevel 		goto done;
2399a016c63Sstevel 	}
2409a016c63Sstevel 
2419a016c63Sstevel 	ddi_soft_state_fini(&sgen_soft_state);
2429a016c63Sstevel 	sgen_cleanup_binddb();
2439a016c63Sstevel 
2449a016c63Sstevel done:
2459a016c63Sstevel 	sgen_log(NULL, SGEN_DIAG2, "%s sgen_fini()", err ? "failed" : "done");
2469a016c63Sstevel 	return (err);
2479a016c63Sstevel }
2489a016c63Sstevel 
2499a016c63Sstevel int
2509a016c63Sstevel _info(struct modinfo *modinfop)
2519a016c63Sstevel {
2529a016c63Sstevel 	return (mod_info(&modlinkage, modinfop));
2539a016c63Sstevel }
2549a016c63Sstevel 
2559a016c63Sstevel /*
2569a016c63Sstevel  * sgen_typename()
2579a016c63Sstevel  * 	return a device type's name by looking it up in the sgen_devtypes table.
2589a016c63Sstevel  */
2599a016c63Sstevel static char *
2609a016c63Sstevel sgen_typename(uchar_t typeno)
2619a016c63Sstevel {
2629a016c63Sstevel 	if (typeno >= SGEN_NDEVTYPES)
2639a016c63Sstevel 		return ("type_unknown");
2649a016c63Sstevel 	return (sgen_devtypes[typeno]);
2659a016c63Sstevel }
2669a016c63Sstevel 
2679a016c63Sstevel /*
2689a016c63Sstevel  * sgen_typenum()
2699a016c63Sstevel  * 	return a device type's number by looking it up in the sgen_devtypes
2709a016c63Sstevel  * 	table.
2719a016c63Sstevel  */
2729a016c63Sstevel static int
2739a016c63Sstevel sgen_typenum(const char *typename, uchar_t *typenum)
2749a016c63Sstevel {
2759a016c63Sstevel 	int i;
2769a016c63Sstevel 	for (i = 0; i < SGEN_NDEVTYPES; i++) {
2779a016c63Sstevel 		if (strcasecmp(sgen_devtypes[i], typename) == 0) {
2789a016c63Sstevel 			*typenum = (uchar_t)i;
2799a016c63Sstevel 			return (0);
2809a016c63Sstevel 		}
2819a016c63Sstevel 	}
2829a016c63Sstevel 	return (-1);
2839a016c63Sstevel }
2849a016c63Sstevel 
2859a016c63Sstevel /*
2869a016c63Sstevel  * sgen_setup_binddb()
2879a016c63Sstevel  * 	initialize a data structure which stores all of the information about
2889a016c63Sstevel  * 	which devices and device types the driver should bind to.
2899a016c63Sstevel  */
2909a016c63Sstevel static void
2919a016c63Sstevel sgen_setup_binddb(dev_info_t *dip)
2929a016c63Sstevel {
2939a016c63Sstevel 	char **strs = NULL, *cp, *pcp, *vcp;
2949a016c63Sstevel 	uint_t nelems, pcplen, vcplen, idx;
2959a016c63Sstevel 
2969a016c63Sstevel 	ASSERT(sgen_binddb.sdb_init == 0);
2979a016c63Sstevel 	ASSERT(MUTEX_HELD(&sgen_binddb.sdb_lock));
2989a016c63Sstevel 
2999a016c63Sstevel 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3009a016c63Sstevel 	    "device-type-config-list", &strs, &nelems) == DDI_PROP_SUCCESS) {
3019a016c63Sstevel 		/*
3029a016c63Sstevel 		 * for each device type specifier make a copy and put it into a
3039a016c63Sstevel 		 * node in the binddb.
3049a016c63Sstevel 		 */
3059a016c63Sstevel 		for (idx = 0; idx < nelems; idx++) {
3069a016c63Sstevel 			sgen_type_node_t *nodep;
3079a016c63Sstevel 			uchar_t devtype;
3089a016c63Sstevel 			cp = strs[idx];
3099a016c63Sstevel 			if (sgen_typenum(cp, &devtype) != 0) {
3109a016c63Sstevel 				sgen_log(NULL, CE_WARN,
3119a016c63Sstevel 				    "unknown device type '%s', "
3129a016c63Sstevel 				    "device unit-address @%s",
3139a016c63Sstevel 				    cp, ddi_get_name_addr(dip));
3149a016c63Sstevel 				continue;
3159a016c63Sstevel 			}
3169a016c63Sstevel 			nodep = kmem_zalloc(sizeof (sgen_type_node_t),
3179a016c63Sstevel 			    KM_SLEEP);
3189a016c63Sstevel 			nodep->node_type = devtype;
3199a016c63Sstevel 			nodep->node_next = sgen_binddb.sdb_type_nodes;
3209a016c63Sstevel 			sgen_binddb.sdb_type_nodes = nodep;
3219a016c63Sstevel 
3229a016c63Sstevel 			sgen_log(NULL, SGEN_DIAG2, "found device type "
3239a016c63Sstevel 			    "'%s' in device-type-config-list, "
3249a016c63Sstevel 			    "device unit-address @%s",
3259a016c63Sstevel 			    cp, ddi_get_name_addr(dip));
3269a016c63Sstevel 		}
3279a016c63Sstevel 		ddi_prop_free(strs);
3289a016c63Sstevel 	}
3299a016c63Sstevel 
3309a016c63Sstevel 	/*
3319a016c63Sstevel 	 * for each Vendor/Product inquiry pair, build a node and put it
3329a016c63Sstevel 	 * into the the binddb.
3339a016c63Sstevel 	 */
3349a016c63Sstevel 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3359a016c63Sstevel 	    "inquiry-config-list", &strs, &nelems) == DDI_PROP_SUCCESS) {
3369a016c63Sstevel 
3379a016c63Sstevel 		if (nelems % 2 == 1) {
3389a016c63Sstevel 			sgen_log(NULL, CE_WARN, "inquiry-config-list must "
3399a016c63Sstevel 			    "contain Vendor/Product pairs, "
3409a016c63Sstevel 			    "device unit-address @%s",
3419a016c63Sstevel 			    ddi_get_name_addr(dip));
3429a016c63Sstevel 			nelems--;
3439a016c63Sstevel 		}
3449a016c63Sstevel 		for (idx = 0; idx < nelems; idx += 2) {
3459a016c63Sstevel 			sgen_inq_node_t *nodep;
3469a016c63Sstevel 			/*
3479a016c63Sstevel 			 * Grab vendor and product ID.
3489a016c63Sstevel 			 */
3499a016c63Sstevel 			vcp = strs[idx];
3509a016c63Sstevel 			vcplen = strlen(vcp);
3519a016c63Sstevel 			if (vcplen == 0 || vcplen > SGEN_VENDID_MAX) {
3529a016c63Sstevel 				sgen_log(NULL, CE_WARN,
3539a016c63Sstevel 				    "Invalid vendor ID '%s', "
3549a016c63Sstevel 				    "device unit-address @%s",
3559a016c63Sstevel 				    vcp, ddi_get_name_addr(dip));
3569a016c63Sstevel 				continue;
3579a016c63Sstevel 			}
3589a016c63Sstevel 
3599a016c63Sstevel 			pcp = strs[idx + 1];
3609a016c63Sstevel 			pcplen = strlen(pcp);
3619a016c63Sstevel 			if (pcplen == 0 || pcplen > SGEN_PRODID_MAX) {
3629a016c63Sstevel 				sgen_log(NULL, CE_WARN,
3639a016c63Sstevel 				    "Invalid product ID '%s', "
3649a016c63Sstevel 				    "device unit-address @%s",
3659a016c63Sstevel 				    pcp, ddi_get_name_addr(dip));
3669a016c63Sstevel 				continue;
3679a016c63Sstevel 			}
3689a016c63Sstevel 
3699a016c63Sstevel 			nodep = kmem_zalloc(sizeof (sgen_inq_node_t),
3709a016c63Sstevel 			    KM_SLEEP);
3719a016c63Sstevel 			nodep->node_vendor = kmem_alloc(vcplen + 1, KM_SLEEP);
3729a016c63Sstevel 			(void) strcpy(nodep->node_vendor, vcp);
3739a016c63Sstevel 			nodep->node_product = kmem_alloc(pcplen + 1, KM_SLEEP);
3749a016c63Sstevel 			(void) strcpy(nodep->node_product, pcp);
3759a016c63Sstevel 
3769a016c63Sstevel 			nodep->node_next = sgen_binddb.sdb_inq_nodes;
3779a016c63Sstevel 			sgen_binddb.sdb_inq_nodes = nodep;
3789a016c63Sstevel 
3799a016c63Sstevel 			sgen_log(NULL, SGEN_DIAG2, "found inquiry string "
3809a016c63Sstevel 			    "'%s' '%s' in device-type-config-list, "
3819a016c63Sstevel 			    "device unit-address @%s",
3829a016c63Sstevel 			    nodep->node_vendor, nodep->node_product,
3839a016c63Sstevel 			    ddi_get_name_addr(dip));
3849a016c63Sstevel 		}
3859a016c63Sstevel 		ddi_prop_free(strs);
3869a016c63Sstevel 	}
3879a016c63Sstevel 
3889a016c63Sstevel 	sgen_binddb.sdb_init = 1;
3899a016c63Sstevel }
3909a016c63Sstevel 
3919a016c63Sstevel /*
3929a016c63Sstevel  * sgen_cleanup_binddb()
3939a016c63Sstevel  * 	deallocate data structures for binding database.
3949a016c63Sstevel  */
3959a016c63Sstevel static void
3969a016c63Sstevel sgen_cleanup_binddb()
3979a016c63Sstevel {
3989a016c63Sstevel 	sgen_inq_node_t *inqp, *inqnextp;
3999a016c63Sstevel 	sgen_type_node_t *typep, *typenextp;
4009a016c63Sstevel 
4019a016c63Sstevel 	mutex_enter(&sgen_binddb.sdb_lock);
4029a016c63Sstevel 	if (sgen_binddb.sdb_init == 0) {
4039a016c63Sstevel 		mutex_exit(&sgen_binddb.sdb_lock);
4049a016c63Sstevel 		return;
4059a016c63Sstevel 	}
4069a016c63Sstevel 
4079a016c63Sstevel 	for (inqp = sgen_binddb.sdb_inq_nodes; inqp != NULL; inqp = inqnextp) {
4089a016c63Sstevel 		inqnextp = inqp->node_next;
4099a016c63Sstevel 		ASSERT(inqp->node_vendor && inqp->node_product);
4109a016c63Sstevel 		kmem_free(inqp->node_vendor,
4119a016c63Sstevel 		    strlen(inqp->node_vendor) + 1);
4129a016c63Sstevel 		kmem_free(inqp->node_product,
4139a016c63Sstevel 		    strlen(inqp->node_product) + 1);
4149a016c63Sstevel 		kmem_free(inqp, sizeof (sgen_inq_node_t));
4159a016c63Sstevel 	}
4169a016c63Sstevel 
4179a016c63Sstevel 	for (typep = sgen_binddb.sdb_type_nodes; typep != NULL;
4189a016c63Sstevel 	    typep = typenextp) {
4199a016c63Sstevel 		typenextp = typep->node_next;
4209a016c63Sstevel 		kmem_free(typep, sizeof (sgen_type_node_t));
4219a016c63Sstevel 	}
4229a016c63Sstevel 	mutex_exit(&sgen_binddb.sdb_lock);
4239a016c63Sstevel }
4249a016c63Sstevel 
4259a016c63Sstevel /*
4269a016c63Sstevel  * sgen_bind_byinq()
4279a016c63Sstevel  * 	lookup a device in the binding database by its inquiry data.
4289a016c63Sstevel  */
4299a016c63Sstevel static int
4309a016c63Sstevel sgen_bind_byinq(dev_info_t *dip)
4319a016c63Sstevel {
4329a016c63Sstevel 	sgen_inq_node_t *nodep;
4339a016c63Sstevel 	char vend_str[SGEN_VENDID_MAX+1];
4349a016c63Sstevel 	char prod_str[SGEN_PRODID_MAX+1];
4359a016c63Sstevel 	struct scsi_device *scsidevp;
4369a016c63Sstevel 
4379a016c63Sstevel 	scsidevp = ddi_get_driver_private(dip);
4389a016c63Sstevel 
4399a016c63Sstevel 	/*
4409a016c63Sstevel 	 * inq_vid and inq_pid are laid out by the protocol in order in the
4419a016c63Sstevel 	 * inquiry structure, and are not delimited by \0.
4429a016c63Sstevel 	 */
4439a016c63Sstevel 	bcopy(scsidevp->sd_inq->inq_vid, vend_str, SGEN_VENDID_MAX);
4449a016c63Sstevel 	vend_str[SGEN_VENDID_MAX] = '\0';
4459a016c63Sstevel 	bcopy(scsidevp->sd_inq->inq_pid, prod_str, SGEN_PRODID_MAX);
4469a016c63Sstevel 	prod_str[SGEN_PRODID_MAX] = '\0';
4479a016c63Sstevel 
4489a016c63Sstevel 	for (nodep = sgen_binddb.sdb_inq_nodes; nodep != NULL;
4499a016c63Sstevel 	    nodep = nodep->node_next) {
4509a016c63Sstevel 		/*
4519a016c63Sstevel 		 * Allow the "*" wildcard to match all vendor IDs.
4529a016c63Sstevel 		 */
4539a016c63Sstevel 		if (strcmp(nodep->node_vendor, "*") != 0) {
4549a016c63Sstevel 			if (strncasecmp(nodep->node_vendor, vend_str,
4559a016c63Sstevel 			    strlen(nodep->node_vendor)) != 0) {
4569a016c63Sstevel 				continue;
4579a016c63Sstevel 			}
4589a016c63Sstevel 		}
4599a016c63Sstevel 
4609a016c63Sstevel 		/*
4619a016c63Sstevel 		 * Using strncasecmp() with the key length allows substring
4629a016c63Sstevel 		 * matching for product data.
4639a016c63Sstevel 		 */
4649a016c63Sstevel 		if (strncasecmp(nodep->node_product, prod_str,
4659a016c63Sstevel 		    strlen(nodep->node_product)) == 0) {
4669a016c63Sstevel 			return (0);
4679a016c63Sstevel 		}
4689a016c63Sstevel 	}
4699a016c63Sstevel 	return (-1);
4709a016c63Sstevel }
4719a016c63Sstevel 
4729a016c63Sstevel /*
4739a016c63Sstevel  * sgen_bind_bytype()
4749a016c63Sstevel  * 	lookup a device type in the binding database; if found, return a
4759a016c63Sstevel  * 	format string corresponding to the string in the .conf file.
4769a016c63Sstevel  */
4779a016c63Sstevel static int
4789a016c63Sstevel sgen_bind_bytype(dev_info_t *dip)
4799a016c63Sstevel {
4809a016c63Sstevel 	sgen_type_node_t *nodep;
4819a016c63Sstevel 	struct scsi_device *scsidevp;
4829a016c63Sstevel 
4839a016c63Sstevel 	scsidevp = ddi_get_driver_private(dip);
4849a016c63Sstevel 
4859a016c63Sstevel 	for (nodep = sgen_binddb.sdb_type_nodes; nodep != NULL;
4869a016c63Sstevel 	    nodep = nodep->node_next) {
4879a016c63Sstevel 		if (nodep->node_type == scsidevp->sd_inq->inq_dtype) {
4889a016c63Sstevel 			return (0);
4899a016c63Sstevel 		}
4909a016c63Sstevel 	}
4919a016c63Sstevel 	return (-1);
4929a016c63Sstevel }
4939a016c63Sstevel 
4949a016c63Sstevel /*
4959a016c63Sstevel  * sgen_get_binding()
4969a016c63Sstevel  * 	Check to see if the device in question matches the criteria for
4979a016c63Sstevel  * 	sgen to bind.
4989a016c63Sstevel  *
4999a016c63Sstevel  * 	Either the .conf file must specify a device_type entry which
5009a016c63Sstevel  * 	matches the SCSI device type of this device, or the inquiry
5019a016c63Sstevel  * 	string provided by the device must match an inquiry string specified
5029a016c63Sstevel  * 	in the .conf file.  Inquiry data is matched first.
5039a016c63Sstevel  */
5049a016c63Sstevel static int
5059a016c63Sstevel sgen_get_binding(dev_info_t *dip)
5069a016c63Sstevel {
5079a016c63Sstevel 	int retval = 0;
5089a016c63Sstevel 
5099a016c63Sstevel 	mutex_enter(&sgen_binddb.sdb_lock);
5109a016c63Sstevel 	if (sgen_binddb.sdb_init == 0)
5119a016c63Sstevel 		sgen_setup_binddb(dip);
5129a016c63Sstevel 	mutex_exit(&sgen_binddb.sdb_lock);
5139a016c63Sstevel 
5149a016c63Sstevel 
5159a016c63Sstevel 	/*
5169a016c63Sstevel 	 * Check device-type-config-list for a match by device type.
5179a016c63Sstevel 	 */
5189a016c63Sstevel 	if (sgen_bind_bytype(dip) == 0)
5199a016c63Sstevel 		goto done;
5209a016c63Sstevel 
5219a016c63Sstevel 	/*
5229a016c63Sstevel 	 * Check inquiry-config-list for a match by Vendor/Product ID.
5239a016c63Sstevel 	 */
5249a016c63Sstevel 	if (sgen_bind_byinq(dip) == 0)
5259a016c63Sstevel 		goto done;
5269a016c63Sstevel 
5279a016c63Sstevel 	retval = -1;
5289a016c63Sstevel done:
5299a016c63Sstevel 	return (retval);
5309a016c63Sstevel }
5319a016c63Sstevel 
5329a016c63Sstevel /*
5339a016c63Sstevel  * sgen_attach()
5349a016c63Sstevel  * 	attach(9e) entrypoint.
5359a016c63Sstevel  */
5369a016c63Sstevel static int
5379a016c63Sstevel sgen_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5389a016c63Sstevel {
5399a016c63Sstevel 	int err;
5409a016c63Sstevel 
5419a016c63Sstevel 	sgen_log(NULL, SGEN_DIAG2, "in sgen_attach(), device unit-address @%s",
5429a016c63Sstevel 	    ddi_get_name_addr(dip));
5439a016c63Sstevel 
5449a016c63Sstevel 	switch (cmd) {
5459a016c63Sstevel 	case DDI_ATTACH:
5469a016c63Sstevel 		err = sgen_do_attach(dip);
5479a016c63Sstevel 		break;
5489a016c63Sstevel 	case DDI_RESUME:
5499a016c63Sstevel 		err = DDI_SUCCESS;
5509a016c63Sstevel 		break;
5519a016c63Sstevel 	case DDI_PM_RESUME:
5529a016c63Sstevel 	default:
5539a016c63Sstevel 		err = DDI_FAILURE;
5549a016c63Sstevel 		break;
5559a016c63Sstevel 	}
5569a016c63Sstevel 
5579a016c63Sstevel done:
5589a016c63Sstevel 	sgen_log(NULL, SGEN_DIAG2, "%s sgen_attach(), device unit-address @%s",
5599a016c63Sstevel 	    err == DDI_SUCCESS ? "done" : "failed", ddi_get_name_addr(dip));
5609a016c63Sstevel 	return (err);
5619a016c63Sstevel }
5629a016c63Sstevel 
5639a016c63Sstevel /*
5649a016c63Sstevel  * sgen_do_attach()
5659a016c63Sstevel  *	handle the nitty details of attach.
5669a016c63Sstevel  */
5679a016c63Sstevel static int
5689a016c63Sstevel sgen_do_attach(dev_info_t *dip)
5699a016c63Sstevel {
5709a016c63Sstevel 	int instance;
5719a016c63Sstevel 	struct scsi_device *scsidevp;
5729a016c63Sstevel 	sgen_state_t *sg_state;
5739a016c63Sstevel 	uchar_t devtype;
5749a016c63Sstevel 	struct scsi_inquiry *inq;
5759a016c63Sstevel 
5769a016c63Sstevel 	instance = ddi_get_instance(dip);
5779a016c63Sstevel 
5789a016c63Sstevel 	scsidevp = ddi_get_driver_private(dip);
5799a016c63Sstevel 	ASSERT(scsidevp);
5809a016c63Sstevel 
5819a016c63Sstevel 	sgen_log(NULL, SGEN_DIAG2, "sgen_do_attach: instance = %d, "
5829a016c63Sstevel 	    "device unit-address @%s", instance, ddi_get_name_addr(dip));
5839a016c63Sstevel 
5849a016c63Sstevel 	/*
5859a016c63Sstevel 	 * Probe the device in order to get its device type to name the minor
5869a016c63Sstevel 	 * node.
5879a016c63Sstevel 	 */
5889a016c63Sstevel 	if (scsi_probe(scsidevp, NULL_FUNC) != SCSIPROBE_EXISTS) {
5899a016c63Sstevel 		scsi_unprobe(scsidevp);
5909a016c63Sstevel 		return (DDI_FAILURE);
5919a016c63Sstevel 	}
5929a016c63Sstevel 
5939a016c63Sstevel 	if (ddi_soft_state_zalloc(sgen_soft_state, instance) != DDI_SUCCESS) {
5949a016c63Sstevel 		sgen_log(NULL, SGEN_DIAG1,
5959a016c63Sstevel 		    "sgen_do_attach: failed to allocate softstate, "
5969a016c63Sstevel 		    "device unit-address @%s", ddi_get_name_addr(dip));
5979a016c63Sstevel 		scsi_unprobe(scsidevp);
5989a016c63Sstevel 		return (DDI_FAILURE);
5999a016c63Sstevel 	}
6009a016c63Sstevel 
6019a016c63Sstevel 	inq = scsidevp->sd_inq;		/* valid while device is probed... */
6029a016c63Sstevel 	devtype = inq->inq_dtype;
6039a016c63Sstevel 
6049a016c63Sstevel 	sg_state = ddi_get_soft_state(sgen_soft_state, instance);
6059a016c63Sstevel 	sg_state->sgen_scsidev = scsidevp;
6069a016c63Sstevel 	scsidevp->sd_dev = dip;
6079a016c63Sstevel 
6089a016c63Sstevel 	/*
6099a016c63Sstevel 	 * Now that sg_state->sgen_scsidev is initialized, it's ok to
6109a016c63Sstevel 	 * call sgen_log with sg_state instead of NULL.
6119a016c63Sstevel 	 */
6129a016c63Sstevel 
6139a016c63Sstevel 	/*
6149a016c63Sstevel 	 * If the user specified the sgen_diag property, override the global
6159a016c63Sstevel 	 * sgen_diag setting by setting sg_state's sgen_diag value.  If the
6169a016c63Sstevel 	 * user gave a value out of range, default to '0'.
6179a016c63Sstevel 	 */
6189a016c63Sstevel 	sg_state->sgen_diag = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
6199a016c63Sstevel 	    "sgen-diag", -1);
6209a016c63Sstevel 
6219a016c63Sstevel 	if (sg_state->sgen_diag != -1) {
6229a016c63Sstevel 		if (sg_state->sgen_diag < 0 || sg_state->sgen_diag > 3)
6239a016c63Sstevel 			sg_state->sgen_diag = 0;
6249a016c63Sstevel 	}
6259a016c63Sstevel 
6269a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2,
6279a016c63Sstevel 	    "sgen_do_attach: sgen_soft_state=0x%p, instance=%d, "
6289a016c63Sstevel 	    "device unit-address @%s",
6299a016c63Sstevel 	    sgen_soft_state, instance, ddi_get_name_addr(dip));
6309a016c63Sstevel 
6319a016c63Sstevel 	/*
6329a016c63Sstevel 	 * For simplicity, the minor number == the instance number
6339a016c63Sstevel 	 */
6349a016c63Sstevel 	if (ddi_create_minor_node(dip, sgen_typename(devtype), S_IFCHR,
6359a016c63Sstevel 	    instance, DDI_NT_SGEN, NULL) == DDI_FAILURE) {
6369a016c63Sstevel 		scsi_unprobe(scsidevp);
6379a016c63Sstevel 		ddi_prop_remove_all(dip);
6389a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1,
6399a016c63Sstevel 		    "sgen_do_attach: minor node creation failed, "
6409a016c63Sstevel 		    "device unit-address @%s", ddi_get_name_addr(dip));
6419a016c63Sstevel 		ddi_soft_state_free(sgen_soft_state, instance);
6429a016c63Sstevel 		return (DDI_FAILURE);
6439a016c63Sstevel 	}
6449a016c63Sstevel 
6459a016c63Sstevel 	/*
6469a016c63Sstevel 	 * Allocate the command buffer, then create a condition variable for
6479a016c63Sstevel 	 * managing it; mark the command buffer as free.
6489a016c63Sstevel 	 */
6499a016c63Sstevel 	sg_state->sgen_cmdbuf = getrbuf(KM_SLEEP);
6509a016c63Sstevel 	cv_init(&sg_state->sgen_cmdbuf_cv, NULL, CV_DRIVER, NULL);
6519a016c63Sstevel 
6529a016c63Sstevel 	SGEN_CLR_BUSY(sg_state);
6539a016c63Sstevel 	SGEN_CLR_OPEN(sg_state);
6549a016c63Sstevel 	SGEN_CLR_SUSP(sg_state);
6559a016c63Sstevel 
6569a016c63Sstevel 	/*
6579a016c63Sstevel 	 * If the hba and the target both support wide xfers, enable them.
6589a016c63Sstevel 	 */
6599a016c63Sstevel 	if (scsi_ifgetcap(&sg_state->sgen_scsiaddr, "wide-xfer", 1) != -1) {
6609a016c63Sstevel 		int wide = 0;
6619a016c63Sstevel 		if ((inq->inq_rdf == RDF_SCSI2) &&
6629a016c63Sstevel 		    (inq->inq_wbus16 || inq->inq_wbus32))
6639a016c63Sstevel 			wide = 1;
6649a016c63Sstevel 		if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "wide-xfer",
6659a016c63Sstevel 		    wide, 1) == 1) {
6669a016c63Sstevel 			sgen_log(sg_state, SGEN_DIAG1,
6679a016c63Sstevel 			    "sgen_attach: wide xfer %s, "
6689a016c63Sstevel 			    "device unit-address @%s",
6699a016c63Sstevel 			    wide ? "enabled" : "disabled",
6709a016c63Sstevel 			    ddi_get_name_addr(dip));
6719a016c63Sstevel 		}
6729a016c63Sstevel 	}
6739a016c63Sstevel 
6749a016c63Sstevel 	/*
6759a016c63Sstevel 	 * This is a little debugging code-- since the codepath for auto-sense
6769a016c63Sstevel 	 * and 'manual' sense is split, toggling this variable will make
6779a016c63Sstevel 	 * sgen act as though the adapter in question can't do auto-sense.
6789a016c63Sstevel 	 */
6799a016c63Sstevel 	if (sgen_force_manual_sense) {
6809a016c63Sstevel 		if (scsi_ifsetcap(&sg_state->sgen_scsiaddr, "auto-rqsense",
6819a016c63Sstevel 		    0, 1) == 1) {
6829a016c63Sstevel 			sg_state->sgen_arq_enabled = 0;
6839a016c63Sstevel 		} else {
6849a016c63Sstevel 			sg_state->sgen_arq_enabled = 1;
6859a016c63Sstevel 		}
6869a016c63Sstevel 	} else {
6879a016c63Sstevel 		/*
6889a016c63Sstevel 		 * Enable autorequest sense, if supported
6899a016c63Sstevel 		 */
6909a016c63Sstevel 		if (scsi_ifgetcap(&sg_state->sgen_scsiaddr,
6919a016c63Sstevel 		    "auto-rqsense", 1) != 1) {
6929a016c63Sstevel 			if (scsi_ifsetcap(&sg_state->sgen_scsiaddr,
6939a016c63Sstevel 			    "auto-rqsense", 1, 1) == 1) {
6949a016c63Sstevel 				sg_state->sgen_arq_enabled = 1;
6959a016c63Sstevel 				sgen_log(sg_state, SGEN_DIAG1,
6969a016c63Sstevel 				    "sgen_attach: auto-request-sense enabled, "
6979a016c63Sstevel 				    "device unit-address @%s",
6989a016c63Sstevel 				    ddi_get_name_addr(dip));
6999a016c63Sstevel 			} else {
7009a016c63Sstevel 				sg_state->sgen_arq_enabled = 0;
7019a016c63Sstevel 				sgen_log(sg_state, SGEN_DIAG1,
7029a016c63Sstevel 				    "sgen_attach: auto-request-sense disabled, "
7039a016c63Sstevel 				    "device unit-address @%s",
7049a016c63Sstevel 				    ddi_get_name_addr(dip));
7059a016c63Sstevel 			}
7069a016c63Sstevel 		} else {
7079a016c63Sstevel 			sg_state->sgen_arq_enabled = 1;	/* already enabled */
7089a016c63Sstevel 			sgen_log(sg_state, SGEN_DIAG1,
7099a016c63Sstevel 			    "sgen_attach: auto-request-sense enabled, "
7109a016c63Sstevel 			    "device unit-address @%s", ddi_get_name_addr(dip));
7119a016c63Sstevel 		}
7129a016c63Sstevel 	}
7139a016c63Sstevel 
7149a016c63Sstevel 	/*
7159a016c63Sstevel 	 * Allocate plumbing for manually fetching sense.
7169a016c63Sstevel 	 */
7179a016c63Sstevel 	if (sgen_setup_sense(sg_state) != 0) {
7189a016c63Sstevel 		freerbuf(sg_state->sgen_cmdbuf);
7199a016c63Sstevel 		ddi_prop_remove_all(dip);
7209a016c63Sstevel 		ddi_remove_minor_node(dip, NULL);
7219a016c63Sstevel 		scsi_unprobe(scsidevp);
7229a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1,
7239a016c63Sstevel 		    "sgen_do_attach: failed to setup request-sense, "
7249a016c63Sstevel 		    "device unit-address @%s", ddi_get_name_addr(dip));
7259a016c63Sstevel 		ddi_soft_state_free(sgen_soft_state, instance);
7269a016c63Sstevel 		return (DDI_FAILURE);
7279a016c63Sstevel 	}
7289a016c63Sstevel 
7299a016c63Sstevel 	sgen_create_errstats(sg_state, instance);
7309a016c63Sstevel 
7319a016c63Sstevel 	ddi_report_dev(dip);
7329a016c63Sstevel 
7339a016c63Sstevel 	return (DDI_SUCCESS);
7349a016c63Sstevel }
7359a016c63Sstevel 
7369a016c63Sstevel /*
7379a016c63Sstevel  * sgen_setup_sense()
7389a016c63Sstevel  * 	Allocate a request sense packet so that if sgen needs to fetch sense
7399a016c63Sstevel  * 	data for the user, it will have a pkt ready to send.
7409a016c63Sstevel  */
7419a016c63Sstevel static int
7429a016c63Sstevel sgen_setup_sense(sgen_state_t *sg_state)
7439a016c63Sstevel {
7449a016c63Sstevel 	struct buf *bp;
7459a016c63Sstevel 	struct scsi_pkt *rqpkt;
7469a016c63Sstevel 
7479a016c63Sstevel 	if ((bp = scsi_alloc_consistent_buf(&sg_state->sgen_scsiaddr, NULL,
74830ab6db6Slh195018 	    MAX_SENSE_LENGTH, B_READ, SLEEP_FUNC, NULL)) == NULL) {
7499a016c63Sstevel 		return (-1);
7509a016c63Sstevel 	}
7519a016c63Sstevel 
7529a016c63Sstevel 	if ((rqpkt = scsi_init_pkt(&sg_state->sgen_scsiaddr, NULL, bp,
7539a016c63Sstevel 	    CDB_GROUP0, 1, 0, PKT_CONSISTENT, SLEEP_FUNC, NULL)) == NULL) {
7549a016c63Sstevel 		scsi_free_consistent_buf(bp);
7559a016c63Sstevel 		return (-1);
7569a016c63Sstevel 	}
7579a016c63Sstevel 
7589a016c63Sstevel 	/*
7599a016c63Sstevel 	 * Make the results of running a SENSE available by filling out the
7609a016c63Sstevel 	 * sd_sense field of the scsi device (sgen_sense is just an alias).
7619a016c63Sstevel 	 */
7629a016c63Sstevel 	sg_state->sgen_sense = (struct scsi_extended_sense *)bp->b_un.b_addr;
7639a016c63Sstevel 
7649a016c63Sstevel 	(void) scsi_setup_cdb((union scsi_cdb *)rqpkt->pkt_cdbp,
76530ab6db6Slh195018 	    SCMD_REQUEST_SENSE, 0, MAX_SENSE_LENGTH, 0);
7669a016c63Sstevel 	FILL_SCSI1_LUN(sg_state->sgen_scsidev, rqpkt);
7679a016c63Sstevel 
7689a016c63Sstevel 	rqpkt->pkt_comp = sgen_callback;
7699a016c63Sstevel 	rqpkt->pkt_time = SGEN_IO_TIME;
7709a016c63Sstevel 	rqpkt->pkt_flags |= FLAG_SENSING;
7719a016c63Sstevel 	rqpkt->pkt_private = sg_state;
7729a016c63Sstevel 
7739a016c63Sstevel 	sg_state->sgen_rqspkt = rqpkt;
7749a016c63Sstevel 	sg_state->sgen_rqsbuf = bp;
7759a016c63Sstevel 
7769a016c63Sstevel 	return (0);
7779a016c63Sstevel }
7789a016c63Sstevel 
7799a016c63Sstevel /*
7809a016c63Sstevel  * sgen_create_errstats()
781275c9da8Seschrock  * 	create named kstats for tracking occurrence of errors.
7829a016c63Sstevel  */
7839a016c63Sstevel static void
7849a016c63Sstevel sgen_create_errstats(sgen_state_t *sg_state, int instance)
7859a016c63Sstevel {
7869a016c63Sstevel 	char kstatname[KSTAT_STRLEN];
7879a016c63Sstevel 	struct sgen_errstats *stp;
7889a016c63Sstevel 
789a204de77Scth 	(void) snprintf(kstatname, KSTAT_STRLEN, "%s%d,err",
790a204de77Scth 	    sgen_label, instance);
7919a016c63Sstevel 	sg_state->sgen_kstats = kstat_create("sgenerr", instance,
7929a016c63Sstevel 	    kstatname, "device_error", KSTAT_TYPE_NAMED,
7939a016c63Sstevel 	    sizeof (struct sgen_errstats) / sizeof (kstat_named_t),
7949a016c63Sstevel 	    KSTAT_FLAG_PERSISTENT);
7959a016c63Sstevel 
7969a016c63Sstevel 	if (sg_state->sgen_kstats == NULL)
7979a016c63Sstevel 		return;
7989a016c63Sstevel 
7999a016c63Sstevel 	stp = (struct sgen_errstats *)sg_state->sgen_kstats->ks_data;
8009a016c63Sstevel 	kstat_named_init(&stp->sgen_trans_err, "transport_errors",
8019a016c63Sstevel 	    KSTAT_DATA_UINT32);
8029a016c63Sstevel 	kstat_named_init(&stp->sgen_restart, "command_restarts",
8039a016c63Sstevel 	    KSTAT_DATA_UINT32);
8049a016c63Sstevel 	kstat_named_init(&stp->sgen_incmp_err, "incomplete_commands",
8059a016c63Sstevel 	    KSTAT_DATA_UINT32);
8069a016c63Sstevel 	kstat_named_init(&stp->sgen_autosen_rcv, "autosense_occurred",
8079a016c63Sstevel 	    KSTAT_DATA_UINT32);
8089a016c63Sstevel 	kstat_named_init(&stp->sgen_autosen_bad, "autosense_undecipherable",
8099a016c63Sstevel 	    KSTAT_DATA_UINT32);
8109a016c63Sstevel 	kstat_named_init(&stp->sgen_sense_rcv, "sense_fetches",
8119a016c63Sstevel 	    KSTAT_DATA_UINT32);
8129a016c63Sstevel 	kstat_named_init(&stp->sgen_sense_bad, "sense_data_undecipherable",
8139a016c63Sstevel 	    KSTAT_DATA_UINT32);
8149a016c63Sstevel 	kstat_named_init(&stp->sgen_recov_err, "recoverable_error",
8159a016c63Sstevel 	    KSTAT_DATA_UINT32);
8169a016c63Sstevel 	kstat_named_init(&stp->sgen_nosen_err, "NO_SENSE_sense_key",
8179a016c63Sstevel 	    KSTAT_DATA_UINT32);
8189a016c63Sstevel 	kstat_named_init(&stp->sgen_unrecov_err, "unrecoverable_sense_error",
8199a016c63Sstevel 	    KSTAT_DATA_UINT32);
8209a016c63Sstevel 	sg_state->sgen_kstats->ks_private = sg_state;
8219a016c63Sstevel 	sg_state->sgen_kstats->ks_update = nulldev;
8229a016c63Sstevel 	kstat_install(sg_state->sgen_kstats);
8239a016c63Sstevel }
8249a016c63Sstevel 
8259a016c63Sstevel /*
8269a016c63Sstevel  * sgen_detach()
8279a016c63Sstevel  * 	detach(9E) entrypoint
8289a016c63Sstevel  */
8299a016c63Sstevel static int
8309a016c63Sstevel sgen_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
8319a016c63Sstevel {
8329a016c63Sstevel 	int instance;
8339a016c63Sstevel 	sgen_state_t *sg_state;
8349a016c63Sstevel 
8359a016c63Sstevel 	instance = ddi_get_instance(dip);
8369a016c63Sstevel 	sg_state = ddi_get_soft_state(sgen_soft_state, instance);
8379a016c63Sstevel 
8389a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_detach(), "
8399a016c63Sstevel 	    "device unit-address @%s", ddi_get_name_addr(dip));
8409a016c63Sstevel 
8419a016c63Sstevel 	if (sg_state == NULL) {
8429a016c63Sstevel 		sgen_log(NULL, SGEN_DIAG1,
8439a016c63Sstevel 		    "sgen_detach: failed, no softstate found (%d), "
8449a016c63Sstevel 		    "device unit-address @%s",
8459a016c63Sstevel 		    instance, ddi_get_name_addr(dip));
8469a016c63Sstevel 		return (DDI_FAILURE);
8479a016c63Sstevel 	}
8489a016c63Sstevel 
8499a016c63Sstevel 	switch (cmd) {
8509a016c63Sstevel 	case DDI_DETACH:
8519a016c63Sstevel 		return (sgen_do_detach(dip));
8529a016c63Sstevel 	case DDI_SUSPEND:
8539a016c63Sstevel 		return (sgen_do_suspend(dip));
8549a016c63Sstevel 	case DDI_PM_SUSPEND:
8559a016c63Sstevel 	default:
8569a016c63Sstevel 		return (DDI_FAILURE);
8579a016c63Sstevel 	}
8589a016c63Sstevel }
8599a016c63Sstevel 
8609a016c63Sstevel /*
8619a016c63Sstevel  * sgen_do_detach()
8629a016c63Sstevel  * 	detach the driver, tearing down resources.
8639a016c63Sstevel  */
8649a016c63Sstevel static int
8659a016c63Sstevel sgen_do_detach(dev_info_t *dip)
8669a016c63Sstevel {
8679a016c63Sstevel 	int instance;
8689a016c63Sstevel 	sgen_state_t *sg_state;
8699a016c63Sstevel 	struct scsi_device *devp;
8709a016c63Sstevel 
8719a016c63Sstevel 	instance = ddi_get_instance(dip);
8729a016c63Sstevel 	sg_state = ddi_get_soft_state(sgen_soft_state, instance);
8739a016c63Sstevel 	ASSERT(sg_state);
8749a016c63Sstevel 
8759a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_do_detach(), "
8769a016c63Sstevel 	    "device unit-address @%s", ddi_get_name_addr(dip));
8779a016c63Sstevel 	devp = ddi_get_driver_private(dip);
8789a016c63Sstevel 
8799a016c63Sstevel 	mutex_enter(&sg_state->sgen_mutex);
8809a016c63Sstevel 	if (SGEN_IS_BUSY(sg_state)) {
8819a016c63Sstevel 		mutex_exit(&sg_state->sgen_mutex);
8829a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_do_detach: failed because "
8839a016c63Sstevel 		    "device is busy, device unit-address @%s",
8849a016c63Sstevel 		    ddi_get_name_addr(dip));
8859a016c63Sstevel 		return (DDI_FAILURE);
8869a016c63Sstevel 	}
8879a016c63Sstevel 	mutex_exit(&sg_state->sgen_mutex);
8889a016c63Sstevel 
8899a016c63Sstevel 	/*
8909a016c63Sstevel 	 * Final approach for detach.  Free data allocated by scsi_probe()
8919a016c63Sstevel 	 * in attach.
8929a016c63Sstevel 	 */
8939a016c63Sstevel 	if (sg_state->sgen_restart_timeid)
8949a016c63Sstevel 		(void) untimeout(sg_state->sgen_restart_timeid);
8959a016c63Sstevel 	sg_state->sgen_restart_timeid = 0;
8969a016c63Sstevel 	scsi_unprobe(devp);
8979a016c63Sstevel 
8989a016c63Sstevel 	/*
8999a016c63Sstevel 	 * Free auto-request plumbing.
9009a016c63Sstevel 	 */
9019a016c63Sstevel 	scsi_free_consistent_buf(sg_state->sgen_rqsbuf);
9029a016c63Sstevel 	scsi_destroy_pkt(sg_state->sgen_rqspkt);
9039a016c63Sstevel 
9049a016c63Sstevel 	if (sg_state->sgen_kstats) {
9059a016c63Sstevel 		kstat_delete(sg_state->sgen_kstats);
9069a016c63Sstevel 		sg_state->sgen_kstats = NULL;
9079a016c63Sstevel 	}
9089a016c63Sstevel 
9099a016c63Sstevel 	/*
9109a016c63Sstevel 	 * Free command buffer and clean up
9119a016c63Sstevel 	 */
9129a016c63Sstevel 	freerbuf(sg_state->sgen_cmdbuf);
9139a016c63Sstevel 	cv_destroy(&sg_state->sgen_cmdbuf_cv);
9149a016c63Sstevel 
9159a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_do_detach(), "
9169a016c63Sstevel 	    "device unit-address @%s", ddi_get_name_addr(dip));
9179a016c63Sstevel 
9189a016c63Sstevel 	ddi_soft_state_free(sgen_soft_state, instance);
9199a016c63Sstevel 	ddi_prop_remove_all(dip);
9209a016c63Sstevel 	ddi_remove_minor_node(dip, NULL);
9219a016c63Sstevel 	return (DDI_SUCCESS);
9229a016c63Sstevel }
9239a016c63Sstevel 
9249a016c63Sstevel /*
9259a016c63Sstevel  * sgen_do_suspend()
9269a016c63Sstevel  * 	suspend the driver.  This sets the "suspend" bit for this target if it
9279a016c63Sstevel  * 	is currently open; once resumed, the suspend bit will cause
9289a016c63Sstevel  * 	subsequent I/Os to fail.  We want user programs to close and
9299a016c63Sstevel  * 	reopen the device to acknowledge that they need to reexamine its
9309a016c63Sstevel  * 	state and do the right thing.
9319a016c63Sstevel  */
9329a016c63Sstevel static int
9339a016c63Sstevel sgen_do_suspend(dev_info_t *dip)
9349a016c63Sstevel {
9359a016c63Sstevel 	int instance;
9369a016c63Sstevel 	sgen_state_t *sg_state;
9379a016c63Sstevel 
9389a016c63Sstevel 	instance = ddi_get_instance(dip);
9399a016c63Sstevel 	sg_state = ddi_get_soft_state(sgen_soft_state, instance);
9409a016c63Sstevel 	ASSERT(sg_state);
9419a016c63Sstevel 
9429a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_do_suspend(), "
9439a016c63Sstevel 	    "device unit-address @%s", ddi_get_name_addr(dip));
9449a016c63Sstevel 
9459a016c63Sstevel 	if (sg_state->sgen_restart_timeid) {
9469a016c63Sstevel 		(void) untimeout(sg_state->sgen_restart_timeid);
9479a016c63Sstevel 	}
9489a016c63Sstevel 	sg_state->sgen_restart_timeid = 0;
9499a016c63Sstevel 
9509a016c63Sstevel 	mutex_enter(&sg_state->sgen_mutex);
9519a016c63Sstevel 	if (SGEN_IS_OPEN(sg_state))
9529a016c63Sstevel 		SGEN_SET_SUSP(sg_state);
9539a016c63Sstevel 	mutex_exit(&sg_state->sgen_mutex);
9549a016c63Sstevel 
9559a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_do_suspend(), "
9569a016c63Sstevel 	    "device unit-address @%s", ddi_get_name_addr(dip));
9579a016c63Sstevel 	return (DDI_SUCCESS);
9589a016c63Sstevel }
9599a016c63Sstevel 
9609a016c63Sstevel /*
9619a016c63Sstevel  * sgen_getinfo()
9629a016c63Sstevel  *	getinfo(9e) entrypoint.
9639a016c63Sstevel  */
9649a016c63Sstevel /*ARGSUSED*/
9659a016c63Sstevel static int
9669a016c63Sstevel sgen_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
9679a016c63Sstevel {
9689a016c63Sstevel 	dev_t dev;
9699a016c63Sstevel 	sgen_state_t *sg_state;
9709a016c63Sstevel 	int instance, error;
9719a016c63Sstevel 	switch (infocmd) {
9729a016c63Sstevel 	case DDI_INFO_DEVT2DEVINFO:
9739a016c63Sstevel 		dev = (dev_t)arg;
9749a016c63Sstevel 		instance = getminor(dev);
9759a016c63Sstevel 		if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance))
9769a016c63Sstevel 		    == NULL)
9779a016c63Sstevel 			return (DDI_FAILURE);
9789a016c63Sstevel 		*result = (void *) sg_state->sgen_scsidev->sd_dev;
9799a016c63Sstevel 		error = DDI_SUCCESS;
9809a016c63Sstevel 		break;
9819a016c63Sstevel 	case DDI_INFO_DEVT2INSTANCE:
9829a016c63Sstevel 		dev = (dev_t)arg;
9839a016c63Sstevel 		instance = getminor(dev);
9849a016c63Sstevel 		*result = (void *)(uintptr_t)instance;
9859a016c63Sstevel 		error = DDI_SUCCESS;
9869a016c63Sstevel 		break;
9879a016c63Sstevel 	default:
9889a016c63Sstevel 		error = DDI_FAILURE;
9899a016c63Sstevel 	}
9909a016c63Sstevel 	return (error);
9919a016c63Sstevel }
9929a016c63Sstevel 
9939a016c63Sstevel /*
9949a016c63Sstevel  * sgen_probe()
9959a016c63Sstevel  * 	probe(9e) entrypoint.  sgen *never* returns DDI_PROBE_PARTIAL, in
9969a016c63Sstevel  * 	order to avoid leaving around extra devinfos.  If sgen's binding
9979a016c63Sstevel  * 	rules indicate that it should bind, it returns DDI_PROBE_SUCCESS.
9989a016c63Sstevel  */
9999a016c63Sstevel static int
10009a016c63Sstevel sgen_probe(dev_info_t *dip)
10019a016c63Sstevel {
10029a016c63Sstevel 	struct scsi_device *scsidevp;
10039a016c63Sstevel 	int instance;
10049a016c63Sstevel 	int rval;
10059a016c63Sstevel 
10069a016c63Sstevel 	scsidevp = ddi_get_driver_private(dip);
10079a016c63Sstevel 	instance = ddi_get_instance(dip);
10089a016c63Sstevel 	sgen_log(NULL, SGEN_DIAG2, "in sgen_probe(): instance = %d, "
10099a016c63Sstevel 	    "device unit-address @%s", instance, ddi_get_name_addr(dip));
10109a016c63Sstevel 
10119a016c63Sstevel 	if (ddi_dev_is_sid(dip) == DDI_SUCCESS)
10129a016c63Sstevel 		return (DDI_PROBE_DONTCARE);
10139a016c63Sstevel 
10149a016c63Sstevel 	if (ddi_get_soft_state(sgen_soft_state, instance) != NULL)
10159a016c63Sstevel 		return (DDI_PROBE_FAILURE);
10169a016c63Sstevel 
10179a016c63Sstevel 	mutex_enter(&sgen_binddb.sdb_lock);
10189a016c63Sstevel 	if (sgen_binddb.sdb_init == 0) {
10199a016c63Sstevel 		sgen_setup_binddb(dip);
10209a016c63Sstevel 	}
10219a016c63Sstevel 	mutex_exit(&sgen_binddb.sdb_lock);
10229a016c63Sstevel 
10239a016c63Sstevel 	/*
10249a016c63Sstevel 	 * A small optimization: if it's impossible for sgen to bind to
10259a016c63Sstevel 	 * any devices, don't bother probing, just fail.
10269a016c63Sstevel 	 */
10279a016c63Sstevel 	if ((sgen_binddb.sdb_inq_nodes == NULL) &&
10289a016c63Sstevel 	    (sgen_binddb.sdb_type_nodes == NULL)) {
10299a016c63Sstevel 		return (DDI_PROBE_FAILURE);
10309a016c63Sstevel 	}
10319a016c63Sstevel 
10329a016c63Sstevel 	if (scsi_probe(scsidevp, NULL_FUNC) == SCSIPROBE_EXISTS) {
10339a016c63Sstevel 		if (sgen_get_binding(dip) == 0) {
10349a016c63Sstevel 			rval = DDI_PROBE_SUCCESS;
10359a016c63Sstevel 		}
10369a016c63Sstevel 	} else {
10379a016c63Sstevel 		rval = DDI_PROBE_FAILURE;
10389a016c63Sstevel 	}
10399a016c63Sstevel 	scsi_unprobe(scsidevp);
10409a016c63Sstevel 
10419a016c63Sstevel 	sgen_log(NULL, SGEN_DIAG2, "sgen_probe() %s, device unit-address @%s",
10429a016c63Sstevel 	    rval == DDI_PROBE_SUCCESS ? "succeeded" : "failed",
10439a016c63Sstevel 	    ddi_get_name_addr(dip));
10449a016c63Sstevel 	return (rval);
10459a016c63Sstevel }
10469a016c63Sstevel 
10479a016c63Sstevel /*
10489a016c63Sstevel  * sgen_open()
10499a016c63Sstevel  * 	open(9e) entrypoint.  sgen enforces a strict exclusive open policy per
10509a016c63Sstevel  * 	target.
10519a016c63Sstevel  */
10529a016c63Sstevel /*ARGSUSED1*/
10539a016c63Sstevel static int
10549a016c63Sstevel sgen_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
10559a016c63Sstevel {
10569a016c63Sstevel 	dev_t dev = *dev_p;
10579a016c63Sstevel 	sgen_state_t *sg_state;
10589a016c63Sstevel 	int instance;
10599a016c63Sstevel 
10609a016c63Sstevel 	instance = getminor(dev);
10619a016c63Sstevel 
10629a016c63Sstevel 	if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
10639a016c63Sstevel 		return (ENXIO);
10649a016c63Sstevel 
10659a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_open(): instance = %d",
10669a016c63Sstevel 	    instance);
10679a016c63Sstevel 
10689a016c63Sstevel 	mutex_enter(&sg_state->sgen_mutex);
10699a016c63Sstevel 
1070275c9da8Seschrock 	/*
1071275c9da8Seschrock 	 * Don't allow new opens of a suspended device until the last close has
1072275c9da8Seschrock 	 * happened.  This is rather simplistic, but keeps the implementation
1073275c9da8Seschrock 	 * straightforward.
1074275c9da8Seschrock 	 */
1075275c9da8Seschrock 	if (SGEN_IS_SUSP(sg_state)) {
1076275c9da8Seschrock 		mutex_exit(&sg_state->sgen_mutex);
1077275c9da8Seschrock 		return (EIO);
1078275c9da8Seschrock 	}
1079275c9da8Seschrock 
1080275c9da8Seschrock 	/*
1081275c9da8Seschrock 	 * Enforce exclusive access.
1082275c9da8Seschrock 	 */
1083275c9da8Seschrock 	if (SGEN_IS_EXCL(sg_state) ||
1084275c9da8Seschrock 	    (SGEN_IS_OPEN(sg_state) && (flag & FEXCL))) {
10859a016c63Sstevel 		mutex_exit(&sg_state->sgen_mutex);
10869a016c63Sstevel 		return (EBUSY);
10879a016c63Sstevel 	}
10889a016c63Sstevel 
1089275c9da8Seschrock 	if (flag & FEXCL)
1090275c9da8Seschrock 		SGEN_SET_EXCL(sg_state);
1091275c9da8Seschrock 
10929a016c63Sstevel 	SGEN_SET_OPEN(sg_state);
10939a016c63Sstevel 
10949a016c63Sstevel 	mutex_exit(&sg_state->sgen_mutex);
10959a016c63Sstevel 
10969a016c63Sstevel 	return (0);
10979a016c63Sstevel }
10989a016c63Sstevel 
10999a016c63Sstevel /*
11009a016c63Sstevel  * sgen_close()
11019a016c63Sstevel  * 	close(9e) entrypoint.
11029a016c63Sstevel  */
11039a016c63Sstevel /*ARGSUSED1*/
11049a016c63Sstevel static int
11059a016c63Sstevel sgen_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
11069a016c63Sstevel {
11079a016c63Sstevel 	sgen_state_t *sg_state;
11089a016c63Sstevel 	int instance;
11099a016c63Sstevel 
11109a016c63Sstevel 	instance = getminor(dev);
11119a016c63Sstevel 
11129a016c63Sstevel 	if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
11139a016c63Sstevel 		return (ENXIO);
11149a016c63Sstevel 
11159a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_close(): instance = %d",
11169a016c63Sstevel 	    instance);
11179a016c63Sstevel 
11189a016c63Sstevel 	mutex_enter(&sg_state->sgen_mutex);
11199a016c63Sstevel 	SGEN_CLR_OPEN(sg_state);
1120275c9da8Seschrock 	SGEN_CLR_EXCL(sg_state);
11219a016c63Sstevel 	SGEN_CLR_SUSP(sg_state); /* closing clears the 'I was suspended' bit */
11229a016c63Sstevel 	mutex_exit(&sg_state->sgen_mutex);
11239a016c63Sstevel 
11249a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_close()");
11259a016c63Sstevel 
11269a016c63Sstevel 	return (0);
11279a016c63Sstevel }
11289a016c63Sstevel 
11299a016c63Sstevel /*
11309a016c63Sstevel  * sgen_ioctl()
11319a016c63Sstevel  * 	sgen supports the USCSI(7I) ioctl interface.
11329a016c63Sstevel  */
11339a016c63Sstevel /*ARGSUSED4*/
11349a016c63Sstevel static int
11359a016c63Sstevel sgen_ioctl(dev_t dev,
11369a016c63Sstevel     int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p)
11379a016c63Sstevel {
11389a016c63Sstevel 	int retval = 0;
11399a016c63Sstevel 	sgen_state_t *sg_state;
11409a016c63Sstevel 	int instance;
11419a016c63Sstevel 
11429a016c63Sstevel 	instance = getminor(dev);
11439a016c63Sstevel 
11449a016c63Sstevel 	if ((sg_state = ddi_get_soft_state(sgen_soft_state, instance)) == NULL)
11459a016c63Sstevel 		return (ENXIO);
11469a016c63Sstevel 
11479a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_ioctl(): instance = %d",
11489a016c63Sstevel 	    instance);
11499a016c63Sstevel 
11509a016c63Sstevel 	/*
11519a016c63Sstevel 	 * If the driver has been suspended since the last open, fail all
11529a016c63Sstevel 	 * subsequent IO's so that the userland consumer reinitializes state.
11539a016c63Sstevel 	 */
11549a016c63Sstevel 	mutex_enter(&sg_state->sgen_mutex);
11559a016c63Sstevel 	if (SGEN_IS_SUSP(sg_state)) {
11569a016c63Sstevel 		mutex_exit(&sg_state->sgen_mutex);
11579a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_ioctl: returning EIO: "
11589a016c63Sstevel 		    "driver instance %d was previously suspended", instance);
11599a016c63Sstevel 		return (EIO);
11609a016c63Sstevel 	}
11619a016c63Sstevel 	mutex_exit(&sg_state->sgen_mutex);
11629a016c63Sstevel 
11639a016c63Sstevel 	switch (cmd) {
11649a016c63Sstevel 	case SGEN_IOC_DIAG: {
11659a016c63Sstevel 		if (arg > 3) {
11669a016c63Sstevel 			arg = 0;
11679a016c63Sstevel 		}
11689a016c63Sstevel 		sg_state->sgen_diag = (int)arg;
11699a016c63Sstevel 		retval = 0;
11709a016c63Sstevel 		break;
11719a016c63Sstevel 	}
11729a016c63Sstevel 
11739a016c63Sstevel 	case SGEN_IOC_READY: {
11749a016c63Sstevel 		if (sgen_tur(dev) != 0) {
11759a016c63Sstevel 			retval = EIO;
11769a016c63Sstevel 		} else {
11779a016c63Sstevel 			retval = 0;
11789a016c63Sstevel 		}
11799a016c63Sstevel 		break;
11809a016c63Sstevel 	}
11819a016c63Sstevel 
11826567eb0aSlh195018 	case USCSICMD:
11836567eb0aSlh195018 		retval = sgen_uscsi_cmd(dev, (struct uscsi_cmd *)arg, flag);
11846567eb0aSlh195018 		break;
11859a016c63Sstevel 
11869a016c63Sstevel 	default:
11879a016c63Sstevel 		retval = ENOTTY;
11889a016c63Sstevel 	}
11899a016c63Sstevel 
11909a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_ioctl(), returning %d",
11919a016c63Sstevel 	    retval);
11929a016c63Sstevel 
11939a016c63Sstevel 	return (retval);
11949a016c63Sstevel }
11959a016c63Sstevel 
11969a016c63Sstevel /*
11979a016c63Sstevel  * sgen_uscsi_cmd()
11989a016c63Sstevel  * 	Setup, configuration and teardown for a uscsi(7I) command
11999a016c63Sstevel  */
12009a016c63Sstevel /*ARGSUSED*/
12019a016c63Sstevel static int
12026567eb0aSlh195018 sgen_uscsi_cmd(dev_t dev, struct uscsi_cmd *ucmd, int flag)
12039a016c63Sstevel {
12046567eb0aSlh195018 	struct uscsi_cmd	*uscmd;
12059a016c63Sstevel 	struct buf	*bp;
12069a016c63Sstevel 	sgen_state_t	*sg_state;
12076567eb0aSlh195018 	enum uio_seg	uioseg;
12086567eb0aSlh195018 	int	instance;
12096567eb0aSlh195018 	int	flags;
12106567eb0aSlh195018 	int	err;
12119a016c63Sstevel 
12129a016c63Sstevel 	instance = getminor(dev);
12139a016c63Sstevel 
12149a016c63Sstevel 	sg_state = ddi_get_soft_state(sgen_soft_state, instance);
12159a016c63Sstevel 	ASSERT(sg_state);
12169a016c63Sstevel 
12179a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_uscsi_cmd(): instance = %d",
12189a016c63Sstevel 	    instance);
12199a016c63Sstevel 
1220275c9da8Seschrock 	/*
1221275c9da8Seschrock 	 * At this point, we start affecting state relevant to the target,
1222275c9da8Seschrock 	 * so access needs to be serialized.
1223275c9da8Seschrock 	 */
1224275c9da8Seschrock 	if (sgen_hold_cmdbuf(sg_state) != 0) {
1225275c9da8Seschrock 		sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: interrupted");
1226275c9da8Seschrock 		return (EINTR);
1227275c9da8Seschrock 	}
1228275c9da8Seschrock 
12296567eb0aSlh195018 	err = scsi_uscsi_alloc_and_copyin((intptr_t)ucmd, flag,
12306567eb0aSlh195018 	    &sg_state->sgen_scsiaddr, &uscmd);
12316567eb0aSlh195018 	if (err != 0) {
1232275c9da8Seschrock 		sgen_rele_cmdbuf(sg_state);
12336567eb0aSlh195018 		sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: "
12346567eb0aSlh195018 		    "scsi_uscsi_alloc_and_copyin failed\n");
12356567eb0aSlh195018 		return (err);
12366567eb0aSlh195018 	}
12379a016c63Sstevel 
12389a016c63Sstevel 	/*
12399a016c63Sstevel 	 * Clear out undesirable command flags
12409a016c63Sstevel 	 */
12416567eb0aSlh195018 	flags = (uscmd->uscsi_flags & ~(USCSI_NOINTR | USCSI_NOPARITY |
12429a016c63Sstevel 	    USCSI_OTAG | USCSI_HTAG | USCSI_HEAD));
12436567eb0aSlh195018 	if (flags != uscmd->uscsi_flags) {
12449a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_uscsi_cmd: cleared "
1245ad72f872Slh195018 		    "unsafe uscsi_flags 0x%x", uscmd->uscsi_flags & ~flags);
12466567eb0aSlh195018 		uscmd->uscsi_flags = flags;
12479a016c63Sstevel 	}
12489a016c63Sstevel 
12496567eb0aSlh195018 	if (uscmd->uscsi_cdb != NULL) {
12509a016c63Sstevel 		sgen_dump_cdb(sg_state, "sgen_uscsi_cmd: ",
12516567eb0aSlh195018 		    (union scsi_cdb *)uscmd->uscsi_cdb, uscmd->uscsi_cdblen);
12526567eb0aSlh195018 	}
12539a016c63Sstevel 
12549a016c63Sstevel 	/*
12559a016c63Sstevel 	 * Stash the sense buffer into sgen_rqs_sen for convenience.
12569a016c63Sstevel 	 */
12576567eb0aSlh195018 	sg_state->sgen_rqs_sen = uscmd->uscsi_rqbuf;
12589a016c63Sstevel 
12596567eb0aSlh195018 	bp = sg_state->sgen_cmdbuf;
12609a016c63Sstevel 	bp->av_back = NULL;
12619a016c63Sstevel 	bp->av_forw = NULL;
12626567eb0aSlh195018 	bp->b_private = (struct buf *)uscmd;
12636567eb0aSlh195018 	uioseg = (flag & FKIOCTL) ? UIO_SYSSPACE : UIO_USERSPACE;
12649a016c63Sstevel 
12656567eb0aSlh195018 	err = scsi_uscsi_handle_cmd(dev, uioseg, uscmd, sgen_start, bp, NULL);
12669a016c63Sstevel 
12679a016c63Sstevel 	if (sg_state->sgen_cmdpkt != NULL) {
12686567eb0aSlh195018 		uscmd->uscsi_status = SCBP_C(sg_state->sgen_cmdpkt);
12699a016c63Sstevel 	} else {
12706567eb0aSlh195018 		uscmd->uscsi_status = 0;
12719a016c63Sstevel 	}
12729a016c63Sstevel 
12739a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG3, "sgen_uscsi_cmd: awake from waiting "
12746567eb0aSlh195018 	    "for command.  Status is 0x%x", uscmd->uscsi_status);
12759a016c63Sstevel 
12766567eb0aSlh195018 	if (uscmd->uscsi_rqbuf != NULL) {
12776567eb0aSlh195018 		int rqlen = uscmd->uscsi_rqlen - uscmd->uscsi_rqresid;
12789a016c63Sstevel 		sgen_dump_sense(sg_state, rqlen,
12796567eb0aSlh195018 		    (uchar_t *)uscmd->uscsi_rqbuf);
12809a016c63Sstevel 	}
12819a016c63Sstevel 
12826567eb0aSlh195018 	(void) scsi_uscsi_copyout_and_free((intptr_t)ucmd, uscmd);
12836567eb0aSlh195018 
12849a016c63Sstevel 	if (sg_state->sgen_cmdpkt != NULL) {
12859a016c63Sstevel 		scsi_destroy_pkt(sg_state->sgen_cmdpkt);
12869a016c63Sstevel 		sg_state->sgen_cmdpkt = NULL;
12879a016c63Sstevel 	}
12889a016c63Sstevel 
12899a016c63Sstevel 	/*
12909a016c63Sstevel 	 * After this point, we can't touch per-target state.
12919a016c63Sstevel 	 */
12929a016c63Sstevel 	sgen_rele_cmdbuf(sg_state);
12939a016c63Sstevel 
12949a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_uscsi_cmd()");
12959a016c63Sstevel 
12969a016c63Sstevel 	return (err);
12979a016c63Sstevel }
12989a016c63Sstevel 
12999a016c63Sstevel /*
13009a016c63Sstevel  * sgen_hold_cmdbuf()
1301275c9da8Seschrock  * 	Acquire a lock on the command buffer for the given target.  Returns
1302275c9da8Seschrock  * 	non-zero if interrupted.
13039a016c63Sstevel  */
1304275c9da8Seschrock static int
13059a016c63Sstevel sgen_hold_cmdbuf(sgen_state_t *sg_state)
13069a016c63Sstevel {
13079a016c63Sstevel 	mutex_enter(&sg_state->sgen_mutex);
1308275c9da8Seschrock 	while (SGEN_IS_BUSY(sg_state)) {
1309275c9da8Seschrock 		if (!cv_wait_sig(&sg_state->sgen_cmdbuf_cv,
1310275c9da8Seschrock 		    &sg_state->sgen_mutex)) {
1311275c9da8Seschrock 			mutex_exit(&sg_state->sgen_mutex);
1312275c9da8Seschrock 			return (-1);
1313275c9da8Seschrock 		}
1314275c9da8Seschrock 	}
13159a016c63Sstevel 	SGEN_SET_BUSY(sg_state);
13169a016c63Sstevel 	mutex_exit(&sg_state->sgen_mutex);
1317275c9da8Seschrock 	return (0);
13189a016c63Sstevel }
13199a016c63Sstevel 
13209a016c63Sstevel /*
13219a016c63Sstevel  * sgen_rele_cmdbuf()
13229a016c63Sstevel  * 	release the command buffer for a particular target.
13239a016c63Sstevel  */
13249a016c63Sstevel static void
13259a016c63Sstevel sgen_rele_cmdbuf(sgen_state_t *sg_state)
13269a016c63Sstevel {
13279a016c63Sstevel 	mutex_enter(&sg_state->sgen_mutex);
13289a016c63Sstevel 	SGEN_CLR_BUSY(sg_state);
13299a016c63Sstevel 	cv_signal(&sg_state->sgen_cmdbuf_cv);
13309a016c63Sstevel 	mutex_exit(&sg_state->sgen_mutex);
13319a016c63Sstevel }
13329a016c63Sstevel 
13339a016c63Sstevel /*
13349a016c63Sstevel  * sgen_start()
13359a016c63Sstevel  * 	Transport a uscsi command; this is invoked by physio() or directly
13369a016c63Sstevel  * 	by sgen_uscsi_cmd().
13379a016c63Sstevel  */
13389a016c63Sstevel static int
13399a016c63Sstevel sgen_start(struct buf *bp)
13409a016c63Sstevel {
13419a016c63Sstevel 	sgen_state_t *sg_state;
13429a016c63Sstevel 	dev_t dev = bp->b_edev;
13439a016c63Sstevel 	int trans_err;
13449a016c63Sstevel 
13459a016c63Sstevel 	if ((sg_state = ddi_get_soft_state(sgen_soft_state,
13469a016c63Sstevel 	    getminor(dev))) == NULL) {
13479a016c63Sstevel 		bp->b_resid = bp->b_bcount;
13489a016c63Sstevel 		bioerror(bp, ENXIO);
13499a016c63Sstevel 		biodone(bp);
13509a016c63Sstevel 		return (ENXIO);
13519a016c63Sstevel 	}
13529a016c63Sstevel 
13539a016c63Sstevel 	/*
13549a016c63Sstevel 	 * Sanity checks - command should not be complete, no packet should
13559a016c63Sstevel 	 * be allocated, and there ought to be a uscsi cmd in b_private
13569a016c63Sstevel 	 */
13579a016c63Sstevel 	ASSERT(bp == sg_state->sgen_cmdbuf && sg_state->sgen_cmdpkt == NULL);
13589a016c63Sstevel 	ASSERT((bp->b_flags & B_DONE) == 0);
13599a016c63Sstevel 	ASSERT(bp->b_private);
13609a016c63Sstevel 	if (sgen_make_uscsi_cmd(sg_state, bp) != 0) {
13619a016c63Sstevel 		bp->b_resid = bp->b_bcount;
13629a016c63Sstevel 		bioerror(bp, EFAULT);
13639a016c63Sstevel 		biodone(bp);
13649a016c63Sstevel 		return (EFAULT);
13659a016c63Sstevel 	}
13669a016c63Sstevel 
13679a016c63Sstevel 	ASSERT(sg_state->sgen_cmdpkt != NULL);
13689a016c63Sstevel 
13699a016c63Sstevel 	/*
13709a016c63Sstevel 	 * Clear out the residual and error fields
13719a016c63Sstevel 	 */
13729a016c63Sstevel 	bp->b_resid = 0;
13739a016c63Sstevel 	bp->b_error = 0;
13749a016c63Sstevel 
13759a016c63Sstevel 	trans_err = sgen_scsi_transport(sg_state->sgen_cmdpkt);
13769a016c63Sstevel 	switch (trans_err) {
13779a016c63Sstevel 	case TRAN_ACCEPT:
13789a016c63Sstevel 		break;
13799a016c63Sstevel 	case TRAN_BUSY:
13809a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG2,
13819a016c63Sstevel 		    "sgen_start: scsi_transport() returned TRAN_BUSY");
13829a016c63Sstevel 		sg_state->sgen_restart_timeid = timeout(sgen_restart, sg_state,
13839a016c63Sstevel 		    SGEN_BSY_TIMEOUT);
13849a016c63Sstevel 		break;
13859a016c63Sstevel 	default:
13869a016c63Sstevel 		/*
13879a016c63Sstevel 		 * Indicate there has been an I/O transfer error.
13889a016c63Sstevel 		 * Be done with the command.
13899a016c63Sstevel 		 */
13909a016c63Sstevel 		mutex_enter(&sg_state->sgen_mutex);
13919a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_trans_err);
13929a016c63Sstevel 		mutex_exit(&sg_state->sgen_mutex);
13939a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "sgen_start: scsi_transport() "
13949a016c63Sstevel 		    "returned %d", trans_err);
13959a016c63Sstevel 		bioerror(bp, EIO);
13969a016c63Sstevel 		biodone(bp);
13979a016c63Sstevel 		return (EIO);
13989a016c63Sstevel 	}
13999a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "sgen_start: b_flags 0x%x", bp->b_flags);
14009a016c63Sstevel 	return (0);
14019a016c63Sstevel }
14029a016c63Sstevel 
14039a016c63Sstevel /*
14049a016c63Sstevel  * sgen_scsi_transport()
14059a016c63Sstevel  * 	a simple scsi_transport() wrapper which can be configured to inject
14069a016c63Sstevel  * 	sporadic errors for testing.
14079a016c63Sstevel  */
14089a016c63Sstevel static int
14099a016c63Sstevel sgen_scsi_transport(struct scsi_pkt *pkt)
14109a016c63Sstevel {
14119a016c63Sstevel 	int trans_err;
14129a016c63Sstevel 	static int cnt = 0;
14139a016c63Sstevel 	sgen_state_t *sg_state = pkt->pkt_private;
14149a016c63Sstevel 
14159a016c63Sstevel 	if (sgen_sporadic_failures == 0) {
14169a016c63Sstevel 		return (scsi_transport(pkt));
14179a016c63Sstevel 	}
14189a016c63Sstevel 
14199a016c63Sstevel 	cnt = (cnt * 2416 + 374441) % 1771875;	/* borrowed from kmem.c */
14209a016c63Sstevel 	if (cnt % 40 == 1) {
14219a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_scsi_transport: "
14229a016c63Sstevel 		    "injecting sporadic BUSY");
14239a016c63Sstevel 		trans_err = TRAN_BUSY;
14249a016c63Sstevel 	} else if (cnt % 40 == 2) {
14259a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_scsi_transport: "
14269a016c63Sstevel 		    "injecting sporadic BADPKT");
14279a016c63Sstevel 		trans_err = TRAN_BADPKT;
14289a016c63Sstevel 	} else {
14299a016c63Sstevel 		/*
14309a016c63Sstevel 		 * Most of the time we take the normal path
14319a016c63Sstevel 		 */
14329a016c63Sstevel 		trans_err = scsi_transport(pkt);
14339a016c63Sstevel 	}
14349a016c63Sstevel 	return (trans_err);
14359a016c63Sstevel }
14369a016c63Sstevel 
14379a016c63Sstevel /*
14389a016c63Sstevel  * sgen_make_uscsi_cmd()
14399a016c63Sstevel  * 	Initialize a SCSI packet usable for USCSI.
14409a016c63Sstevel  */
14419a016c63Sstevel static int
14429a016c63Sstevel sgen_make_uscsi_cmd(sgen_state_t *sg_state, struct buf *bp)
14439a016c63Sstevel {
14449a016c63Sstevel 	struct scsi_pkt	*pkt;
14459a016c63Sstevel 	struct uscsi_cmd *ucmd;
144630ab6db6Slh195018 	int stat_size = 1;
144730ab6db6Slh195018 	int flags = 0;
14489a016c63Sstevel 
14499a016c63Sstevel 	ASSERT(bp);
14509a016c63Sstevel 
14519a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_make_uscsi_cmd()");
14529a016c63Sstevel 
14539a016c63Sstevel 	ucmd = (struct uscsi_cmd *)bp->b_private;
14549a016c63Sstevel 
14559a016c63Sstevel 	if (ucmd->uscsi_flags & USCSI_RQENABLE) {
145630ab6db6Slh195018 		if (ucmd->uscsi_rqlen > SENSE_LENGTH) {
145730ab6db6Slh195018 			stat_size = (int)(ucmd->uscsi_rqlen) +
145830ab6db6Slh195018 			    sizeof (struct scsi_arq_status) -
145930ab6db6Slh195018 			    sizeof (struct scsi_extended_sense);
146030ab6db6Slh195018 			flags = PKT_XARQ;
14613a27445bSdm120769 		} else {
146230ab6db6Slh195018 			stat_size = sizeof (struct scsi_arq_status);
146330ab6db6Slh195018 		}
14649a016c63Sstevel 	}
14659a016c63Sstevel 
14669a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG3, "sgen_make_uscsi_cmd: b_bcount = %ld",
14679a016c63Sstevel 	    bp->b_bcount);
14689a016c63Sstevel 	pkt = scsi_init_pkt(&sg_state->sgen_scsiaddr,
14699a016c63Sstevel 	    NULL,			/* in_pkt - null so it'll be alloc'd */
14709a016c63Sstevel 	    bp->b_bcount ? bp : NULL,	/* buf structure for data xfer */
14719a016c63Sstevel 	    ucmd->uscsi_cdblen,		/* cmdlen */
14729a016c63Sstevel 	    stat_size,			/* statuslen */
14739a016c63Sstevel 	    0,				/* privatelen */
147430ab6db6Slh195018 	    flags,			/* flags */
14759a016c63Sstevel 	    SLEEP_FUNC,			/* callback */
14769a016c63Sstevel 	    (caddr_t)sg_state);		/* callback_arg */
14779a016c63Sstevel 
14789a016c63Sstevel 	if (pkt == NULL) {
14799a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "failed sgen_make_uscsi_cmd()");
14809a016c63Sstevel 		return (-1);
14819a016c63Sstevel 	}
14829a016c63Sstevel 
14839a016c63Sstevel 	pkt->pkt_comp = sgen_callback;
14849a016c63Sstevel 	pkt->pkt_private = sg_state;
14859a016c63Sstevel 	sg_state->sgen_cmdpkt = pkt;
14869a016c63Sstevel 
14879a016c63Sstevel 	/*
14889a016c63Sstevel 	 * We *don't* call scsi_setup_cdb here, as is customary, since the
14899a016c63Sstevel 	 * user could specify a command from one group, but pass cdblen
14909a016c63Sstevel 	 * as something totally different.  If cdblen is smaller than expected,
14919a016c63Sstevel 	 * this results in scsi_setup_cdb writing past the end of the cdb.
14929a016c63Sstevel 	 */
14939a016c63Sstevel 	bcopy(ucmd->uscsi_cdb, pkt->pkt_cdbp, ucmd->uscsi_cdblen);
14949a016c63Sstevel 	if (ucmd->uscsi_cdblen >= CDB_GROUP0) {
14959a016c63Sstevel 		FILL_SCSI1_LUN(sg_state->sgen_scsidev, pkt);
14969a016c63Sstevel 	}
14979a016c63Sstevel 
14989a016c63Sstevel 	if (ucmd->uscsi_timeout > 0)
14999a016c63Sstevel 		pkt->pkt_time = ucmd->uscsi_timeout;
15009a016c63Sstevel 	else
15019a016c63Sstevel 		pkt->pkt_time = SGEN_IO_TIME;
15029a016c63Sstevel 
15039a016c63Sstevel 	/*
15049a016c63Sstevel 	 * Set packet options
15059a016c63Sstevel 	 */
15069a016c63Sstevel 	if (ucmd->uscsi_flags & USCSI_SILENT)
15079a016c63Sstevel 		pkt->pkt_flags |= FLAG_SILENT;
15089a016c63Sstevel 	if (ucmd->uscsi_flags & USCSI_ISOLATE)
15099a016c63Sstevel 		pkt->pkt_flags |= FLAG_ISOLATE;
15109a016c63Sstevel 	if (ucmd->uscsi_flags & USCSI_DIAGNOSE)
15119a016c63Sstevel 		pkt->pkt_flags |= FLAG_DIAGNOSE;
15129a016c63Sstevel 	if (ucmd->uscsi_flags & USCSI_RENEGOT) {
15139a016c63Sstevel 		pkt->pkt_flags |= FLAG_RENEGOTIATE_WIDE_SYNC;
15149a016c63Sstevel 	}
15159a016c63Sstevel 
1516602ca9eaScth 	/* Transfer uscsi information to scsi_pkt */
1517602ca9eaScth 	(void) scsi_uscsi_pktinit(ucmd, pkt);
1518602ca9eaScth 
15199a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_make_uscsi_cmd()");
15209a016c63Sstevel 	return (0);
15219a016c63Sstevel }
15229a016c63Sstevel 
15239a016c63Sstevel 
15249a016c63Sstevel /*
15259a016c63Sstevel  * sgen_restart()
15269a016c63Sstevel  * 	sgen_restart() is called after a timeout, when a command has been
15279a016c63Sstevel  * 	postponed due to a TRAN_BUSY response from the HBA.
15289a016c63Sstevel  */
15299a016c63Sstevel static void
15309a016c63Sstevel sgen_restart(void *arg)
15319a016c63Sstevel {
15329a016c63Sstevel 	sgen_state_t *sg_state = (sgen_state_t *)arg;
15339a016c63Sstevel 	struct scsi_pkt *pkt;
15349a016c63Sstevel 	struct buf *bp;
15359a016c63Sstevel 
15369a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "in sgen_restart()");
15379a016c63Sstevel 
15389a016c63Sstevel 	bp = sg_state->sgen_cmdbuf;
15399a016c63Sstevel 	pkt = sg_state->sgen_cmdpkt;
15409a016c63Sstevel 	ASSERT(bp && pkt);
15419a016c63Sstevel 
15429a016c63Sstevel 	SGEN_DO_ERRSTATS(sg_state, sgen_restart);
15439a016c63Sstevel 
15449a016c63Sstevel 	/*
15459a016c63Sstevel 	 * If the packet is marked with the sensing flag, sgen is off running
15469a016c63Sstevel 	 * a request sense, and *that packet* is what needs to be restarted.
15479a016c63Sstevel 	 */
15489a016c63Sstevel 	if (pkt->pkt_flags & FLAG_SENSING) {
15499a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG3,
15509a016c63Sstevel 		    "sgen_restart: restarting REQUEST SENSE");
15519a016c63Sstevel 		pkt = sg_state->sgen_rqspkt;
15529a016c63Sstevel 	}
15539a016c63Sstevel 
15549a016c63Sstevel 	if (sgen_scsi_transport(pkt) != TRAN_ACCEPT) {
15559a016c63Sstevel 		bp->b_resid = bp->b_bcount;
15569a016c63Sstevel 		bioerror(bp, EIO);
15579a016c63Sstevel 		biodone(bp);
15589a016c63Sstevel 	}
15599a016c63Sstevel }
15609a016c63Sstevel 
15619a016c63Sstevel /*
15629a016c63Sstevel  * sgen_callback()
15639a016c63Sstevel  * 	Command completion processing
15649a016c63Sstevel  *
15659a016c63Sstevel  * 	sgen's completion processing is very pessimistic-- it does not retry
15669a016c63Sstevel  * 	failed commands; instead, it allows the user application to make
15679a016c63Sstevel  * 	decisions about what has gone wrong.
15689a016c63Sstevel  */
15699a016c63Sstevel static void
15709a016c63Sstevel sgen_callback(struct scsi_pkt *pkt)
15719a016c63Sstevel {
15729a016c63Sstevel 	sgen_state_t *sg_state;
1573602ca9eaScth 	struct uscsi_cmd *ucmd;
15749a016c63Sstevel 	struct buf *bp;
15759a016c63Sstevel 	int action;
15769a016c63Sstevel 
15779a016c63Sstevel 	sg_state = pkt->pkt_private;
15789a016c63Sstevel 	/*
15799a016c63Sstevel 	 * bp should always be the command buffer regardless of whether
15809a016c63Sstevel 	 * this is a command completion or a request-sense completion.
15819a016c63Sstevel 	 * This is because there is no need to biodone() the sense buf
15829a016c63Sstevel 	 * when it completes-- we want to biodone() the actual command buffer!
15839a016c63Sstevel 	 */
15849a016c63Sstevel 	bp = sg_state->sgen_cmdbuf;
15859a016c63Sstevel 	if (pkt->pkt_flags & FLAG_SENSING) {
15869a016c63Sstevel 		ASSERT(pkt == sg_state->sgen_rqspkt);
15879a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG2,
15889a016c63Sstevel 		    "in sgen_callback() (SENSE completion callback)");
15899a016c63Sstevel 	} else {
15909a016c63Sstevel 		ASSERT(pkt == sg_state->sgen_cmdpkt);
15919a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG2,
15929a016c63Sstevel 		    "in sgen_callback() (command completion callback)");
15939a016c63Sstevel 	}
1594602ca9eaScth 	ucmd = (struct uscsi_cmd *)bp->b_private;
15959a016c63Sstevel 
15969a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: reason=0x%x resid=%ld "
15979a016c63Sstevel 	    "state=0x%x", pkt->pkt_reason, pkt->pkt_resid, pkt->pkt_state);
15989a016c63Sstevel 
1599602ca9eaScth 	/* Transfer scsi_pkt information to uscsi */
1600602ca9eaScth 	(void) scsi_uscsi_pktfini(pkt, ucmd);
1601602ca9eaScth 
16029a016c63Sstevel 	if (pkt->pkt_reason != CMD_CMPLT) {
16039a016c63Sstevel 		/*
16049a016c63Sstevel 		 * The command did not complete.
16059a016c63Sstevel 		 */
16069a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG3,
16079a016c63Sstevel 		    "sgen_callback: command did not complete");
16089a016c63Sstevel 		action = sgen_handle_incomplete(sg_state, pkt);
16099a016c63Sstevel 	} else if (sg_state->sgen_arq_enabled &&
16109a016c63Sstevel 	    (pkt->pkt_state & STATE_ARQ_DONE)) {
16119a016c63Sstevel 		/*
16129a016c63Sstevel 		 * The auto-rqsense happened, and the packet has a filled-in
16139a016c63Sstevel 		 * scsi_arq_status structure, pointed to by pkt_scbp.
16149a016c63Sstevel 		 */
16159a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG3,
16169a016c63Sstevel 		    "sgen_callback: received auto-requested sense");
16179a016c63Sstevel 		action = sgen_handle_autosense(sg_state, pkt);
16189a016c63Sstevel 		ASSERT(action != FETCH_SENSE);
16199a016c63Sstevel 	} else if (pkt->pkt_flags & FLAG_SENSING) {
16209a016c63Sstevel 		/*
16219a016c63Sstevel 		 * sgen was running a REQUEST SENSE. Decode the sense data and
16229a016c63Sstevel 		 * decide what to do next.
16239a016c63Sstevel 		 *
16249a016c63Sstevel 		 * Clear FLAG_SENSING on the original packet for completeness.
16259a016c63Sstevel 		 */
16269a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG3, "sgen_callback: received sense");
16279a016c63Sstevel 		sg_state->sgen_cmdpkt->pkt_flags &= ~FLAG_SENSING;
16289a016c63Sstevel 		action = sgen_handle_sense(sg_state);
16299a016c63Sstevel 		ASSERT(action != FETCH_SENSE);
16309a016c63Sstevel 	} else {
16319a016c63Sstevel 		/*
16329a016c63Sstevel 		 * Command completed and we're not getting sense. Check for
16339a016c63Sstevel 		 * errors and decide what to do next.
16349a016c63Sstevel 		 */
16359a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG3,
16369a016c63Sstevel 		    "sgen_callback: command appears complete");
16379a016c63Sstevel 		action = sgen_check_error(sg_state, bp);
16389a016c63Sstevel 	}
16399a016c63Sstevel 
16409a016c63Sstevel 	switch (action) {
16419a016c63Sstevel 	case FETCH_SENSE:
16429a016c63Sstevel 		/*
16439a016c63Sstevel 		 * If there is sense to fetch, break out to prevent biodone'ing
16449a016c63Sstevel 		 * until the sense fetch is complete.
16459a016c63Sstevel 		 */
1646602ca9eaScth 		if (sgen_initiate_sense(sg_state,
1647602ca9eaScth 		    scsi_pkt_allocated_correctly(pkt) ?
1648602ca9eaScth 		    pkt->pkt_path_instance : 0) == 0)
16499a016c63Sstevel 			break;
16509a016c63Sstevel 		/*FALLTHROUGH*/
16519a016c63Sstevel 	case COMMAND_DONE_ERROR:
16529a016c63Sstevel 		bp->b_resid = bp->b_bcount;
16539a016c63Sstevel 		bioerror(bp, EIO);
16549a016c63Sstevel 		/*FALLTHROUGH*/
16559a016c63Sstevel 	case COMMAND_DONE:
16569a016c63Sstevel 		biodone(bp);
16579a016c63Sstevel 		break;
16589a016c63Sstevel 	default:
16599a016c63Sstevel 		ASSERT(0);
16609a016c63Sstevel 		break;
16619a016c63Sstevel 	}
16629a016c63Sstevel 
16639a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG2, "done sgen_callback()");
16649a016c63Sstevel }
16659a016c63Sstevel 
16669a016c63Sstevel /*
16679a016c63Sstevel  * sgen_initiate_sense()
16689a016c63Sstevel  *	Send the sgen_rqspkt to the target, thereby requesting sense data.
16699a016c63Sstevel  */
16709a016c63Sstevel static int
1671602ca9eaScth sgen_initiate_sense(sgen_state_t *sg_state, int path_instance)
16729a016c63Sstevel {
1673602ca9eaScth 	/* use same path_instance as command */
1674602ca9eaScth 	if (scsi_pkt_allocated_correctly(sg_state->sgen_rqspkt))
1675602ca9eaScth 		sg_state->sgen_rqspkt->pkt_path_instance = path_instance;
1676602ca9eaScth 
16779a016c63Sstevel 	switch (sgen_scsi_transport(sg_state->sgen_rqspkt)) {
16789a016c63Sstevel 	case TRAN_ACCEPT:
16799a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG3, "sgen_initiate_sense: "
16809a016c63Sstevel 		    "sense fetch transport accepted.");
16819a016c63Sstevel 		return (0);
16829a016c63Sstevel 	case TRAN_BUSY:
16839a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "sgen_initiate_sense: "
16849a016c63Sstevel 		    "sense fetch transport busy, setting timeout.");
16859a016c63Sstevel 		sg_state->sgen_restart_timeid = timeout(sgen_restart, sg_state,
16869a016c63Sstevel 		    SGEN_BSY_TIMEOUT);
16879a016c63Sstevel 		return (0);
16889a016c63Sstevel 	default:
16899a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "sgen_initiate_sense: "
16909a016c63Sstevel 		    "sense fetch transport failed or busy.");
16919a016c63Sstevel 		return (-1);
16929a016c63Sstevel 	}
16939a016c63Sstevel }
16949a016c63Sstevel 
16959a016c63Sstevel /*
16969a016c63Sstevel  * sgen_handle_incomplete()
16979a016c63Sstevel  * 	sgen is pessimistic, but also careful-- it doesn't try to retry
16989a016c63Sstevel  * 	incomplete commands, but it also doesn't go resetting devices;
16999a016c63Sstevel  * 	it is hard to tell if the device will be tolerant of that sort
17009a016c63Sstevel  * 	of prodding.
17019a016c63Sstevel  *
17029a016c63Sstevel  * 	This routine has been left as a guide for the future--- the
17039a016c63Sstevel  * 	current administration's hands-off policy may need modification.
17049a016c63Sstevel  */
17059a016c63Sstevel /*ARGSUSED*/
17069a016c63Sstevel static int
17079a016c63Sstevel sgen_handle_incomplete(sgen_state_t *sg_state, struct scsi_pkt *pkt)
17089a016c63Sstevel {
17099a016c63Sstevel 	SGEN_DO_ERRSTATS(sg_state, sgen_incmp_err);
17109a016c63Sstevel 	return (COMMAND_DONE_ERROR);
17119a016c63Sstevel }
17129a016c63Sstevel 
17139a016c63Sstevel /*
17149a016c63Sstevel  * sgen_handle_autosense()
17159a016c63Sstevel  * 	Deal with SENSE data acquired automatically via the auto-request-sense
17169a016c63Sstevel  * 	facility.
17179a016c63Sstevel  *
17189a016c63Sstevel  * 	Sgen takes a pessimistic view of things-- it doesn't retry commands,
17199a016c63Sstevel  * 	and unless the device recovered from the problem, this routine returns
17209a016c63Sstevel  * 	COMMAND_DONE_ERROR.
17219a016c63Sstevel  */
17229a016c63Sstevel static int
17239a016c63Sstevel sgen_handle_autosense(sgen_state_t *sg_state, struct scsi_pkt *pkt)
17249a016c63Sstevel {
17259a016c63Sstevel 	struct scsi_arq_status *arqstat;
17269a016c63Sstevel 	struct uscsi_cmd *ucmd =
17279a016c63Sstevel 	    (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
17289a016c63Sstevel 	int amt;
17299a016c63Sstevel 
17309a016c63Sstevel 	arqstat = (struct scsi_arq_status *)(pkt->pkt_scbp);
17319a016c63Sstevel 
17329a016c63Sstevel 	SGEN_DO_ERRSTATS(sg_state, sgen_autosen_rcv);
17339a016c63Sstevel 
17349a016c63Sstevel 	if (arqstat->sts_rqpkt_reason != CMD_CMPLT) {
17359a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: ARQ"
17369a016c63Sstevel 		    "failed to complete.");
17379a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
17389a016c63Sstevel 		return (COMMAND_DONE_ERROR);
17399a016c63Sstevel 	}
17409a016c63Sstevel 
174130ab6db6Slh195018 	if (pkt->pkt_state & STATE_XARQ_DONE) {
174230ab6db6Slh195018 		amt = MAX_SENSE_LENGTH - arqstat->sts_rqpkt_resid;
174330ab6db6Slh195018 	} else {
174430ab6db6Slh195018 		if (arqstat->sts_rqpkt_resid > SENSE_LENGTH) {
174530ab6db6Slh195018 			amt = MAX_SENSE_LENGTH - arqstat->sts_rqpkt_resid;
174630ab6db6Slh195018 		} else {
174730ab6db6Slh195018 			amt = SENSE_LENGTH - arqstat->sts_rqpkt_resid;
174830ab6db6Slh195018 		}
174930ab6db6Slh195018 	}
175030ab6db6Slh195018 
17519a016c63Sstevel 	if (ucmd->uscsi_flags & USCSI_RQENABLE) {
17529a016c63Sstevel 		ucmd->uscsi_rqstatus = *((char *)&arqstat->sts_rqpkt_status);
175330ab6db6Slh195018 		uchar_t rqlen = min((uchar_t)amt, ucmd->uscsi_rqlen);
175430ab6db6Slh195018 		ucmd->uscsi_rqresid = ucmd->uscsi_rqlen - rqlen;
17559a016c63Sstevel 		ASSERT(ucmd->uscsi_rqlen && sg_state->sgen_rqs_sen);
175630ab6db6Slh195018 		bcopy(&(arqstat->sts_sensedata), sg_state->sgen_rqs_sen, rqlen);
17579a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "sgen_handle_autosense: "
17589a016c63Sstevel 		    "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
17599a016c63Sstevel 		    ucmd->uscsi_rqstatus, ucmd->uscsi_rqresid);
17609a016c63Sstevel 	}
17619a016c63Sstevel 
17629a016c63Sstevel 	if (arqstat->sts_rqpkt_status.sts_chk) {
17639a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: got "
17649a016c63Sstevel 		    "check condition on auto request sense!");
17659a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
17669a016c63Sstevel 		return (COMMAND_DONE_ERROR);
17679a016c63Sstevel 	}
17689a016c63Sstevel 
17699a016c63Sstevel 	if (((arqstat->sts_rqpkt_state & STATE_XFERRED_DATA) == 0) ||
17709a016c63Sstevel 	    (amt == 0)) {
17719a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: got "
17729a016c63Sstevel 		    "auto-sense, but it contains no data!");
17739a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_autosen_bad);
17749a016c63Sstevel 		return (COMMAND_DONE_ERROR);
17759a016c63Sstevel 	}
17769a016c63Sstevel 
17779a016c63Sstevel 	/*
17789a016c63Sstevel 	 * Stuff the sense data pointer into sgen_sense for later retrieval
17799a016c63Sstevel 	 */
17809a016c63Sstevel 	sg_state->sgen_sense = &arqstat->sts_sensedata;
17819a016c63Sstevel 
17829a016c63Sstevel 	/*
17839a016c63Sstevel 	 * Now, check to see whether we got enough sense data to make any
17849a016c63Sstevel 	 * sense out if it (heh-heh).
17859a016c63Sstevel 	 */
17869a016c63Sstevel 	if (amt < SUN_MIN_SENSE_LENGTH) {
17879a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_autosense: not "
17889a016c63Sstevel 		    "enough auto sense data");
17899a016c63Sstevel 		return (COMMAND_DONE_ERROR);
17909a016c63Sstevel 	}
17919a016c63Sstevel 
17929a016c63Sstevel 	switch (arqstat->sts_sensedata.es_key) {
17939a016c63Sstevel 	case KEY_RECOVERABLE_ERROR:
17949a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_recov_err);
17959a016c63Sstevel 		break;
17969a016c63Sstevel 	case KEY_NO_SENSE:
17979a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_nosen_err);
17989a016c63Sstevel 		break;
17999a016c63Sstevel 	default:
18009a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_unrecov_err);
18019a016c63Sstevel 		break;
18029a016c63Sstevel 	}
18039a016c63Sstevel 
18049a016c63Sstevel 	return (COMMAND_DONE);
18059a016c63Sstevel }
18069a016c63Sstevel 
18079a016c63Sstevel /*
18089a016c63Sstevel  * sgen_handle_sense()
18099a016c63Sstevel  * 	Examine sense data that was manually fetched from the target.
18109a016c63Sstevel  */
18119a016c63Sstevel static int
18129a016c63Sstevel sgen_handle_sense(sgen_state_t *sg_state)
18139a016c63Sstevel {
18149a016c63Sstevel 	struct scsi_pkt *rqpkt = sg_state->sgen_rqspkt;
18159a016c63Sstevel 	struct scsi_status *rqstatus = (struct scsi_status *)rqpkt->pkt_scbp;
18169a016c63Sstevel 	struct uscsi_cmd *ucmd =
18179a016c63Sstevel 	    (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
18189a016c63Sstevel 	int amt;
18199a016c63Sstevel 
18209a016c63Sstevel 	SGEN_DO_ERRSTATS(sg_state, sgen_sense_rcv);
18219a016c63Sstevel 
182230ab6db6Slh195018 	amt = MAX_SENSE_LENGTH - rqpkt->pkt_resid;
182330ab6db6Slh195018 
18249a016c63Sstevel 	if (ucmd->uscsi_flags & USCSI_RQENABLE) {
18259a016c63Sstevel 		ucmd->uscsi_rqstatus = *((char *)rqstatus);
182630ab6db6Slh195018 		uchar_t rqlen = min((uchar_t)amt, ucmd->uscsi_rqlen);
182730ab6db6Slh195018 		ucmd->uscsi_rqresid = ucmd->uscsi_rqlen - rqlen;
18289a016c63Sstevel 		ASSERT(ucmd->uscsi_rqlen && sg_state->sgen_rqs_sen);
182930ab6db6Slh195018 		bcopy(sg_state->sgen_sense, sg_state->sgen_rqs_sen, rqlen);
18309a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG2, "sgen_handle_sense: "
18319a016c63Sstevel 		    "uscsi_rqstatus=0x%x uscsi_rqresid=%d\n",
18329a016c63Sstevel 		    ucmd->uscsi_rqstatus, ucmd->uscsi_rqresid);
18339a016c63Sstevel 	}
18349a016c63Sstevel 
18359a016c63Sstevel 	if (rqstatus->sts_busy) {
18369a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got busy "
18379a016c63Sstevel 		    "on request sense");
18389a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18399a016c63Sstevel 		return (COMMAND_DONE_ERROR);
18409a016c63Sstevel 	}
18419a016c63Sstevel 
18429a016c63Sstevel 	if (rqstatus->sts_chk) {
18439a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got check "
18449a016c63Sstevel 		    "condition on request sense!");
18459a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18469a016c63Sstevel 		return (COMMAND_DONE_ERROR);
18479a016c63Sstevel 	}
18489a016c63Sstevel 
18499a016c63Sstevel 	if ((rqpkt->pkt_state & STATE_XFERRED_DATA) == 0 || amt == 0) {
18509a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: got "
18519a016c63Sstevel 		    "sense, but it contains no data");
18529a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18539a016c63Sstevel 		return (COMMAND_DONE_ERROR);
18549a016c63Sstevel 	}
18559a016c63Sstevel 
18569a016c63Sstevel 	/*
18579a016c63Sstevel 	 * Now, check to see whether we got enough sense data to make any
18589a016c63Sstevel 	 * sense out if it (heh-heh).
18599a016c63Sstevel 	 */
18609a016c63Sstevel 	if (amt < SUN_MIN_SENSE_LENGTH) {
18619a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1, "sgen_handle_sense: not "
18629a016c63Sstevel 		    "enough sense data");
18639a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_sense_bad);
18649a016c63Sstevel 		return (COMMAND_DONE_ERROR);
18659a016c63Sstevel 	}
18669a016c63Sstevel 
18679a016c63Sstevel 	/*
18689a016c63Sstevel 	 * Decode the sense data-- this was deposited here for us by the
18699a016c63Sstevel 	 * setup in sgen_do_attach(). (note that sgen_sense is an alias for
18709a016c63Sstevel 	 * the sd_sense field in the scsi_device).
18719a016c63Sstevel 	 */
18729a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG1, "Sense key is %s [0x%x]",
18739a016c63Sstevel 	    scsi_sname(sg_state->sgen_sense->es_key),
18749a016c63Sstevel 	    sg_state->sgen_sense->es_key);
18759a016c63Sstevel 	switch (sg_state->sgen_sense->es_key) {
18769a016c63Sstevel 	case KEY_RECOVERABLE_ERROR:
18779a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_recov_err);
18789a016c63Sstevel 		break;
18799a016c63Sstevel 	case KEY_NO_SENSE:
18809a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_nosen_err);
18819a016c63Sstevel 		break;
18829a016c63Sstevel 	default:
18839a016c63Sstevel 		SGEN_DO_ERRSTATS(sg_state, sgen_unrecov_err);
18849a016c63Sstevel 		break;
18859a016c63Sstevel 	}
18869a016c63Sstevel 
18879a016c63Sstevel 	return (COMMAND_DONE);
18889a016c63Sstevel }
18899a016c63Sstevel 
18909a016c63Sstevel /*
18919a016c63Sstevel  * sgen_check_error()
18929a016c63Sstevel  * 	examine the command packet for abnormal completion.
18939a016c63Sstevel  *
18949a016c63Sstevel  *	sgen_check_error should only be called at the completion of the
18959a016c63Sstevel  *	command packet.
18969a016c63Sstevel  */
18979a016c63Sstevel static int
18989a016c63Sstevel sgen_check_error(sgen_state_t *sg_state, struct buf *bp)
18999a016c63Sstevel {
19009a016c63Sstevel 	struct scsi_pkt *pkt = sg_state->sgen_cmdpkt;
19019a016c63Sstevel 	struct scsi_status *status = (struct scsi_status *)pkt->pkt_scbp;
19029a016c63Sstevel 	struct uscsi_cmd *ucmd =
19039a016c63Sstevel 	    (struct uscsi_cmd *)sg_state->sgen_cmdbuf->b_private;
19049a016c63Sstevel 
19059a016c63Sstevel 	if (status->sts_busy) {
19069a016c63Sstevel 		sgen_log(sg_state, SGEN_DIAG1,
19079a016c63Sstevel 		    "sgen_check_error: target is busy");
19089a016c63Sstevel 		return (COMMAND_DONE_ERROR);
19099a016c63Sstevel 	}
19109a016c63Sstevel 
19119a016c63Sstevel 	/*
19129a016c63Sstevel 	 * pkt_resid will reflect, at this point, a residual of how many bytes
19139a016c63Sstevel 	 * were not transferred; a non-zero pkt_resid is an error.
19149a016c63Sstevel 	 */
19159a016c63Sstevel 	if (pkt->pkt_resid) {
19169a016c63Sstevel 		bp->b_resid += pkt->pkt_resid;
19179a016c63Sstevel 	}
19189a016c63Sstevel 
19199a016c63Sstevel 	if (status->sts_chk) {
19209a016c63Sstevel 		if (ucmd->uscsi_flags & USCSI_RQENABLE) {
19219a016c63Sstevel 			if (sg_state->sgen_arq_enabled) {
19229a016c63Sstevel 				sgen_log(sg_state, SGEN_DIAG1,
19239a016c63Sstevel 				    "sgen_check_error: strange: target "
19249a016c63Sstevel 				    "indicates CHECK CONDITION with auto-sense "
19259a016c63Sstevel 				    "enabled.");
19269a016c63Sstevel 			}
19279a016c63Sstevel 			sgen_log(sg_state, SGEN_DIAG2, "sgen_check_error: "
19289a016c63Sstevel 			    "target ready for sense fetch");
19299a016c63Sstevel 			return (FETCH_SENSE);
19309a016c63Sstevel 		} else {
19319a016c63Sstevel 			sgen_log(sg_state, SGEN_DIAG2, "sgen_check_error: "
19329a016c63Sstevel 			    "target indicates CHECK CONDITION");
19339a016c63Sstevel 		}
19349a016c63Sstevel 	}
19359a016c63Sstevel 
19369a016c63Sstevel 	return (COMMAND_DONE);
19379a016c63Sstevel }
19389a016c63Sstevel 
19399a016c63Sstevel /*
19409a016c63Sstevel  * sgen_tur()
19419a016c63Sstevel  * 	test if a target is ready to operate by sending it a TUR command.
19429a016c63Sstevel  */
19439a016c63Sstevel static int
19449a016c63Sstevel sgen_tur(dev_t dev)
19459a016c63Sstevel {
19469a016c63Sstevel 	char cmdblk[CDB_GROUP0];
19479a016c63Sstevel 	struct uscsi_cmd scmd;
19489a016c63Sstevel 
19499a016c63Sstevel 	bzero(&scmd, sizeof (scmd));
19509a016c63Sstevel 	scmd.uscsi_bufaddr = 0;
19519a016c63Sstevel 	scmd.uscsi_buflen = 0;
19529a016c63Sstevel 	bzero(cmdblk, CDB_GROUP0);
19539a016c63Sstevel 	cmdblk[0] = (char)SCMD_TEST_UNIT_READY;
19549a016c63Sstevel 	scmd.uscsi_flags = USCSI_DIAGNOSE | USCSI_SILENT | USCSI_WRITE;
19559a016c63Sstevel 	scmd.uscsi_cdb = cmdblk;
19569a016c63Sstevel 	scmd.uscsi_cdblen = CDB_GROUP0;
19579a016c63Sstevel 
19586567eb0aSlh195018 	return (sgen_uscsi_cmd(dev, &scmd, FKIOCTL));
19599a016c63Sstevel }
19609a016c63Sstevel 
19619a016c63Sstevel /*
19629a016c63Sstevel  * sgen_diag_ok()
19639a016c63Sstevel  * 	given an sg_state and a desired diagnostic level, return true if
19649a016c63Sstevel  * 	it is acceptable to output a message.
19659a016c63Sstevel  */
19669a016c63Sstevel /*ARGSUSED*/
19679a016c63Sstevel static int
19689a016c63Sstevel sgen_diag_ok(sgen_state_t *sg_state, int level)
19699a016c63Sstevel {
19709a016c63Sstevel 	int diag_lvl;
19719a016c63Sstevel 
19729a016c63Sstevel 	switch (level) {
19739a016c63Sstevel 	case CE_WARN:
19749a016c63Sstevel 	case CE_NOTE:
19759a016c63Sstevel 	case CE_CONT:
19769a016c63Sstevel 	case CE_PANIC:
19779a016c63Sstevel 		return (1);
19789a016c63Sstevel 	case SGEN_DIAG1:
19799a016c63Sstevel 	case SGEN_DIAG2:
19809a016c63Sstevel 	case SGEN_DIAG3:
19819a016c63Sstevel 		if (sg_state) {
19829a016c63Sstevel 			/*
19839a016c63Sstevel 			 * Check to see if user overrode the diagnostics level
19849a016c63Sstevel 			 * for this instance (either via SGEN_IOC_DIAG or via
19859a016c63Sstevel 			 * .conf file).  If not, fall back to the global diag
19869a016c63Sstevel 			 * level.
19879a016c63Sstevel 			 */
19889a016c63Sstevel 			if (sg_state->sgen_diag != -1)
19899a016c63Sstevel 				diag_lvl = sg_state->sgen_diag;
19909a016c63Sstevel 			else
19919a016c63Sstevel 				diag_lvl = sgen_diag;
19929a016c63Sstevel 		} else {
19939a016c63Sstevel 			diag_lvl = sgen_diag;
19949a016c63Sstevel 		}
19959a016c63Sstevel 		if (((diag_lvl << 8) | CE_CONT) >= level) {
19969a016c63Sstevel 			return (1);
19979a016c63Sstevel 		} else {
19989a016c63Sstevel 			return (0);
19999a016c63Sstevel 		}
20009a016c63Sstevel 	default:
20019a016c63Sstevel 		return (1);
20029a016c63Sstevel 	}
20039a016c63Sstevel }
20049a016c63Sstevel 
20059a016c63Sstevel /*PRINTFLIKE3*/
20069a016c63Sstevel static void
20079a016c63Sstevel sgen_log(sgen_state_t *sg_state, int level, const char *fmt, ...)
20089a016c63Sstevel {
20099a016c63Sstevel 	va_list	ap;
20109a016c63Sstevel 	char buf[256];
20119a016c63Sstevel 
20129a016c63Sstevel 	if (!sgen_diag_ok(sg_state, level))
20139a016c63Sstevel 		return;
20149a016c63Sstevel 
20159a016c63Sstevel 	va_start(ap, fmt);
20169a016c63Sstevel 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
20179a016c63Sstevel 	va_end(ap);
20189a016c63Sstevel 
20199a016c63Sstevel 	switch (level) {
20209a016c63Sstevel 	case CE_NOTE:
20219a016c63Sstevel 	case CE_CONT:
20229a016c63Sstevel 	case CE_WARN:
20239a016c63Sstevel 	case CE_PANIC:
20249a016c63Sstevel 		if (sg_state == (sgen_state_t *)NULL) {
20259a016c63Sstevel 			cmn_err(level, "%s", buf);
20269a016c63Sstevel 		} else {
2027a204de77Scth 			scsi_log(sg_state->sgen_devinfo, sgen_label, level,
20289a016c63Sstevel 			    "%s", buf);
20299a016c63Sstevel 		}
20309a016c63Sstevel 		break;
20319a016c63Sstevel 	case SGEN_DIAG1:
20329a016c63Sstevel 	case SGEN_DIAG2:
20339a016c63Sstevel 	case SGEN_DIAG3:
20349a016c63Sstevel 	default:
20359a016c63Sstevel 		if (sg_state == (sgen_state_t *)NULL) {
2036a204de77Scth 			scsi_log(NULL, sgen_label, CE_CONT, "%s", buf);
20379a016c63Sstevel 		} else {
2038a204de77Scth 			scsi_log(sg_state->sgen_devinfo, sgen_label, CE_CONT,
20399a016c63Sstevel 			    "%s", buf);
20409a016c63Sstevel 		}
20419a016c63Sstevel 	}
20429a016c63Sstevel }
20439a016c63Sstevel 
20449a016c63Sstevel /*
20459a016c63Sstevel  * sgen_dump_cdb()
20469a016c63Sstevel  * 	dump out the contents of a cdb.  Take care that 'label' is not too
20479a016c63Sstevel  * 	large, or 'buf' could overflow.
20489a016c63Sstevel  */
20499a016c63Sstevel static void
20509a016c63Sstevel sgen_dump_cdb(sgen_state_t *sg_state, const char *label,
20519a016c63Sstevel     union scsi_cdb *cdb, int cdblen)
20529a016c63Sstevel {
20539a016c63Sstevel 	static char hex[] = "0123456789abcdef";
20549a016c63Sstevel 	char *buf, *p;
20559a016c63Sstevel 	size_t nbytes;
20569a016c63Sstevel 	int i;
20579a016c63Sstevel 	uchar_t	*cdbp = (uchar_t *)cdb;
20589a016c63Sstevel 
20599a016c63Sstevel 	/*
20609a016c63Sstevel 	 * fastpath-- if we're not able to print out, don't do all of this
20619a016c63Sstevel 	 * extra work.
20629a016c63Sstevel 	 */
20639a016c63Sstevel 	if (!sgen_diag_ok(sg_state, SGEN_DIAG3))
20649a016c63Sstevel 		return;
20659a016c63Sstevel 
20669a016c63Sstevel 	/*
20679a016c63Sstevel 	 * 3 characters for each byte (because of the ' '), plus the size of
20689a016c63Sstevel 	 * the label, plus the trailing ']' and the null character.
20699a016c63Sstevel 	 */
20709a016c63Sstevel 	nbytes = 3 * cdblen + strlen(label) + strlen(" CDB = [") + 2;
20719a016c63Sstevel 	buf = kmem_alloc(nbytes, KM_SLEEP);
20729a016c63Sstevel 	(void) sprintf(buf, "%s CDB = [", label);
20739a016c63Sstevel 	p = &buf[strlen(buf)];
20749a016c63Sstevel 	for (i = 0; i < cdblen; i++, cdbp++) {
20759a016c63Sstevel 		if (i > 0)
20769a016c63Sstevel 			*p++ = ' ';
20779a016c63Sstevel 		*p++ = hex[(*cdbp >> 4) & 0x0f];
20789a016c63Sstevel 		*p++ = hex[*cdbp & 0x0f];
20799a016c63Sstevel 	}
20809a016c63Sstevel 	*p++ = ']';
20819a016c63Sstevel 	*p = 0;
20829a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG3, buf);
20839a016c63Sstevel 	kmem_free(buf, nbytes);
20849a016c63Sstevel }
20859a016c63Sstevel 
20869a016c63Sstevel static void
20879a016c63Sstevel sgen_dump_sense(sgen_state_t *sg_state, size_t rqlen, uchar_t *rqbuf)
20889a016c63Sstevel {
20899a016c63Sstevel 	static char hex[] = "0123456789abcdef";
20909a016c63Sstevel 	char *buf, *p;
20919a016c63Sstevel 	size_t nbytes;
20929a016c63Sstevel 	int i;
20939a016c63Sstevel 
20949a016c63Sstevel 	/*
20959a016c63Sstevel 	 * fastpath-- if we're not able to print out, don't do all of this
20969a016c63Sstevel 	 * extra work.
20979a016c63Sstevel 	 */
20989a016c63Sstevel 	if (!sgen_diag_ok(sg_state, SGEN_DIAG3))
20999a016c63Sstevel 		return;
21009a016c63Sstevel 
21019a016c63Sstevel 	/*
21029a016c63Sstevel 	 * 3 characters for each byte (because of the ' '), plus the size of
21039a016c63Sstevel 	 * the label, plus the trailing ']' and the null character.
21049a016c63Sstevel 	 */
21059a016c63Sstevel 	nbytes = 3 * rqlen + strlen(" SENSE = [") + 2;
21069a016c63Sstevel 	buf = kmem_alloc(nbytes, KM_SLEEP);
21079a016c63Sstevel 	(void) sprintf(buf, "SENSE = [");
21089a016c63Sstevel 	p = &buf[strlen(buf)];
21099a016c63Sstevel 	for (i = 0; i < rqlen; i++, rqbuf++) {
21109a016c63Sstevel 		if (i > 0)
21119a016c63Sstevel 			*p++ = ' ';
21129a016c63Sstevel 		*p++ = hex[(*rqbuf >> 4) & 0x0f];
21139a016c63Sstevel 		*p++ = hex[*rqbuf & 0x0f];
21149a016c63Sstevel 	}
21159a016c63Sstevel 	*p++ = ']';
21169a016c63Sstevel 	*p = 0;
21179a016c63Sstevel 	sgen_log(sg_state, SGEN_DIAG3, buf);
21189a016c63Sstevel 	kmem_free(buf, nbytes);
21199a016c63Sstevel }
2120