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