/*
 * bst.c - Block SCSI Target driver; an example block SCSA target
 *	   driver for Solaris 2.x.
 *
 * This file is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify this file without charge, but are not authorized to
 * license or distribute it to anyone else except as part of a product
 * or program developed by the user.
 *
 * THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 *
 * This file is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 *
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS FILE
 * OR ANY PART THEREOF.
 *
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even
 * if Sun has been advised of the possibility of such damages.
 *
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 *
 * ---------------------------------------------------------------------------
 *
 * This driver is intended as an example of programming a block SCSA
 * target driver for SVR4; it is not intended for any particular
 * direct access device, although it has been tested on a hard disk drive.
 *
 * This driver looks for a Sun-style disk label (struct dk_label, as defined
 * in <sys/dklabel.h>) on sector zero of the device. The label contains the
 * offsets and lengths of each data partition on the device. The driver
 * creates a minor device node for each partition, and uses the partition
 * info from the label to calculate the physical block number from the
 * logical block requested.
 *
 * This driver also supports the "DKIO" ioctls necessary to format and
 * partition the disk (defined in <sys/dkio.h>).
 *
 * The other major difference between this and the character-only driver
 * (sst) is that this driver supports I/O request queueing.
 *
 * Terminology in this driver:
 *	"instance" - this device, e.g. the disk drive
 *	"partition" - the part of the device ("slice") that's being accessed
 * The instance and partition are coded into the minor device number; the
 * partition is the low three bits (i.e. 8 partitions), and the instance is
 * the rest (i.e. bits 3 - 15).
 *
 * NOTE: Areas where you may need to change this code or add your own to
 *	deal with your specific device are marked "Note".
 * Other warnings are marked "WARNING"
 *
 * To compile:
 *	% cc -D_KERNEL -c bst.c
 *	% ld -r bst.o -o bst
 * WARNING: Only the -Xa and -Xt (the default) will work! The header files
 *	    don't conform to strict ANSI C; -Xs and -Xc will fail.
 *
 * To install:
 * 1. Copy the module (bst) and config file (bst.conf) into /usr/kernel/drv
 * 2. Run add_drv(1M).
 *
 * Note: you only need to run add_drv the very first time you introduce
 *	 the driver to the system. Thereafter you can use modunload to
 *	 unload the driver and just reference it (e.g. open(2) it) to
 *	reload.
 *
 * Setting variables
 *	Variables can be explicitly set from the /etc/system file, by
 *	adding an entry of the form
 *		"set bst:<variable name>=<value>"
 *	Alternatively, you can use adb to set variables and debug as
 *	follows:
 *		# adb -kw /dev/ksyms /dev/mem
 *	The /etc/system file is read only once at boot time, if you change
 *	it you must reboot for the change to take effect.
 */

#pragma ident	"@(#)bst.c	1.14	94/06/16 SMI"

/*
 * Includes, Declarations and Local Data
 */
#include <sys/scsi/scsi.h>
#include <sys/dkio.h>
#include <sys/vtoc.h>
#include <sys/stat.h>
#include <sys/modctl.h>
#include <sys/file.h>
#include "bst_def.h"

/*
 * Local Function Prototypes
 */
static int bst_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
static int bst_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
static int bst_strategy(struct buf *bp);
static int bst_ioctl(dev_t, int, int, int, cred_t *, int *);
static int bst_read(dev_t dev, struct uio *uio, cred_t *cred_p);
static int bst_write(dev_t dev, struct uio *uio, cred_t *cred_p);

static int bst_platform_ioctl(dev_t dev, int cmd, int arg, int flag,
	cred_t *cred_p, int *rval_p);
static int bst_rw(dev_t dev, struct uio *uio, int rw);
static int bst_ioctl_cmd(dev_t, struct uscsi_cmd *, enum uio_seg, enum uio_seg);
static int bst_checklabel(struct bst_soft *targ);
static void bst_user_vtoc(struct bst_soft *targ, struct vtoc *vtoc);
static int bst_label_vtoc(struct vtoc *vtoc, struct bst_soft *targ);
static int bst_write_label(dev_t dev);
static void bst_getmap(struct bst_soft *targ, struct dk_map *map);
static void bst_chgmap(struct bst_soft *targ, struct dk_map *map);
static void bst_chgvtoc(struct bst_soft *targ);

static int bst_ready(struct bst_soft *targ, dev_t dev);
static void bst_read_capacity(dev_t dev);
static int bst_simple(dev_t dev, int cmd, int flag);
static void bst_start(struct bst_soft *targ);
static int bst_runout(caddr_t arg);
static void bst_done(struct bst_soft *targ, struct buf *bp);
static void bst_make_cmd(struct bst_soft *targ, struct buf *bp, int (*f)());
static void bst_restart(caddr_t arg);
static void bst_callback(struct scsi_pkt *pkt);
static int bst_handle_incomplete(struct bst_soft *targ, struct buf *bp);
static int bst_handle_arq(struct scsi_pkt *pktp, struct bst_soft *targ,
    struct buf *bp);
static int bst_handle_sense(struct bst_soft *targ, struct buf *bp);
static int bst_check_error(struct bst_soft *targ, struct buf *bp);
static void bst_log(struct scsi_device *devp, int level, const char *fmt, ...);
static void hex_print(char *msg, char *cp, int len);

/*
 * Local Static Data
 */
static int bst_io_time		= BST_IO_TIME;
static int bst_retry_count	= BST_RETRY_COUNT;
static void *bst_state;

/*
 * Array of commands supported by the device, suitable for scsi_errmsg(9f)
 * Note: Add or remove commands here as appropriate for your device.
 */
static struct scsi_key_strings bst_cmds[] = {
	0x00, "test unit ready",
	0x01, "rezero",
	0x03, "request sense",
	0x04, "format",
	0x05, "read block limits",
	0x07, "reassign",
	0x08, "read",
	0x0a, "write",
	0x0b, "seek",
	0x0f, "read reverse",
	0x12, "inquiry",
	0x13, "verify",
	0x14, "recover buffered data",
	0x15, "mode select",
	0x16, "reserve",
	0x17, "release",
	0x18, "copy",
	0x1a, "mode sense",
	0x1b, "start/stop/load",
	0x1e, "door lock",
	0x28, "write (10)",
	0x2a, "read (10)",
	0x37, "read defect data",
	-1, NULL,
};

/*
 * Errors at or above this level will be reported
 */
static
int bst_error_reporting = SCSI_ERR_RETRYABLE;

#ifdef	_DDICT
/*
 * GETCMD is a SCSI macro that is not visible to
 * the DDI/DKI; we define it here to stop DDICT complaints.
 */
#define	GETCMD(x)	0
#endif

/*
 * Debug message control
 * Debug Levels:
 *	0 = no messages
 *	1 = Errors
 *	2 = Subroutine calls & control flow
 *	3 = I/O Data (verbose!)
 * Can be set with adb or in the /etc/system file with
 * "set bst:bst_debug=<value>"
 * Defining DEBUG on the compile line (-DDEBUG) will enable debugging
 * statements in this driver, and will also enable the ASSERT statements.
 */
static
#ifdef DEBUG
int bst_debug = 3;
#else
int bst_debug = 0;
#endif /* DEBUG */

/*
 * -----------------------------------------------------------------------------
 *	Module Loading/Unloading and Autoconfiguration Routines
 */

/*
 * Device driver ops vector - cb_ops(9s)
 * Device switch table fields (equivalent to the old 4.x cdevsw and bdevsw).
 * Unsupported entry points (e.g. for xxprint() and xxdump()) are set to
 * nodev, except for the poll routine, which is set to nochpoll(), a routine
 * that returns ENXIO.
 * Note	This uses ddi_prop_op for the prop_op(9e) routine. If your device
 *	has its own properties, you should implement a bst_prop_op() routine
 *	to manage them.
 */
static struct cb_ops bst_cb_ops = {
	bst_open,		/* b/c open */
	bst_close,		/* b/c close */
	bst_strategy,		/* b strategy */
	nodev,			/* b print */
	nodev,			/* b dump */
	bst_read,		/* c read */
	bst_write,		/* c write */
	bst_ioctl,		/* c ioctl */
	nodev,			/* c devmap */
	nodev,			/* c mmap */
	nodev,			/* c segmap */
	nochpoll,		/* c poll */
	ddi_prop_op,		/* cb_prop_op */
	NULL,			/* streamtab  */
	D_MP | D_NEW		/* Driver compatibility flag */

};


/*
 * dev_ops(9S) structure
 * Device Operations table, for autoconfiguration
 * Note	If you replace the bst_detach entry here with "nulldev", it
 *	implies that the detach is always successful. We need a real detach to
 *	free the sense packet and the unit structure. If you don't want the
 *	driver to ever be unloaded, replace the bst_detach entry with "nodev"
 *	(which always fails).
 */
static int bst_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
    void **result);
static int bst_identify(dev_info_t *devi);
static int bst_probe(dev_info_t *devi);
static int bst_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
static int bst_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);

static struct dev_ops bst_ops = {
	DEVO_REV,		/* devo_rev, */
	0,			/* refcnt  */
	bst_info,		/* info */
	bst_identify,		/* identify */
	bst_probe,		/* probe */
	bst_attach,		/* attach */
	bst_detach,		/* detach */
	nodev,			/* reset */
	&bst_cb_ops,		/* driver operations */
	NULL			/* bus operations */
};


static struct modldrv modldrv = {
	&mod_driverops,			/* Type of module (driver). */
	"Sample Block SCSI Target Driver Version 1.5",  /* Description */
	&bst_ops,			/* driver ops */
};

static struct modlinkage modlinkage = {
	MODREV_1, &modldrv, NULL
};

/*
 * Tell the system that we depend on the general scsi support routines,
 * i.e the scsi "misc" module must be loaded
 * Note: This line must be present! Unfortunately it's not documented,
 *	 but you must have it here or the driver won't load.
 */
#ifndef lint
char _depends_on[] = "misc/scsi";
#endif /* lint */


/*
 * -----------------------------------------------------------------------------
 *	Module Loading/Installation Routines
 */

/*
 * Module Installation
 * Install the driver and initialize private data structure allocation
 */
int
_init(void)
{
	int e;

	if ((e = ddi_soft_state_init(&bst_state, sizeof (struct bst_soft),
								0)) != 0) {
		BST_LOG(0, BST_CE_DEBUG2,
			"_init ddi_soft_state_init failed: 0x%x\n", e);
		return (e);
	}

	if ((e = mod_install(&modlinkage)) != 0) {
		BST_LOG(0, BST_CE_DEBUG2,
				"_init mod_install failed: 0x%x\n", e);
		ddi_soft_state_fini(&bst_state);
	}

	BST_LOG(0, BST_CE_DEBUG2, "_init succeeded\n");
	return (e);
}


/*
 * Module removal
 */
int
_fini(void)
{
	int e;

	if ((e = mod_remove(&modlinkage)) != 0) {
		BST_LOG(0, BST_CE_DEBUG1, "_fini mod_remove failed, 0x%x\n", e);
		return (e);
	}

	ddi_soft_state_fini(&bst_state);

	BST_LOG(0, BST_CE_DEBUG2, "_fini succeeded\n");
	return (e);
}

/*
 * Return module info
 */
int
_info(struct modinfo *modinfop)
{
	BST_LOG(0, BST_CE_DEBUG2, "_info\n");
	return (mod_info(&modlinkage, modinfop));
}


/*
 * -----------------------------------------------------------------------------
 *	Autoconfiguration Routines
 */

/*
 * identify(9e) - are we the right driver?
 * Called during autoconfiguration to match a found dev_info node with
 * a driver
 */
static int
bst_identify(dev_info_t *dip)
{
	BST_LOG(0, BST_CE_DEBUG2, "bst_identify, name = %s\n",
	    ddi_get_name(dip));
	if (strcmp(ddi_get_name(dip), "bst") == 0) {
		return (DDI_IDENTIFIED);
	} else {
		return (DDI_NOT_IDENTIFIED);
	}
}


/*
 * probe(9e) - Check that we're talking to the right device on the SCSI bus.
 * Calls scsi_probe() to see if there's a device at our target id. If
 * there is, scsi_probe will fill in the sd_inq struct (in the devp
 * scsi device struct) with the inquiry data. Validate the data here,
 * allocate a request sense packet, and start filling in the private data
 * structure.
 * Note: Probe should have no side-effects. Just check that we have the right
 *	 device, don't set up any data or initialize the device here. Also,
 *	 it's a Sun convention to probe quietly; send messages to the log
 *	 file only, not to the console.  Finally, call scsi_unprobe to undo
 *	 the effects of scsi_probe.
 * Note: The host adapter driver sets up the scsi_device structure and puts
 *	 it into the dev_info structure with ddi_set_driver_private().
 * Note: No need to allow for probing/attaching in the open() routine because
 *	 of the loadability - the first reference to the device will auto-load
 *	 it, i.e. will call this routine.
 */
#define	VIDSZ		8	/* Vendor Id length in Inquiry Data */
#define	PIDSZ		16	/* Product Id length in Inquiry Data */

static int
bst_probe(dev_info_t *dip)
{
	struct scsi_device *devp;
	int	err, rval = DDI_PROBE_FAILURE;
	int	tgt, lun;
	char	vpid[VIDSZ+PIDSZ+1];

	tgt = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "target", -1);
	lun = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "lun", -1);

	devp = (struct scsi_device *) ddi_get_driver_private(dip);
	BST_LOG(devp, BST_CE_DEBUG2, "bst_probe: target %d, lun %d\n",
	    ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "target", -1),
	    ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "lun", -1));

	/*
	 * scsi_probe(9f) uses the SCSI Inquiry command to  test for the
	 * presence of the device. If it's successful, it will fill in
	 * the sd_inq field in the scsi_device structure.
	 */
	switch (err = scsi_probe(devp, NULL_FUNC)) {
	case SCSIPROBE_NORESP:
		bst_log(devp, CE_CONT, "No response from target %d, lun %d\n",
		    tgt, lun);
		rval = DDI_PROBE_FAILURE;
		break;

	case SCSIPROBE_NONCCS:
	case SCSIPROBE_NOMEM:
	case SCSIPROBE_FAILURE:
	default:
		BST_LOG(devp, BST_CE_DEBUG1,
		    "bst_probe: scsi_probe failed, 0x%x\n", err);
		rval = DDI_PROBE_FAILURE;
		break;

	case SCSIPROBE_EXISTS:
		/*
		 * Inquiry succeeded, devp->sd_inq is now filled in
		 * Note: Check inq_dtype, inq_vid, inq_pid and any other
		 *	 fields to make sure the target/unit is what's
		 *	 expected (sd_inq is a struct scsi_inquiry,
		 *	 defined in scsi/generic/inquiry.h).
		 * Note: Put device-specific checking into the appropriate
		 *	 case statement, and delete the rest.
		 * Note: The DTYPE_* defines are from <scsi/generic/inquiry.h>,
		 *	 this is the full list as of "now", check it for new
		 *	 types.
		 */
		switch (devp->sd_inq->inq_dtype) {
		case DTYPE_OPTICAL:
		case DTYPE_DIRECT:
		case DTYPE_WORM:
		case DTYPE_RODIRECT:
			/*
			 * Print what was found on the console.
			 * Note	For your device, you should send the 'found'
			 *	message to the system log file only, by
			 *	inserting an exclamation point, "!", as the
			 *	first character of the message - see
			 *	cmn_err(9f).
			 */
			bst_log(devp, CE_CONT,
			    "Found %s device at tgt%d, lun%d\n",
			    scsi_dname((int)devp->sd_inq->inq_dtype), tgt, lun);
			bcopy((caddr_t)devp->sd_inq->inq_vid, (caddr_t)vpid,
			    VIDSZ);
			bcopy((caddr_t)devp->sd_inq->inq_pid,
			    (caddr_t)vpid+VIDSZ, PIDSZ);
			vpid[VIDSZ+PIDSZ] = 0;
			bst_log(devp, CE_CONT, "Vendor/Product ID = %s\n",
			    vpid);
			rval = DDI_PROBE_SUCCESS;
			break;

		case DTYPE_NOTPRESENT:
			bst_log(devp, CE_NOTE,
			    "Target reports no device present\n");
			rval = DDI_PROBE_FAILURE;
			break;

		default:
			bst_log(devp, CE_NOTE,
			    "Unsupported %s device at tgt%d lun%d\n",
			    scsi_dname((int)devp->sd_inq->inq_dtype), tgt, lun);
			rval = DDI_PROBE_FAILURE;
			break;
		}
	}
	scsi_unprobe(devp);	/* it's OK to call this if scsi_probe failed */
	return (rval);
}


/*
 * Attach
 *	- create minor device nodes for all 8 partitions
 *	- initialize per-unit mutex's & conditional variables
 *	- if it's a fixed-disk device, read & validate the label now; if
 *	  it's removable, wait until open().
 */
static int
bst_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	int			instance, slice, minor;
	struct	scsi_pkt	*rqpkt;
	struct bst_soft	*targ;
	struct scsi_device	*devp;
	struct buf			*bp;
	char	name[8];
	int	max_slice;


	if (cmd != DDI_ATTACH) {
		BST_LOG(0, BST_CE_DEBUG1,
		    "bst_attach: unsupported cmd 0x%x\n", cmd);
		return (DDI_FAILURE);
	}


	/*
	 * Find which instance we are, and create a data structure for
	 * the per-instance data. Cross-link the scsi_device struct (devp)
	 * with the per-instance structure (targ).
	 */
	devp = (struct scsi_device *) ddi_get_driver_private(dip);
	instance = ddi_get_instance(dip);
	BST_LOG(devp, BST_CE_DEBUG2, "bst_attach: unit %d\n", instance);

	/*
	 * Re-probe the device to get the Inquiry data; it's used
	 * elsewhere in the driver. The Inquiry data was validated in
	 * bst_probe so there's no need to look at it again here.
	 */
	if (scsi_probe(devp, NULL_FUNC) != SCSIPROBE_EXISTS) {
		BST_LOG(0, BST_CE_DEBUG1, "bst_attach: re-probe failed\n");
		scsi_unprobe(devp);
		return (DDI_FAILURE);
	}

	if (ddi_soft_state_zalloc(bst_state, instance) != DDI_SUCCESS) {
		scsi_unprobe(devp);
		return (DDI_FAILURE);
	}
	targ = ddi_get_soft_state(bst_state, instance);
	devp->sd_private = (opaque_t) targ;
	targ->targ_devp = devp;

	/*
	 * Allocate a buffer for 'special' (driver internal) commands
	 */
	targ->targ_sbufp = getrbuf(KM_NOSLEEP);
	if (targ->targ_sbufp == NULL) {
		goto error;
	}

	/*
	 * Set auto-rqsense, per-target; record whether it's allowed
	 * in targ_arq
	 */
	targ->targ_arq = ((scsi_ifsetcap(ROUTE(targ),
	    "auto-rqsense", 1, 1) == 1) ? 1 : 0);

	BST_LOG(devp, BST_CE_DEBUG2, "bst_attach: Auto Sensing %s\n",
	    targ->targ_arq ? "enabled" : "disabled");

	if (!targ->targ_arq) {
		/*
		 * No auto sensing, have to do it ourselves. Set up a packet
		 * for it now so we don't have to possibly wait for resources
		 * for it when a command fails.
		 */
		bp = scsi_alloc_consistent_buf(&devp->sd_address,
		    NULL, SENSE_LENGTH, B_READ, NULL_FUNC,
		    NULL);
		if (!bp) {
			goto error;
		}

		rqpkt = scsi_init_pkt(&devp->sd_address, NULL, bp, CDB_GROUP0,
		    1, 0, PKT_CONSISTENT, NULL_FUNC, NULL);
		if (!rqpkt) {
			goto error;
		}
		devp->sd_sense = (struct scsi_extended_sense *) bp->b_un.b_addr;

		makecom_g0(rqpkt, devp, FLAG_NOPARITY, SCMD_REQUEST_SENSE, 0,
		    SENSE_LENGTH);

		rqpkt->pkt_comp = bst_callback;
		rqpkt->pkt_time = bst_io_time;
		rqpkt->pkt_flags |= FLAG_SENSING;
		targ->targ_rqbp = bp;
		targ->targ_rqs = rqpkt;
	}

	/*
	 * For a fixed-disk drive, read and verify the label
	 * Don't worry if it fails, the drive may not be formatted.
	 * Also don't worry if the drive's not ready, bst_open will
	 * retry.
	 */
	targ->targ_geom_ok = 0;		/* mark geometry invalid */
	if (!BST_REMOVABLE(targ)) {
		(void) bst_checklabel(targ);
	}

	/*
	 * determine the number of Solaris slices
	 * in i386, add the fdisk partitions
	 */
#ifdef i386
	max_slice = NDKMAP + 5;
#elif defined(sparc)
	max_slice = NDKMAP;
#else
#error No Platform defined.
#endif
	/*
	 * Create the minor nodes; one per logical partition. See the man
	 * page for ddi_create_minor_node(9f).
	 * The 2nd parameter is the minor node name; drvconfig(1M) appends
	 * it to the /devices entry, after the colon.
	 * The 4th parameter ('instance') is the actual minor number, put
	 * into the /devices entry's inode and passed to the driver.
	 * The 5th parameter ("DDI_NT_BLOCK_CHAN") is the node type; it's
	 * used by disks(1M) to create the links from /dev to /devices.
	 */
	for (slice = 0; slice < max_slice; slice++) {
		minor = BST_MINOR(instance, slice);
		(void) sprintf(name, "%c", 'a' + slice);
		if (ddi_create_minor_node(dip, name, S_IFBLK,
			    minor, DDI_NT_BLOCK_CHAN, 0) == DDI_FAILURE) {
			BST_LOG(devp, BST_CE_DEBUG1,
			    "Create block minor failed\n");
			goto error;
		}

		(void) sprintf(name, "%c,raw", 'a' + slice);
		if (ddi_create_minor_node(dip, name, S_IFCHR,
			    minor, DDI_NT_BLOCK_CHAN, 0) == DDI_FAILURE) {
			BST_LOG(devp, BST_CE_DEBUG1,
			    "Create raw minor failed\n");
			goto error;
		}
	}

	/*
	 * Initialize the conditional variable.
	 * Note: We don't need to initialize the driver instance mutex
	 *	 because it's actually devp->sd_mutex (in the struct
	 *	 scsi_device) and is initialized by our parent, the
	 *	 host adapter driver.
	 */
	cv_init(&targ->targ_sbuf_cv, "targ_cv", CV_DRIVER, NULL);

	/*
	 * Initialize the open/close semaphore. This allows open() and
	 * close() calls to be serialized (i.e. a current open/close
	 * thread will complete before another is allowed).
	 */
	sema_init(&targ->targ_semaphore, 1, "bst_sem", SEMA_DRIVER, NULL);

	BST_LOG(devp, BST_CE_DEBUG2, "Attached bst driver\n");
	ddi_report_dev(dip);
	return (DDI_SUCCESS);

error:
	if (targ->targ_rqbp) {
		scsi_free_consistent_buf(targ->targ_rqbp);
	}
	if (targ->targ_rqs) {
		scsi_destroy_pkt(targ->targ_rqs);
	}
	if (targ->targ_sbufp) {
		freerbuf(targ->targ_sbufp);
	}
	ddi_remove_minor_node(dip, NULL);	/* remove all minor nodes */
	ddi_soft_state_free(bst_state, instance);
	devp->sd_private = (opaque_t) 0;
	devp->sd_sense = (struct scsi_extended_sense *) 0;
	scsi_unprobe(devp);
	return (DDI_FAILURE);
}


/*
 * Detach
 * Free resources allocated in bst_attach
 * Note: If you implement a timeout routine in this driver, cancel it
 *	 here. Note that scsi_init_pkt is called with SLEEP_FUNC, so it
 *	 will wait for resources if non are available. Changing it to
 *	 a callback constitutes a timeout; however this detach routine
 *	 will be called only if the driver is not open, and there should be
 *	 no outstanding scsi_init_pkt's if we're closed.
 */
static int
bst_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
	int			instance;
	struct scsi_device	*devp;
	struct bst_soft		*targ;


	if (cmd != DDI_DETACH) {
		BST_LOG(0, BST_CE_DEBUG1,
		    "bst_detach: unsupported cmd 0x%x\n", cmd);
		return (DDI_FAILURE);
	}

	devp = (struct scsi_device *) ddi_get_driver_private(dip);
	instance = ddi_get_instance(dip);
	BST_LOG(devp, BST_CE_DEBUG2, "bst_detach: unit %d\n", instance);

	if ((targ = ddi_get_soft_state(bst_state, instance)) == NULL) {
		BST_LOG(devp, CE_WARN, "No Target Struct for bst%d\n",
		    instance);
		return (DDI_FAILURE);
	}

	/*
	 * Remove other data structures allocated in bst_attach()
	 */
	cv_destroy(&targ->targ_sbuf_cv);
	if (targ->targ_rqbp) {
		scsi_free_consistent_buf(targ->targ_rqbp);
	}
	if (targ->targ_rqs) {
		scsi_destroy_pkt(targ->targ_rqs);
	}
	if (targ->targ_sbufp)
		freerbuf(targ->targ_sbufp);
	sema_destroy(&targ->targ_semaphore);
	ddi_soft_state_free(bst_state, instance);
	devp->sd_private = (opaque_t) 0;
	devp->sd_sense = (struct scsi_extended_sense *) 0;
	ddi_remove_minor_node(dip, NULL);
	scsi_unprobe(devp);
	return (DDI_SUCCESS);
}


/*
 * -----------------------------------------------------------------------------
 *	Unix Entry Points
 */


/*
 * open(9e)
 * Called for each open(2) call on the device.
 * There are no mutexes in the open or close routines here. Synchronization
 * and locking are done with a semaphore. This serializes open and close
 * calls: if one is in progress, another will block until it completes. If
 * you add access to data that may also be accessed by other than open/close,
 * you may need to add a mutex.
 * If bst_open is called for a non-existent instance, we return ENXIO. This
 * causes the framework to retry the autoconfiguration (including calling
 * bst_probe and bst_attach). This is known as "deferred attach".
 * Note: Credp can be used to restrict access to root, by calling drv_priv(9f);
 *	 see also cred(9s).
 *	 Flag shows the access mode (read/write). Check it if the device is
 *	 read or write only, or has modes where this is relevant.
 *	 Otyp is an open type flag, see open.h.
 */
/*ARGSUSED*/
static int
bst_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
{
	dev_t dev = *dev_p;
	struct bst_soft *targ;
	int	slice, firstopen;
	char	*msg;

	if ((targ = ddi_get_soft_state(bst_state, BST_INST(dev))) == NULL) {
		BST_LOG(0, BST_CE_DEBUG1, "open - unattached instance %d\n",
		    BST_INST(dev));
		return (ENXIO);		/* causes deferred attach */
	}
	slice = BST_SLICE(dev);	/* slice being opened */

	if ((otyp != OTYP_CHR) && (otyp != OTYP_BLK)) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
		    "Unsupported open type %d", otyp);
		return (EINVAL);
	}
	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "open - %s slice%d\n",
	    (otyp == OTYP_CHR) ? "raw" : "block", slice);

	/*
	 * Decrement semaphore to lock out other open/close calls while
	 * this thread is active (see semaphore(9F)).
	 */
	sema_p(&targ->targ_semaphore);

	/*
	 * Find out if this is the first open. Don't need the mutex because
	 * this is read only access, also raw_map/block_map are accessed only
	 * in bst_open and bst_close which are protected by the semaphore.
	 */
	firstopen = (targ->targ_raw_map == 0) && (targ->targ_block_map == 0);

	/*
	 * If this is the first open or the device hasn't been responding,
	 * test to make sure it's still powered on and ready by sending
	 * the SCSI Test Unit Ready command.
	 */
	if (firstopen || (targ->targ_state == BST_STATE_NORESP)) {
		if (!bst_ready(targ, dev)) {
			bst_log(targ->targ_devp, CE_NOTE, "Device not ready\n");
			sema_v(&targ->targ_semaphore);
			return (EIO);
		}
	}

	/*
	 * Check for exclusive open - exclusivity affects the whole disk,
	 * not just the slice being opened.
	 */
	if (flag & FEXCL) {
		if (!firstopen) {
			BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
			    "(excl) slice already open\n");
			sema_v(&targ->targ_semaphore);
			return (EBUSY);
		}
		targ->targ_excl = 1;
	} else if (targ->targ_excl) {
			BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
			    "already opened exclusively\n");
			sema_v(&targ->targ_semaphore);
			return (EBUSY);
	}

	/*
	 * Validate the slice being opened
	 */
	if (!targ->targ_geom_ok) {
		if (!bst_checklabel(targ)) {
			msg = "Bad label on disk";
		}
		if (!msg && !targ->targ_part[slice].p_size) {
			msg = "Non-existent slice";
		} else if (!msg) {
			msg = "Bad geometry";
		}
		/*
		 * Fail unless we're opened for formatting (raw device
		 * opened NDELAY).
		 */
		if ((flag & (FNDELAY|FNONBLOCK)) == 0 || otyp != OTYP_CHR) {
			sema_v(&targ->targ_semaphore);
			BST_LOG(targ->targ_devp, BST_CE_DEBUG1, msg);
			return (ENXIO);
		}
	}


	/*
	 * Mark the slice open in the appropriate map
	 */
	if (otyp == OTYP_CHR) {
		targ->targ_raw_map |= 1 << slice;
	} else {
		targ->targ_block_map |= 1 << slice;
	}

	/*
	 * For removable disk drives, send the SCSI lock command on
	 * the first open
	 */
	if (firstopen && BST_REMOVABLE(targ)) {
		(void) bst_simple(dev, SCMD_DOORLOCK, BST_LOCK);
	}

	/*
	 * Read the disk capacity
	 */
	if (firstopen)
		(void) bst_read_capacity(dev);

	sema_v(&targ->targ_semaphore);
	return (0);
}


/*
 * close(9e)
 * Called on final close only, i.e. the last close(2) call.
 */
/*ARGSUSED*/
static int
bst_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
{
	struct bst_soft *targ;
	int	slice;


	if ((targ = ddi_get_soft_state(bst_state, BST_INST(dev))) == NULL) {
		BST_LOG(0, BST_CE_DEBUG1, "bst_close bad instance %d\n",
		    BST_INST(dev));
		return (ENXIO);
	}
	slice = BST_SLICE(dev);

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "close - %s slice%d\n",
	    (otyp == OTYP_CHR) ? "raw" : "block", slice);

	sema_p(&targ->targ_semaphore);

	/*
	 * Mark the slice closed in the appropriate map
	 */
	if (otyp == OTYP_CHR) {
		targ->targ_raw_map &= ~(1 << slice);
	} else {
		targ->targ_block_map &= ~(1 << slice);
	}

	targ->targ_excl = 0;		/* turn off exclusivity */

	/*
	 * If this was the last close (i.e. no partitions are open),
	 * and it's a removable disk, unlock the drive and invalidate
	 * the geometry
	 */
	if (!targ->targ_raw_map && !targ->targ_block_map &&
	    BST_REMOVABLE(targ)) {
		(void) bst_simple(dev, SCMD_DOORLOCK, BST_UNLOCK);
		targ->targ_geom_ok = 0;
	}

	sema_v(&targ->targ_semaphore);
	return (0);
}


/*
 * Device Configuration Routine
 * link instance number (unit) with dev_info structure
 */
/*ARGSUSED*/
static int
bst_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
	struct bst_soft	*targ;
	int		instance, error;

	BST_LOG(0, BST_CE_DEBUG2, "bst_info\n");

	switch (infocmd) {
	case DDI_INFO_DEVT2DEVINFO:
		instance = BST_INST((dev_t)arg);
		if ((targ = ddi_get_soft_state(bst_state, instance)) == NULL)
			return (DDI_FAILURE);
		*result = (void *) targ->targ_devp->sd_dev;
		error = DDI_SUCCESS;
		break;
	case DDI_INFO_DEVT2INSTANCE:
		*result = (void *) BST_INST((dev_t) arg);
		error = DDI_SUCCESS;
		break;
	default:
		error = DDI_FAILURE;
	}
	return (error);
}


/*
 * Character (raw) read and write routines, called via read(2) and
 * write(2). These routines perform "raw" (i.e. unbuffered) i/o.
 * Since they're so similar, there's actually one 'rw' routine for both,
 * these devops entry points just call the general routine with the
 * appropriate flag.
 */
/* ARGSUSED2 */
static int
bst_read(dev_t dev, struct uio *uio, cred_t *cred_p)
{
	return (bst_rw(dev, uio, B_READ));
}

/* ARGSUSED2 */
static int
bst_write(dev_t dev, struct uio *uio, cred_t *cred_p)
{
	return (bst_rw(dev, uio, B_WRITE));
}

/*
 * General raw read/write routine
 * Just verify the unit number and transfer offset & length, and call
 * strategy via physio. Physio(9f) will take care of address mapping
 * and locking, and will split the transfer if ncessary, based on minphys,
 * possibly calling the strategy routine multiple times.
 */
static int
bst_rw(dev_t dev, struct uio *uio, int flag)
{
	struct bst_soft	*targ;

	if ((targ = ddi_get_soft_state(bst_state, BST_INST(dev))) == NULL) {
		return (ENXIO);
	}
	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "%s\n",
	    (flag == B_READ) ? "Read" : "Write");

	/*
	 * Verify the offset/length of the request - disk drives will
	 * accept only multiples of sectors.
	 */
	if (uio->uio_offset & (DEV_BSIZE-1)) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
		    "Offset %d is not an integral number of sectors\n",
		    uio->uio_offset);
		return (EINVAL);
	} else if (uio->uio_iov->iov_len & (DEV_BSIZE-1)) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
		    "Transfer length %d is not an integral number of sectors\n",
		    uio->uio_iov->iov_len);
		return (EINVAL);
	}

	return (physio(bst_strategy, NULL, dev, flag, minphys, uio));
}


/*
 * strategy
 * Main routine for commands to the device.  Called from bst_rw(),
 * bst_ioctl_cmd, and directly from the file system.
 * The mutex prevents this routine from being called simultaneously by
 * two threads.
 */
static int
bst_strategy(struct buf *bp)
{
	struct bst_soft *targ;
	struct diskhd *dp;

	if ((targ = ddi_get_soft_state(bst_state, BST_INST(bp->b_edev)))
								    == NULL) {
		bp->b_resid = bp->b_bcount;
		bp->b_error = ENXIO;
		bp->b_flags |= B_ERROR;
		biodone(bp);
		return (0);
	}

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "bst_strategy - %s\n",
	    (bp == targ->targ_sbufp) ? "special" : "regular");

	if (bp != targ->targ_sbufp) {		/* Regular read/write */
		daddr_t		bn = bp->b_blkno;
		struct partition *dkp;
		int		blks;

		/*
		 * First check that the device is still there
		 */
		if (targ->targ_state == BST_STATE_NORESP) {
			if (!bst_ready(targ, bp->b_edev)) {
				bp->b_resid = bp->b_bcount;
				bp->b_error = ENXIO;
				bp->b_flags |= B_ERROR;
				biodone(bp);
				return (0);
			}
		}

		/*
		 * bp->b_blkno is the offset into the disk slice, it
		 * must be within the slice (dkblock extracts it from bp).
		 * Also, the offset plus the requested length must not be
		 * off the end of the slice.
		 */
		dkp = &targ->targ_part[BST_SLICE(bp->b_edev)];
		blks = bp->b_bcount >> DEV_BSHIFT;
		if ((bn < 0) || (bn + blks > dkp->p_size)) {
			bst_log(targ->targ_devp, CE_WARN,
			    "block %d out of range (len=%d, count=%d)\n",
			    bn, dkp->p_size, blks);
			bp->b_resid = bp->b_bcount;
			bp->b_error = EINVAL;
			bp->b_flags |= B_ERROR;
			biodone(bp);
			return (0);
		}

		/*
		 * Calculate the physical block number for disksort(9F);
		 * it uses b_resid as the sort key
		 */
		bp->b_resid = bn + dkp->p_start + targ->targ_ustart;
	} else {
		/*
		 * Special command, put it at the front of the queue
		 */
		bp->b_resid = 0;
	}

	/*
	 * Add the new request into the i/o queue, pointed to by
	 * targ->targ_iorq. The queue is chained through av_forw
	 * and dp->b_forw holds the currently active request.
	 * Requests are added to the queue by disksort(9F), which
	 * uses the value in b_resid as the sort key. I'm using
	 * the physical block number here, although the cylinder
	 * number can also be used.
	 */
	dp = &targ->targ_iorq;		/* i/o request queue for this device */

	bp->av_forw = NULL;
	bp->av_back = NULL;		/* av_back is BST_BP_PKT */

	/*
	 * LOCK the targ struct so we can fiddle with the queue pointers.
	 * We need to hold it for a bit because we don't want anyone to
	 * change dp->b_forw or dp->av_forw while we're accessing them.
	 */
	mutex_enter(BST_MUTEX(targ));

	disksort(dp, bp);		/* add request into queue */

	if (dp->b_forw == NULL) { /* this device inactive? */
		mutex_exit(BST_MUTEX(targ));
		bst_start(targ);
	} else if (BST_BP_PKT(dp->av_forw) == 0) {
		/*
		 * There's an active request, so bst_done will call bst_start
		 * when it's completed. Try to pre-allocate resources for
		 * the next request in line. Don't care if it fails.
		 */
		bst_make_cmd(targ, dp->av_forw, NULL_FUNC);
		mutex_exit(BST_MUTEX(targ));
	} else {
		mutex_exit(BST_MUTEX(targ));
	}

	return (0);
}


/*
 * ioctl calls.
 * Ioctls are device specific. This driver supports all of the DKIOC commands
 * and the USCSI (CDB pass through) command, used by format(1M).
 */
/* ARGSUSED3 */
static int
bst_ioctl(dev_t dev, int cmd, int arg, int flag, cred_t *cred_p, int *rval_p)
{
	int		err = 0, len;
	struct scsi_device	*devp;
	struct bst_soft		*targ;
	char			data[DEV_BSIZE];
	u_char			otheropen;
	int			tgt, lun;
	struct dk_cinfo		*ctlr;
	struct dk_label		*lp;
	struct dk_geom		*gp;
	struct vtoc		*vtocp;

	if ((targ = ddi_get_soft_state(bst_state, BST_INST(dev))) == NULL) {
		return (ENXIO);		/* invalid minor number */
	}
	devp = targ->targ_devp;
	bzero((caddr_t) data, DEV_BSIZE);

	BST_LOG(devp, BST_CE_DEBUG2, "bst_ioctl: cmd = 0x%x\n", cmd);

	switch (cmd) {

	case USCSICMD:
		/*
		 * Check root permissions
		 */
		if (drv_priv(cred_p) != 0) {
			return (EPERM);
		}

		BST_LOG(devp, BST_CE_DEBUG3, "USCSICMD\n");
		/*
		 * Send an arbitrary SCSI command to the device. The user
		 * has specified the CDB, and and address/length for data
		 * (if needed). We do no validation of the passed uscsi_cmd
		 * structure (defined in <sys/scsi/impl/uscsi.h>).
		 */
		if (copyin((caddr_t)arg, data, sizeof (struct uscsi_cmd))) {
			return (EFAULT);
		}
		err = bst_ioctl_cmd(dev, (struct uscsi_cmd *)data,
		    UIO_USERSPACE, UIO_USERSPACE);
		if (copyout(data, (caddr_t)arg, sizeof (struct uscsi_cmd))) {
			err = EFAULT;
		}
		break;

	/*
	 * Ioctl commands for general disk access (formatting etc)
	 */
	case DKIOCINFO:		/* return controller info (struct dk_cinfo) */
		tgt = ddi_getprop(DDI_DEV_T_ANY, devp->sd_dev,
		    DDI_PROP_DONTPASS, "target", -1);
		lun = ddi_getprop(DDI_DEV_T_ANY, devp->sd_dev,
		    DDI_PROP_DONTPASS, "lun", -1);
		ctlr = (struct dk_cinfo *) data;
		(void) strcpy(ctlr->dki_cname,
		    ddi_get_name(ddi_get_parent(devp->sd_dev)));
		ctlr->dki_ctype = DKC_SCSI_CCS;
		ctlr->dki_flags = DKI_FMTVOL;
		ctlr->dki_cnum = ddi_get_instance(ddi_get_parent(devp->sd_dev));
		(void) strcpy(ctlr->dki_dname, ddi_get_name(devp->sd_dev));

		/*
		 * WARNING: Format(1M) assumes that the unit number is unique
		 * for all disks. This is OK if sd is the only disk driver on
		 * the system, but our instance (dki_unit) numbers will
		 * collide with sd's - sd unit 0 is the first disk found
		 * by sd, and bst unit 0 is our first disk; thus format(1M)
		 * will be confused. So fake it out by using the target number
		 * instead. This'll work unless you have more than one SCSI
		 * bus, in which case you'll have to think of another scheme!
		 * The correct value for dki_unit is:
		 *	ctlr->dki_unit = ddi_get_instance(devp->sd_dev);
		 * New scheme below.
		 */
		ctlr->dki_unit = ctlr->dki_cnum | (8 * tgt);
		ctlr->dki_slave = (tgt << 3) | lun;
		ctlr->dki_partition = BST_SLICE(dev);

#ifndef _DDICT
		/*
		 * Max Transfer size of this device
		 * maxphys is the system transfer limit in bytes, which
		 * is not part of the DDI/DKI.
		 */
		ctlr->dki_maxtransfer = maxphys / DEV_BSIZE;
#endif

		BST_LOG(targ->targ_devp, BST_CE_DEBUG3,
		    "DKIOCINFO: cname=%s, cnum=%d, unit=%d, slave=%d, ptn=%d\n",
		    ctlr->dki_cname, ctlr->dki_cnum, ctlr->dki_unit,
		    ctlr->dki_slave, ctlr->dki_partition);

		if (copyout(data, (caddr_t) arg, sizeof (struct dk_cinfo))) {
			err = EFAULT;
		}
		break;

	case DKIOCGGEOM:	/* get geometry (struct dk_geom) */
		BST_LOG(targ->targ_devp, BST_CE_DEBUG3, "DKIOCGGEOM\n");

		gp = (struct dk_geom *) data;
		lp = &targ->targ_label;

		/*
		 * This is a good potential case for finer mutex
		 * granularity - there's no need to lock the whole
		 * instance just to access the label. However since this
		 * ioctl command is infrequent, it's not worth it.
		 */
		mutex_enter(BST_MUTEX(targ));	/* LOCK the targ struct */
		gp->dkg_ncyl = lp->dkl_ncyl;
		gp->dkg_acyl = lp->dkl_acyl;
		gp->dkg_nhead = lp->dkl_nhead;
		gp->dkg_nsect = lp->dkl_nsect;
		gp->dkg_intrlv = lp->dkl_intrlv;
		gp->dkg_apc = lp->dkl_apc;
		gp->dkg_rpm = lp->dkl_rpm;
		gp->dkg_pcyl = lp->dkl_pcyl;
		gp->dkg_write_reinstruct = lp->dkl_write_reinstruct;
		gp->dkg_read_reinstruct = lp->dkl_read_reinstruct;
		mutex_exit(BST_MUTEX(targ));		/* UNLOCK targ */

		if (copyout(data, (caddr_t) arg, sizeof (struct dk_geom))) {
			err = EFAULT;
		}
		break;

	case DKIOCSGEOM:	/* set geometry (struct dk_geom) */
		BST_LOG(targ->targ_devp, BST_CE_DEBUG3, "DKIOCSGEOM\n");

		if (copyin((caddr_t) arg, (caddr_t) data,
		    sizeof (struct dk_geom))) {
			err = EFAULT;
		} else {
			gp = (struct dk_geom *) data;
			lp = &targ->targ_label;

			mutex_enter(BST_MUTEX(targ));	/* LOCK targ struct */
			lp->dkl_ncyl = gp->dkg_ncyl;
			lp->dkl_acyl = gp->dkg_acyl;
			lp->dkl_nhead = gp->dkg_nhead;
			lp->dkl_nsect = gp->dkg_nsect;
			lp->dkl_intrlv = gp->dkg_intrlv;
			lp->dkl_apc = gp->dkg_apc;
			lp->dkl_rpm = gp->dkg_rpm;
			lp->dkl_pcyl = gp->dkg_pcyl;
			lp->dkl_write_reinstruct = gp->dkg_write_reinstruct;
			lp->dkl_read_reinstruct = gp->dkg_read_reinstruct;
			mutex_exit(BST_MUTEX(targ));	/* UNLOCK targ */
		}
		break;

	case DKIOCGAPART:	/* get partition map  (struct dk_map[NDKMAP]) */
		BST_LOG(targ->targ_devp, BST_CE_DEBUG3, "DKIOCGAPART\n");
		bst_getmap(targ, (struct dk_map *)data);
		if (copyout((caddr_t) data, (caddr_t) arg,
		    sizeof (struct dk_map) * NDKMAP)) {
			err = EFAULT;
		}
		break;

	case DKIOCSAPART:	/* set partition map  (struct dk_map[NDKMAP]) */
		BST_LOG(targ->targ_devp, BST_CE_DEBUG3, "DKIOCSAPART\n");
		len = sizeof (struct dk_map) * NDKMAP;
		if (copyin((caddr_t) arg, data, len)) {
			err = EFAULT;
		} else {
			mutex_enter(BST_MUTEX(targ));
			bst_chgmap(targ, (struct dk_map *)data);
			mutex_exit(BST_MUTEX(targ));
		}
		break;

	case DKIOCEJECT:	/* eject removable media */
		BST_LOG(targ->targ_devp, BST_CE_DEBUG3, "DKIOCEJECT\n");
		otheropen = targ->targ_raw_map & ~(1 << BST_SLICE(dev));
		if (!BST_REMOVABLE(targ)) {
			err = ENOTTY;
		} else if (targ->targ_block_map || otheropen) {
			err = EBUSY;	/* someone else has the device open */
		} else if (!bst_simple(dev, SCMD_DOORLOCK, BST_UNLOCK) ||
		    !bst_simple(dev, SCMD_START_STOP, BST_EJECT)) {
			err = EIO;
		} else {
			/*
			 * Don't need to mutex here because we know we're
			 * the only thread in the driver (there are no other
			 * opens). If the user's forked they're on their own!
			 */
			targ->targ_geom_ok = 0;		/* disk ejected */
		}
		break;

	case DKIOCGVTOC:	/* get volume table of contents (struct vtoc) */
		BST_LOG(targ->targ_devp, BST_CE_DEBUG3, "DKIOCGVTOC\n");

		if (!targ->targ_geom_ok) {
			err = EINVAL;
		} else {
			mutex_enter(BST_MUTEX(targ));
			bst_user_vtoc(targ, (struct vtoc *) data);
			mutex_exit(BST_MUTEX(targ));
			if (copyout(data, (caddr_t) arg,
			    sizeof (struct vtoc))) {
				err = EFAULT;
			}
		}
		break;

	case DKIOCSVTOC:	/* set volume table of contents (struct vtoc) */
		BST_LOG(targ->targ_devp, BST_CE_DEBUG3, "DKIOCSVTOC\n");

		if (!targ->targ_geom_ok) {
			err = EINVAL;
		} else if (copyin((caddr_t) arg, data, sizeof (struct vtoc))) {
			err = EFAULT;
		} else {
			vtocp = (struct vtoc *) data;
			mutex_enter(BST_MUTEX(targ));
			if ((err = bst_label_vtoc(vtocp, targ)) == 0) {
				if ((err = bst_write_label(dev)) == 0) {
					(void) bst_checklabel(targ);
				}
			}
			mutex_exit(BST_MUTEX(targ));
		}

		break;

	default:

		err = bst_platform_ioctl(dev, cmd, arg, flag, cred_p, rval_p);
		break;
	}
	return (err);
}


/*
 * Run a command for user (from bst_ioctl) or from someone else in the driver.
 *
 * cdbspace is for address space of cdb; dataspace is for address space
 * of the buffer - user or kernel.
 */
static int
bst_ioctl_cmd(dev_t dev, struct uscsi_cmd *scmd,
    enum uio_seg cdbspace, enum uio_seg dataspace)
{
	caddr_t	cdb, user_cdbp;
	int	err, rw;
	struct	buf *bp;
	struct	scsi_pkt *pkt;
	struct	bst_soft *targ;

	if ((targ = ddi_get_soft_state(bst_state, BST_INST(dev))) == NULL) {
		BST_LOG(0, BST_CE_DEBUG1,
		    "bst_ioctl_cmd: invalid instance %d\n", BST_INST(dev));
		return (ENXIO);
	}
	ASSERT(mutex_owned(BST_MUTEX(targ)) == 0);

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "bst_ioctl_cmd\n");

	/*
	 * The uscsi structure itself is already in kernel space (copied
	 * in by bst_ioctl, or declared there by our caller); but
	 * we need to copy in the cdb here.
	 */
	cdb = kmem_zalloc((size_t) scmd->uscsi_cdblen, KM_SLEEP);
	if (cdbspace == UIO_SYSSPACE) {
		bcopy(scmd->uscsi_cdb, cdb, scmd->uscsi_cdblen);
	} else if (copyin(scmd->uscsi_cdb, cdb, (u_int) scmd->uscsi_cdblen)) {
		kmem_free(cdb, (size_t) scmd->uscsi_cdblen);
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
		    "bst_ioctl_cmd: copyin failed\n");
		return (EFAULT);
	}

	/*
	 * The cdb pointer in the structure passed by the user is pointing
	 * to user space. We've just copied the cdb into a local buffer,
	 * so point uscsi_cdb to it now. We'll restore hte user's pointer
	 * at the end.
	 */
	user_cdbp = scmd->uscsi_cdb;	/* save the user's pointer */
	scmd->uscsi_cdb = cdb;		/* point to the local cdb buffer */
	rw = (scmd->uscsi_flags & USCSI_READ) ? B_READ : B_WRITE;

	/*
	 * Get the 'special command' buffer.
	 * First lock the targ struct; if the buffer's busy, wait for
	 * it to become free. Once we get the buffer, mark it busy to
	 * lock out other requests for it.
	 * Note	cv_wait will release the mutex, allowing other parts
	 *	of the driver to acquire it.
	 * Note	Once we have the special buffer, we can safely release
	 *	the mutex; the buffer is now busy and another thread will
	 *	block in the cv_wait until we release it. All the code
	 *	from here until we unset the busy flag is non-reentrant,
	 *	including the physio/strategy/start/callback/done thread.
	 */
	mutex_enter(BST_MUTEX(targ));
	while (targ->targ_sbuf_busy)
		cv_wait(&targ->targ_sbuf_cv, BST_MUTEX(targ));
	targ->targ_sbuf_busy = 1;

	bp = targ->targ_sbufp;
	mutex_exit(BST_MUTEX(targ));

	if (scmd->uscsi_buflen) {
		/*
		 * We're sending/receiving data; create a uio structure and
		 * call physio to do the right things.
		 */
		struct iovec aiov;
		struct uio auio;
		struct uio *uio = &auio;

		BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
		    "Data transfer (%d bytes)\n", scmd->uscsi_buflen);

		bzero((caddr_t) &auio, sizeof (struct uio));
		bzero((caddr_t) &aiov, sizeof (struct iovec));
		aiov.iov_base = scmd->uscsi_bufaddr;
		aiov.iov_len = scmd->uscsi_buflen;
		uio->uio_iov = &aiov;

		uio->uio_iovcnt = 1;
		uio->uio_resid = scmd->uscsi_buflen;
		uio->uio_segflg = dataspace;
		uio->uio_offset = 0;
		uio->uio_fmode = 0;

		/*
		 * Let physio do the rest...
		 */
		bp->av_back = NULL;		/* av_back is BST_BP_PKT */
		bp->b_private = (void *) scmd;
		err = physio(bst_strategy, bp, dev, rw, minphys, uio);
	} else {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "No data\n");
		/*
		 * No data transfer, we can call bst_strategy directly
		 */
		bp->av_back = NULL;		/* av_back is BST_BP_PKT */
		bp->b_private = (void *) scmd;
		bp->b_flags = B_BUSY | rw;
		bp->b_edev = dev;
		bp->b_bcount = bp->b_blkno = 0;
		(void) bst_strategy(bp);
		err = biowait(bp);
	}

	/*
	 * get the status block, if any, and
	 * release any resources that we had.
	 */
	scmd->uscsi_status = 0;
	if ((pkt = BST_BP_PKT(bp)) != NULL) {
		scmd->uscsi_status = SCBP_C(pkt);
		scsi_destroy_pkt(pkt);
		bp->av_back = NULL;		/* av_back is BST_BP_PKT */
	}
	BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
	    "SCSI Command status = 0x%x\n", scmd->uscsi_status);

	/*
	 * Tell anybody who cares that the special buffer is now available.
	 * Again, lock the targ struct first, then mark the buffer not
	 * busy and signal anyone who may be waiting for it.
	 */
	mutex_enter(BST_MUTEX(targ));		/* LOCK the targ struct */
	targ->targ_sbuf_busy = 0;
	cv_signal(&targ->targ_sbuf_cv);
	mutex_exit(BST_MUTEX(targ));		/* UNLOCK the targ struct */

	kmem_free(scmd->uscsi_cdb, (size_t) scmd->uscsi_cdblen);
	scmd->uscsi_cdb = user_cdbp;	/* restore the user's pointer */
	BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
	    "bst_ioctl_cmd returning 0x%x\n", err);
	return (err);
}


/*
 * Unit start and Completion
 * Get the next command from the queue, allocate resources if they
 * haven't already been allocated, and send off the command.
 */
static void
bst_start(struct bst_soft *targ)
{
	struct buf	*bp;
	struct diskhd	*dp;

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "bst_start\n");

	dp = &targ->targ_iorq;

	/*
	 * LOCK the targ struct, we're going to fiddle with the
	 * queue pointers
	 */
	mutex_enter(BST_MUTEX(targ));

	/*
	 * If b_forw is non-NULL there's an active request already,
	 * if av_forw is NULL there are no requests queued. In either
	 * case do nothing.
	 */
	if (dp->b_forw || (bp = dp->av_forw) == NULL) {
		mutex_exit(BST_MUTEX(targ));
		return;
	}

	/*
	 * If resources have not been allocated yet, allocate them now.
	 * Unlike the 'pre-allocation' calls, specify bst_runout here;
	 * if the allocation fails bst_runout will be called to retry
	 * later.
	 */
	if (!BST_BP_PKT(bp)) {
		bp->b_error = 0;
		bst_make_cmd(targ, bp, bst_runout);
		if (!BST_BP_PKT(bp)) {
			if (bp->b_error) {
				/*
				 * scsi_init_pkt failed for other than
				 * out of resources. Mark error and pull
				 * the request off the pending queue
				 * for bst_done.
				 */
				bp->b_flags |= B_ERROR;
				bp->b_resid = bp->b_bcount;
				dp->b_forw = bp;
				dp->av_forw = bp->av_forw;
				mutex_exit(BST_MUTEX(targ));
				bst_done(targ, bp);
				return;
			}
			targ->targ_state = BST_STATE_RWAIT;
			mutex_exit(BST_MUTEX(targ));
			return;
		} else if (targ->targ_state == BST_STATE_RWAIT) {
			targ->targ_state = BST_STATE_NIL;
		}
	}

	dp->b_forw = bp;		/* new active request */
	dp->av_forw = bp->av_forw;	/* take it off the pending queue */

	mutex_exit(BST_MUTEX(targ));	/* UNLOCK the targ structure */

	bp->av_forw = 0;

	/*
	 * Clear the resid now. Must do it after bst_make_cmd, else if
	 * that fails and we get held in RWAIT, the next call to disksort
	 * (from another thread) will use our (zero) b_resid as the sort
	 * key, which would be wrong.
	 */
	bp->b_resid = 0;

	/*
	 * Send the SCSI command to the host adapter
	 */
	if (scsi_transport(BST_BP_PKT(bp)) != TRAN_ACCEPT) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
		    "Command transport failed\n");
		bp->b_flags |= B_ERROR;
		bp->b_error = EIO;
		bst_done(targ, bp);
	} else {
		/*
		 * Command sent OK, try to pre-allocate resources for
		 * the next request, don't care if it fails.
		 * Need to reacquire the mutex to lock the targ struct
		 */
		BST_LOG(targ->targ_devp, BST_CE_DEBUG3,
		    "Command Transported\n");
		mutex_enter(BST_MUTEX(targ));
		if (dp->av_forw && !BST_BP_PKT(dp->av_forw)) {
			bst_make_cmd(targ, dp->av_forw, NULL_FUNC);
		}
		mutex_exit(BST_MUTEX(targ));
	}
}


/*
 * 'Out of resources' callback routine. Passed to scsi_init_pkt(); if
 * the allocation fails, this routine is called when resources may be
 * available.
 * Just call bst_start to retry the resource allocation.
 */
/*ARGSUSED*/
static int
bst_runout(caddr_t arg)
{
	int		rval = 1;
	struct bst_soft	*targ = (struct bst_soft *) arg;

	BST_LOG(0, BST_CE_DEBUG2, "bst_runout\n");

	if (targ->targ_state == BST_STATE_RWAIT) {
		bst_start(targ);
		if (targ->targ_state == BST_STATE_RWAIT) {
			rval = 0;
		}
	}

	return (rval);
}


/*
 * Done with a command.
 * Start the next command and then call biodone() to tell physio or
 * bst_ioctl_cmd that this i/o has completed.
 *
 */
static void
bst_done(struct bst_soft *targ, struct buf *bp)
{
	struct diskhd *dp;

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "bst_done\n");

	dp = &targ->targ_iorq;		/* get the queue pointer */
	mutex_enter(BST_MUTEX(targ));	/* LOCK the targ struct */

	if (bp == dp->b_forw) {
		dp->b_forw = NULL;	/* request is no longer active */
	}
	mutex_exit(BST_MUTEX(targ));	/* UNLOCK the targ struct */

	/*
	 * Start the next one before releasing resources on this one
	 */
	bst_start(targ);

	/*
	 * Free resources unless this was a special command (in which
	 * case bst_ioctl_cmd will do the free).
	 */
	if ((bp != targ->targ_sbufp) && BST_BP_PKT(bp)) {
		scsi_destroy_pkt(BST_BP_PKT(bp));
	}
	biodone(bp);	/* tell interested parties that the i/o is done */
}


/*
 * Allocate resources for a SCSI command - call scsi_init_pkt to get
 * a packet and allocate DVMA resources, and create the SCSI CDB.
 * If scsi_init_pkt fails, give up silently. If bst_runout was specified
 * as func, the allocation will be retried, if NULL_FUNC was called
 * nothing will happen - the caller will not be notified of the failure.
 */
static void
bst_make_cmd(struct bst_soft *targ, struct buf *bp, int (*func)())
{
	int		com;
	struct scsi_pkt	*pkt;
	struct uscsi_cmd *scmd = (struct uscsi_cmd *) bp->b_private;
	int		flags, tval;

	/*
	 * Normally, mutexes are used to serialize data structure
	 * access. Here we use it to serialize access to this routine,
	 * which is called from several places in the driver. We could
	 * acquire and drop it in this routine, but since the caller may
	 * need the lock anyway, it's easier just to hold it than to keep
	 * dropping and reacquiring it
	 */
	ASSERT(mutex_owned(BST_MUTEX(targ)));

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
	    "bst_make_cmd: block %d, count=%d\n", bp->b_blkno, bp->b_bcount);

	flags = 0;
	if ((scsi_options & SCSI_OPTIONS_DR) == 0)
		flags = FLAG_NODISCON;

	if (bp != targ->targ_sbufp) {
		daddr_t	blkno;

		/*
		 * get starting blk # of slice (partition)
		 */
		blkno = targ->targ_part[BST_SLICE(bp->b_edev)].p_start +
			targ->targ_ustart +
			bp->b_blkno;		/* starting block # */

		pkt = scsi_init_pkt(ROUTE(targ), NULL, bp, CDB_GROUP0,
		    targ->targ_arq ? sizeof (struct scsi_arq_status) : 1,
		    0, 0, func, (caddr_t) targ);
		if (pkt == (struct scsi_pkt *) 0) {
			return;
		}

		/*
		 * Note: I'm using the group 0 Read/Write commands here.
		 *	 If your device needs different commands for normal
		 *	 data transfer (e.g. extended read/write), change
		 *	 it here.
		 */
		com = (bp->b_flags & B_READ) ? SCMD_READ : SCMD_WRITE;
		makecom_g0(pkt, targ->targ_devp, flags, com, blkno,
		    bp->b_bcount >> DEV_BSHIFT);
		pkt->pkt_flags = flags;
		tval = bst_io_time;
	} else {
		/*
		 * All special command come through bst_ioctl_cmd, which
		 * uses the uscsi interface. Just need to get the CDB
		 * from scmd and plug it in. Still call makecom_g0 because
		 * it fills in some of the pkt field for us. Its cdb
		 * manipulations will be overwritten by the bcopy.
		 */
		if (scmd->uscsi_flags & USCSI_SILENT)
			flags |= FLAG_SILENT;
		if (scmd->uscsi_flags & USCSI_DIAGNOSE)
			flags |= FLAG_DIAGNOSE;
		if (scmd->uscsi_flags & USCSI_ISOLATE)
			flags |= FLAG_ISOLATE;
		if (scmd->uscsi_flags & USCSI_NODISCON)
			flags |= FLAG_NODISCON;
		pkt = scsi_init_pkt(ROUTE(targ), (struct scsi_pkt *) NULL,
		    (bp->b_bcount) ? bp : NULL, scmd->uscsi_cdblen,
		    targ->targ_arq ? sizeof (struct scsi_arq_status) : 1,
		    0, 0, func, (caddr_t) targ);
		makecom_g0(pkt, targ->targ_devp, flags, scmd->uscsi_cdb[0],
		    0, 0);
		bcopy(scmd->uscsi_cdb,
		    (caddr_t) pkt->pkt_cdbp, scmd->uscsi_cdblen);
		tval = scmd->uscsi_timeout;
	}

	pkt->pkt_comp = bst_callback;
	pkt->pkt_time = tval;
	pkt->pkt_private = (opaque_t) bp;
	bp->av_back = (struct buf *) pkt;	/* av_back is BST_BP_PKT */

	if (bst_debug > 2) {
		hex_print("bst_make_cmd, CDB", (caddr_t) pkt->pkt_cdbp, 6);
	}
}


/*
 * -----------------------------------------------------------------------------
 * Interrupt Service Routines
 */

/*
 * Restart a command - the device was either busy or not ready
 */
static void
bst_restart(caddr_t arg)
{
	struct bst_soft *targ = (struct bst_soft *) arg;
	struct buf	*bp;

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "bst_restart\n");

	bp = targ->targ_iorq.b_forw;	/* read only, no need to lock */
	if (bp) {
		struct scsi_pkt *pkt = BST_BP_PKT(bp);
		if (!targ->targ_arq && (pkt->pkt_flags & FLAG_SENSING)) {
			pkt = targ->targ_rqs;
		}

		if (scsi_transport(pkt) != TRAN_ACCEPT) {
			bp->b_resid = bp->b_bcount;
			bp->b_flags |= B_ERROR;
			bst_done(targ, bp);
		}
	}
}


/*
 * Command completion processing, called by the host adapter driver
 * when it's done with the command.
 */
static void
bst_callback(struct scsi_pkt *pkt)
{
	struct bst_soft	*targ;
	struct buf	*bp;
	int		action;

	bp = (struct buf *) pkt->pkt_private;
	targ = ddi_get_soft_state(bst_state, BST_INST(bp->b_edev));

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
	    "bst_callback: pkt_reason = 0x%x, pkt_flags = 0x%x\n",
	    pkt->pkt_reason, pkt->pkt_flags);

	/*
	 * This mutex locks the targ struct (as usual!).
	 * We're interested in targ_retry_ct - if we retry, we don't want
	 * the count touched by another thread. Also, if we do a request
	 * sense we'll update the sense data in sd_sense.
	 */
	mutex_enter(BST_MUTEX(targ));

	if (pkt->pkt_reason != CMD_CMPLT) {
		/*
		 * The command did not complete. Retry if possible.
		 */
		action = bst_handle_incomplete(targ, bp);
	} else if (targ->targ_arq && pkt->pkt_state & STATE_ARQ_DONE) {
		/*
		 * The auto-rqsense happened, and the packet has a
		 * filled-in scsi_arq_status structure, pointed to by
		 * pkt_scbp.
		 */
		action = bst_handle_arq(pkt, targ, bp);
	} else if (pkt->pkt_flags & FLAG_SENSING) {
		/*
		 * We were running a REQUEST SENSE. Decode the
		 * sense data and decide what to do next.
		 */
		pkt = BST_BP_PKT(bp);	/* get the pkt for the orig command */
		pkt->pkt_flags &= ~FLAG_SENSING;
		action = bst_handle_sense(targ, bp);
	} else {
		/*
		 * Command completed and we're not getting sense. Check
		 * for errors and decide what to do next.
		 */
		action = bst_check_error(targ, bp);
	}
	mutex_exit(BST_MUTEX(targ));

	switch (action) {
	case QUE_SENSE:
		BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "Getting Sense\n");
		/*
		 * LOCK targ struct, there's only one sense packet & data for
		 * the unit - don't want to zero sd_sense while someone's
		 * looking at it!
		 */
		mutex_enter(BST_MUTEX(targ));
		pkt->pkt_flags |= FLAG_SENSING;
		targ->targ_rqs->pkt_private = (opaque_t) bp;
		bzero((caddr_t)targ->targ_devp->sd_sense, SENSE_LENGTH);
		mutex_exit(BST_MUTEX(targ));
		if (scsi_transport(targ->targ_rqs) == TRAN_ACCEPT) {
			break;
		}
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
		    "Request Sense transport failed\n");
		/*FALLTHROUGH*/
	case COMMAND_DONE_ERROR:
		bp->b_resid = bp->b_bcount;
		bp->b_flags |= B_ERROR;
		/*FALLTHROUGH*/
	case COMMAND_DONE:
		BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "Command Done\n");
		bst_done(targ, bp);
		break;

	case QUE_COMMAND:
		if ((int)targ->targ_retry_ct++ >= bst_retry_count) {
			BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
			    "Out of retries, failing command\n");
			bp->b_resid = bp->b_bcount;
			bp->b_flags |= B_ERROR;
			bst_done(targ, bp);
		} else {
			BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
			    "Retrying Command\n");
			if (scsi_transport(BST_BP_PKT(bp)) != TRAN_ACCEPT) {
				BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
				    "Retry transport failed\n");
				bp->b_resid = bp->b_bcount;
				bp->b_flags |= B_ERROR;
				bst_done(targ, bp);
				return;
			}
		}
		break;
	case JUST_RETURN:
		break;
	}

}


/*
 * Incomplete command handling. Figure out what to do based on
 * how far the command did get.
 */
static int
bst_handle_incomplete(struct bst_soft *targ, struct buf *bp)
{
	int		rval = COMMAND_DONE_ERROR;
	struct scsi_pkt	*pkt = BST_BP_PKT(bp);

	/*
	 * Need the lock, we may be doing retries (targ_retry_ct)
	 * or looking at the sense packet (targ_rqs)
	 */
	ASSERT(mutex_owned(BST_MUTEX(targ)));

	if (!targ->targ_arq && (pkt->pkt_flags & FLAG_SENSING)) {
		pkt = targ->targ_rqs;
	}

	/*
	 * The target may still be running the  command,
	 * so try and reset it, to get it into a known state.
	 * Note: This is forcible, there may be a more polite
	 *	 method for your device.
	 * WARNING: If the targets reset fails, we give up, we do
	 *	not reset the entire bus! Resetting the bus can
	 *	have grave effects if the system disk is on it.
	 *	Just give up, print a message and let the user
	 *	decide what to do. If your device is critical to the
	 *	system's operation, you can reset the bus by #define-ing
	 *	SCSI_BUS_RESET. However this is not recommended. Note that
	 *	the bus can also be reset in bst_check_error().
	 */
	if ((pkt->pkt_statistics &
	    (STAT_BUS_RESET|STAT_DEV_RESET|STAT_ABORTED)) == 0) {
		/*
		 * We can temporarily give up the lock here
		 */
		mutex_exit(BST_MUTEX(targ));
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1, "Aborting Command\n");
		if (!(scsi_abort(ROUTE(targ), (struct scsi_pkt *) 0))) {
			BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
			    "Resetting Target\n");
			if (!(scsi_reset(ROUTE(targ), RESET_TARGET))) {
#ifdef SCSI_BUS_RESET
				BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
				    "Resetting SCSI Bus\n");
				if (!scsi_reset(ROUTE(targ), RESET_ALL)) {
					bst_log(targ->targ_devp, CE_WARN,
					    "SCSI bus reset failed\n");
				}
#else SCSI_BUS_RESET
				bst_log(targ->targ_devp, CE_WARN,
				    "Reset Target failed\n");
#endif SCSI_BUS_RESET
				mutex_enter(BST_MUTEX(targ));
				return (COMMAND_DONE_ERROR);
			}
		}
		mutex_enter(BST_MUTEX(targ));	/* reacquire the lock */
	}

	/*
	 * If we were running a request sense, try it again if possible.
	 * Some devices can handle retries, others will not.
	 */
	if (pkt->pkt_flags & FLAG_SENSING) {
		if ((int) targ->targ_retry_ct++ < bst_retry_count) {
			rval = QUE_SENSE;
		}
	} else if (bp == targ->targ_sbufp && (pkt->pkt_flags & FLAG_ISOLATE)) {
		rval = COMMAND_DONE_ERROR;
	} else {
		rval = QUE_COMMAND;	/* retry the command */
	}

	if (pkt->pkt_state == STATE_GOT_BUS && rval == COMMAND_DONE_ERROR) {
		/*
		 * The device is not responding
		 */
		bst_log(targ->targ_devp, CE_WARN, "Device not responding");
		targ->targ_state = BST_STATE_NORESP;
	}

	BST_LOG(targ->targ_devp, BST_CE_DEBUG1, "Cmd incomplete, %s\n",
	    (rval == COMMAND_DONE_ERROR) ? "giving up" : "retrying");

	return (rval);
}


/*
 * Decode sense data
 */
static int
bst_handle_sense(struct bst_soft *targ, struct buf *bp)
{
	struct diskhd	*dp = &targ->targ_iorq;
	struct scsi_pkt	*pkt = BST_BP_PKT(bp),
			*rqpkt = targ->targ_rqs;
	int		rval = COMMAND_DONE_ERROR;
	int		level, amt;

	/*
	 * Need the lock, we may be doing retries (targ_retry_ct)
	 * or looking at the sense packet (targ_rqs) or data
	 * (sd_sense)
	 */
	ASSERT(mutex_owned(BST_MUTEX(targ)));


	if (SCBP(rqpkt)->sts_busy) {
		/*
		 * Retry the command unless we're out of retries or the
		 * 'fail on error' flag is set
		 */
		if (pkt && !(pkt->pkt_flags & FLAG_DIAGNOSE) &&
		    (int) targ->targ_retry_ct++ < bst_retry_count) {
			if (dp->b_forw == NULL)
				dp->b_forw = bp;
			(void) timeout(bst_restart, (caddr_t)targ,
			    BST_BSY_TIMEOUT);
			rval = JUST_RETURN;
		}
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1, "Target Busy, %s\n",
		    (rval == JUST_RETURN) ? "restarting" : "giving up");
		return (rval);
	}

	if (SCBP(rqpkt)->sts_chk) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
		    "Check Condition on Request Sense!\n");
		return (rval);
	}

	amt = SENSE_LENGTH - rqpkt->pkt_resid;
	if ((rqpkt->pkt_state & STATE_XFERRED_DATA) == 0 || amt == 0) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "no sense data\n");
		return (rval);
	}

	/*
	 * Now, check to see whether we got enough sense data to make any
	 * sense out if it (heh-heh).
	 */
	if (amt < SUN_MIN_SENSE_LENGTH) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
		    "not enough sense data\n");
		return (rval);
	}

	if (bst_debug > 2) {
		hex_print("bst Sense Data", (caddr_t)targ->targ_devp->sd_sense,
		    SENSE_LENGTH);
	}

	/*
	 * Decode the sense data
	 * Note: I'm only looking at the sense key here. Most devices
	 *	 have unique additional sense codes & qualifiers, so
	 *	 it's often more useful to look at them instead.
	 *
	 */
	switch (targ->targ_devp->sd_sense->es_key) {
	case KEY_NOT_READY:
		/*
		 * If we get a not-ready indication, wait a bit and
		 * try it again, unless this is a special command with
		 * the 'fail on error' (FLAG_DIAGNOSE) option set.
		 */
		if ((bp == targ->targ_sbufp) &&
		    (pkt->pkt_flags & FLAG_DIAGNOSE)) {
			rval = COMMAND_DONE_ERROR;
			level = SCSI_ERR_FATAL;
		} else if (pkt &&
		    (int)targ->targ_retry_ct++ < bst_retry_count) {
			if (dp->b_forw == NULL)
				dp->b_forw = bp;
			(void) timeout(bst_restart, (caddr_t)targ,
			    BST_BSY_TIMEOUT);
			rval = JUST_RETURN;
			level = SCSI_ERR_RETRYABLE;
		} else {
			rval = COMMAND_DONE_ERROR;
			level = SCSI_ERR_FATAL;
		}
		break;

	case KEY_ABORTED_COMMAND:
	case KEY_UNIT_ATTENTION:
		rval = QUE_COMMAND;
		level = SCSI_ERR_INFO;
		break;
	case KEY_RECOVERABLE_ERROR:
	case KEY_NO_SENSE:
		rval = COMMAND_DONE;
		level = SCSI_ERR_RECOVERED;
		break;
	case KEY_HARDWARE_ERROR:
	case KEY_MEDIUM_ERROR:
	case KEY_MISCOMPARE:
	case KEY_VOLUME_OVERFLOW:
	case KEY_WRITE_PROTECT:
	case KEY_BLANK_CHECK:
	case KEY_ILLEGAL_REQUEST:
	default:
		rval = COMMAND_DONE_ERROR;
		level = SCSI_ERR_FATAL;
		break;
	}

	/*
	 * If this was for a special command, check the options
	 */
	if (bp == targ->targ_sbufp) {
		if ((rval == QUE_COMMAND) &&
		    (pkt->pkt_flags & FLAG_DIAGNOSE)) {
			rval = COMMAND_DONE_ERROR;
		}
		if (((pkt->pkt_flags & FLAG_SILENT) == 0) || bst_debug) {
			/*
			 * Report the error.
			 * Note: the zero is the error block number.
			 *	You may want to fill it in from the sense data
			 *	for appropriate errors.
			 */
			scsi_errmsg(targ->targ_devp, pkt, "bst", level,
			    bp->b_blkno, 0, bst_cmds,
			    targ->targ_devp->sd_sense);
		}
	} else if ((level >= bst_error_reporting) || bst_debug) {
		scsi_errmsg(targ->targ_devp, pkt, "bst", level,
		    bp->b_blkno, 0, bst_cmds,
		    targ->targ_devp->sd_sense);
	}

	return (rval);
}


/*
 * Decode auto-rqsense data
 */
static int
bst_handle_arq(struct scsi_pkt *pktp, struct bst_soft *targ, struct buf *bp)
{
	int		rval = COMMAND_DONE_ERROR;
	struct diskhd	*dp = &targ->targ_iorq;
	int		level, amt;

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "Auto Request Sense done\n");

	if (ARQP(pktp)->sts_rqpkt_status.sts_chk) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
		    "Check Condition on Auto Request Sense!\n");
		return (rval);
	} else if (ARQP(pktp)->sts_rqpkt_reason != CMD_CMPLT) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
		    "Auto Request Sense command incomplete\n");
		return (QUE_COMMAND);	/* retry original command */
	}

	amt = SENSE_LENGTH - ARQP(pktp)->sts_rqpkt_resid;
	if ((ARQP(pktp)->sts_rqpkt_state & STATE_XFERRED_DATA) == 0 ||
	    amt == 0) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
		    "no auto sense data\n");
		return (rval);
	}

	/*
	 * Stuff the sense data pointer into sd_sense
	 */
	targ->targ_devp->sd_sense = &(ARQP(pktp)->sts_sensedata);

	/*
	 * Now, check to see whether we got enough sense data to make any
	 * sense out if it (heh-heh).
	 */
	if (amt < SUN_MIN_SENSE_LENGTH) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
		    "not enough auto sense data\n");
		return (rval);
	}

	if (bst_debug > 2) {
		hex_print("bst Auto Sense Data",
		    (caddr_t)&(ARQP(pktp)->sts_sensedata), SENSE_LENGTH);
	}

	/*
	 * Decode the sense data
	 * Note: I'm only looking at the sense key here. Most devices
	 *	 have unique additional sense codes & qualifiers, so
	 *	 it's often more useful to look at them instead.
	 *
	 */
	switch (ARQP(pktp)->sts_sensedata.es_key) {
	case KEY_NOT_READY:
		/*
		 * If we get a not-ready indication, wait a bit and
		 * try it again, unless this is a special command with
		 * the 'fail on error' (FLAG_DIAGNOSE) option set.
		 */
		if ((bp == targ->targ_sbufp) &&
		    (pktp->pkt_flags & FLAG_DIAGNOSE)) {
			rval = COMMAND_DONE_ERROR;
			level = SCSI_ERR_FATAL;
		} else if (targ->targ_retry_ct++ < bst_retry_count) {
			if (dp->b_forw == NULL)
				dp->b_forw = bp;
			(void) timeout(bst_restart, (caddr_t)targ,
			    BST_BSY_TIMEOUT);
			rval = JUST_RETURN;
			level = SCSI_ERR_RETRYABLE;
		} else {
			rval = COMMAND_DONE_ERROR;
			level = SCSI_ERR_FATAL;
		}
		break;

	case KEY_ABORTED_COMMAND:
	case KEY_UNIT_ATTENTION:
		rval = QUE_COMMAND;
		level = SCSI_ERR_INFO;
		break;
	case KEY_RECOVERABLE_ERROR:
	case KEY_NO_SENSE:
		rval = COMMAND_DONE;
		level = SCSI_ERR_RECOVERED;
		break;
	case KEY_HARDWARE_ERROR:
	case KEY_MEDIUM_ERROR:
	case KEY_MISCOMPARE:
	case KEY_VOLUME_OVERFLOW:
	case KEY_WRITE_PROTECT:
	case KEY_BLANK_CHECK:
	case KEY_ILLEGAL_REQUEST:
	default:
		rval = COMMAND_DONE_ERROR;
		level = SCSI_ERR_FATAL;
		break;
	}

	/*
	 * If this was for a special command, check the options
	 */
	if (bp == targ->targ_sbufp) {
		if ((rval == QUE_COMMAND) &&
		    (pktp->pkt_flags & FLAG_DIAGNOSE)) {
			rval = COMMAND_DONE_ERROR;
		}
		if (((pktp->pkt_flags & FLAG_SILENT) == 0) || bst_debug) {
			/*
			 * Report the error.
			 * Note: the zero is the error block number.
			 *	You may want to fill it in from the sense data
			 *	for appropriate errors.
			 */
			scsi_errmsg(targ->targ_devp, pktp, "bst", level,
			    bp->b_blkno, 0, bst_cmds,
			    &(ARQP(pktp)->sts_sensedata));
		}
	} else if ((level >= bst_error_reporting) || bst_debug) {
		scsi_errmsg(targ->targ_devp, pktp, "bst", level,
		    bp->b_blkno, 0, bst_cmds,
		    &(ARQP(pktp)->sts_sensedata));
	}

	return (rval);
}

/*
 * Command completion routine. Check the returned status of the
 * command
 */
static int
bst_check_error(struct bst_soft *targ, struct buf *bp)
{
	struct diskhd	*dp = &targ->targ_iorq;
	struct scsi_pkt	*pkt = BST_BP_PKT(bp);
	int		action;

	/*
	 * Need the lock, we may be doing retries (targ_retry_ct)
	 * or looking at the sense packet (targ_rqs)
	 */
	ASSERT(mutex_owned(BST_MUTEX(targ)));

	if (SCBP(pkt)->sts_busy) {
		/*
		 * Target was busy. If we're not out of retries, call
		 * timeout to restart in a bit; otherwise give up and
		 * reset the target. If the fail on error flag is
		 * set, give up immediately.
		 */
		int tval = (pkt->pkt_flags & FLAG_DIAGNOSE) ? 0
							    : BST_BSY_TIMEOUT;

		if (SCBP(pkt)->sts_is) {
			/*
			 * Implicit assumption here is that a device
			 * will only be reserved long enough to
			 * permit a single i/o operation to complete.
			 */
			tval = bst_io_time * drv_usectohz(1000000);
		}

		if ((int) targ->targ_retry_ct++ < bst_retry_count) {
			if (tval) {
				if (dp->b_forw == NULL)
					dp->b_forw = bp;
				(void) timeout(bst_restart, (caddr_t)targ,
				    tval);
				action = JUST_RETURN;
				BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
				    "Target busy, retrying\n");
			} else {
				action = COMMAND_DONE_ERROR;
				BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
				    "Target busy, no retries\n");
			}
		} else {
			/*
			 * We can temporarily give up the lock here. If we're
			 * pre-empted it's safe for another thread to get
			 * the lock
			 */
			mutex_exit(BST_MUTEX(targ));
			/*
			 * WARNING: See the warning in bst_handle_incomplete
			 */
			BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
			    "Resetting Target\n");
			if (!scsi_reset(ROUTE(targ), RESET_TARGET)) {
#ifdef SCSI_BUS_RESET
				BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
				    "Resetting SCSI Bus\n");
				if (!scsi_reset(ROUTE(targ), RESET_ALL)) {
					bst_log(targ->targ_devp, CE_WARN,
					    "SCSI bus reset failed\n");
				}
#else SCSI_BUS_RESET
				bst_log(targ->targ_devp, CE_WARN,
				    "Reset Target failed\n");
#endif SCSI_BUS_RESET
			}
			action = COMMAND_DONE_ERROR;
			mutex_enter(BST_MUTEX(targ));	/* reacquire the lock */
		}
	} else if (SCBP(pkt)->sts_chk) {
		if (targ->targ_arq) {
			bst_log(targ->targ_devp, CE_WARN,
			    "Check condition with Auto Sensing\n");
			action = COMMAND_DONE_ERROR;
		} else {
			BST_LOG(targ->targ_devp, BST_CE_DEBUG2,
			    "Check Condition\n");
			action = QUE_SENSE;	/* check condition: get sense */
		}
	} else {
		int cmd = GETCMD((union scsi_cdb *)pkt->pkt_cdbp);

		action = COMMAND_DONE;	/* assume command completed OK */

		/*
		 * pkt_resid will reflect, at this point, a residual
		 * of how many bytes were not transferred; a non-zero
		 * pkt_resid on a read or a write is an error.
		 * Note	I'm doing this because format asks for too much
		 *	data, so there's always a resid on the USCSICMDs
		 *	it sends; if your device uses different commands
		 *	for normal data transfer, test for them here.
		 */
		if (pkt->pkt_resid && (cmd == SCMD_READ || cmd == SCMD_WRITE)) {
			action = COMMAND_DONE_ERROR;
			bp->b_resid += pkt->pkt_resid;
			BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
			    "Residual data (%d bytes)\n", bp->b_resid);
		}
		targ->targ_retry_ct = 0;
		BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "Command Complete\n");
	}
	return (action);
}


/*
 * -----------------------------------------------------------------------------
 *	General Utility Routines
 */
static void
bst_chgmap(struct bst_soft *targ, struct dk_map *map)
{
	struct partition *pp;
	int	i;
	daddr_t spc;

	spc = targ->targ_label.dkl_nhead * targ->targ_label.dkl_nsect;

	pp = targ->targ_part;
	for (i = 0; i < NDKMAP; i++, pp++) {
		pp->p_start = map[i].dkl_cylno * spc;
		pp->p_size  = (long)map[i].dkl_nblk;
	}

	bst_chgvtoc(targ);
}


static void
bst_getmap(struct bst_soft *targ, struct dk_map *map)
{
	struct partition *pp;
	int	i;
	daddr_t spc;

	spc = targ->targ_label.dkl_nhead * targ->targ_label.dkl_nsect;

	pp = targ->targ_part;
	for (i = 0; i < NDKMAP; i++, pp++) {
		map[i].dkl_cylno = (daddr_t)pp->p_start / spc;
		map[i].dkl_nblk  = (daddr_t)pp->p_size;
	}
}

#ifdef i386

#include <sys/dktp/fdisk.h>

static int bst_checkfdisk(struct bst_soft *targ, struct mboot *mbp);
static int bst_checkpclabel(struct bst_soft *targ);

/*
 * Handle platform specific ioctl's
 */
/*ARGSUSED*/
static int
bst_platform_ioctl(dev_t dev, int cmd, int arg, int flag, cred_t *cred_p,
	int *rval_p)
{
	struct bst_soft		*targ;

	if ((targ = ddi_get_soft_state(bst_state, BST_INST(dev))) == NULL) {
		return (ENXIO);		/* invalid minor number */
	}

	switch (cmd) {
	/*
	 * retrieve the HBA geometry
	 */
	case DKIOCG_VIRTGEOM:
		{
		struct dk_geom dkg;
		int i;
		int argsiz;

		argsiz = sizeof (struct dk_geom);
		bzero((caddr_t)&dkg, argsiz);

		i = scsi_ifgetcap(ROUTE(targ), "geometry", 1);
		dkg.dkg_nhead = (i >> 16) & 0xff;
		dkg.dkg_nsect = i & 0xff;
		dkg.dkg_ncyl  = targ->targ_capacity / (dkg.dkg_nhead *
				dkg.dkg_nsect);
		dkg.dkg_acyl  = 0;
		dkg.dkg_pcyl  = dkg.dkg_ncyl + dkg.dkg_acyl;
		if (copyout((caddr_t)&dkg, (caddr_t) arg, argsiz))
			return (EFAULT);
		else
			return (0);
		}
	/*
	 * retrieve the physical geometry of the disk
	 */
	case DKIOCG_PHYGEOM:
		{
		struct dk_geom dkg;
		int argsiz;

		argsiz = sizeof (struct dk_geom);
		bzero((caddr_t)&dkg, argsiz);

		dkg.dkg_nhead = targ->targ_label.dkl_nhead;
		dkg.dkg_nsect = targ->targ_label.dkl_nsect;
		dkg.dkg_ncyl  = targ->targ_capacity / (dkg.dkg_nhead *
				dkg.dkg_nsect);
		dkg.dkg_acyl  = 0;
		dkg.dkg_pcyl  = dkg.dkg_ncyl + dkg.dkg_acyl;
		if (copyout((caddr_t)&dkg, (caddr_t) arg, argsiz))
			return (EFAULT);
		else
			return (0);
		}
	default:
		return (ENOTTY);
	}
}

/*
 * Update the vtoc image in the disk label
 */
static void
bst_chgvtoc(struct bst_soft *targ)
{
	struct partition *pp;
	struct dkl_partition *dlpp;
	int	i;

	pp = targ->targ_part;
	dlpp = targ->targ_label.dkl_vtoc.v_part;
	for (i = 0; i < NDKMAP; i++, pp++, dlpp++) {
		dlpp->p_tag = pp->p_tag;
		dlpp->p_flag = pp->p_flag;
		dlpp->p_start = pp->p_start;
		dlpp->p_size = pp->p_size;
	}
}

/*
 * identify the fdisk table
 */
static int
bst_checkfdisk(struct bst_soft *targ, struct mboot *mbp)
{
	struct ipart	*fdp;
	int		uidx;
	int		i;

	if (mbp->signature != MBB_MAGIC)
		return (0);

	fdp = (struct ipart *)&(mbp->parts[0]);
	uidx = -1;
	for (i = 1; i <= FD_NUMPART; i++, fdp++)  {
		if (!fdp->numsect)
			continue;
		if ((fdp->systid == SUNIXOS) && (fdp->bootid == ACTIVE)) {
			targ->targ_ustart = fdp->relsect;
			uidx = i;
		}
	}

	if (uidx == -1)
		return (0);

	return (1);
}

/*
 * Identify the Solaris disk label on pc platform
 */
static int
bst_checkpclabel(struct bst_soft *targ)
{
	struct scsi_pkt	*pkt;
	struct dk_label	*dkl;
	struct buf	*bp;
	int		count;

	bp = scsi_alloc_consistent_buf(&targ->targ_devp->sd_address,
	    NULL, DEV_BSIZE, B_READ, NULL_FUNC, NULL);
	if (!bp) {
		bst_log(targ->targ_devp, CE_WARN,
		    "No resources to read disk label\n");
		return (0);
	}

	pkt = scsi_init_pkt(&targ->targ_devp->sd_address, NULL, bp, CDB_GROUP0,
	    targ->targ_arq ? sizeof (struct scsi_arq_status) : 1,
	    0, PKT_CONSISTENT, NULL_FUNC, NULL);
	if (!pkt) {
		bst_log(targ->targ_devp, CE_WARN,
		    "No resources to read disk label\n");
		scsi_free_consistent_buf(bp);
		return (0);
	}
	dkl = (struct dk_label *) bp->b_un.b_addr;

	/*
	 */
	targ->targ_geom_ok = 0;

	bzero((caddr_t) dkl,  sizeof (struct dk_label));
	makecom_g0(pkt, targ->targ_devp, 0, SCMD_READ, targ->targ_ustart +
		DK_LABEL_LOC, 1);

	/*
	 * Try 3 times to read the label
	 */
	for (count = 0; count < 3; count++) {
		if (scsi_poll(pkt) || SCBP_C(pkt) != STATUS_GOOD ||
				(pkt->pkt_state & STATE_XFERRED_DATA) == 0 ||
				(pkt->pkt_resid != 0)) {
			if (SCBP(pkt)->sts_chk && targ->targ_rqs) {
				(void) scsi_poll(targ->targ_rqs);
				BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
				    "read fdisk failed, key=0x%x, code=0x%x\n",
				    targ->targ_devp->sd_sense->es_key,
				    targ->targ_devp->sd_sense->es_code);
			}
		} else {
			break;		/* read fdisk */
		}
	}
	if (count >= 3) {
		bst_log(targ->targ_devp, CE_WARN, "Unable to read fdisk\n");
		scsi_free_consistent_buf(bp);
		scsi_destroy_pkt(pkt);
		return (0);
	}

	/*
	 * Copy the label into the targ struct and free the scsi
	 * resources
	 */
	bcopy((caddr_t) dkl, (caddr_t) &targ->targ_label,
	    sizeof (struct dk_label));

	scsi_free_consistent_buf(bp);
	scsi_destroy_pkt(pkt);
	return (1);
}


/*
 * Read and validate the disk's label and geometry
 * Don't need mutexes in this routine because it's is called only from
 * attach and open, both of which are single threaded and can't run
 * concurrently.
 * Note This routine uses polled commands because it's called from attach,
 *	which might be called during boot before interrupts are enabled.
 *	This is not so good at open time - the polling holds up other
 *	SCSI devices. If the system disk is SCSI, the whole system could
 *	'freeze' momentarily, especially if the bst device is slow. If
 *	this is the case for you, you should implement a switch to use
 *	bst_ioctl_cmd instead of scsi_poll if bst_checklabel is called
 *	from bst_open.
 * Note	I'm relying on the label here; you might want to Mode Sense the
 *	disk's geometry.
 */

static int
bst_checklabel(struct bst_soft *targ)
{
	struct scsi_pkt	*pkt;
	struct dk_label	*dkl;
	struct buf	*bp;
	short		sum, *sp;
	int		count;
	int		status;

	bp = scsi_alloc_consistent_buf(&targ->targ_devp->sd_address,
	    NULL, DEV_BSIZE, B_READ, NULL_FUNC, NULL);
	if (!bp) {
		bst_log(targ->targ_devp, CE_WARN,
		    "No resources to read disk label\n");
		return (0);
	}

	pkt = scsi_init_pkt(&targ->targ_devp->sd_address, NULL, bp, CDB_GROUP0,
	    targ->targ_arq ? sizeof (struct scsi_arq_status) : 1,
	    0, PKT_CONSISTENT, NULL_FUNC, NULL);
	if (!pkt) {
		bst_log(targ->targ_devp, CE_WARN,
		    "No resources to read disk label\n");
		scsi_free_consistent_buf(bp);
		return (0);
	}

	/*
	 */
	targ->targ_geom_ok = 0;

	bzero((caddr_t) bp->b_un.b_addr,  DEV_BSIZE);
	makecom_g0(pkt, targ->targ_devp, 0, SCMD_READ, 0, 1);

	/*
	 * Try 3 times to read the fdisk
	 */
	for (count = 0; count < 3; count++) {
		if (scsi_poll(pkt) || SCBP_C(pkt) != STATUS_GOOD ||
				(pkt->pkt_state & STATE_XFERRED_DATA) == 0 ||
				(pkt->pkt_resid != 0)) {
			if (SCBP(pkt)->sts_chk && targ->targ_rqs) {
				(void) scsi_poll(targ->targ_rqs);
				BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
				    "read label failed, key=0x%x, code=0x%x\n",
				    targ->targ_devp->sd_sense->es_key,
				    targ->targ_devp->sd_sense->es_code);
			}
		} else {
			break;		/* read label */
		}
	}
	if (count >= 3) {
		bst_log(targ->targ_devp, CE_WARN, "Unable to read fdisk\n");
		scsi_free_consistent_buf(bp);
		scsi_destroy_pkt(pkt);
		return (0);
	}

	/*
	 * check fdisk table
	 */
	status = bst_checkfdisk(targ, (struct mboot *) bp->b_un.b_addr);

	scsi_free_consistent_buf(bp);
	scsi_destroy_pkt(pkt);

	if (status == 0)
		return (0);

	/* check the Solaris disk label on the pc platform */
	if (bst_checkpclabel(targ) == 0)
		return (0);

	dkl = &targ->targ_label;	/* point to our copy of the label */

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "read label: <%s>\n",
	    dkl->dkl_asciilabel);

	/*
	 * Check Magic number
	 */
	if (dkl->dkl_magic != DKL_MAGIC) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1, "Bad magic number\n");
		return (0);
	}

	/*
	 * Check the checksum of the label,
	 */
	sp = (short *) dkl;
	sum = 0;
	count = sizeof (struct dk_label) / sizeof (short);
	while (count--)	 {
		sum ^= *sp++;
	}

	if (sum) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1, "Bad label checksum\n");
		return (0);
	}

	/*
	 * translate the disk label info into common vtoc format
	 */
	for (count = 0; count < NDKMAP; count++) {
		targ->targ_part[count].p_tag =
			targ->targ_label.dkl_vtoc.v_part[count].p_tag;
		targ->targ_part[count].p_flag =
			targ->targ_label.dkl_vtoc.v_part[count].p_flag;
		targ->targ_part[count].p_start =
			targ->targ_label.dkl_vtoc.v_part[count].p_start;
		targ->targ_part[count].p_size =
			targ->targ_label.dkl_vtoc.v_part[count].p_size;
	}

	if (bst_debug > 2) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG3,
		    "Pcyl=%d, ncyl=%d, nhead=%d, nsect=%d\n",
		    dkl->dkl_pcyl, dkl->dkl_ncyl, dkl->dkl_nhead,
		    dkl->dkl_nsect);
		for (count = 0; count < NDKMAP; count++) {
			BST_LOG(targ->targ_devp, BST_CE_DEBUG3,
			    "Slice %d: cyl%d, %d blocks\n", count,
			    (targ->targ_part[count].p_start +
			    targ->targ_ustart) / (dkl->dkl_nhead *
			    dkl->dkl_nsect), targ->targ_part[count].p_size);
		}
	}

	targ->targ_geom_ok = 1;	/* mutex not necessary, see top of routine */
	return (1);
}

#elif defined(sparc)
static int
bst_checklabel(struct bst_soft *targ)
{
	struct scsi_pkt	*pkt;
	struct dk_label	*dkl;
	struct buf	*bp;
	short		sum, *sp;
	int		count;

	bp = scsi_alloc_consistent_buf(&targ->targ_devp->sd_address,
	    NULL, DEV_BSIZE, B_READ, NULL_FUNC, NULL);
	if (!bp) {
		bst_log(targ->targ_devp, CE_WARN,
		    "No resources to read disk label\n");
		return (0);
	}

	pkt = scsi_init_pkt(&targ->targ_devp->sd_address, NULL, bp, CDB_GROUP0,
	    targ->targ_arq ? sizeof (struct scsi_arq_status) : 1,
	    0, PKT_CONSISTENT, NULL_FUNC, NULL);
	if (!pkt) {
		bst_log(targ->targ_devp, CE_WARN,
		    "No resources to read disk label\n");
		scsi_free_consistent_buf(bp);
		return (0);
	}
	dkl = (struct dk_label *) bp->b_un.b_addr;

	/*
	 */
	targ->targ_geom_ok = 0;

	bzero((caddr_t) dkl,  sizeof (struct dk_label));
	makecom_g0(pkt, targ->targ_devp, 0, SCMD_READ, 0, 1);

	/*
	 * Try 3 times to read the label
	 */
	for (count = 0; count < 3; count++) {
		if (scsi_poll(pkt) || SCBP_C(pkt) != STATUS_GOOD ||
				(pkt->pkt_state & STATE_XFERRED_DATA) == 0 ||
				(pkt->pkt_resid != 0)) {
			if (SCBP(pkt)->sts_chk && targ->targ_rqs) {
				(void) scsi_poll(targ->targ_rqs);
				BST_LOG(targ->targ_devp, BST_CE_DEBUG1,
				    "read label failed, key=0x%x, code=0x%x\n",
				    targ->targ_devp->sd_sense->es_key,
				    targ->targ_devp->sd_sense->es_code);
			}
		} else {
			break;		/* read label */
		}
	}
	if (count >= 3) {
		bst_log(targ->targ_devp, CE_WARN, "Unable to read label\n");
		scsi_free_consistent_buf(bp);
		scsi_destroy_pkt(pkt);
		return (0);
	}

	/*
	 * Copy the label into the targ struct and free the scsi
	 * resources
	 */
	bcopy((caddr_t) dkl, (caddr_t) &targ->targ_label,
	    sizeof (struct dk_label));

	scsi_free_consistent_buf(bp);
	scsi_destroy_pkt(pkt);

	dkl = &targ->targ_label;	/* point to our copy of the label */

	BST_LOG(targ->targ_devp, BST_CE_DEBUG2, "read label: <%s>\n",
	    dkl->dkl_asciilabel);

	/*
	 * Check Magic number
	 */
	if (dkl->dkl_magic != DKL_MAGIC) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1, "Bad magic number\n");
		return (0);
	}

	/*
	 * Check the checksum of the label,
	 */
	sp = (short *) dkl;
	sum = 0;
	count = sizeof (struct dk_label) / sizeof (short);
	while (count--)	 {
		sum ^= *sp++;
	}

	if (sum) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG1, "Bad label checksum\n");
		return (0);
	}

	targ->targ_ustart = 0;
	/*
	 * translate the disk label info into common vtoc format
	 */
	for (count = 0; count < NDKMAP; count++) {
		targ->targ_part[count].p_tag =
			targ->targ_label.dkl_vtoc.v_part[count].p_tag;
		targ->targ_part[count].p_flag =
			targ->targ_label.dkl_vtoc.v_part[count].p_flag;
		targ->targ_part[count].p_start =
			targ->targ_label.dkl_map[count].dkl_cylno *
			targ->targ_label.dkl_nhead * targ->targ_label.dkl_nsect;
		targ->targ_part[count].p_size =
			targ->targ_label.dkl_map[count].dkl_nblk;
	}

	if (bst_debug > 2) {
		BST_LOG(targ->targ_devp, BST_CE_DEBUG3,
		    "Pcyl=%d, ncyl=%d, nhead=%d, nsect=%d\n",
		    dkl->dkl_pcyl, dkl->dkl_ncyl, dkl->dkl_nhead,
		    dkl->dkl_nsect);
		for (count = 0; count < NDKMAP; count++) {
			BST_LOG(targ->targ_devp, BST_CE_DEBUG3,
			    "Slice %d: cyl%d, %d blocks\n", count,
			    dkl->dkl_map[count].dkl_cylno,
			    dkl->dkl_map[count].dkl_nblk);
		}
	}

	targ->targ_geom_ok = 1;	/* mutex not necessary, see top of routine */
	return (1);
}

/*
 * Update the vtoc image in the disk label
 */
void
bst_chgvtoc(struct bst_soft *targ)
{
	struct partition *pp;
	struct dk_map *dlmp;
	struct dk_map2 *vtocp;
	int	i;
	daddr_t spc;

	pp = targ->targ_part;
	dlmp = targ->targ_label.dkl_map;
	vtocp = targ->targ_label.dkl_vtoc.v_part;
	spc = targ->targ_label.dkl_nsect * targ->targ_label.dkl_nhead;

	for (i = 0; i < NDKMAP; i++, pp++, vtocp++, dlmp++) {
		vtocp->p_tag = pp->p_tag;
		vtocp->p_flag = pp->p_flag;
		dlmp->dkl_cylno = pp->p_start / spc;
		dlmp->dkl_nblk = (daddr_t)pp->p_size;
	}


}

/*
 * Handle platform specific ioctl's
 */
/*ARGSUSED*/
static int
bst_platform_ioctl(dev_t dev, int cmd, int arg, int flag, cred_t *cred_p,
	int *rval_p)
{

		return (ENOTTY);

}

#else
#error No Platform defined.
#endif

static void
bst_read_capacity(dev_t dev)
{
	struct bst_soft		*targ;
	struct uscsi_cmd scmd, *com = &scmd;
	char	cmdblk[CDB_GROUP1];
	int	err;
	ulong_t	blksiz;
	ulong_t	capacity;
	char	*dksz;

	if ((targ = ddi_get_soft_state(bst_state, BST_INST(dev))) == NULL)
		return;
	dksz = kmem_alloc((size_t)(sizeof (struct scsi_capacity)), KM_SLEEP);
	if (!dksz)
		return;

	com->uscsi_bufaddr = dksz;
	com->uscsi_buflen = sizeof (struct scsi_capacity);
	bzero(cmdblk, CDB_GROUP1);
	cmdblk[0] = SCMD_READ_CAPACITY;
	com->uscsi_flags = USCSI_SILENT | USCSI_READ;
	com->uscsi_cdb = cmdblk;
	com->uscsi_cdblen = CDB_GROUP1;

	if (err = bst_ioctl_cmd(dev, com, UIO_SYSSPACE, UIO_SYSSPACE)) {
		BST_LOG(0, BST_CE_DEBUG1,
			"bst_read_capacity failed: 0x%x\n", err);
		kmem_free(dksz, (size_t)(sizeof (struct scsi_capacity)));
		return;
	}
	capacity = (dksz[0] << 24 & 0xff000000) |
		    (dksz[1] << 16 & 0xff0000) |
		    (dksz[2] << 8 & 0xff00) |
		    (dksz[3] & 0xff);
	blksiz = (dksz[4] << 24 & 0xff000000) |
		    (dksz[5] << 16 & 0xff0000) |
		    (dksz[6] << 8 & 0xff00) |
		    (dksz[7] & 0xff);
	targ->targ_capacity = capacity;

	scsi_ifsetcap(ROUTE(targ), "sector-size", blksiz, 1);
	scsi_ifsetcap(ROUTE(targ), "total-sectors", capacity, 1);
	kmem_free(dksz, (size_t)(sizeof (struct scsi_capacity)));
}

/*
 * See if the device is ready. Send a couple of Test Unit Ready commands
 * to clear Unit Attention conditions. If it's still not ready, send the
 * Start command and try another Test Unit Ready
 * Return 1 is the unit's ready, 0 if not.
 */
static int
bst_ready(struct bst_soft *targ, dev_t dev)
{
	int retries = 3;

	while (--retries) {
		if (bst_simple(dev, SCMD_TEST_UNIT_READY, 0)) {
			break;
		}
	}

	if (retries > 0) {
		BST_LOG(0, BST_CE_DEBUG2, "bst%d is ready\n", BST_INST(dev));
		targ->targ_state = BST_STATE_NIL;
		return (1);
	}

	/*
	 * Not Ready, try a Start command
	 */
	BST_LOG(0, BST_CE_DEBUG3, "starting bst%d\n", BST_INST(dev));
	if (bst_simple(dev, SCMD_START_STOP, BST_START) == 0) {
		BST_LOG(0, BST_CE_DEBUG1, "unable to start bst%d\n",
		    BST_INST(dev));
		return (0);
	}

	/*
	 * Started OK, try another Test Unit Ready
	 */
	if (bst_simple(dev, SCMD_TEST_UNIT_READY, 0) == 0) {
		BST_LOG(0, BST_CE_DEBUG1, "bst%d not ready\n", BST_INST(dev));
		return (0);
	}

	BST_LOG(0, BST_CE_DEBUG2, "bst%d is ready\n", BST_INST(dev));
	targ->targ_state = BST_STATE_NIL;
	return (1);	/* Unit is ready */
}


/*
 * Send a simple (no DMA) command to the device
 * return 1 if the command succeeds, 0 if it fails.
 */
static int
bst_simple(dev_t dev, int cmd, int flag)
{
	struct uscsi_cmd scmd, *com = &scmd;
	char	cmdblk[CDB_GROUP0];
	int	err;

	com->uscsi_bufaddr = 0;
	com->uscsi_buflen = 0;
	bzero(cmdblk, CDB_GROUP0);
	cmdblk[0] = (char) cmd;
	cmdblk[4] = flag;
	com->uscsi_flags = USCSI_DIAGNOSE|USCSI_SILENT|USCSI_WRITE;
	com->uscsi_cdb = cmdblk;
	com->uscsi_cdblen = CDB_GROUP0;

	if (err = bst_ioctl_cmd(dev, com, UIO_SYSSPACE, UIO_SYSSPACE)) {
		BST_LOG(0, BST_CE_DEBUG1, "bst_simple failed: 0x%x\n", err);
		err = 0;
	} else {
		err = 1;
	}
	return (err);
}


/*
 * Convert the label's struct dk_vtoc (<sys/dklabel.h>) to a user
 * struct vtoc (<sys/vtoc.h>).
 */
static void
bst_user_vtoc(struct bst_soft *targ, struct vtoc *vtoc)
{
	int 		slice;
	struct dk_vtoc	*dkv;
	struct partition *vpart;

	BST_LOG(targ->targ_devp, BST_CE_DEBUG3, "sizeof struct vtoc = %d\n",
	    sizeof (struct vtoc));
	dkv = &targ->targ_label.dkl_vtoc;
	bcopy((caddr_t) dkv->v_bootinfo, (caddr_t) vtoc->v_bootinfo,
	    sizeof (vtoc->v_bootinfo));
	vtoc->v_sanity = VTOC_SANE;
	vtoc->v_version = dkv->v_version;

	bcopy((caddr_t) dkv->v_volume, (caddr_t) vtoc->v_volume, LEN_DKL_VVOL);

	vtoc->v_sectorsz = DEV_BSIZE;
	vtoc->v_nparts = dkv->v_nparts;

	bcopy((caddr_t) dkv->v_reserved, (caddr_t) vtoc->v_reserved,
	    sizeof (vtoc->v_reserved));

	vpart = vtoc->v_part;
	for (slice = 0; slice < V_NUMPAR; slice++) {
		vpart[slice] = targ->targ_part[slice];

		BST_LOG(targ->targ_devp, BST_CE_DEBUG3,
		    "Slice%d, start=%d, size=%d\n",
		    slice, vpart->p_start, vpart->p_size);

	}

	bcopy((caddr_t) dkv->v_timestamp, (caddr_t) vtoc->timestamp,
	    sizeof (vtoc->timestamp));

	bcopy((caddr_t) targ->targ_label.dkl_asciilabel,
	    (caddr_t) vtoc->v_asciilabel, LEN_DKL_ASCII);
	BST_LOG(targ->targ_devp, BST_CE_DEBUG3, "Label = %s\n",
	    vtoc->v_asciilabel);
}


/*
 * Convert a user struct vtoc (<sys/vtoc.h>) to the
 * label's struct dk_vtoc (<sys/dklabel.h>).
 */
static int
bst_label_vtoc(struct vtoc *vtoc, struct bst_soft *targ)
{
	int 		slice;
	u_short		ncyl, npart;
	long		nblks;
	struct dk_vtoc	*dkv;
	struct partition *vpart;

	/*
	 * Sanity-check the vtoc
	 */
	if (vtoc->v_sanity != VTOC_SANE || vtoc->v_sectorsz != DEV_BSIZE ||
			vtoc->v_nparts != V_NUMPAR) {
		return (EINVAL);
	}

	nblks = targ->targ_label.dkl_nsect * targ->targ_label.dkl_nhead;
	vpart = vtoc->v_part;
	for (slice = 0; slice < V_NUMPAR; slice++) {
		if ((vpart->p_start % nblks) != 0)
			return (EINVAL);
		ncyl = vpart->p_start / nblks;
		ncyl += vpart->p_size / nblks;
		if ((vpart->p_size % nblks) != 0)
			ncyl++;
		if (ncyl > targ->targ_label.dkl_ncyl)
			return (EINVAL);
		vpart++;
	}

	/*
	 * New vtoc is OK, convert to dkl_vtoc
	 */
	dkv = &targ->targ_label.dkl_vtoc;

	bcopy((caddr_t) vtoc->v_asciilabel,
	    (caddr_t) targ->targ_label.dkl_asciilabel, LEN_DKL_ASCII);

	dkv->v_version = vtoc->v_version;
	bcopy(vtoc->v_volume, (caddr_t) dkv->v_volume, LEN_DKL_VVOL);
	dkv->v_nparts = vtoc->v_nparts;

	vpart = vtoc->v_part;
	for (npart = 0; npart < V_NUMPAR; npart++) {
		targ->targ_part[npart] = vpart[npart];
	}

	bst_chgvtoc(targ);

	bcopy((caddr_t) vtoc->v_bootinfo, (caddr_t) dkv->v_bootinfo,
	    sizeof (dkv->v_bootinfo));
	dkv->v_sanity = VTOC_SANE;
	bcopy((caddr_t) vtoc->v_reserved, (caddr_t) dkv->v_reserved,
	    sizeof (dkv->v_reserved));
	bcopy((caddr_t) vtoc->timestamp, (caddr_t) dkv->v_timestamp,
	    sizeof (dkv->v_timestamp));
	return (0);
}


/*
 * Disk geometry macros
 *
 *	spc:		sectors per cylinder
 *	chs2bn:		cyl/head/sector to block number
 */
#define	spc(l)		(((l)->dkl_nhead*(l)->dkl_nsect)-(l)->dkl_apc)

#define	chs2bn(l, c, h, s)	\
			((daddr_t)((c)*spc(l)+(h)*(l)->dkl_nsect+(s)))


static int
bst_write_label(dev_t dev)
{
	int			i, status;
	int			sec, blk, head, cyl;
	short			*sp, sum;
	struct bst_soft		*targ;
	struct dk_label		*dkl;
	struct uscsi_cmd	ucmd;
	u_char			cdb[10];	/* enough for Group 1 */


	if ((targ = ddi_get_soft_state(bst_state, BST_INST(dev))) == NULL) {
		return (ENXIO);
	}
	dkl = &targ->targ_label;

	ASSERT(mutex_owned(BST_MUTEX(targ)));

	/*
	 * Construct checksum for the new disk label
	 */
	sum = 0;
	dkl->dkl_cksum = 0;
	sp = (short *) dkl;
	i = sizeof (struct dk_label)/sizeof (short);
	while (i--) {
		sum ^= *sp++;
	}
	dkl->dkl_cksum = sum;
	dkl->dkl_magic = DKL_MAGIC;

	/*
	 * Do the USCSI Write here
	 */

	/*
	 * Write the Primary label to block 0 using the USCSI interface
	 * Note	We're using a Group0 write here; if your device uses
	 *	group 1 commands, change it!
	 */
	(void) bzero((caddr_t) &ucmd, sizeof (ucmd));
	(void) bzero((caddr_t) cdb, CDB_GROUP0);
	cdb[0] = SCMD_WRITE;			/* Group 0 Write */
	cdb[3] = 0;				/* address = block 0 */
	cdb[4] = (u_char) 1;			/* length = 1 block */
	ucmd.uscsi_cdblen = CDB_GROUP0;
	ucmd.uscsi_cdb = (caddr_t) cdb;
	ucmd.uscsi_bufaddr = (caddr_t) dkl;
	ucmd.uscsi_buflen = DEV_BSIZE;
	ucmd.uscsi_flags = USCSI_WRITE|USCSI_SILENT;
	mutex_exit(BST_MUTEX(targ));
	status = bst_ioctl_cmd(dev, &ucmd, UIO_SYSSPACE, UIO_SYSSPACE);
	mutex_enter(BST_MUTEX(targ));
	if (status != 0) {
		return (status);
	}

	/*
	 * Calculate where the backup labels go - the first 5 odd
	 * sectors of the last track on the last alternate cylinder.
	 */
	cyl = dkl->dkl_ncyl + dkl->dkl_acyl - 1;
	head = dkl->dkl_nhead-1;

	/*
	 * Write and verify the backup labels.
	 */
	for (sec = 1; sec < 5 * 2 + 1; sec += 2) {
		blk = chs2bn(dkl, cyl, head, sec);
		if (blk < (2<<20)) {
			cdb[0] = SCMD_WRITE;		/* Group 0 Write */
			cdb[1] = (blk >> 16) & 0x1F;
			cdb[2] = (blk >> 8) & 0xFF;
			cdb[3] = blk & 0xFF;
			cdb[4] = (u_char) 1;
			ucmd.uscsi_cdblen = CDB_GROUP0;
		} else {
			cdb[0] = SCMD_WRITE_G1;		/* Group 1 Write */
			cdb[2] = (blk >> 24) & 0xFF;
			cdb[3] = (blk >> 16) & 0xFF;
			cdb[4] = (blk >> 8) & 0xFF;
			cdb[5] = blk & 0xFF;
			cdb[8] = (u_char) 1;
			ucmd.uscsi_cdblen = CDB_GROUP1;
		}
		mutex_exit(BST_MUTEX(targ));
		status = bst_ioctl_cmd(dev, &ucmd, UIO_SYSSPACE, UIO_SYSSPACE);
		mutex_enter(BST_MUTEX(targ));
		if (status != 0) {
			return (status);
		}
	}

	return (0);
}



/*
 * -----------------------------------------------------------------------------
 *	Error Message Data and Routines
 */

/*
 * Log a message to the console and/or syslog with cmn_err
 */
/*ARGSUSED*/
static void
bst_log(struct scsi_device *devp, int level, const char *fmt, ...)
{
	char name[16];
	char buf[256];
	va_list ap;

#ifdef	lint
	ap = 0;
#endif

	if (devp) {
		(void) sprintf(name, "%s%d", ddi_get_name(devp->sd_dev),
		    ddi_get_instance(devp->sd_dev));
	} else {
		(void) sprintf(name, "bst");
	}

#ifndef	lint
	va_start(ap, fmt);
#endif
	(void) vsprintf(buf, fmt, ap);
#ifndef	lint
	va_end(ap);
#endif

	switch (level) {
	case CE_CONT:
	case CE_NOTE:
	case CE_WARN:
	case CE_PANIC:
		cmn_err(level, "%s:\t%s", name, buf);
		break;

	case BST_CE_DEBUG4: if (bst_debug < 4) break;
		/*FALLTHROUGH*/
	case BST_CE_DEBUG3: if (bst_debug < 3) break;
		/*FALLTHROUGH*/
	case BST_CE_DEBUG2: if (bst_debug < 2) break;
		/*FALLTHROUGH*/
	case BST_CE_DEBUG1:
	default:
		cmn_err(CE_CONT, "^%s:\t%s", name, buf);
		break;
	}
}


/*
 * Print a buffer readably
 */
static void
hex_print(char *msg, char *cp, int len)
{
	int i = 0, j;
	char	buf[BUFLEN];

	bzero(buf, BUFLEN);
	for (i = 0; i < len; i++) {
		if ((j = strlen(buf)) >= BUFLEN) {
			buf[BUFLEN-2] = '>';
			buf[BUFLEN-1] = 0;
			break;		/* cp too long, give up */
		}
		(void) sprintf(&buf[j], "%x ", cp[i]);
	}

	cmn_err(CE_CONT, "^%s: %s\n", msg, buf);
}
