xref: /onnv-gate/usr/src/uts/common/io/emul64.c (revision 9106:ee8f18cdafb2)
185Scth /*
285Scth  * CDDL HEADER START
385Scth  *
485Scth  * The contents of this file are subject to the terms of the
57656SSherry.Moore@Sun.COM  * Common Development and Distribution License (the "License").
67656SSherry.Moore@Sun.COM  * You may not use this file except in compliance with the License.
785Scth  *
885Scth  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
985Scth  * or http://www.opensolaris.org/os/licensing.
1085Scth  * See the License for the specific language governing permissions
1185Scth  * and limitations under the License.
1285Scth  *
1385Scth  * When distributing Covered Code, include this CDDL HEADER in each
1485Scth  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1585Scth  * If applicable, add the following below this CDDL HEADER, with the
1685Scth  * fields enclosed by brackets "[]" replaced with your own identifying
1785Scth  * information: Portions Copyright [yyyy] [name of copyright owner]
1885Scth  *
1985Scth  * CDDL HEADER END
2085Scth  */
2185Scth /*
22*9106SSrivijitha.Dugganapalli@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2385Scth  * Use is subject to license terms.
2485Scth  */
2585Scth 
2685Scth 
2785Scth /*
2885Scth  * SCSA HBA nexus driver that emulates an HBA connected to SCSI target
2985Scth  * devices (large disks).
3085Scth  */
3185Scth 
3285Scth #ifdef DEBUG
3385Scth #define	EMUL64DEBUG
3485Scth #endif
3585Scth 
3685Scth #include <sys/scsi/scsi.h>
3785Scth #include <sys/ddi.h>
3885Scth #include <sys/sunddi.h>
3985Scth #include <sys/taskq.h>
4085Scth #include <sys/disp.h>
4185Scth #include <sys/types.h>
4285Scth #include <sys/buf.h>
4385Scth #include <sys/cpuvar.h>
4485Scth #include <sys/dklabel.h>
4585Scth 
4685Scth #include <sys/emul64.h>
4785Scth #include <sys/emul64cmd.h>
4885Scth #include <sys/emul64var.h>
4985Scth 
5085Scth int emul64_usetaskq	= 1;	/* set to zero for debugging */
5185Scth int emul64debug		= 0;
5285Scth #ifdef	EMUL64DEBUG
5385Scth static int emul64_cdb_debug	= 0;
5485Scth #include <sys/debug.h>
5585Scth #endif
5685Scth 
5785Scth /*
5885Scth  * cb_ops function prototypes
5985Scth  */
6085Scth static int emul64_ioctl(dev_t, int cmd, intptr_t arg, int mode,
6185Scth 			cred_t *credp, int *rvalp);
6285Scth 
6385Scth /*
6485Scth  * dev_ops functions prototypes
6585Scth  */
6685Scth static int emul64_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
6785Scth     void *arg, void **result);
6885Scth static int emul64_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
6985Scth static int emul64_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
7085Scth 
7185Scth /*
7285Scth  * Function prototypes
7385Scth  *
7485Scth  * SCSA functions exported by means of the transport table
7585Scth  */
7685Scth static int emul64_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
7785Scth 	scsi_hba_tran_t *tran, struct scsi_device *sd);
7885Scth static int emul64_scsi_start(struct scsi_address *ap, struct scsi_pkt *pkt);
7985Scth static void emul64_pkt_comp(void *);
8085Scth static int emul64_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt);
8185Scth static int emul64_scsi_reset(struct scsi_address *ap, int level);
8285Scth static int emul64_scsi_getcap(struct scsi_address *ap, char *cap, int whom);
8385Scth static int emul64_scsi_setcap(struct scsi_address *ap, char *cap, int value,
8485Scth     int whom);
8585Scth static struct scsi_pkt *emul64_scsi_init_pkt(struct scsi_address *ap,
8685Scth     struct scsi_pkt *pkt, struct buf *bp, int cmdlen, int statuslen,
8785Scth     int tgtlen, int flags, int (*callback)(), caddr_t arg);
8885Scth static void emul64_scsi_destroy_pkt(struct scsi_address *ap,
8985Scth 					struct scsi_pkt *pkt);
9085Scth static void emul64_scsi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt);
9185Scth static void emul64_scsi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt);
9285Scth static int emul64_scsi_reset_notify(struct scsi_address *ap, int flag,
9385Scth     void (*callback)(caddr_t), caddr_t arg);
9485Scth 
9585Scth /*
9685Scth  * internal functions
9785Scth  */
9885Scth static void emul64_i_initcap(struct emul64 *emul64);
9985Scth 
10085Scth static void emul64_i_log(struct emul64 *emul64, int level, char *fmt, ...);
10185Scth static int emul64_get_tgtrange(struct emul64 *,
10285Scth 				intptr_t,
10385Scth 				emul64_tgt_t **,
10485Scth 				emul64_tgt_range_t *);
10585Scth static int emul64_write_off(struct emul64 *,
10685Scth 			    emul64_tgt_t *,
10785Scth 			    emul64_tgt_range_t *);
10885Scth static int emul64_write_on(struct emul64 *,
10985Scth 				emul64_tgt_t *,
11085Scth 				emul64_tgt_range_t *);
11185Scth static emul64_nowrite_t *emul64_nowrite_alloc(emul64_range_t *);
11285Scth static void emul64_nowrite_free(emul64_nowrite_t *);
11385Scth static emul64_nowrite_t *emul64_find_nowrite(emul64_tgt_t *,
11485Scth 					diskaddr_t start_block,
11585Scth 					size_t blkcnt,
11685Scth 					emul64_rng_overlap_t *overlapp,
11785Scth 					emul64_nowrite_t ***prevp);
11885Scth 
11985Scth extern emul64_tgt_t *find_tgt(struct emul64 *, ushort_t, ushort_t);
12085Scth 
12185Scth #ifdef EMUL64DEBUG
12285Scth static void emul64_debug_dump_cdb(struct scsi_address *ap,
12385Scth 		struct scsi_pkt *pkt);
12485Scth #endif
12585Scth 
12685Scth 
12785Scth #ifdef	_DDICT
12885Scth static int	ddi_in_panic(void);
ddi_in_panic()12985Scth static int	ddi_in_panic() { return (0); }
13085Scth #ifndef	SCSI_CAP_RESET_NOTIFICATION
13185Scth #define	SCSI_CAP_RESET_NOTIFICATION		14
13285Scth #endif
13385Scth #ifndef	SCSI_RESET_NOTIFY
13485Scth #define	SCSI_RESET_NOTIFY			0x01
13585Scth #endif
13685Scth #ifndef	SCSI_RESET_CANCEL
13785Scth #define	SCSI_RESET_CANCEL			0x02
13885Scth #endif
13985Scth #endif
14085Scth 
14185Scth /*
14285Scth  * Tunables:
14385Scth  *
14485Scth  * emul64_max_task
14585Scth  *	The taskq facility is used to queue up SCSI start requests on a per
14685Scth  *	controller basis.  If the maximum number of queued tasks is hit,
14785Scth  *	taskq_ent_alloc() delays for a second, which adversely impacts our
14885Scth  *	performance.  This value establishes the maximum number of task
14985Scth  *	queue entries when taskq_create is called.
15085Scth  *
15185Scth  * emul64_task_nthreads
15285Scth  *	Specifies the number of threads that should be used to process a
15385Scth  *	controller's task queue.  Our init function sets this to the number
15485Scth  *	of CPUs on the system, but this can be overridden in emul64.conf.
15585Scth  */
15685Scth int emul64_max_task = 16;
15785Scth int emul64_task_nthreads = 1;
15885Scth 
15985Scth /*
16085Scth  * Local static data
16185Scth  */
16285Scth static void		*emul64_state = NULL;
16385Scth 
16485Scth /*
16585Scth  * Character/block operations.
16685Scth  */
16785Scth static struct cb_ops emul64_cbops = {
16885Scth 	scsi_hba_open,		/* cb_open */
16985Scth 	scsi_hba_close,		/* cb_close */
17085Scth 	nodev,			/* cb_strategy */
17185Scth 	nodev,			/* cb_print */
17285Scth 	nodev,			/* cb_dump */
17385Scth 	nodev,			/* cb_read */
17485Scth 	nodev,			/* cb_write */
17585Scth 	emul64_ioctl,		/* cb_ioctl */
17685Scth 	nodev,			/* cb_devmap */
17785Scth 	nodev,			/* cb_mmap */
17885Scth 	nodev,			/* cb_segmap */
17985Scth 	nochpoll,		/* cb_chpoll */
18085Scth 	ddi_prop_op,		/* cb_prop_op */
18185Scth 	NULL,			/* cb_str */
18285Scth 	D_MP | D_64BIT | D_HOTPLUG, /* cb_flag */
18385Scth 	CB_REV,			/* cb_rev */
18485Scth 	nodev,			/* cb_aread */
18585Scth 	nodev			/* cb_awrite */
18685Scth };
18785Scth 
18885Scth /*
18985Scth  * autoconfiguration routines.
19085Scth  */
19185Scth 
19285Scth static struct dev_ops emul64_ops = {
19385Scth 	DEVO_REV,			/* rev, */
19485Scth 	0,				/* refcnt */
19585Scth 	emul64_info,			/* getinfo */
19685Scth 	nulldev,			/* identify */
19785Scth 	nulldev,			/* probe */
19885Scth 	emul64_attach,			/* attach */
19985Scth 	emul64_detach,			/* detach */
20085Scth 	nodev,				/* reset */
20185Scth 	&emul64_cbops,			/* char/block ops */
2027656SSherry.Moore@Sun.COM 	NULL,				/* bus ops */
2037656SSherry.Moore@Sun.COM 	NULL,				/* power */
2047656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,			/* quiesce */
20585Scth };
20685Scth 
20785Scth char _depends_on[] = "misc/scsi";
20885Scth 
20985Scth static struct modldrv modldrv = {
21085Scth 	&mod_driverops,			/* module type - driver */
21185Scth 	"emul64 SCSI Host Bus Adapter",	/* module name */
21285Scth 	&emul64_ops,			/* driver ops */
21385Scth };
21485Scth 
21585Scth static struct modlinkage modlinkage = {
21685Scth 	MODREV_1,			/* ml_rev - must be MODREV_1 */
21785Scth 	&modldrv,			/* ml_linkage */
21885Scth 	NULL				/* end of driver linkage */
21985Scth };
22085Scth 
22185Scth int
_init(void)22285Scth _init(void)
22385Scth {
22485Scth 	int	ret;
22585Scth 
22685Scth 	ret = ddi_soft_state_init(&emul64_state, sizeof (struct emul64),
22785Scth 	    EMUL64_INITIAL_SOFT_SPACE);
22885Scth 	if (ret != 0)
22985Scth 		return (ret);
23085Scth 
23185Scth 	if ((ret = scsi_hba_init(&modlinkage)) != 0) {
23285Scth 		ddi_soft_state_fini(&emul64_state);
23385Scth 		return (ret);
23485Scth 	}
23585Scth 
23685Scth 	/* Set the number of task threads to the number of CPUs */
23785Scth 	if (boot_max_ncpus == -1) {
23885Scth 		emul64_task_nthreads = max_ncpus;
23985Scth 	} else {
24085Scth 		emul64_task_nthreads = boot_max_ncpus;
24185Scth 	}
24285Scth 
24385Scth 	emul64_bsd_init();
24485Scth 
24585Scth 	ret = mod_install(&modlinkage);
24685Scth 	if (ret != 0) {
24785Scth 		emul64_bsd_fini();
24885Scth 		scsi_hba_fini(&modlinkage);
24985Scth 		ddi_soft_state_fini(&emul64_state);
25085Scth 	}
25185Scth 
25285Scth 	return (ret);
25385Scth }
25485Scth 
25585Scth int
_fini(void)25685Scth _fini(void)
25785Scth {
25885Scth 	int	ret;
25985Scth 
26085Scth 	if ((ret = mod_remove(&modlinkage)) != 0)
26185Scth 		return (ret);
26285Scth 
26385Scth 	emul64_bsd_fini();
26485Scth 
26585Scth 	scsi_hba_fini(&modlinkage);
26685Scth 
26785Scth 	ddi_soft_state_fini(&emul64_state);
26885Scth 
26985Scth 	return (ret);
27085Scth }
27185Scth 
27285Scth int
_info(struct modinfo * modinfop)27385Scth _info(struct modinfo *modinfop)
27485Scth {
27585Scth 	return (mod_info(&modlinkage, modinfop));
27685Scth }
27785Scth 
27885Scth /*
27985Scth  * Given the device number return the devinfo pointer
28085Scth  * from the scsi_device structure.
28185Scth  */
28285Scth /*ARGSUSED*/
28385Scth static int
emul64_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)28485Scth emul64_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
28585Scth {
28685Scth 	struct emul64	*foo;
28785Scth 	int		instance = getminor((dev_t)arg);
28885Scth 
28985Scth 	switch (cmd) {
29085Scth 	case DDI_INFO_DEVT2DEVINFO:
29185Scth 		foo = ddi_get_soft_state(emul64_state, instance);
29285Scth 		if (foo != NULL)
29385Scth 			*result = (void *)foo->emul64_dip;
29485Scth 		else {
29585Scth 			*result = NULL;
29685Scth 			return (DDI_FAILURE);
29785Scth 		}
29885Scth 		break;
29985Scth 
30085Scth 	case DDI_INFO_DEVT2INSTANCE:
30185Scth 		*result = (void *)(uintptr_t)instance;
30285Scth 		break;
30385Scth 
30485Scth 	default:
30585Scth 		return (DDI_FAILURE);
30685Scth 	}
30785Scth 
30885Scth 	return (DDI_SUCCESS);
30985Scth }
31085Scth 
31185Scth /*
31285Scth  * Attach an instance of an emul64 host adapter.  Allocate data structures,
31385Scth  * initialize the emul64 and we're on the air.
31485Scth  */
31585Scth /*ARGSUSED*/
31685Scth static int
emul64_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)31785Scth emul64_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
31885Scth {
31985Scth 	int		mutex_initted = 0;
32085Scth 	struct emul64	*emul64;
32185Scth 	int		instance;
32285Scth 	scsi_hba_tran_t	*tran = NULL;
32385Scth 	ddi_dma_attr_t	tmp_dma_attr;
32485Scth 
32585Scth 	emul64_bsd_get_props(dip);
32685Scth 
32785Scth 	bzero((void *) &tmp_dma_attr, sizeof (tmp_dma_attr));
32885Scth 	instance = ddi_get_instance(dip);
32985Scth 
33085Scth 	switch (cmd) {
33185Scth 	case DDI_ATTACH:
33285Scth 		break;
33385Scth 
33485Scth 	case DDI_RESUME:
33585Scth 		tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
33685Scth 		if (!tran) {
33785Scth 			return (DDI_FAILURE);
33885Scth 		}
33985Scth 		emul64 = TRAN2EMUL64(tran);
34085Scth 
34185Scth 		return (DDI_SUCCESS);
34285Scth 
34385Scth 	default:
34485Scth 		emul64_i_log(NULL, CE_WARN,
34585Scth 		    "emul64%d: Cmd != DDI_ATTACH/DDI_RESUME", instance);
34685Scth 		return (DDI_FAILURE);
34785Scth 	}
34885Scth 
34985Scth 	/*
35085Scth 	 * Allocate emul64 data structure.
35185Scth 	 */
35285Scth 	if (ddi_soft_state_zalloc(emul64_state, instance) != DDI_SUCCESS) {
35385Scth 		emul64_i_log(NULL, CE_WARN,
3547656SSherry.Moore@Sun.COM 		    "emul64%d: Failed to alloc soft state",
3557656SSherry.Moore@Sun.COM 		    instance);
35685Scth 		return (DDI_FAILURE);
35785Scth 	}
35885Scth 
35985Scth 	emul64 = (struct emul64 *)ddi_get_soft_state(emul64_state, instance);
36085Scth 	if (emul64 == (struct emul64 *)NULL) {
36185Scth 		emul64_i_log(NULL, CE_WARN, "emul64%d: Bad soft state",
3627656SSherry.Moore@Sun.COM 		    instance);
36385Scth 		ddi_soft_state_free(emul64_state, instance);
36485Scth 		return (DDI_FAILURE);
36585Scth 	}
36685Scth 
36785Scth 
36885Scth 	/*
36985Scth 	 * Allocate a transport structure
37085Scth 	 */
37185Scth 	tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);
37285Scth 	if (tran == NULL) {
37385Scth 		cmn_err(CE_WARN, "emul64: scsi_hba_tran_alloc failed\n");
37485Scth 		goto fail;
37585Scth 	}
37685Scth 
37785Scth 	emul64->emul64_tran			= tran;
37885Scth 	emul64->emul64_dip			= dip;
37985Scth 
38085Scth 	tran->tran_hba_private		= emul64;
38185Scth 	tran->tran_tgt_private		= NULL;
38285Scth 	tran->tran_tgt_init		= emul64_tran_tgt_init;
38385Scth 	tran->tran_tgt_probe		= scsi_hba_probe;
38485Scth 	tran->tran_tgt_free		= NULL;
38585Scth 
38685Scth 	tran->tran_start		= emul64_scsi_start;
38785Scth 	tran->tran_abort		= emul64_scsi_abort;
38885Scth 	tran->tran_reset		= emul64_scsi_reset;
38985Scth 	tran->tran_getcap		= emul64_scsi_getcap;
39085Scth 	tran->tran_setcap		= emul64_scsi_setcap;
39185Scth 	tran->tran_init_pkt		= emul64_scsi_init_pkt;
39285Scth 	tran->tran_destroy_pkt		= emul64_scsi_destroy_pkt;
39385Scth 	tran->tran_dmafree		= emul64_scsi_dmafree;
39485Scth 	tran->tran_sync_pkt		= emul64_scsi_sync_pkt;
39585Scth 	tran->tran_reset_notify 	= emul64_scsi_reset_notify;
39685Scth 
39785Scth 	tmp_dma_attr.dma_attr_minxfer = 0x1;
39885Scth 	tmp_dma_attr.dma_attr_burstsizes = 0x7f;
39985Scth 
40085Scth 	/*
40185Scth 	 * Attach this instance of the hba
40285Scth 	 */
40385Scth 	if (scsi_hba_attach_setup(dip, &tmp_dma_attr, tran,
40485Scth 	    0) != DDI_SUCCESS) {
40585Scth 		cmn_err(CE_WARN, "emul64: scsi_hba_attach failed\n");
40685Scth 		goto fail;
40785Scth 	}
40885Scth 
40985Scth 	emul64->emul64_initiator_id = 2;
41085Scth 
41185Scth 	/*
41285Scth 	 * Look up the scsi-options property
41385Scth 	 */
41485Scth 	emul64->emul64_scsi_options =
4157656SSherry.Moore@Sun.COM 	    ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0, "scsi-options",
4167656SSherry.Moore@Sun.COM 	    EMUL64_DEFAULT_SCSI_OPTIONS);
41785Scth 	EMUL64_DEBUG(emul64, SCSI_DEBUG, "emul64 scsi-options=%x",
41885Scth 	    emul64->emul64_scsi_options);
41985Scth 
42085Scth 
42185Scth 	/* mutexes to protect the emul64 request and response queue */
42285Scth 	mutex_init(EMUL64_REQ_MUTEX(emul64), NULL, MUTEX_DRIVER,
42385Scth 	    emul64->emul64_iblock);
42485Scth 	mutex_init(EMUL64_RESP_MUTEX(emul64), NULL, MUTEX_DRIVER,
42585Scth 	    emul64->emul64_iblock);
42685Scth 
42785Scth 	mutex_initted = 1;
42885Scth 
42985Scth 	EMUL64_MUTEX_ENTER(emul64);
43085Scth 
43185Scth 	/*
43285Scth 	 * Initialize the default Target Capabilities and Sync Rates
43385Scth 	 */
43485Scth 	emul64_i_initcap(emul64);
43585Scth 
43685Scth 	EMUL64_MUTEX_EXIT(emul64);
43785Scth 
43885Scth 
43985Scth 	ddi_report_dev(dip);
44085Scth 	emul64->emul64_taskq = taskq_create("emul64_comp",
4417656SSherry.Moore@Sun.COM 	    emul64_task_nthreads, MINCLSYSPRI, 1, emul64_max_task, 0);
44285Scth 
44385Scth 	return (DDI_SUCCESS);
44485Scth 
44585Scth fail:
44685Scth 	emul64_i_log(NULL, CE_WARN, "emul64%d: Unable to attach", instance);
44785Scth 
44885Scth 	if (mutex_initted) {
44985Scth 		mutex_destroy(EMUL64_REQ_MUTEX(emul64));
45085Scth 		mutex_destroy(EMUL64_RESP_MUTEX(emul64));
45185Scth 	}
45285Scth 	if (tran) {
45385Scth 		scsi_hba_tran_free(tran);
45485Scth 	}
45585Scth 	ddi_soft_state_free(emul64_state, instance);
45685Scth 	return (DDI_FAILURE);
45785Scth }
45885Scth 
45985Scth /*ARGSUSED*/
46085Scth static int
emul64_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)46185Scth emul64_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
46285Scth {
46385Scth 	struct emul64	*emul64;
46485Scth 	scsi_hba_tran_t	*tran;
46585Scth 	int		instance = ddi_get_instance(dip);
46685Scth 
46785Scth 
46885Scth 	/* get transport structure pointer from the dip */
46985Scth 	if (!(tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip))) {
47085Scth 		return (DDI_FAILURE);
47185Scth 	}
47285Scth 
47385Scth 	/* get soft state from transport structure */
47485Scth 	emul64 = TRAN2EMUL64(tran);
47585Scth 
47685Scth 	if (!emul64) {
47785Scth 		return (DDI_FAILURE);
47885Scth 	}
47985Scth 
48085Scth 	EMUL64_DEBUG(emul64, SCSI_DEBUG, "emul64_detach: cmd = %d", cmd);
48185Scth 
48285Scth 	switch (cmd) {
48385Scth 	case DDI_DETACH:
48485Scth 		EMUL64_MUTEX_ENTER(emul64);
48585Scth 
48685Scth 		taskq_destroy(emul64->emul64_taskq);
48785Scth 		(void) scsi_hba_detach(dip);
48885Scth 
48985Scth 		scsi_hba_tran_free(emul64->emul64_tran);
49085Scth 
49185Scth 
49285Scth 		EMUL64_MUTEX_EXIT(emul64);
49385Scth 
49485Scth 		mutex_destroy(EMUL64_REQ_MUTEX(emul64));
49585Scth 		mutex_destroy(EMUL64_RESP_MUTEX(emul64));
49685Scth 
49785Scth 
49885Scth 		EMUL64_DEBUG(emul64, SCSI_DEBUG, "emul64_detach: done");
49985Scth 		ddi_soft_state_free(emul64_state, instance);
50085Scth 
50185Scth 		return (DDI_SUCCESS);
50285Scth 
50385Scth 	case DDI_SUSPEND:
50485Scth 		return (DDI_SUCCESS);
50585Scth 
50685Scth 	default:
50785Scth 		return (DDI_FAILURE);
50885Scth 	}
50985Scth }
51085Scth 
51185Scth /*
51285Scth  * Function name : emul64_tran_tgt_init
51385Scth  *
51485Scth  * Return Values : DDI_SUCCESS if target supported, DDI_FAILURE otherwise
51585Scth  *
51685Scth  */
51785Scth /*ARGSUSED*/
51885Scth static int
emul64_tran_tgt_init(dev_info_t * hba_dip,dev_info_t * tgt_dip,scsi_hba_tran_t * tran,struct scsi_device * sd)51985Scth emul64_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
52085Scth 	scsi_hba_tran_t *tran, struct scsi_device *sd)
52185Scth {
52285Scth 	struct emul64	*emul64;
52385Scth 	emul64_tgt_t	*tgt;
52485Scth 	char		**geo_vidpid = NULL;
52585Scth 	char		*geo, *vidpid;
52685Scth 	uint32_t	*geoip = NULL;
52785Scth 	uint_t		length;
52885Scth 	uint_t		length2;
52985Scth 	lldaddr_t	sector_count;
53085Scth 	char		prop_name[15];
53185Scth 	int		ret = DDI_FAILURE;
53285Scth 
53385Scth 	emul64 = TRAN2EMUL64(tran);
53485Scth 	EMUL64_MUTEX_ENTER(emul64);
53585Scth 
53685Scth 	/*
53785Scth 	 * We get called for each target driver.conf node, multiple
53885Scth 	 * nodes may map to the same tgt,lun (sd.conf, st.conf, etc).
53985Scth 	 * Check to see if transport to tgt,lun already established.
54085Scth 	 */
54185Scth 	tgt = find_tgt(emul64, sd->sd_address.a_target, sd->sd_address.a_lun);
54285Scth 	if (tgt) {
54385Scth 		ret = DDI_SUCCESS;
54485Scth 		goto out;
54585Scth 	}
54685Scth 
54785Scth 	/* see if we have driver.conf specified device for this target,lun */
54885Scth 	(void) snprintf(prop_name, sizeof (prop_name), "targ_%d_%d",
54985Scth 	    sd->sd_address.a_target, sd->sd_address.a_lun);
55085Scth 	if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, hba_dip,
55185Scth 	    DDI_PROP_DONTPASS, prop_name,
55285Scth 	    &geo_vidpid, &length) != DDI_PROP_SUCCESS)
55385Scth 		goto out;
55485Scth 	if (length < 2) {
55585Scth 		cmn_err(CE_WARN, "emul64: %s property does not have 2 "
5567656SSherry.Moore@Sun.COM 		    "elements", prop_name);
55785Scth 		goto out;
55885Scth 	}
55985Scth 
56085Scth 	/* pick geometry name and vidpid string from string array */
56185Scth 	geo = *geo_vidpid;
56285Scth 	vidpid = *(geo_vidpid + 1);
56385Scth 
56485Scth 	/* lookup geometry property integer array */
56585Scth 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, hba_dip, DDI_PROP_DONTPASS,
56685Scth 	    geo, (int **)&geoip, &length2) != DDI_PROP_SUCCESS) {
56785Scth 		cmn_err(CE_WARN, "emul64: didn't get prop '%s'", geo);
56885Scth 		goto out;
56985Scth 	}
57085Scth 	if (length2 < 6) {
57185Scth 		cmn_err(CE_WARN, "emul64: property %s does not have 6 "
5727656SSherry.Moore@Sun.COM 		    "elements", *geo_vidpid);
57385Scth 		goto out;
57485Scth 	}
57585Scth 
57685Scth 	/* allocate and initialize tgt structure for tgt,lun */
57785Scth 	tgt = kmem_zalloc(sizeof (emul64_tgt_t), KM_SLEEP);
57885Scth 	rw_init(&tgt->emul64_tgt_nw_lock, NULL, RW_DRIVER, NULL);
57985Scth 	mutex_init(&tgt->emul64_tgt_blk_lock, NULL, MUTEX_DRIVER, NULL);
58085Scth 
58185Scth 	/* create avl for data block storage */
58285Scth 	avl_create(&tgt->emul64_tgt_data, emul64_bsd_blkcompare,
5837656SSherry.Moore@Sun.COM 	    sizeof (blklist_t), offsetof(blklist_t, bl_node));
58485Scth 
58585Scth 	/* save scsi_address and vidpid */
58685Scth 	bcopy(sd, &tgt->emul64_tgt_saddr, sizeof (struct scsi_address));
58785Scth 	(void) strncpy(tgt->emul64_tgt_inq, vidpid,
5887656SSherry.Moore@Sun.COM 	    sizeof (emul64->emul64_tgt->emul64_tgt_inq));
58985Scth 
59085Scth 	/*
59185Scth 	 * The high order 4 bytes of the sector count always come first in
59285Scth 	 * emul64.conf.  They are followed by the low order 4 bytes.  Not
59385Scth 	 * all CPU types want them in this order, but laddr_t takes care of
59485Scth 	 * this for us.  We then pick up geometry (ncyl X nheads X nsect).
59585Scth 	 */
59685Scth 	sector_count._p._u	= *(geoip + 0);
59785Scth 	sector_count._p._l	= *(geoip + 1);
59885Scth 	/*
59985Scth 	 * On 32-bit platforms, fix block size if it's greater than the
60085Scth 	 * allowable maximum.
60185Scth 	 */
60285Scth #if !defined(_LP64)
60385Scth 	if (sector_count._f > DK_MAX_BLOCKS)
60485Scth 		sector_count._f = DK_MAX_BLOCKS;
60585Scth #endif
60685Scth 	tgt->emul64_tgt_sectors = sector_count._f;
60785Scth 	tgt->emul64_tgt_dtype	= *(geoip + 2);
60885Scth 	tgt->emul64_tgt_ncyls	= *(geoip + 3);
60985Scth 	tgt->emul64_tgt_nheads	= *(geoip + 4);
61085Scth 	tgt->emul64_tgt_nsect	= *(geoip + 5);
61185Scth 
61285Scth 	/* insert target structure into list */
61385Scth 	tgt->emul64_tgt_next = emul64->emul64_tgt;
61485Scth 	emul64->emul64_tgt = tgt;
61585Scth 	ret = DDI_SUCCESS;
61685Scth 
61785Scth out:	EMUL64_MUTEX_EXIT(emul64);
61885Scth 	if (geoip)
61985Scth 		ddi_prop_free(geoip);
62085Scth 	if (geo_vidpid)
62185Scth 		ddi_prop_free(geo_vidpid);
62285Scth 	return (ret);
62385Scth }
62485Scth 
62585Scth /*
62685Scth  * Function name : emul64_i_initcap
62785Scth  *
62885Scth  * Return Values : NONE
62985Scth  * Description	 : Initializes the default target capabilities and
63085Scth  *		   Sync Rates.
63185Scth  *
63285Scth  * Context	 : Called from the user thread through attach.
63385Scth  *
63485Scth  */
63585Scth static void
emul64_i_initcap(struct emul64 * emul64)63685Scth emul64_i_initcap(struct emul64 *emul64)
63785Scth {
63885Scth 	uint16_t	cap, synch;
63985Scth 	int		i;
64085Scth 
64185Scth 	cap = 0;
64285Scth 	synch = 0;
64385Scth 	for (i = 0; i < NTARGETS_WIDE; i++) {
64485Scth 		emul64->emul64_cap[i] = cap;
64585Scth 		emul64->emul64_synch[i] = synch;
64685Scth 	}
64785Scth 	EMUL64_DEBUG(emul64, SCSI_DEBUG, "default cap = 0x%x", cap);
64885Scth }
64985Scth 
65085Scth /*
65185Scth  * Function name : emul64_scsi_getcap()
65285Scth  *
65385Scth  * Return Values : current value of capability, if defined
65485Scth  *		   -1 if capability is not defined
65585Scth  * Description	 : returns current capability value
65685Scth  *
65785Scth  * Context	 : Can be called from different kernel process threads.
65885Scth  *		   Can be called by interrupt thread.
65985Scth  */
66085Scth static int
emul64_scsi_getcap(struct scsi_address * ap,char * cap,int whom)66185Scth emul64_scsi_getcap(struct scsi_address *ap, char *cap, int whom)
66285Scth {
66385Scth 	struct emul64	*emul64	= ADDR2EMUL64(ap);
66485Scth 	int		rval = 0;
66585Scth 
66685Scth 	/*
66785Scth 	 * We don't allow inquiring about capabilities for other targets
66885Scth 	 */
66985Scth 	if (cap == NULL || whom == 0) {
67085Scth 		return (-1);
67185Scth 	}
67285Scth 
67385Scth 	EMUL64_MUTEX_ENTER(emul64);
67485Scth 
67585Scth 	switch (scsi_hba_lookup_capstr(cap)) {
67685Scth 	case SCSI_CAP_DMA_MAX:
67785Scth 		rval = 1 << 24; /* Limit to 16MB max transfer */
67885Scth 		break;
67985Scth 	case SCSI_CAP_MSG_OUT:
68085Scth 		rval = 1;
68185Scth 		break;
68285Scth 	case SCSI_CAP_DISCONNECT:
68385Scth 		rval = 1;
68485Scth 		break;
68585Scth 	case SCSI_CAP_SYNCHRONOUS:
68685Scth 		rval = 1;
68785Scth 		break;
68885Scth 	case SCSI_CAP_WIDE_XFER:
68985Scth 		rval = 1;
69085Scth 		break;
69185Scth 	case SCSI_CAP_TAGGED_QING:
69285Scth 		rval = 1;
69385Scth 		break;
69485Scth 	case SCSI_CAP_UNTAGGED_QING:
69585Scth 		rval = 1;
69685Scth 		break;
69785Scth 	case SCSI_CAP_PARITY:
69885Scth 		rval = 1;
69985Scth 		break;
70085Scth 	case SCSI_CAP_INITIATOR_ID:
70185Scth 		rval = emul64->emul64_initiator_id;
70285Scth 		break;
70385Scth 	case SCSI_CAP_ARQ:
70485Scth 		rval = 1;
70585Scth 		break;
70685Scth 	case SCSI_CAP_LINKED_CMDS:
70785Scth 		break;
70885Scth 	case SCSI_CAP_RESET_NOTIFICATION:
70985Scth 		rval = 1;
71085Scth 		break;
71185Scth 
71285Scth 	default:
71385Scth 		rval = -1;
71485Scth 		break;
71585Scth 	}
71685Scth 
71785Scth 	EMUL64_MUTEX_EXIT(emul64);
71885Scth 
71985Scth 	return (rval);
72085Scth }
72185Scth 
72285Scth /*
72385Scth  * Function name : emul64_scsi_setcap()
72485Scth  *
72585Scth  * Return Values : 1 - capability exists and can be set to new value
72685Scth  *		   0 - capability could not be set to new value
72785Scth  *		  -1 - no such capability
72885Scth  *
72985Scth  * Description	 : sets a capability for a target
73085Scth  *
73185Scth  * Context	 : Can be called from different kernel process threads.
73285Scth  *		   Can be called by interrupt thread.
73385Scth  */
73485Scth static int
emul64_scsi_setcap(struct scsi_address * ap,char * cap,int value,int whom)73585Scth emul64_scsi_setcap(struct scsi_address *ap, char *cap, int value, int whom)
73685Scth {
73785Scth 	struct emul64	*emul64	= ADDR2EMUL64(ap);
73885Scth 	int		rval = 0;
73985Scth 
74085Scth 	/*
74185Scth 	 * We don't allow setting capabilities for other targets
74285Scth 	 */
74385Scth 	if (cap == NULL || whom == 0) {
74485Scth 		return (-1);
74585Scth 	}
74685Scth 
74785Scth 	EMUL64_MUTEX_ENTER(emul64);
74885Scth 
74985Scth 	switch (scsi_hba_lookup_capstr(cap)) {
75085Scth 	case SCSI_CAP_DMA_MAX:
75185Scth 	case SCSI_CAP_MSG_OUT:
75285Scth 	case SCSI_CAP_PARITY:
75385Scth 	case SCSI_CAP_UNTAGGED_QING:
75485Scth 	case SCSI_CAP_LINKED_CMDS:
75585Scth 	case SCSI_CAP_RESET_NOTIFICATION:
75685Scth 		/*
75785Scth 		 * None of these are settable via
75885Scth 		 * the capability interface.
75985Scth 		 */
76085Scth 		break;
76185Scth 	case SCSI_CAP_DISCONNECT:
76285Scth 		rval = 1;
76385Scth 		break;
76485Scth 	case SCSI_CAP_SYNCHRONOUS:
76585Scth 		rval = 1;
76685Scth 		break;
76785Scth 	case SCSI_CAP_TAGGED_QING:
76885Scth 		rval = 1;
76985Scth 		break;
77085Scth 	case SCSI_CAP_WIDE_XFER:
77185Scth 		rval = 1;
77285Scth 		break;
77385Scth 	case SCSI_CAP_INITIATOR_ID:
77485Scth 		rval = -1;
77585Scth 		break;
77685Scth 	case SCSI_CAP_ARQ:
77785Scth 		rval = 1;
77885Scth 		break;
77985Scth 	case SCSI_CAP_TOTAL_SECTORS:
78085Scth 		emul64->nt_total_sectors[ap->a_target][ap->a_lun] = value;
78185Scth 		rval = TRUE;
78285Scth 		break;
78385Scth 	case SCSI_CAP_SECTOR_SIZE:
78485Scth 		rval = TRUE;
78585Scth 		break;
78685Scth 	default:
78785Scth 		rval = -1;
78885Scth 		break;
78985Scth 	}
79085Scth 
79185Scth 
79285Scth 	EMUL64_MUTEX_EXIT(emul64);
79385Scth 
79485Scth 	return (rval);
79585Scth }
79685Scth 
79785Scth /*
79885Scth  * Function name : emul64_scsi_init_pkt
79985Scth  *
80085Scth  * Return Values : pointer to scsi_pkt, or NULL
80185Scth  * Description	 : Called by kernel on behalf of a target driver
80285Scth  *		   calling scsi_init_pkt(9F).
80385Scth  *		   Refer to tran_init_pkt(9E) man page
80485Scth  *
80585Scth  * Context	 : Can be called from different kernel process threads.
80685Scth  *		   Can be called by interrupt thread.
80785Scth  */
80885Scth /* ARGSUSED */
80985Scth static struct scsi_pkt *
emul64_scsi_init_pkt(struct scsi_address * ap,struct scsi_pkt * pkt,struct buf * bp,int cmdlen,int statuslen,int tgtlen,int flags,int (* callback)(),caddr_t arg)81085Scth emul64_scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt,
81185Scth 	struct buf *bp, int cmdlen, int statuslen, int tgtlen,
81285Scth 	int flags, int (*callback)(), caddr_t arg)
81385Scth {
81485Scth 	struct emul64		*emul64	= ADDR2EMUL64(ap);
81585Scth 	struct emul64_cmd	*sp;
81685Scth 
81785Scth 	ASSERT(callback == NULL_FUNC || callback == SLEEP_FUNC);
81885Scth 
81985Scth 	/*
82085Scth 	 * First step of emul64_scsi_init_pkt:  pkt allocation
82185Scth 	 */
82285Scth 	if (pkt == NULL) {
82385Scth 		pkt = scsi_hba_pkt_alloc(emul64->emul64_dip, ap, cmdlen,
8247656SSherry.Moore@Sun.COM 		    statuslen,
8257656SSherry.Moore@Sun.COM 		    tgtlen, sizeof (struct emul64_cmd), callback, arg);
82685Scth 		if (pkt == NULL) {
82785Scth 			cmn_err(CE_WARN, "emul64_scsi_init_pkt: "
8287656SSherry.Moore@Sun.COM 			    "scsi_hba_pkt_alloc failed");
82985Scth 			return (NULL);
83085Scth 		}
83185Scth 
83285Scth 		sp = PKT2CMD(pkt);
83385Scth 
83485Scth 		/*
83585Scth 		 * Initialize the new pkt - we redundantly initialize
83685Scth 		 * all the fields for illustrative purposes.
83785Scth 		 */
83885Scth 		sp->cmd_pkt		= pkt;
83985Scth 		sp->cmd_flags		= 0;
84085Scth 		sp->cmd_scblen		= statuslen;
84185Scth 		sp->cmd_cdblen		= cmdlen;
84285Scth 		sp->cmd_emul64		= emul64;
84385Scth 		pkt->pkt_address	= *ap;
84485Scth 		pkt->pkt_comp		= (void (*)())NULL;
84585Scth 		pkt->pkt_flags		= 0;
84685Scth 		pkt->pkt_time		= 0;
84785Scth 		pkt->pkt_resid		= 0;
84885Scth 		pkt->pkt_statistics	= 0;
84985Scth 		pkt->pkt_reason		= 0;
85085Scth 
85185Scth 	} else {
85285Scth 		sp = PKT2CMD(pkt);
85385Scth 	}
85485Scth 
85585Scth 	/*
85685Scth 	 * Second step of emul64_scsi_init_pkt:  dma allocation/move
85785Scth 	 */
85885Scth 	if (bp && bp->b_bcount != 0) {
85985Scth 		if (bp->b_flags & B_READ) {
86085Scth 			sp->cmd_flags &= ~CFLAG_DMASEND;
86185Scth 		} else {
86285Scth 			sp->cmd_flags |= CFLAG_DMASEND;
86385Scth 		}
86485Scth 		bp_mapin(bp);
86585Scth 		sp->cmd_addr = (unsigned char *) bp->b_un.b_addr;
86685Scth 		sp->cmd_count = bp->b_bcount;
86785Scth 		pkt->pkt_resid = 0;
86885Scth 	}
86985Scth 
87085Scth 	return (pkt);
87185Scth }
87285Scth 
87385Scth 
87485Scth /*
87585Scth  * Function name : emul64_scsi_destroy_pkt
87685Scth  *
87785Scth  * Return Values : none
87885Scth  * Description	 : Called by kernel on behalf of a target driver
87985Scth  *		   calling scsi_destroy_pkt(9F).
88085Scth  *		   Refer to tran_destroy_pkt(9E) man page
88185Scth  *
88285Scth  * Context	 : Can be called from different kernel process threads.
88385Scth  *		   Can be called by interrupt thread.
88485Scth  */
88585Scth static void
emul64_scsi_destroy_pkt(struct scsi_address * ap,struct scsi_pkt * pkt)88685Scth emul64_scsi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
88785Scth {
88885Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
88985Scth 
89085Scth 	/*
89185Scth 	 * emul64_scsi_dmafree inline to make things faster
89285Scth 	 */
89385Scth 	if (sp->cmd_flags & CFLAG_DMAVALID) {
89485Scth 		/*
89585Scth 		 * Free the mapping.
89685Scth 		 */
89785Scth 		sp->cmd_flags &= ~CFLAG_DMAVALID;
89885Scth 	}
89985Scth 
90085Scth 	/*
90185Scth 	 * Free the pkt
90285Scth 	 */
90385Scth 	scsi_hba_pkt_free(ap, pkt);
90485Scth }
90585Scth 
90685Scth 
90785Scth /*
90885Scth  * Function name : emul64_scsi_dmafree()
90985Scth  *
91085Scth  * Return Values : none
91185Scth  * Description	 : free dvma resources
91285Scth  *
91385Scth  * Context	 : Can be called from different kernel process threads.
91485Scth  *		   Can be called by interrupt thread.
91585Scth  */
91685Scth /*ARGSUSED*/
91785Scth static void
emul64_scsi_dmafree(struct scsi_address * ap,struct scsi_pkt * pkt)91885Scth emul64_scsi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
91985Scth {
92085Scth }
92185Scth 
92285Scth /*
92385Scth  * Function name : emul64_scsi_sync_pkt()
92485Scth  *
92585Scth  * Return Values : none
92685Scth  * Description	 : sync dma
92785Scth  *
92885Scth  * Context	 : Can be called from different kernel process threads.
92985Scth  *		   Can be called by interrupt thread.
93085Scth  */
93185Scth /*ARGSUSED*/
93285Scth static void
emul64_scsi_sync_pkt(struct scsi_address * ap,struct scsi_pkt * pkt)93385Scth emul64_scsi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
93485Scth {
93585Scth }
93685Scth 
93785Scth /*
93885Scth  * routine for reset notification setup, to register or cancel.
93985Scth  */
94085Scth static int
emul64_scsi_reset_notify(struct scsi_address * ap,int flag,void (* callback)(caddr_t),caddr_t arg)94185Scth emul64_scsi_reset_notify(struct scsi_address *ap, int flag,
94285Scth void (*callback)(caddr_t), caddr_t arg)
94385Scth {
94485Scth 	struct emul64				*emul64 = ADDR2EMUL64(ap);
94585Scth 	struct emul64_reset_notify_entry	*p, *beforep;
94685Scth 	int					rval = DDI_FAILURE;
94785Scth 
94885Scth 	mutex_enter(EMUL64_REQ_MUTEX(emul64));
94985Scth 
95085Scth 	p = emul64->emul64_reset_notify_listf;
95185Scth 	beforep = NULL;
95285Scth 
95385Scth 	while (p) {
95485Scth 		if (p->ap == ap)
95585Scth 			break;	/* An entry exists for this target */
95685Scth 		beforep = p;
95785Scth 		p = p->next;
95885Scth 	}
95985Scth 
96085Scth 	if ((flag & SCSI_RESET_CANCEL) && (p != NULL)) {
96185Scth 		if (beforep == NULL) {
96285Scth 			emul64->emul64_reset_notify_listf = p->next;
96385Scth 		} else {
96485Scth 			beforep->next = p->next;
96585Scth 		}
96685Scth 		kmem_free((caddr_t)p,
9677656SSherry.Moore@Sun.COM 		    sizeof (struct emul64_reset_notify_entry));
96885Scth 		rval = DDI_SUCCESS;
96985Scth 
97085Scth 	} else if ((flag & SCSI_RESET_NOTIFY) && (p == NULL)) {
97185Scth 		p = kmem_zalloc(sizeof (struct emul64_reset_notify_entry),
9727656SSherry.Moore@Sun.COM 		    KM_SLEEP);
97385Scth 		p->ap = ap;
97485Scth 		p->callback = callback;
97585Scth 		p->arg = arg;
97685Scth 		p->next = emul64->emul64_reset_notify_listf;
97785Scth 		emul64->emul64_reset_notify_listf = p;
97885Scth 		rval = DDI_SUCCESS;
97985Scth 	}
98085Scth 
98185Scth 	mutex_exit(EMUL64_REQ_MUTEX(emul64));
98285Scth 
98385Scth 	return (rval);
98485Scth }
98585Scth 
98685Scth /*
98785Scth  * Function name : emul64_scsi_start()
98885Scth  *
98985Scth  * Return Values : TRAN_FATAL_ERROR	- emul64 has been shutdown
99085Scth  *		   TRAN_BUSY		- request queue is full
99185Scth  *		   TRAN_ACCEPT		- pkt has been submitted to emul64
99285Scth  *
99385Scth  * Description	 : init pkt, start the request
99485Scth  *
99585Scth  * Context	 : Can be called from different kernel process threads.
99685Scth  *		   Can be called by interrupt thread.
99785Scth  */
99885Scth static int
emul64_scsi_start(struct scsi_address * ap,struct scsi_pkt * pkt)99985Scth emul64_scsi_start(struct scsi_address *ap, struct scsi_pkt *pkt)
100085Scth {
100185Scth 	struct emul64_cmd	*sp	= PKT2CMD(pkt);
100285Scth 	int			rval	= TRAN_ACCEPT;
100385Scth 	struct emul64		*emul64	= ADDR2EMUL64(ap);
100485Scth 	clock_t			cur_lbolt;
100585Scth 	taskqid_t		dispatched;
100685Scth 
100785Scth 	ASSERT(mutex_owned(EMUL64_REQ_MUTEX(emul64)) == 0 || ddi_in_panic());
100885Scth 	ASSERT(mutex_owned(EMUL64_RESP_MUTEX(emul64)) == 0 || ddi_in_panic());
100985Scth 
101085Scth 	EMUL64_DEBUG2(emul64, SCSI_DEBUG, "emul64_scsi_start %x", sp);
101185Scth 
101285Scth 	pkt->pkt_reason = CMD_CMPLT;
101385Scth 
101485Scth #ifdef	EMUL64DEBUG
101585Scth 	if (emul64_cdb_debug) {
101685Scth 		emul64_debug_dump_cdb(ap, pkt);
101785Scth 	}
101885Scth #endif	/* EMUL64DEBUG */
101985Scth 
102085Scth 	/*
102185Scth 	 * calculate deadline from pkt_time
102285Scth 	 * Instead of multiplying by 100 (ie. HZ), we multiply by 128 so
102385Scth 	 * we can shift and at the same time have a 28% grace period
102485Scth 	 * we ignore the rare case of pkt_time == 0 and deal with it
102585Scth 	 * in emul64_i_watch()
102685Scth 	 */
102785Scth 	cur_lbolt = ddi_get_lbolt();
102885Scth 	sp->cmd_deadline = cur_lbolt + (pkt->pkt_time * 128);
102985Scth 
103085Scth 	if ((emul64_usetaskq == 0) || (pkt->pkt_flags & FLAG_NOINTR) != 0) {
103185Scth 		emul64_pkt_comp((caddr_t)pkt);
103285Scth 	} else {
103385Scth 		dispatched = NULL;
103485Scth 		if (emul64_collect_stats) {
103585Scth 			/*
103685Scth 			 * If we are collecting statistics, call
103785Scth 			 * taskq_dispatch in no sleep mode, so that we can
103885Scth 			 * detect if we are exceeding the queue length that
103985Scth 			 * was established in the call to taskq_create in
104085Scth 			 * emul64_attach.  If the no sleep call fails
104185Scth 			 * (returns NULL), the task will be dispatched in
104285Scth 			 * sleep mode below.
104385Scth 			 */
104485Scth 			dispatched = taskq_dispatch(emul64->emul64_taskq,
10457656SSherry.Moore@Sun.COM 			    emul64_pkt_comp, (void *)pkt, TQ_NOSLEEP);
104685Scth 			if (dispatched == NULL) {
104785Scth 				/* Queue was full.  dispatch failed. */
104885Scth 				mutex_enter(&emul64_stats_mutex);
104985Scth 				emul64_taskq_max++;
105085Scth 				mutex_exit(&emul64_stats_mutex);
105185Scth 			}
105285Scth 		}
105385Scth 		if (dispatched == NULL) {
105485Scth 			(void) taskq_dispatch(emul64->emul64_taskq,
10557656SSherry.Moore@Sun.COM 			    emul64_pkt_comp, (void *)pkt, TQ_SLEEP);
105685Scth 		}
105785Scth 	}
105885Scth 
105985Scth done:
106085Scth 	ASSERT(mutex_owned(EMUL64_REQ_MUTEX(emul64)) == 0 || ddi_in_panic());
106185Scth 	ASSERT(mutex_owned(EMUL64_RESP_MUTEX(emul64)) == 0 || ddi_in_panic());
106285Scth 
106385Scth 	return (rval);
106485Scth }
106585Scth 
106685Scth void
emul64_check_cond(struct scsi_pkt * pkt,uchar_t key,uchar_t asc,uchar_t ascq)106785Scth emul64_check_cond(struct scsi_pkt *pkt, uchar_t key, uchar_t asc, uchar_t ascq)
106885Scth {
106985Scth 	struct scsi_arq_status *arq =
10707656SSherry.Moore@Sun.COM 	    (struct scsi_arq_status *)pkt->pkt_scbp;
107185Scth 
107285Scth 	/* got check, no data transferred and ARQ done */
107385Scth 	arq->sts_status.sts_chk = 1;
107485Scth 	pkt->pkt_state |= STATE_ARQ_DONE;
107585Scth 	pkt->pkt_state &= ~STATE_XFERRED_DATA;
107685Scth 
107785Scth 	/* for ARQ */
107885Scth 	arq->sts_rqpkt_reason = CMD_CMPLT;
107985Scth 	arq->sts_rqpkt_resid = 0;
108085Scth 	arq->sts_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
108185Scth 	    STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
108285Scth 	arq->sts_sensedata.es_valid = 1;
108385Scth 	arq->sts_sensedata.es_class = 0x7;
108485Scth 	arq->sts_sensedata.es_key = key;
108585Scth 	arq->sts_sensedata.es_add_code = asc;
108685Scth 	arq->sts_sensedata.es_qual_code = ascq;
108785Scth }
108885Scth 
10891144Spd144616 ushort_t
emul64_error_inject(struct scsi_pkt * pkt)10901144Spd144616 emul64_error_inject(struct scsi_pkt *pkt)
10911144Spd144616 {
10921144Spd144616 	struct emul64_cmd	*sp	= PKT2CMD(pkt);
10931144Spd144616 	emul64_tgt_t		*tgt;
10941144Spd144616 	struct scsi_arq_status *arq =
10957656SSherry.Moore@Sun.COM 	    (struct scsi_arq_status *)pkt->pkt_scbp;
10961144Spd144616 	uint_t			max_sense_len;
10971144Spd144616 
10981144Spd144616 	EMUL64_MUTEX_ENTER(sp->cmd_emul64);
10991144Spd144616 	tgt = find_tgt(sp->cmd_emul64,
11007656SSherry.Moore@Sun.COM 	    pkt->pkt_address.a_target, pkt->pkt_address.a_lun);
11011144Spd144616 	EMUL64_MUTEX_EXIT(sp->cmd_emul64);
11021144Spd144616 
11031144Spd144616 	/*
11041144Spd144616 	 * If there is no target, skip the error injection and
11051144Spd144616 	 * let the packet be handled normally.  This would normally
11061144Spd144616 	 * never happen since a_target and a_lun are setup in
11071144Spd144616 	 * emul64_scsi_init_pkt.
11081144Spd144616 	 */
11091144Spd144616 	if (tgt == NULL) {
11101144Spd144616 		return (ERR_INJ_DISABLE);
11111144Spd144616 	}
11121144Spd144616 
11131144Spd144616 	if (tgt->emul64_einj_state != ERR_INJ_DISABLE) {
11141144Spd144616 		arq->sts_status = tgt->emul64_einj_scsi_status;
11151144Spd144616 		pkt->pkt_state = tgt->emul64_einj_pkt_state;
11161144Spd144616 		pkt->pkt_reason = tgt->emul64_einj_pkt_reason;
11171144Spd144616 
11181144Spd144616 		/*
11191144Spd144616 		 * Calculate available sense buffer length.  We could just
11201144Spd144616 		 * assume sizeof(struct scsi_extended_sense) but hopefully
11211144Spd144616 		 * that limitation will go away soon.
11221144Spd144616 		 */
11231144Spd144616 		max_sense_len = sp->cmd_scblen  -
11241144Spd144616 		    (sizeof (struct scsi_arq_status) -
11257656SSherry.Moore@Sun.COM 		    sizeof (struct scsi_extended_sense));
11261144Spd144616 		if (max_sense_len > tgt->emul64_einj_sense_length) {
11271144Spd144616 			max_sense_len = tgt->emul64_einj_sense_length;
11281144Spd144616 		}
11291144Spd144616 
11301144Spd144616 		/* for ARQ */
11311144Spd144616 		arq->sts_rqpkt_reason = CMD_CMPLT;
11321144Spd144616 		arq->sts_rqpkt_resid = 0;
11331144Spd144616 		arq->sts_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
11341144Spd144616 		    STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
11351144Spd144616 
11361144Spd144616 		/* Copy sense data */
11371144Spd144616 		if (tgt->emul64_einj_sense_data != 0) {
11381144Spd144616 			bcopy(tgt->emul64_einj_sense_data,
11391144Spd144616 			    (uint8_t *)&arq->sts_sensedata,
11401144Spd144616 			    max_sense_len);
11411144Spd144616 		}
11421144Spd144616 	}
11431144Spd144616 
11441144Spd144616 	/* Return current error injection state */
11451144Spd144616 	return (tgt->emul64_einj_state);
11461144Spd144616 }
11471144Spd144616 
11481144Spd144616 int
emul64_error_inject_req(struct emul64 * emul64,intptr_t arg)11491144Spd144616 emul64_error_inject_req(struct emul64 *emul64, intptr_t arg)
11501144Spd144616 {
11511144Spd144616 	emul64_tgt_t		*tgt;
11521144Spd144616 	struct emul64_error_inj_data error_inj_req;
11531144Spd144616 
11541144Spd144616 	/* Check args */
11551144Spd144616 	if (arg == NULL) {
11561144Spd144616 		return (EINVAL);
11571144Spd144616 	}
11581144Spd144616 
11591144Spd144616 	if (ddi_copyin((void *)arg, &error_inj_req,
11601144Spd144616 	    sizeof (error_inj_req), 0) != 0) {
11611144Spd144616 		cmn_err(CE_WARN, "emul64: ioctl - inj copyin failed\n");
11621144Spd144616 		return (EFAULT);
11631144Spd144616 	}
11641144Spd144616 
11651144Spd144616 	EMUL64_MUTEX_ENTER(emul64);
11661144Spd144616 	tgt = find_tgt(emul64, error_inj_req.eccd_target,
11671144Spd144616 	    error_inj_req.eccd_lun);
11681144Spd144616 	EMUL64_MUTEX_EXIT(emul64);
11691144Spd144616 
11701144Spd144616 	/* Make sure device exists */
11711144Spd144616 	if (tgt == NULL) {
11721144Spd144616 		return (ENODEV);
11731144Spd144616 	}
11741144Spd144616 
11751144Spd144616 	/* Free old sense buffer if we have one */
11761144Spd144616 	if (tgt->emul64_einj_sense_data != NULL) {
11771144Spd144616 		ASSERT(tgt->emul64_einj_sense_length != 0);
11781144Spd144616 		kmem_free(tgt->emul64_einj_sense_data,
11791144Spd144616 		    tgt->emul64_einj_sense_length);
11801144Spd144616 		tgt->emul64_einj_sense_data = NULL;
11811144Spd144616 		tgt->emul64_einj_sense_length = 0;
11821144Spd144616 	}
11831144Spd144616 
11841144Spd144616 	/*
11851144Spd144616 	 * Now handle error injection request.  If error injection
11861144Spd144616 	 * is requested we will return the sense data provided for
11871144Spd144616 	 * any I/O to this target until told to stop.
11881144Spd144616 	 */
11891144Spd144616 	tgt->emul64_einj_state = error_inj_req.eccd_inj_state;
11901144Spd144616 	tgt->emul64_einj_sense_length = error_inj_req.eccd_sns_dlen;
11911144Spd144616 	tgt->emul64_einj_pkt_state = error_inj_req.eccd_pkt_state;
11921144Spd144616 	tgt->emul64_einj_pkt_reason = error_inj_req.eccd_pkt_reason;
11931144Spd144616 	tgt->emul64_einj_scsi_status = error_inj_req.eccd_scsi_status;
11941144Spd144616 	switch (error_inj_req.eccd_inj_state) {
11951144Spd144616 	case ERR_INJ_ENABLE:
11961144Spd144616 	case ERR_INJ_ENABLE_NODATA:
11971144Spd144616 		if (error_inj_req.eccd_sns_dlen) {
11981144Spd144616 			tgt->emul64_einj_sense_data =
11991144Spd144616 			    kmem_alloc(error_inj_req.eccd_sns_dlen, KM_SLEEP);
12001144Spd144616 			/* Copy sense data */
12011144Spd144616 			if (ddi_copyin((void *)(arg + sizeof (error_inj_req)),
12027656SSherry.Moore@Sun.COM 			    tgt->emul64_einj_sense_data,
12037656SSherry.Moore@Sun.COM 			    error_inj_req.eccd_sns_dlen, 0) != 0) {
12041144Spd144616 				cmn_err(CE_WARN,
12051144Spd144616 				    "emul64: sense data copy in failed\n");
12061144Spd144616 				return (EFAULT);
12071144Spd144616 			}
12081144Spd144616 		}
12091144Spd144616 		break;
12101144Spd144616 	case ERR_INJ_DISABLE:
12111144Spd144616 	default:
12121144Spd144616 		break;
12131144Spd144616 	}
12141144Spd144616 
12151144Spd144616 	return (0);
12161144Spd144616 }
12171144Spd144616 
121885Scth int bsd_scsi_start_stop_unit(struct scsi_pkt *);
121985Scth int bsd_scsi_test_unit_ready(struct scsi_pkt *);
122085Scth int bsd_scsi_request_sense(struct scsi_pkt *);
122185Scth int bsd_scsi_inquiry(struct scsi_pkt *);
122285Scth int bsd_scsi_format(struct scsi_pkt *);
122385Scth int bsd_scsi_io(struct scsi_pkt *);
122485Scth int bsd_scsi_log_sense(struct scsi_pkt *);
122585Scth int bsd_scsi_mode_sense(struct scsi_pkt *);
122685Scth int bsd_scsi_mode_select(struct scsi_pkt *);
122785Scth int bsd_scsi_read_capacity(struct scsi_pkt *);
122885Scth int bsd_scsi_read_capacity_16(struct scsi_pkt *);
122985Scth int bsd_scsi_reserve(struct scsi_pkt *);
123085Scth int bsd_scsi_format(struct scsi_pkt *);
123185Scth int bsd_scsi_release(struct scsi_pkt *);
123285Scth int bsd_scsi_read_defect_list(struct scsi_pkt *);
123385Scth int bsd_scsi_reassign_block(struct scsi_pkt *);
123485Scth int bsd_freeblkrange(emul64_tgt_t *, emul64_range_t *);
123585Scth 
123685Scth static void
emul64_handle_cmd(struct scsi_pkt * pkt)123785Scth emul64_handle_cmd(struct scsi_pkt *pkt)
123885Scth {
12391144Spd144616 	if (emul64_error_inject(pkt) == ERR_INJ_ENABLE_NODATA) {
12401144Spd144616 		/*
12411144Spd144616 		 * If error injection is configured to return with
12421144Spd144616 		 * no data return now without handling the command.
12431144Spd144616 		 * This is how normal check conditions work.
12441144Spd144616 		 *
12451144Spd144616 		 * If the error injection state is ERR_INJ_ENABLE
12461144Spd144616 		 * (or if error injection is disabled) continue and
12471144Spd144616 		 * handle the command.  This would be used for
12481144Spd144616 		 * KEY_RECOVERABLE_ERROR type conditions.
12491144Spd144616 		 */
12501144Spd144616 		return;
12511144Spd144616 	}
12521144Spd144616 
125385Scth 	switch (pkt->pkt_cdbp[0]) {
125485Scth 	case SCMD_START_STOP:
125585Scth 		(void) bsd_scsi_start_stop_unit(pkt);
125685Scth 		break;
125785Scth 	case SCMD_TEST_UNIT_READY:
125885Scth 		(void) bsd_scsi_test_unit_ready(pkt);
125985Scth 		break;
126085Scth 	case SCMD_REQUEST_SENSE:
126185Scth 		(void) bsd_scsi_request_sense(pkt);
126285Scth 		break;
126385Scth 	case SCMD_INQUIRY:
126485Scth 		(void) bsd_scsi_inquiry(pkt);
126585Scth 		break;
126685Scth 	case SCMD_FORMAT:
126785Scth 		(void) bsd_scsi_format(pkt);
126885Scth 		break;
126985Scth 	case SCMD_READ:
127085Scth 	case SCMD_WRITE:
127185Scth 	case SCMD_READ_G1:
127285Scth 	case SCMD_WRITE_G1:
127385Scth 	case SCMD_READ_G4:
127485Scth 	case SCMD_WRITE_G4:
127585Scth 		(void) bsd_scsi_io(pkt);
127685Scth 		break;
127785Scth 	case SCMD_LOG_SENSE_G1:
127885Scth 		(void) bsd_scsi_log_sense(pkt);
127985Scth 		break;
128085Scth 	case SCMD_MODE_SENSE:
128185Scth 	case SCMD_MODE_SENSE_G1:
128285Scth 		(void) bsd_scsi_mode_sense(pkt);
128385Scth 		break;
128485Scth 	case SCMD_MODE_SELECT:
128585Scth 	case SCMD_MODE_SELECT_G1:
128685Scth 		(void) bsd_scsi_mode_select(pkt);
128785Scth 		break;
128885Scth 	case SCMD_READ_CAPACITY:
128985Scth 		(void) bsd_scsi_read_capacity(pkt);
129085Scth 		break;
129185Scth 	case SCMD_SVC_ACTION_IN_G4:
129285Scth 		if (pkt->pkt_cdbp[1] == SSVC_ACTION_READ_CAPACITY_G4) {
129385Scth 			(void) bsd_scsi_read_capacity_16(pkt);
129485Scth 		} else {
129585Scth 			cmn_err(CE_WARN, "emul64: unrecognized G4 service "
12967656SSherry.Moore@Sun.COM 			    "action 0x%x", pkt->pkt_cdbp[1]);
129785Scth 		}
129885Scth 		break;
129985Scth 	case SCMD_RESERVE:
130085Scth 	case SCMD_RESERVE_G1:
130185Scth 		(void) bsd_scsi_reserve(pkt);
130285Scth 		break;
130385Scth 	case SCMD_RELEASE:
130485Scth 	case SCMD_RELEASE_G1:
130585Scth 		(void) bsd_scsi_release(pkt);
130685Scth 		break;
130785Scth 	case SCMD_REASSIGN_BLOCK:
130885Scth 		(void) bsd_scsi_reassign_block(pkt);
130985Scth 		break;
131085Scth 	case SCMD_READ_DEFECT_LIST:
131185Scth 		(void) bsd_scsi_read_defect_list(pkt);
131285Scth 		break;
131385Scth 	case SCMD_PRIN:
131485Scth 	case SCMD_PROUT:
131585Scth 	case SCMD_REPORT_LUNS:
131685Scth 		/* ASC 0x24 INVALID FIELD IN CDB */
131785Scth 		emul64_check_cond(pkt, KEY_ILLEGAL_REQUEST, 0x24, 0x0);
131885Scth 		break;
131985Scth 	default:
132085Scth 		cmn_err(CE_WARN, "emul64: unrecognized "
132185Scth 		    "SCSI cmd 0x%x", pkt->pkt_cdbp[0]);
132285Scth 		emul64_check_cond(pkt, KEY_ILLEGAL_REQUEST, 0x24, 0x0);
132385Scth 		break;
132485Scth 	case SCMD_GET_CONFIGURATION:
132585Scth 	case 0x35:			/* SCMD_SYNCHRONIZE_CACHE */
132685Scth 		/* Don't complain */
132785Scth 		break;
132885Scth 	}
132985Scth }
133085Scth 
133185Scth static void
emul64_pkt_comp(void * arg)133285Scth emul64_pkt_comp(void * arg)
133385Scth {
133485Scth 	struct scsi_pkt		*pkt = (struct scsi_pkt *)arg;
133585Scth 	struct emul64_cmd	*sp = PKT2CMD(pkt);
133685Scth 	emul64_tgt_t		*tgt;
133785Scth 
133885Scth 	EMUL64_MUTEX_ENTER(sp->cmd_emul64);
133985Scth 	tgt = find_tgt(sp->cmd_emul64,
13407656SSherry.Moore@Sun.COM 	    pkt->pkt_address.a_target, pkt->pkt_address.a_lun);
134185Scth 	EMUL64_MUTEX_EXIT(sp->cmd_emul64);
134285Scth 	if (!tgt) {
134385Scth 		pkt->pkt_reason = CMD_TIMEOUT;
134485Scth 		pkt->pkt_state = STATE_GOT_BUS | STATE_SENT_CMD;
134585Scth 		pkt->pkt_statistics = STAT_TIMEOUT;
134685Scth 	} else {
134785Scth 		pkt->pkt_reason = CMD_CMPLT;
134885Scth 		*pkt->pkt_scbp = STATUS_GOOD;
134985Scth 		pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
135085Scth 		    STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
135185Scth 		pkt->pkt_statistics = 0;
135285Scth 		emul64_handle_cmd(pkt);
135385Scth 	}
1354*9106SSrivijitha.Dugganapalli@Sun.COM 	scsi_hba_pkt_comp(pkt);
135585Scth }
135685Scth 
135785Scth /* ARGSUSED */
135885Scth static int
emul64_scsi_abort(struct scsi_address * ap,struct scsi_pkt * pkt)135985Scth emul64_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
136085Scth {
136185Scth 	return (1);
136285Scth }
136385Scth 
136485Scth /* ARGSUSED */
136585Scth static int
emul64_scsi_reset(struct scsi_address * ap,int level)136685Scth emul64_scsi_reset(struct scsi_address *ap, int level)
136785Scth {
136885Scth 	return (1);
136985Scth }
137085Scth 
137185Scth static int
emul64_get_tgtrange(struct emul64 * emul64,intptr_t arg,emul64_tgt_t ** tgtp,emul64_tgt_range_t * tgtr)137285Scth emul64_get_tgtrange(struct emul64 *emul64,
137385Scth 		    intptr_t arg,
137485Scth 		    emul64_tgt_t **tgtp,
137585Scth 		    emul64_tgt_range_t *tgtr)
137685Scth {
137785Scth 	if (ddi_copyin((void *)arg, tgtr, sizeof (*tgtr), 0) != 0) {
137885Scth 		cmn_err(CE_WARN, "emul64: ioctl - copy in failed\n");
137985Scth 		return (EFAULT);
138085Scth 	}
138185Scth 	EMUL64_MUTEX_ENTER(emul64);
138285Scth 	*tgtp = find_tgt(emul64, tgtr->emul64_target, tgtr->emul64_lun);
138385Scth 	EMUL64_MUTEX_EXIT(emul64);
138485Scth 	if (*tgtp == NULL) {
138585Scth 		cmn_err(CE_WARN, "emul64: ioctl - no target for %d,%d on %d",
13867656SSherry.Moore@Sun.COM 		    tgtr->emul64_target, tgtr->emul64_lun,
13877656SSherry.Moore@Sun.COM 		    ddi_get_instance(emul64->emul64_dip));
138885Scth 		return (ENXIO);
138985Scth 	}
139085Scth 	return (0);
139185Scth }
139285Scth 
139385Scth static int
emul64_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)139485Scth emul64_ioctl(dev_t dev,
139585Scth 	int cmd,
139685Scth 	intptr_t arg,
139785Scth 	int mode,
139885Scth 	cred_t *credp,
139985Scth 	int *rvalp)
140085Scth {
140185Scth 	struct emul64		*emul64;
140285Scth 	int			instance;
140385Scth 	int			rv = 0;
140485Scth 	emul64_tgt_range_t	tgtr;
140585Scth 	emul64_tgt_t		*tgt;
140685Scth 
140785Scth 	instance = MINOR2INST(getminor(dev));
140885Scth 	emul64 = (struct emul64 *)ddi_get_soft_state(emul64_state, instance);
140985Scth 	if (emul64 == NULL) {
141085Scth 		cmn_err(CE_WARN, "emul64: ioctl - no softstate for %d\n",
14117656SSherry.Moore@Sun.COM 		    getminor(dev));
141285Scth 		return (ENXIO);
141385Scth 	}
141485Scth 
141585Scth 	switch (cmd) {
141685Scth 	case EMUL64_WRITE_OFF:
141785Scth 		rv = emul64_get_tgtrange(emul64, arg, &tgt, &tgtr);
141885Scth 		if (rv == 0) {
141985Scth 			rv = emul64_write_off(emul64, tgt, &tgtr);
142085Scth 		}
142185Scth 		break;
142285Scth 	case EMUL64_WRITE_ON:
142385Scth 		rv = emul64_get_tgtrange(emul64, arg, &tgt, &tgtr);
142485Scth 		if (rv == 0) {
142585Scth 			rv = emul64_write_on(emul64, tgt, &tgtr);
142685Scth 		}
142785Scth 		break;
142885Scth 	case EMUL64_ZERO_RANGE:
142985Scth 		rv = emul64_get_tgtrange(emul64, arg, &tgt, &tgtr);
143085Scth 		if (rv == 0) {
143185Scth 			mutex_enter(&tgt->emul64_tgt_blk_lock);
143285Scth 			rv = bsd_freeblkrange(tgt, &tgtr.emul64_blkrange);
143385Scth 			mutex_exit(&tgt->emul64_tgt_blk_lock);
143485Scth 		}
143585Scth 		break;
14361144Spd144616 	case EMUL64_ERROR_INJECT:
14371144Spd144616 		rv = emul64_error_inject_req(emul64, arg);
14381144Spd144616 		break;
143985Scth 	default:
144085Scth 		rv  = scsi_hba_ioctl(dev, cmd, arg, mode, credp, rvalp);
144185Scth 		break;
144285Scth 	}
144385Scth 	return (rv);
144485Scth }
144585Scth 
144685Scth /* ARGSUSED */
144785Scth static int
emul64_write_off(struct emul64 * emul64,emul64_tgt_t * tgt,emul64_tgt_range_t * tgtr)144885Scth emul64_write_off(struct emul64 *emul64,
144985Scth 	emul64_tgt_t *tgt,
145085Scth 	emul64_tgt_range_t *tgtr)
145185Scth {
145285Scth 	size_t			blkcnt = tgtr->emul64_blkrange.emul64_blkcnt;
145385Scth 	emul64_nowrite_t	*cur;
145485Scth 	emul64_nowrite_t	*nowrite;
145585Scth 	emul64_rng_overlap_t	overlap = O_NONE;
145685Scth 	emul64_nowrite_t	**prev = NULL;
145785Scth 	diskaddr_t		sb = tgtr->emul64_blkrange.emul64_sb;
145885Scth 
145985Scth 	nowrite = emul64_nowrite_alloc(&tgtr->emul64_blkrange);
146085Scth 
146185Scth 	/* Find spot in list */
146285Scth 	rw_enter(&tgt->emul64_tgt_nw_lock, RW_WRITER);
146385Scth 	cur = emul64_find_nowrite(tgt, sb, blkcnt, &overlap, &prev);
146485Scth 	if (overlap == O_NONE) {
146585Scth 		/* Insert into list */
146685Scth 		*prev = nowrite;
146785Scth 		nowrite->emul64_nwnext = cur;
146885Scth 	}
146985Scth 	rw_exit(&tgt->emul64_tgt_nw_lock);
147085Scth 	if (overlap == O_NONE) {
147185Scth 		if (emul64_collect_stats) {
147285Scth 			mutex_enter(&emul64_stats_mutex);
147385Scth 			emul64_nowrite_count++;
147485Scth 			mutex_exit(&emul64_stats_mutex);
147585Scth 		}
147685Scth 	} else {
147785Scth 		cmn_err(CE_WARN, "emul64: EMUL64_WRITE_OFF 0x%llx,0x%"
147885Scth 		    PRIx64 "overlaps 0x%llx,0x%" PRIx64 "\n",
147985Scth 		    nowrite->emul64_blocked.emul64_sb,
148085Scth 		    nowrite->emul64_blocked.emul64_blkcnt,
148185Scth 		    cur->emul64_blocked.emul64_sb,
148285Scth 		    cur->emul64_blocked.emul64_blkcnt);
148385Scth 		emul64_nowrite_free(nowrite);
148485Scth 		return (EINVAL);
148585Scth 	}
148685Scth 	return (0);
148785Scth }
148885Scth 
148985Scth /* ARGSUSED */
149085Scth static int
emul64_write_on(struct emul64 * emul64,emul64_tgt_t * tgt,emul64_tgt_range_t * tgtr)149185Scth emul64_write_on(struct emul64 *emul64,
149285Scth 		emul64_tgt_t *tgt,
149385Scth 		emul64_tgt_range_t *tgtr)
149485Scth {
149585Scth 	size_t			blkcnt = tgtr->emul64_blkrange.emul64_blkcnt;
149685Scth 	emul64_nowrite_t	*cur;
149785Scth 	emul64_rng_overlap_t	overlap = O_NONE;
149885Scth 	emul64_nowrite_t	**prev = NULL;
149985Scth 	int			rv = 0;
150085Scth 	diskaddr_t		sb = tgtr->emul64_blkrange.emul64_sb;
150185Scth 
150285Scth 	/* Find spot in list */
150385Scth 	rw_enter(&tgt->emul64_tgt_nw_lock, RW_WRITER);
150485Scth 	cur = emul64_find_nowrite(tgt, sb, blkcnt, &overlap, &prev);
150585Scth 	if (overlap == O_SAME) {
150685Scth 		/* Remove from list */
150785Scth 		*prev = cur->emul64_nwnext;
150885Scth 	}
150985Scth 	rw_exit(&tgt->emul64_tgt_nw_lock);
151085Scth 
151185Scth 	switch (overlap) {
151285Scth 	case O_NONE:
151385Scth 		cmn_err(CE_WARN, "emul64: EMUL64_WRITE_ON 0x%llx,0x%lx "
15147656SSherry.Moore@Sun.COM 		    "range not found\n", sb, blkcnt);
151585Scth 		rv = ENXIO;
151685Scth 		break;
151785Scth 	case O_SAME:
151885Scth 		if (emul64_collect_stats) {
151985Scth 			mutex_enter(&emul64_stats_mutex);
152085Scth 			emul64_nowrite_count--;
152185Scth 			mutex_exit(&emul64_stats_mutex);
152285Scth 		}
152385Scth 		emul64_nowrite_free(cur);
152485Scth 		break;
152585Scth 	case O_OVERLAP:
152685Scth 	case O_SUBSET:
152785Scth 		cmn_err(CE_WARN, "emul64: EMUL64_WRITE_ON 0x%llx,0x%lx "
15287656SSherry.Moore@Sun.COM 		    "overlaps 0x%llx,0x%" PRIx64 "\n",
15297656SSherry.Moore@Sun.COM 		    sb, blkcnt, cur->emul64_blocked.emul64_sb,
15307656SSherry.Moore@Sun.COM 		    cur->emul64_blocked.emul64_blkcnt);
153185Scth 		rv = EINVAL;
153285Scth 		break;
153385Scth 	}
153485Scth 	return (rv);
153585Scth }
153685Scth 
153785Scth static emul64_nowrite_t *
emul64_find_nowrite(emul64_tgt_t * tgt,diskaddr_t sb,size_t blkcnt,emul64_rng_overlap_t * overlap,emul64_nowrite_t *** prevp)153885Scth emul64_find_nowrite(emul64_tgt_t *tgt,
153985Scth 		    diskaddr_t sb,
154085Scth 		    size_t blkcnt,
154185Scth 		    emul64_rng_overlap_t *overlap,
154285Scth 		    emul64_nowrite_t ***prevp)
154385Scth {
154485Scth 	emul64_nowrite_t	*cur;
154585Scth 	emul64_nowrite_t	**prev;
154685Scth 
154785Scth 	/* Find spot in list */
154885Scth 	*overlap = O_NONE;
154985Scth 	prev = &tgt->emul64_tgt_nowrite;
155085Scth 	cur = tgt->emul64_tgt_nowrite;
155185Scth 	while (cur != NULL) {
155285Scth 		*overlap = emul64_overlap(&cur->emul64_blocked, sb, blkcnt);
155385Scth 		if (*overlap != O_NONE)
155485Scth 			break;
155585Scth 		prev = &cur->emul64_nwnext;
155685Scth 		cur = cur->emul64_nwnext;
155785Scth 	}
155885Scth 
155985Scth 	*prevp = prev;
156085Scth 	return (cur);
156185Scth }
156285Scth 
156385Scth static emul64_nowrite_t *
emul64_nowrite_alloc(emul64_range_t * range)156485Scth emul64_nowrite_alloc(emul64_range_t *range)
156585Scth {
156685Scth 	emul64_nowrite_t	*nw;
156785Scth 
156885Scth 	nw = kmem_zalloc(sizeof (*nw), KM_SLEEP);
156985Scth 	bcopy((void *) range,
15707656SSherry.Moore@Sun.COM 	    (void *) &nw->emul64_blocked,
15717656SSherry.Moore@Sun.COM 	    sizeof (nw->emul64_blocked));
157285Scth 	return (nw);
157385Scth }
157485Scth 
157585Scth static void
emul64_nowrite_free(emul64_nowrite_t * nw)157685Scth emul64_nowrite_free(emul64_nowrite_t *nw)
157785Scth {
157885Scth 	kmem_free((void *) nw, sizeof (*nw));
157985Scth }
158085Scth 
158185Scth emul64_rng_overlap_t
emul64_overlap(emul64_range_t * rng,diskaddr_t sb,size_t cnt)158285Scth emul64_overlap(emul64_range_t *rng, diskaddr_t sb, size_t cnt)
158385Scth {
158485Scth 
158585Scth 	if (rng->emul64_sb >= sb + cnt)
158685Scth 		return (O_NONE);
158785Scth 	if (rng->emul64_sb + rng->emul64_blkcnt <= sb)
158885Scth 		return (O_NONE);
158985Scth 	if ((rng->emul64_sb == sb) && (rng->emul64_blkcnt == cnt))
159085Scth 		return (O_SAME);
159185Scth 	if ((sb >= rng->emul64_sb) &&
159285Scth 	    ((sb + cnt) <= (rng->emul64_sb + rng->emul64_blkcnt))) {
159385Scth 		return (O_SUBSET);
159485Scth 	}
159585Scth 	return (O_OVERLAP);
159685Scth }
159785Scth 
159885Scth #include <sys/varargs.h>
159985Scth 
160085Scth /*
160185Scth  * Error logging, printing, and debug print routines
160285Scth  */
160385Scth 
160485Scth /*VARARGS3*/
160585Scth static void
emul64_i_log(struct emul64 * emul64,int level,char * fmt,...)160685Scth emul64_i_log(struct emul64 *emul64, int level, char *fmt, ...)
160785Scth {
160885Scth 	char	buf[256];
160985Scth 	va_list	ap;
161085Scth 
161185Scth 	va_start(ap, fmt);
161285Scth 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
161385Scth 	va_end(ap);
161485Scth 
161585Scth 	scsi_log(emul64 ? emul64->emul64_dip : NULL,
161685Scth 	    "emul64", level, "%s\n", buf);
161785Scth }
161885Scth 
161985Scth 
162085Scth #ifdef EMUL64DEBUG
162185Scth 
162285Scth static void
emul64_debug_dump_cdb(struct scsi_address * ap,struct scsi_pkt * pkt)162385Scth emul64_debug_dump_cdb(struct scsi_address *ap, struct scsi_pkt *pkt)
162485Scth {
162585Scth 	static char	hex[]	= "0123456789abcdef";
162685Scth 	struct emul64	*emul64	= ADDR2EMUL64(ap);
162785Scth 	struct emul64_cmd	*sp	= PKT2CMD(pkt);
162885Scth 	uint8_t		*cdb	= pkt->pkt_cdbp;
162985Scth 	char		buf [256];
163085Scth 	char		*p;
163185Scth 	int		i;
163285Scth 
163385Scth 	(void) snprintf(buf, sizeof (buf), "emul64%d: <%d,%d> ",
16347656SSherry.Moore@Sun.COM 	    ddi_get_instance(emul64->emul64_dip),
16357656SSherry.Moore@Sun.COM 	    ap->a_target, ap->a_lun);
163685Scth 
163785Scth 	p = buf + strlen(buf);
163885Scth 
163985Scth 	*p++ = '[';
164085Scth 	for (i = 0; i < sp->cmd_cdblen; i++, cdb++) {
164185Scth 		if (i != 0)
164285Scth 			*p++ = ' ';
164385Scth 		*p++ = hex[(*cdb >> 4) & 0x0f];
164485Scth 		*p++ = hex[*cdb & 0x0f];
164585Scth 	}
164685Scth 	*p++ = ']';
164785Scth 	*p++ = '\n';
164885Scth 	*p = 0;
164985Scth 
165085Scth 	cmn_err(CE_CONT, buf);
165185Scth }
165285Scth #endif	/* EMUL64DEBUG */
1653