1139743Simp/*-
239213Sgibbs * Copyright (c) 1997 Justin T. Gibbs.
343819Sken * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry.
439213Sgibbs * All rights reserved.
539213Sgibbs *
639213Sgibbs * Redistribution and use in source and binary forms, with or without
739213Sgibbs * modification, are permitted provided that the following conditions
839213Sgibbs * are met:
939213Sgibbs * 1. Redistributions of source code must retain the above copyright
1039213Sgibbs *    notice, this list of conditions, and the following disclaimer,
1139213Sgibbs *    without modification, immediately at the beginning of the file.
1239213Sgibbs * 2. The name of the author may not be used to endorse or promote products
1339213Sgibbs *    derived from this software without specific prior written permission.
1439213Sgibbs *
1539213Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1639213Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1739213Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1839213Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
1939213Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2039213Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2139213Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2239213Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2339213Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2439213Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2539213Sgibbs * SUCH DAMAGE.
2639213Sgibbs */
27116162Sobrien
2839213Sgibbs/*
2939213Sgibbs * Derived from the NetBSD SCSI changer driver.
3039213Sgibbs *
3139213Sgibbs *	$NetBSD: ch.c,v 1.32 1998/01/12 09:49:12 thorpej Exp $
3239213Sgibbs *
3339213Sgibbs */
34139743Simp/*-
3539213Sgibbs * Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej@and.com>
3639213Sgibbs * All rights reserved.
3739213Sgibbs *
3839213Sgibbs * Partially based on an autochanger driver written by Stefan Grefen
3939213Sgibbs * and on an autochanger driver written by the Systems Programming Group
4039213Sgibbs * at the University of Utah Computer Science Department.
4139213Sgibbs *
4239213Sgibbs * Redistribution and use in source and binary forms, with or without
4339213Sgibbs * modification, are permitted provided that the following conditions
4439213Sgibbs * are met:
4539213Sgibbs * 1. Redistributions of source code must retain the above copyright
4639213Sgibbs *    notice, this list of conditions and the following disclaimer.
4739213Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
4839213Sgibbs *    notice, this list of conditions and the following disclaimer in the
4939213Sgibbs *    documentation and/or other materials provided with the distribution.
5039213Sgibbs * 3. All advertising materials mentioning features or use of this software
5139213Sgibbs *    must display the following acknowledgements:
5239213Sgibbs *	This product includes software developed by Jason R. Thorpe
5339213Sgibbs *	for And Communications, http://www.and.com/
5439213Sgibbs * 4. The name of the author may not be used to endorse or promote products
5539213Sgibbs *    derived from this software without specific prior written permission.
5639213Sgibbs *
5739213Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
5839213Sgibbs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
5939213Sgibbs * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
6039213Sgibbs * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
6139213Sgibbs * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
6239213Sgibbs * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
6339213Sgibbs * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
6439213Sgibbs * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
6539213Sgibbs * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6639213Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6739213Sgibbs * SUCH DAMAGE.
6839213Sgibbs */
6939213Sgibbs
70116162Sobrien#include <sys/cdefs.h>
71116162Sobrien__FBSDID("$FreeBSD$");
72116162Sobrien
7339213Sgibbs#include <sys/param.h>
7439213Sgibbs#include <sys/queue.h>
7539213Sgibbs#include <sys/systm.h>
7639213Sgibbs#include <sys/kernel.h>
7739213Sgibbs#include <sys/types.h>
7839213Sgibbs#include <sys/malloc.h>
7939213Sgibbs#include <sys/fcntl.h>
8039213Sgibbs#include <sys/conf.h>
8139213Sgibbs#include <sys/chio.h>
8239213Sgibbs#include <sys/errno.h>
8339213Sgibbs#include <sys/devicestat.h>
8439213Sgibbs
8539213Sgibbs#include <cam/cam.h>
8639213Sgibbs#include <cam/cam_ccb.h>
8739213Sgibbs#include <cam/cam_periph.h>
8839213Sgibbs#include <cam/cam_xpt_periph.h>
8939213Sgibbs#include <cam/cam_debug.h>
9039213Sgibbs
9139213Sgibbs#include <cam/scsi/scsi_all.h>
9239213Sgibbs#include <cam/scsi/scsi_message.h>
9339213Sgibbs#include <cam/scsi/scsi_ch.h>
9439213Sgibbs
9539213Sgibbs/*
9639213Sgibbs * Timeout definitions for various changer related commands.  They may
9739213Sgibbs * be too short for some devices (especially the timeout for INITIALIZE
9839213Sgibbs * ELEMENT STATUS).
9939213Sgibbs */
10039213Sgibbs
10142017Seivindstatic const u_int32_t	CH_TIMEOUT_MODE_SENSE                = 6000;
102255043Skenstatic const u_int32_t	CH_TIMEOUT_MOVE_MEDIUM               = 15 * 60 * 1000;
103255043Skenstatic const u_int32_t	CH_TIMEOUT_EXCHANGE_MEDIUM           = 15 * 60 * 1000;
104255043Skenstatic const u_int32_t	CH_TIMEOUT_POSITION_TO_ELEMENT       = 15 * 60 * 1000;
105255043Skenstatic const u_int32_t	CH_TIMEOUT_READ_ELEMENT_STATUS       = 5 * 60 * 1000;
10642017Seivindstatic const u_int32_t	CH_TIMEOUT_SEND_VOLTAG		     = 10000;
10742017Seivindstatic const u_int32_t	CH_TIMEOUT_INITIALIZE_ELEMENT_STATUS = 500000;
10839213Sgibbs
10939213Sgibbstypedef enum {
110236138Sken	CH_FLAG_INVALID		= 0x001
11139213Sgibbs} ch_flags;
11239213Sgibbs
11339213Sgibbstypedef enum {
11439213Sgibbs	CH_STATE_PROBE,
11539213Sgibbs	CH_STATE_NORMAL
11639213Sgibbs} ch_state;
11739213Sgibbs
11839213Sgibbstypedef enum {
11939213Sgibbs	CH_CCB_PROBE,
12039213Sgibbs	CH_CCB_WAITING
12139213Sgibbs} ch_ccb_types;
12239213Sgibbs
12339885Skentypedef enum {
12439885Sken	CH_Q_NONE	= 0x00,
125253274Sken	CH_Q_NO_DBD	= 0x01,
126253274Sken	CH_Q_NO_DVCID	= 0x02
12739885Sken} ch_quirks;
12839885Sken
129250792Ssmh#define CH_Q_BIT_STRING	\
130250792Ssmh	"\020"		\
131253274Sken	"\001NO_DBD"	\
132253274Sken	"\002NO_DVCID"
133250792Ssmh
13439213Sgibbs#define ccb_state	ppriv_field0
13539213Sgibbs#define ccb_bp		ppriv_ptr1
13639213Sgibbs
13739213Sgibbsstruct scsi_mode_sense_data {
13839213Sgibbs	struct scsi_mode_header_6 header;
13939885Sken	struct scsi_mode_blk_desc blk_desc;
14039213Sgibbs	union {
14139213Sgibbs		struct page_element_address_assignment ea;
14239213Sgibbs		struct page_transport_geometry_parameters tg;
14339213Sgibbs		struct page_device_capabilities cap;
14439213Sgibbs	} pages;
14539213Sgibbs};
14639213Sgibbs
14739213Sgibbsstruct ch_softc {
14839213Sgibbs	ch_flags	flags;
14939213Sgibbs	ch_state	state;
15039885Sken	ch_quirks	quirks;
15139213Sgibbs	union ccb	saved_ccb;
152112006Sphk	struct devstat	*device_stats;
153244014Sken	struct cdev     *dev;
154244014Sken	int		open_count;
15539213Sgibbs
15639213Sgibbs	int		sc_picker;	/* current picker */
15739213Sgibbs
15839213Sgibbs	/*
15939213Sgibbs	 * The following information is obtained from the
16039213Sgibbs	 * element address assignment page.
16139213Sgibbs	 */
162115464Sphk	int		sc_firsts[CHET_MAX + 1];	/* firsts */
163115464Sphk	int		sc_counts[CHET_MAX + 1];	/* counts */
16439213Sgibbs
16539213Sgibbs	/*
16639213Sgibbs	 * The following mask defines the legal combinations
16739213Sgibbs	 * of elements for the MOVE MEDIUM command.
16839213Sgibbs	 */
169115464Sphk	u_int8_t	sc_movemask[CHET_MAX + 1];
17039213Sgibbs
17139213Sgibbs	/*
17239213Sgibbs	 * As above, but for EXCHANGE MEDIUM.
17339213Sgibbs	 */
174115464Sphk	u_int8_t	sc_exchangemask[CHET_MAX + 1];
17539213Sgibbs
17639213Sgibbs	/*
17739213Sgibbs	 * Quirks; see below.  XXX KDM not implemented yet
17839213Sgibbs	 */
17939213Sgibbs	int		sc_settledelay;	/* delay for settle */
18039213Sgibbs};
18139213Sgibbs
18239213Sgibbsstatic	d_open_t	chopen;
18339213Sgibbsstatic	d_close_t	chclose;
18439213Sgibbsstatic	d_ioctl_t	chioctl;
18539213Sgibbsstatic	periph_init_t	chinit;
18639213Sgibbsstatic  periph_ctor_t	chregister;
18740603Skenstatic	periph_oninv_t	choninvalidate;
18839213Sgibbsstatic  periph_dtor_t   chcleanup;
18939213Sgibbsstatic  periph_start_t  chstart;
19039213Sgibbsstatic	void		chasync(void *callback_arg, u_int32_t code,
19139213Sgibbs				struct cam_path *path, void *arg);
19239213Sgibbsstatic	void		chdone(struct cam_periph *periph,
19339213Sgibbs			       union ccb *done_ccb);
19439213Sgibbsstatic	int		cherror(union ccb *ccb, u_int32_t cam_flags,
19539213Sgibbs				u_int32_t sense_flags);
19639213Sgibbsstatic	int		chmove(struct cam_periph *periph,
19739213Sgibbs			       struct changer_move *cm);
19839213Sgibbsstatic	int		chexchange(struct cam_periph *periph,
19939213Sgibbs				   struct changer_exchange *ce);
20039213Sgibbsstatic	int		chposition(struct cam_periph *periph,
20139213Sgibbs				   struct changer_position *cp);
20239213Sgibbsstatic	int		chgetelemstatus(struct cam_periph *periph,
203249658Sken				int scsi_version, u_long cmd,
20439213Sgibbs				struct changer_element_status_request *csr);
20539213Sgibbsstatic	int		chsetvoltag(struct cam_periph *periph,
20639213Sgibbs				    struct changer_set_voltag_request *csvr);
20739213Sgibbsstatic	int		chielem(struct cam_periph *periph,
20839213Sgibbs				unsigned int timeout);
20939213Sgibbsstatic	int		chgetparams(struct cam_periph *periph);
210249658Skenstatic	int		chscsiversion(struct cam_periph *periph);
21139213Sgibbs
21239213Sgibbsstatic struct periph_driver chdriver =
21339213Sgibbs{
21439213Sgibbs	chinit, "ch",
21539213Sgibbs	TAILQ_HEAD_INITIALIZER(chdriver.units), /* generation */ 0
21639213Sgibbs};
21739213Sgibbs
21872119SpeterPERIPHDRIVER_DECLARE(ch, chdriver);
21939213Sgibbs
22047625Sphkstatic struct cdevsw ch_cdevsw = {
221126080Sphk	.d_version =	D_VERSION,
222236138Sken	.d_flags =	D_TRACKCLOSE,
223111815Sphk	.d_open =	chopen,
224111815Sphk	.d_close =	chclose,
225111815Sphk	.d_ioctl =	chioctl,
226111815Sphk	.d_name =	"ch",
22739213Sgibbs};
22839213Sgibbs
229227293Sedstatic MALLOC_DEFINE(M_SCSICH, "scsi_ch", "scsi_ch buffers");
230169562Sscottl
231104094Sphkstatic void
23239213Sgibbschinit(void)
23339213Sgibbs{
23439213Sgibbs	cam_status status;
23539213Sgibbs
23639213Sgibbs	/*
23739213Sgibbs	 * Install a global async callback.  This callback will
23839213Sgibbs	 * receive async callbacks like "new device found".
23939213Sgibbs	 */
240169605Sscottl	status = xpt_register_async(AC_FOUND_DEVICE, chasync, NULL, NULL);
24139213Sgibbs
24239213Sgibbs	if (status != CAM_REQ_CMP) {
24339213Sgibbs		printf("ch: Failed to attach master async callback "
24439213Sgibbs		       "due to status 0x%x!\n", status);
24539213Sgibbs	}
24639213Sgibbs}
24739213Sgibbs
24839213Sgibbsstatic void
249244014Skenchdevgonecb(void *arg)
250244014Sken{
251244014Sken	struct cam_sim	  *sim;
252244014Sken	struct ch_softc   *softc;
253244014Sken	struct cam_periph *periph;
254244014Sken	int i;
255244014Sken
256244014Sken	periph = (struct cam_periph *)arg;
257244014Sken	sim = periph->sim;
258244014Sken	softc = (struct ch_softc *)periph->softc;
259244014Sken
260244014Sken	KASSERT(softc->open_count >= 0, ("Negative open count %d",
261244014Sken		softc->open_count));
262244014Sken
263244014Sken	mtx_lock(sim->mtx);
264244014Sken
265244014Sken	/*
266244014Sken	 * When we get this callback, we will get no more close calls from
267244014Sken	 * devfs.  So if we have any dangling opens, we need to release the
268244014Sken	 * reference held for that particular context.
269244014Sken	 */
270244014Sken	for (i = 0; i < softc->open_count; i++)
271244014Sken		cam_periph_release_locked(periph);
272244014Sken
273244014Sken	softc->open_count = 0;
274244014Sken
275244014Sken	/*
276244014Sken	 * Release the reference held for the device node, it is gone now.
277244014Sken	 */
278244014Sken	cam_periph_release_locked(periph);
279244014Sken
280244014Sken	/*
281244014Sken	 * We reference the SIM lock directly here, instead of using
282244014Sken	 * cam_periph_unlock().  The reason is that the final call to
283244014Sken	 * cam_periph_release_locked() above could result in the periph
284244014Sken	 * getting freed.  If that is the case, dereferencing the periph
285244014Sken	 * with a cam_periph_unlock() call would cause a page fault.
286244014Sken	 */
287244014Sken	mtx_unlock(sim->mtx);
288244014Sken}
289244014Sken
290244014Skenstatic void
29140603Skenchoninvalidate(struct cam_periph *periph)
29240603Sken{
29340603Sken	struct ch_softc *softc;
29440603Sken
29540603Sken	softc = (struct ch_softc *)periph->softc;
29640603Sken
29740603Sken	/*
29840603Sken	 * De-register any async callbacks.
29940603Sken	 */
300169605Sscottl	xpt_register_async(0, chasync, periph, periph->path);
30140603Sken
30240603Sken	softc->flags |= CH_FLAG_INVALID;
30340603Sken
304244014Sken	/*
305244014Sken	 * Tell devfs this device has gone away, and ask for a callback
306244014Sken	 * when it has cleaned up its state.
307244014Sken	 */
308244014Sken	destroy_dev_sched_cb(softc->dev, chdevgonecb, periph);
30940603Sken}
31040603Sken
31140603Skenstatic void
31239213Sgibbschcleanup(struct cam_periph *periph)
31339213Sgibbs{
31440603Sken	struct ch_softc *softc;
31539213Sgibbs
31640603Sken	softc = (struct ch_softc *)periph->softc;
31740603Sken
318112006Sphk	devstat_remove_entry(softc->device_stats);
319244014Sken
32040603Sken	free(softc, M_DEVBUF);
32139213Sgibbs}
32239213Sgibbs
32339213Sgibbsstatic void
32439213Sgibbschasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
32539213Sgibbs{
32639213Sgibbs	struct cam_periph *periph;
32739213Sgibbs
32839213Sgibbs	periph = (struct cam_periph *)callback_arg;
32939213Sgibbs
33039213Sgibbs	switch(code) {
33139213Sgibbs	case AC_FOUND_DEVICE:
33239213Sgibbs	{
33339213Sgibbs		struct ccb_getdev *cgd;
33439213Sgibbs		cam_status status;
33539213Sgibbs
33639213Sgibbs		cgd = (struct ccb_getdev *)arg;
33779177Smjacob		if (cgd == NULL)
33879177Smjacob			break;
33939213Sgibbs
340195534Sscottl		if (cgd->protocol != PROTO_SCSI)
341195534Sscottl			break;
342195534Sscottl
34356148Smjacob		if (SID_TYPE(&cgd->inq_data)!= T_CHANGER)
34439213Sgibbs			break;
34539213Sgibbs
34639213Sgibbs		/*
34739213Sgibbs		 * Allocate a peripheral instance for
34839213Sgibbs		 * this device and start the probe
34939213Sgibbs		 * process.
35039213Sgibbs		 */
35140603Sken		status = cam_periph_alloc(chregister, choninvalidate,
35240603Sken					  chcleanup, chstart, "ch",
35340603Sken					  CAM_PERIPH_BIO, cgd->ccb_h.path,
35439213Sgibbs					  chasync, AC_FOUND_DEVICE, cgd);
35539213Sgibbs
35639213Sgibbs		if (status != CAM_REQ_CMP
35739213Sgibbs		 && status != CAM_REQ_INPROG)
35839213Sgibbs			printf("chasync: Unable to probe new device "
35939213Sgibbs			       "due to status 0x%x\n", status);
36039213Sgibbs
36139213Sgibbs		break;
36239213Sgibbs
36339213Sgibbs	}
36439213Sgibbs	default:
36547413Sgibbs		cam_periph_async(periph, code, path, arg);
36639213Sgibbs		break;
36739213Sgibbs	}
36839213Sgibbs}
36939213Sgibbs
37039213Sgibbsstatic cam_status
37139213Sgibbschregister(struct cam_periph *periph, void *arg)
37239213Sgibbs{
37339213Sgibbs	struct ch_softc *softc;
37439213Sgibbs	struct ccb_getdev *cgd;
375220644Smav	struct ccb_pathinq cpi;
37639213Sgibbs
37739213Sgibbs	cgd = (struct ccb_getdev *)arg;
37839213Sgibbs	if (cgd == NULL) {
37939213Sgibbs		printf("chregister: no getdev CCB, can't register device\n");
38039213Sgibbs		return(CAM_REQ_CMP_ERR);
38139213Sgibbs	}
38239213Sgibbs
38339213Sgibbs	softc = (struct ch_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
38439213Sgibbs
38539213Sgibbs	if (softc == NULL) {
38639213Sgibbs		printf("chregister: Unable to probe new device. "
38739213Sgibbs		       "Unable to allocate softc\n");
38839213Sgibbs		return(CAM_REQ_CMP_ERR);
38939213Sgibbs	}
39039213Sgibbs
39139213Sgibbs	bzero(softc, sizeof(*softc));
39239213Sgibbs	softc->state = CH_STATE_PROBE;
39339213Sgibbs	periph->softc = softc;
39439885Sken	softc->quirks = CH_Q_NONE;
39539213Sgibbs
396253274Sken	/*
397253274Sken	 * The DVCID and CURDATA bits were not introduced until the SMC
398253274Sken	 * spec.  If this device claims SCSI-2 or earlier support, then it
399253274Sken	 * very likely does not support these bits.
400253274Sken	 */
401253274Sken	if (cgd->inq_data.version <= SCSI_REV_2)
402253274Sken		softc->quirks |= CH_Q_NO_DVCID;
403253274Sken
404220644Smav	bzero(&cpi, sizeof(cpi));
405220644Smav	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
406220644Smav	cpi.ccb_h.func_code = XPT_PATH_INQ;
407220644Smav	xpt_action((union ccb *)&cpi);
408220644Smav
40939213Sgibbs	/*
41039213Sgibbs	 * Changers don't have a blocksize, and obviously don't support
41139213Sgibbs	 * tagged queueing.
41239213Sgibbs	 */
413169605Sscottl	cam_periph_unlock(periph);
414112006Sphk	softc->device_stats = devstat_new_entry("ch",
41539213Sgibbs			  periph->unit_number, 0,
41639213Sgibbs			  DEVSTAT_NO_BLOCKSIZE | DEVSTAT_NO_ORDERED_TAGS,
417220644Smav			  SID_TYPE(&cgd->inq_data) |
418220644Smav			  XPORT_DEVSTAT_TYPE(cpi.transport),
41943819Sken			  DEVSTAT_PRIORITY_OTHER);
42039213Sgibbs
421244014Sken	/*
422244014Sken	 * Acquire a reference to the periph before we create the devfs
423244014Sken	 * instance for it.  We'll release this reference once the devfs
424244014Sken	 * instance has been freed.
425244014Sken	 */
426244014Sken	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
427244014Sken		xpt_print(periph->path, "%s: lost periph during "
428244014Sken			  "registration!\n", __func__);
429244014Sken		cam_periph_lock(periph);
430244014Sken		return (CAM_REQ_CMP_ERR);
431244014Sken	}
432244014Sken
433244014Sken
43453520Smjacob	/* Register the device */
43553520Smjacob	softc->dev = make_dev(&ch_cdevsw, periph->unit_number, UID_ROOT,
43653520Smjacob			      GID_OPERATOR, 0600, "%s%d", periph->periph_name,
43753520Smjacob			      periph->unit_number);
438168752Sscottl	cam_periph_lock(periph);
439101940Snjl	softc->dev->si_drv1 = periph;
44053520Smjacob
44139213Sgibbs	/*
44239213Sgibbs	 * Add an async callback so that we get
44339213Sgibbs	 * notified if this device goes away.
44439213Sgibbs	 */
445169605Sscottl	xpt_register_async(AC_LOST_DEVICE, chasync, periph, periph->path);
44639213Sgibbs
44739213Sgibbs	/*
448168752Sscottl	 * Lock this periph until we are setup.
44939213Sgibbs	 * This first call can't block
45039213Sgibbs	 */
451168752Sscottl	(void)cam_periph_hold(periph, PRIBIO);
452203108Smav	xpt_schedule(periph, CAM_PRIORITY_DEV);
45339213Sgibbs
45439213Sgibbs	return(CAM_REQ_CMP);
45539213Sgibbs}
45639213Sgibbs
45739213Sgibbsstatic int
458130585Sphkchopen(struct cdev *dev, int flags, int fmt, struct thread *td)
45939213Sgibbs{
46039213Sgibbs	struct cam_periph *periph;
46139213Sgibbs	struct ch_softc *softc;
462101940Snjl	int error;
46339213Sgibbs
464101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
465168752Sscottl	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
466168752Sscottl		return (ENXIO);
46739213Sgibbs
46839213Sgibbs	softc = (struct ch_softc *)periph->softc;
46939213Sgibbs
470168752Sscottl	cam_periph_lock(periph);
471168752Sscottl
47240603Sken	if (softc->flags & CH_FLAG_INVALID) {
473236138Sken		cam_periph_release_locked(periph);
474168752Sscottl		cam_periph_unlock(periph);
47539213Sgibbs		return(ENXIO);
47640603Sken	}
47739213Sgibbs
478168752Sscottl	if ((error = cam_periph_hold(periph, PRIBIO | PCATCH)) != 0) {
479168752Sscottl		cam_periph_unlock(periph);
480168752Sscottl		cam_periph_release(periph);
48139213Sgibbs		return (error);
48241297Sken	}
48341297Sken
48439213Sgibbs	/*
48539213Sgibbs	 * Load information about this changer device into the softc.
48639213Sgibbs	 */
48739213Sgibbs	if ((error = chgetparams(periph)) != 0) {
488249658Sken		cam_periph_unhold(periph);
489236138Sken		cam_periph_release_locked(periph);
49039213Sgibbs		cam_periph_unlock(periph);
49139213Sgibbs		return(error);
49239213Sgibbs	}
49339213Sgibbs
494168752Sscottl	cam_periph_unhold(periph);
495244014Sken
496244014Sken	softc->open_count++;
497244014Sken
49839213Sgibbs	cam_periph_unlock(periph);
49939213Sgibbs
50039213Sgibbs	return(error);
50139213Sgibbs}
50239213Sgibbs
50339213Sgibbsstatic int
504130585Sphkchclose(struct cdev *dev, int flag, int fmt, struct thread *td)
50539213Sgibbs{
506244014Sken	struct	cam_sim *sim;
50739213Sgibbs	struct	cam_periph *periph;
508244014Sken	struct  ch_softc *softc;
50939213Sgibbs
510101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
51139213Sgibbs	if (periph == NULL)
51239213Sgibbs		return(ENXIO);
51339213Sgibbs
514244014Sken	sim = periph->sim;
515244014Sken	softc = (struct ch_softc *)periph->softc;
51639213Sgibbs
517244014Sken	mtx_lock(sim->mtx);
518244014Sken
519244014Sken	softc->open_count--;
520244014Sken
521244014Sken	cam_periph_release_locked(periph);
522244014Sken
523244014Sken	/*
524244014Sken	 * We reference the SIM lock directly here, instead of using
525244014Sken	 * cam_periph_unlock().  The reason is that the call to
526244014Sken	 * cam_periph_release_locked() above could result in the periph
527244014Sken	 * getting freed.  If that is the case, dereferencing the periph
528244014Sken	 * with a cam_periph_unlock() call would cause a page fault.
529244014Sken	 *
530244014Sken	 * cam_periph_release() avoids this problem using the same method,
531244014Sken	 * but we're manually acquiring and dropping the lock here to
532244014Sken	 * protect the open count and avoid another lock acquisition and
533244014Sken	 * release.
534244014Sken	 */
535244014Sken	mtx_unlock(sim->mtx);
536244014Sken
53739213Sgibbs	return(0);
53839213Sgibbs}
53939213Sgibbs
54039213Sgibbsstatic void
54139213Sgibbschstart(struct cam_periph *periph, union ccb *start_ccb)
54239213Sgibbs{
54339213Sgibbs	struct ch_softc *softc;
54439213Sgibbs
54539213Sgibbs	softc = (struct ch_softc *)periph->softc;
54639213Sgibbs
54739213Sgibbs	switch (softc->state) {
54839213Sgibbs	case CH_STATE_NORMAL:
54939213Sgibbs	{
55039213Sgibbs		if (periph->immediate_priority <= periph->pinfo.priority){
55139213Sgibbs			start_ccb->ccb_h.ccb_state = CH_CCB_WAITING;
55239213Sgibbs
55339213Sgibbs			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
55439213Sgibbs					  periph_links.sle);
55539213Sgibbs			periph->immediate_priority = CAM_PRIORITY_NONE;
55639213Sgibbs			wakeup(&periph->ccb_list);
557168752Sscottl		}
55839213Sgibbs		break;
55939213Sgibbs	}
56039213Sgibbs	case CH_STATE_PROBE:
56139213Sgibbs	{
56239885Sken		int mode_buffer_len;
56339885Sken		void *mode_buffer;
56439213Sgibbs
56539885Sken		/*
56639885Sken		 * Include the block descriptor when calculating the mode
56739885Sken		 * buffer length,
56839885Sken		 */
56939885Sken		mode_buffer_len = sizeof(struct scsi_mode_header_6) +
57039885Sken				  sizeof(struct scsi_mode_blk_desc) +
57139885Sken				 sizeof(struct page_element_address_assignment);
57239213Sgibbs
573169562Sscottl		mode_buffer = malloc(mode_buffer_len, M_SCSICH, M_NOWAIT);
57439885Sken
57539885Sken		if (mode_buffer == NULL) {
57639213Sgibbs			printf("chstart: couldn't malloc mode sense data\n");
57739213Sgibbs			break;
57839213Sgibbs		}
57939885Sken		bzero(mode_buffer, mode_buffer_len);
58039213Sgibbs
58139213Sgibbs		/*
58239213Sgibbs		 * Get the element address assignment page.
58339213Sgibbs		 */
58439213Sgibbs		scsi_mode_sense(&start_ccb->csio,
58539213Sgibbs				/* retries */ 1,
58639213Sgibbs				/* cbfcnp */ chdone,
58739213Sgibbs				/* tag_action */ MSG_SIMPLE_Q_TAG,
58839885Sken				/* dbd */ (softc->quirks & CH_Q_NO_DBD) ?
58939885Sken					FALSE : TRUE,
59039213Sgibbs				/* page_code */ SMS_PAGE_CTRL_CURRENT,
59139213Sgibbs				/* page */ CH_ELEMENT_ADDR_ASSIGN_PAGE,
59239885Sken				/* param_buf */ (u_int8_t *)mode_buffer,
59339885Sken				/* param_len */ mode_buffer_len,
59439213Sgibbs				/* sense_len */ SSD_FULL_SIZE,
59539213Sgibbs				/* timeout */ CH_TIMEOUT_MODE_SENSE);
59639213Sgibbs
59739213Sgibbs		start_ccb->ccb_h.ccb_bp = NULL;
59839213Sgibbs		start_ccb->ccb_h.ccb_state = CH_CCB_PROBE;
59939213Sgibbs		xpt_action(start_ccb);
60039213Sgibbs		break;
60139213Sgibbs	}
60239213Sgibbs	}
60339213Sgibbs}
60439213Sgibbs
60539213Sgibbsstatic void
60639213Sgibbschdone(struct cam_periph *periph, union ccb *done_ccb)
60739213Sgibbs{
60839213Sgibbs	struct ch_softc *softc;
60939213Sgibbs	struct ccb_scsiio *csio;
61039213Sgibbs
61139213Sgibbs	softc = (struct ch_softc *)periph->softc;
61239213Sgibbs	csio = &done_ccb->csio;
61339213Sgibbs
61439213Sgibbs	switch(done_ccb->ccb_h.ccb_state) {
61539213Sgibbs	case CH_CCB_PROBE:
61639213Sgibbs	{
61739885Sken		struct scsi_mode_header_6 *mode_header;
61839885Sken		struct page_element_address_assignment *ea;
61939213Sgibbs		char announce_buf[80];
62039213Sgibbs
62139213Sgibbs
62239885Sken		mode_header = (struct scsi_mode_header_6 *)csio->data_ptr;
62339885Sken
62439885Sken		ea = (struct page_element_address_assignment *)
62539885Sken			find_mode_page_6(mode_header);
62639885Sken
62739213Sgibbs		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP){
62839213Sgibbs
62939885Sken			softc->sc_firsts[CHET_MT] = scsi_2btoul(ea->mtea);
63039885Sken			softc->sc_counts[CHET_MT] = scsi_2btoul(ea->nmte);
63139885Sken			softc->sc_firsts[CHET_ST] = scsi_2btoul(ea->fsea);
63239885Sken			softc->sc_counts[CHET_ST] = scsi_2btoul(ea->nse);
63339885Sken			softc->sc_firsts[CHET_IE] = scsi_2btoul(ea->fieea);
63439885Sken			softc->sc_counts[CHET_IE] = scsi_2btoul(ea->niee);
63539885Sken			softc->sc_firsts[CHET_DT] = scsi_2btoul(ea->fdtea);
63639885Sken			softc->sc_counts[CHET_DT] = scsi_2btoul(ea->ndte);
63739213Sgibbs			softc->sc_picker = softc->sc_firsts[CHET_MT];
63839213Sgibbs
63939213Sgibbs#define PLURAL(c)	(c) == 1 ? "" : "s"
64041514Sarchie			snprintf(announce_buf, sizeof(announce_buf),
64141514Sarchie				"%d slot%s, %d drive%s, "
64239213Sgibbs				"%d picker%s, %d portal%s",
64339213Sgibbs		    		softc->sc_counts[CHET_ST],
64439213Sgibbs				PLURAL(softc->sc_counts[CHET_ST]),
64539213Sgibbs		    		softc->sc_counts[CHET_DT],
64639213Sgibbs				PLURAL(softc->sc_counts[CHET_DT]),
64739213Sgibbs		    		softc->sc_counts[CHET_MT],
64839213Sgibbs				PLURAL(softc->sc_counts[CHET_MT]),
64939213Sgibbs		    		softc->sc_counts[CHET_IE],
65039213Sgibbs				PLURAL(softc->sc_counts[CHET_IE]));
65139213Sgibbs#undef PLURAL
65239213Sgibbs		} else {
65339213Sgibbs			int error;
65439213Sgibbs
65574840Sken			error = cherror(done_ccb, CAM_RETRY_SELTO,
65674840Sken					SF_RETRY_UA | SF_NO_PRINT);
65739213Sgibbs			/*
65839213Sgibbs			 * Retry any UNIT ATTENTION type errors.  They
65939213Sgibbs			 * are expected at boot.
66039213Sgibbs			 */
66139213Sgibbs			if (error == ERESTART) {
66239213Sgibbs				/*
66339213Sgibbs				 * A retry was scheuled, so
66439213Sgibbs				 * just return.
66539213Sgibbs				 */
66639213Sgibbs				return;
66739213Sgibbs			} else if (error != 0) {
66839885Sken				int retry_scheduled;
66939885Sken				struct scsi_mode_sense_6 *sms;
67039885Sken
67139885Sken				sms = (struct scsi_mode_sense_6 *)
67239885Sken					done_ccb->csio.cdb_io.cdb_bytes;
67339885Sken
67439885Sken				/*
67539885Sken				 * Check to see if block descriptors were
67639885Sken				 * disabled.  Some devices don't like that.
67739885Sken				 * We're taking advantage of the fact that
67839885Sken				 * the first few bytes of the 6 and 10 byte
67939885Sken				 * mode sense commands are the same.  If
68039885Sken				 * block descriptors were disabled, enable
68139885Sken				 * them and re-send the command.
68239885Sken				 */
68339885Sken				if (sms->byte2 & SMS_DBD) {
68439885Sken					sms->byte2 &= ~SMS_DBD;
68539885Sken					xpt_action(done_ccb);
68639885Sken					softc->quirks |= CH_Q_NO_DBD;
68739885Sken					retry_scheduled = 1;
68839885Sken				} else
68939885Sken					retry_scheduled = 0;
69039885Sken
69139213Sgibbs				/* Don't wedge this device's queue */
692199279Smav				if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
693199279Smav					cam_release_devq(done_ccb->ccb_h.path,
69439213Sgibbs						 /*relsim_flags*/0,
69539213Sgibbs						 /*reduction*/0,
69639213Sgibbs						 /*timeout*/0,
69739213Sgibbs						 /*getcount_only*/0);
69839885Sken
69939885Sken				if (retry_scheduled)
70039885Sken					return;
70139885Sken
70239885Sken				if ((done_ccb->ccb_h.status & CAM_STATUS_MASK)
70339885Sken				    == CAM_SCSI_STATUS_ERROR)
70439885Sken					scsi_sense_print(&done_ccb->csio);
70539885Sken				else {
706164906Smjacob					xpt_print(periph->path,
707164906Smjacob					    "got CAM status %#x\n",
708164906Smjacob					    done_ccb->ccb_h.status);
70939885Sken				}
710164906Smjacob				xpt_print(periph->path, "fatal error, failed "
711164906Smjacob				    "to attach to device\n");
71239885Sken
71339885Sken				cam_periph_invalidate(periph);
71439885Sken
71539885Sken				announce_buf[0] = '\0';
71639213Sgibbs			}
71739213Sgibbs		}
718250792Ssmh		if (announce_buf[0] != '\0') {
71939885Sken			xpt_announce_periph(periph, announce_buf);
720250792Ssmh			xpt_announce_quirks(periph, softc->quirks,
721250792Ssmh			    CH_Q_BIT_STRING);
722250792Ssmh		}
72339213Sgibbs		softc->state = CH_STATE_NORMAL;
724169562Sscottl		free(mode_header, M_SCSICH);
72558921Sken		/*
72658921Sken		 * Since our peripheral may be invalidated by an error
72758921Sken		 * above or an external event, we must release our CCB
72858921Sken		 * before releasing the probe lock on the peripheral.
72958921Sken		 * The peripheral will only go away once the last lock
73058921Sken		 * is removed, and we need it around for the CCB release
73158921Sken		 * operation.
73258921Sken		 */
73358921Sken		xpt_release_ccb(done_ccb);
734168752Sscottl		cam_periph_unhold(periph);
73558921Sken		return;
73639213Sgibbs	}
73739213Sgibbs	case CH_CCB_WAITING:
73839213Sgibbs	{
73939213Sgibbs		/* Caller will release the CCB */
74039213Sgibbs		wakeup(&done_ccb->ccb_h.cbfcnp);
74139213Sgibbs		return;
74239213Sgibbs	}
74358921Sken	default:
74458921Sken		break;
74539213Sgibbs	}
74639213Sgibbs	xpt_release_ccb(done_ccb);
74739213Sgibbs}
74839213Sgibbs
74939213Sgibbsstatic int
75039213Sgibbscherror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
75139213Sgibbs{
75239213Sgibbs	struct ch_softc *softc;
75339213Sgibbs	struct cam_periph *periph;
75439213Sgibbs
75539213Sgibbs	periph = xpt_path_periph(ccb->ccb_h.path);
75639213Sgibbs	softc = (struct ch_softc *)periph->softc;
75739213Sgibbs
75839213Sgibbs	return (cam_periph_error(ccb, cam_flags, sense_flags,
75939213Sgibbs				 &softc->saved_ccb));
76039213Sgibbs}
76139213Sgibbs
76239213Sgibbsstatic int
763130585Sphkchioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
76439213Sgibbs{
76539213Sgibbs	struct cam_periph *periph;
76639213Sgibbs	struct ch_softc *softc;
76739213Sgibbs	int error;
76839213Sgibbs
769101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
77039213Sgibbs	if (periph == NULL)
77139213Sgibbs		return(ENXIO);
77239213Sgibbs
773168752Sscottl	cam_periph_lock(periph);
77439213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering chioctl\n"));
77539213Sgibbs
77639213Sgibbs	softc = (struct ch_softc *)periph->softc;
77739213Sgibbs
77839213Sgibbs	error = 0;
77939213Sgibbs
78039213Sgibbs	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
78139904Sken		  ("trying to do ioctl %#lx\n", cmd));
78239213Sgibbs
78339213Sgibbs	/*
78439213Sgibbs	 * If this command can change the device's state, we must
78539213Sgibbs	 * have the device open for writing.
78639213Sgibbs	 */
78739213Sgibbs	switch (cmd) {
78839213Sgibbs	case CHIOGPICKER:
78939213Sgibbs	case CHIOGPARAMS:
790249658Sken	case OCHIOGSTATUS:
79139213Sgibbs	case CHIOGSTATUS:
79239213Sgibbs		break;
79339213Sgibbs
79439213Sgibbs	default:
795168752Sscottl		if ((flag & FWRITE) == 0) {
796168752Sscottl			cam_periph_unlock(periph);
79739213Sgibbs			return (EBADF);
798168752Sscottl		}
79939213Sgibbs	}
80039213Sgibbs
80139213Sgibbs	switch (cmd) {
80239213Sgibbs	case CHIOMOVE:
80339213Sgibbs		error = chmove(periph, (struct changer_move *)addr);
80439213Sgibbs		break;
80539213Sgibbs
80639213Sgibbs	case CHIOEXCHANGE:
80739213Sgibbs		error = chexchange(periph, (struct changer_exchange *)addr);
80839213Sgibbs		break;
80939213Sgibbs
81039213Sgibbs	case CHIOPOSITION:
81139213Sgibbs		error = chposition(periph, (struct changer_position *)addr);
81239213Sgibbs		break;
81339213Sgibbs
81439213Sgibbs	case CHIOGPICKER:
81539213Sgibbs		*(int *)addr = softc->sc_picker - softc->sc_firsts[CHET_MT];
81639213Sgibbs		break;
81739213Sgibbs
81839213Sgibbs	case CHIOSPICKER:
81939213Sgibbs	{
82039213Sgibbs		int new_picker = *(int *)addr;
82139213Sgibbs
822168752Sscottl		if (new_picker > (softc->sc_counts[CHET_MT] - 1)) {
823168752Sscottl			error = EINVAL;
824168752Sscottl			break;
825168752Sscottl		}
82639213Sgibbs		softc->sc_picker = softc->sc_firsts[CHET_MT] + new_picker;
82739213Sgibbs		break;
82839213Sgibbs	}
82939213Sgibbs	case CHIOGPARAMS:
83039213Sgibbs	{
83139213Sgibbs		struct changer_params *cp = (struct changer_params *)addr;
83239213Sgibbs
83339213Sgibbs		cp->cp_npickers = softc->sc_counts[CHET_MT];
83439213Sgibbs		cp->cp_nslots = softc->sc_counts[CHET_ST];
83539213Sgibbs		cp->cp_nportals = softc->sc_counts[CHET_IE];
83639213Sgibbs		cp->cp_ndrives = softc->sc_counts[CHET_DT];
83739213Sgibbs		break;
83839213Sgibbs	}
83939213Sgibbs	case CHIOIELEM:
84039213Sgibbs		error = chielem(periph, *(unsigned int *)addr);
84139213Sgibbs		break;
84239213Sgibbs
843249658Sken	case OCHIOGSTATUS:
844249658Sken	{
845249658Sken		error = chgetelemstatus(periph, SCSI_REV_2, cmd,
846249658Sken		    (struct changer_element_status_request *)addr);
847249658Sken		break;
848249658Sken	}
849249658Sken
85039213Sgibbs	case CHIOGSTATUS:
85139213Sgibbs	{
852249658Sken		int scsi_version;
853249658Sken
854249658Sken		scsi_version = chscsiversion(periph);
855249658Sken		if (scsi_version >= SCSI_REV_0) {
856249658Sken			error = chgetelemstatus(periph, scsi_version, cmd,
857249658Sken			    (struct changer_element_status_request *)addr);
858249658Sken	  	}
859249658Sken		else { /* unable to determine the SCSI version */
860249658Sken			cam_periph_unlock(periph);
861249658Sken			return (ENXIO);
862249658Sken		}
86339213Sgibbs		break;
86439213Sgibbs	}
86539213Sgibbs
86639213Sgibbs	case CHIOSETVOLTAG:
86739213Sgibbs	{
86839213Sgibbs		error = chsetvoltag(periph,
86939213Sgibbs				    (struct changer_set_voltag_request *) addr);
87039213Sgibbs		break;
87139213Sgibbs	}
87239213Sgibbs
87339213Sgibbs	/* Implement prevent/allow? */
87439213Sgibbs
87539213Sgibbs	default:
87639213Sgibbs		error = cam_periph_ioctl(periph, cmd, addr, cherror);
87739213Sgibbs		break;
87839213Sgibbs	}
87939213Sgibbs
880168752Sscottl	cam_periph_unlock(periph);
88139213Sgibbs	return (error);
88239213Sgibbs}
88339213Sgibbs
88439213Sgibbsstatic int
88539213Sgibbschmove(struct cam_periph *periph, struct changer_move *cm)
88639213Sgibbs{
88739213Sgibbs	struct ch_softc *softc;
88839213Sgibbs	u_int16_t fromelem, toelem;
88939213Sgibbs	union ccb *ccb;
89039213Sgibbs	int error;
89139213Sgibbs
89239213Sgibbs	error = 0;
89339213Sgibbs	softc = (struct ch_softc *)periph->softc;
89439213Sgibbs
89539213Sgibbs	/*
89639213Sgibbs	 * Check arguments.
89739213Sgibbs	 */
89839213Sgibbs	if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
89939213Sgibbs		return (EINVAL);
90039213Sgibbs	if ((cm->cm_fromunit > (softc->sc_counts[cm->cm_fromtype] - 1)) ||
90139213Sgibbs	    (cm->cm_tounit > (softc->sc_counts[cm->cm_totype] - 1)))
90239213Sgibbs		return (ENODEV);
90339213Sgibbs
90439213Sgibbs	/*
90539213Sgibbs	 * Check the request against the changer's capabilities.
90639213Sgibbs	 */
90739213Sgibbs	if ((softc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
90841708Sgibbs		return (ENODEV);
90939213Sgibbs
91039213Sgibbs	/*
91139213Sgibbs	 * Calculate the source and destination elements.
91239213Sgibbs	 */
91339213Sgibbs	fromelem = softc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
91439213Sgibbs	toelem = softc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
91539213Sgibbs
916198382Smav	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
91739213Sgibbs
91839213Sgibbs	scsi_move_medium(&ccb->csio,
91939213Sgibbs			 /* retries */ 1,
92039213Sgibbs			 /* cbfcnp */ chdone,
92139213Sgibbs			 /* tag_action */ MSG_SIMPLE_Q_TAG,
92239213Sgibbs			 /* tea */ softc->sc_picker,
92339213Sgibbs			 /* src */ fromelem,
92439213Sgibbs			 /* dst */ toelem,
92539213Sgibbs			 /* invert */ (cm->cm_flags & CM_INVERT) ? TRUE : FALSE,
92639213Sgibbs			 /* sense_len */ SSD_FULL_SIZE,
92739213Sgibbs			 /* timeout */ CH_TIMEOUT_MOVE_MEDIUM);
92839213Sgibbs
92974840Sken	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/CAM_RETRY_SELTO,
93074840Sken				  /*sense_flags*/ SF_RETRY_UA,
931112006Sphk				  softc->device_stats);
93239213Sgibbs
93339213Sgibbs	xpt_release_ccb(ccb);
93439213Sgibbs
93539213Sgibbs	return(error);
93639213Sgibbs}
93739213Sgibbs
93839213Sgibbsstatic int
93939213Sgibbschexchange(struct cam_periph *periph, struct changer_exchange *ce)
94039213Sgibbs{
94139213Sgibbs	struct ch_softc *softc;
94239213Sgibbs	u_int16_t src, dst1, dst2;
94339213Sgibbs	union ccb *ccb;
94439213Sgibbs	int error;
94539213Sgibbs
94639213Sgibbs	error = 0;
94739213Sgibbs	softc = (struct ch_softc *)periph->softc;
94839213Sgibbs	/*
94939213Sgibbs	 * Check arguments.
95039213Sgibbs	 */
95139213Sgibbs	if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
95239213Sgibbs	    (ce->ce_sdsttype > CHET_DT))
95339213Sgibbs		return (EINVAL);
95439213Sgibbs	if ((ce->ce_srcunit > (softc->sc_counts[ce->ce_srctype] - 1)) ||
95539213Sgibbs	    (ce->ce_fdstunit > (softc->sc_counts[ce->ce_fdsttype] - 1)) ||
95639213Sgibbs	    (ce->ce_sdstunit > (softc->sc_counts[ce->ce_sdsttype] - 1)))
95739213Sgibbs		return (ENODEV);
95839213Sgibbs
95939213Sgibbs	/*
96039213Sgibbs	 * Check the request against the changer's capabilities.
96139213Sgibbs	 */
96239213Sgibbs	if (((softc->sc_exchangemask[ce->ce_srctype] &
96339213Sgibbs	     (1 << ce->ce_fdsttype)) == 0) ||
96439213Sgibbs	    ((softc->sc_exchangemask[ce->ce_fdsttype] &
96539213Sgibbs	     (1 << ce->ce_sdsttype)) == 0))
96641708Sgibbs		return (ENODEV);
96739213Sgibbs
96839213Sgibbs	/*
96939213Sgibbs	 * Calculate the source and destination elements.
97039213Sgibbs	 */
97139213Sgibbs	src = softc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
97239213Sgibbs	dst1 = softc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
97339213Sgibbs	dst2 = softc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
97439213Sgibbs
975198382Smav	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
97639213Sgibbs
97739213Sgibbs	scsi_exchange_medium(&ccb->csio,
97839213Sgibbs			     /* retries */ 1,
97939213Sgibbs			     /* cbfcnp */ chdone,
98039213Sgibbs			     /* tag_action */ MSG_SIMPLE_Q_TAG,
98139213Sgibbs			     /* tea */ softc->sc_picker,
98239213Sgibbs			     /* src */ src,
98339213Sgibbs			     /* dst1 */ dst1,
98439213Sgibbs			     /* dst2 */ dst2,
98539213Sgibbs			     /* invert1 */ (ce->ce_flags & CE_INVERT1) ?
98639213Sgibbs			                   TRUE : FALSE,
98739213Sgibbs			     /* invert2 */ (ce->ce_flags & CE_INVERT2) ?
98839213Sgibbs			                   TRUE : FALSE,
98939213Sgibbs			     /* sense_len */ SSD_FULL_SIZE,
99039213Sgibbs			     /* timeout */ CH_TIMEOUT_EXCHANGE_MEDIUM);
99139213Sgibbs
99274840Sken	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/CAM_RETRY_SELTO,
99374840Sken				  /*sense_flags*/ SF_RETRY_UA,
994112006Sphk				  softc->device_stats);
99539213Sgibbs
99639213Sgibbs	xpt_release_ccb(ccb);
99739213Sgibbs
99839213Sgibbs	return(error);
99939213Sgibbs}
100039213Sgibbs
100139213Sgibbsstatic int
100239213Sgibbschposition(struct cam_periph *periph, struct changer_position *cp)
100339213Sgibbs{
100439213Sgibbs	struct ch_softc *softc;
100539213Sgibbs	u_int16_t dst;
100639213Sgibbs	union ccb *ccb;
100739213Sgibbs	int error;
100839213Sgibbs
100939213Sgibbs	error = 0;
101039213Sgibbs	softc = (struct ch_softc *)periph->softc;
101139213Sgibbs
101239213Sgibbs	/*
101339213Sgibbs	 * Check arguments.
101439213Sgibbs	 */
101539213Sgibbs	if (cp->cp_type > CHET_DT)
101639213Sgibbs		return (EINVAL);
101739213Sgibbs	if (cp->cp_unit > (softc->sc_counts[cp->cp_type] - 1))
101839213Sgibbs		return (ENODEV);
101939213Sgibbs
102039213Sgibbs	/*
102139213Sgibbs	 * Calculate the destination element.
102239213Sgibbs	 */
102339213Sgibbs	dst = softc->sc_firsts[cp->cp_type] + cp->cp_unit;
102439213Sgibbs
1025198382Smav	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
102639213Sgibbs
102739213Sgibbs	scsi_position_to_element(&ccb->csio,
102839213Sgibbs				 /* retries */ 1,
102939213Sgibbs				 /* cbfcnp */ chdone,
103039213Sgibbs				 /* tag_action */ MSG_SIMPLE_Q_TAG,
103139213Sgibbs				 /* tea */ softc->sc_picker,
103239213Sgibbs				 /* dst */ dst,
103339213Sgibbs				 /* invert */ (cp->cp_flags & CP_INVERT) ?
103439213Sgibbs					      TRUE : FALSE,
103539213Sgibbs				 /* sense_len */ SSD_FULL_SIZE,
103639213Sgibbs				 /* timeout */ CH_TIMEOUT_POSITION_TO_ELEMENT);
103739213Sgibbs
103874840Sken	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ CAM_RETRY_SELTO,
103974840Sken				  /*sense_flags*/ SF_RETRY_UA,
1040112006Sphk				  softc->device_stats);
104139213Sgibbs
104239213Sgibbs	xpt_release_ccb(ccb);
104339213Sgibbs
104439213Sgibbs	return(error);
104539213Sgibbs}
104639213Sgibbs
104739213Sgibbs/*
104839213Sgibbs * Copy a volume tag to a volume_tag struct, converting SCSI byte order
104939213Sgibbs * to host native byte order in the volume serial number.  The volume
105039213Sgibbs * label as returned by the changer is transferred to user mode as
105139213Sgibbs * nul-terminated string.  Volume labels are truncated at the first
105239213Sgibbs * space, as suggested by SCSI-2.
105339213Sgibbs */
105439213Sgibbsstatic	void
105539213Sgibbscopy_voltag(struct changer_voltag *uvoltag, struct volume_tag *voltag)
105639213Sgibbs{
105739213Sgibbs	int i;
105839213Sgibbs	for (i=0; i<CH_VOLTAG_MAXLEN; i++) {
105939213Sgibbs		char c = voltag->vif[i];
106039213Sgibbs		if (c && c != ' ')
106139213Sgibbs			uvoltag->cv_volid[i] = c;
106239213Sgibbs	        else
106339213Sgibbs			break;
106439213Sgibbs	}
106539213Sgibbs	uvoltag->cv_serial = scsi_2btoul(voltag->vsn);
106639213Sgibbs}
106739213Sgibbs
106839213Sgibbs/*
1069249658Sken * Copy an element status descriptor to a user-mode
107039213Sgibbs * changer_element_status structure.
107139213Sgibbs */
1072249658Skenstatic void
107339213Sgibbscopy_element_status(struct ch_softc *softc,
107439213Sgibbs		    u_int16_t flags,
107539213Sgibbs		    struct read_element_status_descriptor *desc,
1076249658Sken		    struct changer_element_status *ces,
1077249658Sken		    int scsi_version)
107839213Sgibbs{
107939213Sgibbs	u_int16_t eaddr = scsi_2btoul(desc->eaddr);
108039213Sgibbs	u_int16_t et;
1081249658Sken	struct volume_tag *pvol_tag = NULL, *avol_tag = NULL;
1082249658Sken	struct read_element_status_device_id *devid = NULL;
108339213Sgibbs
108439213Sgibbs	ces->ces_int_addr = eaddr;
108539213Sgibbs	/* set up logical address in element status */
108639213Sgibbs	for (et = CHET_MT; et <= CHET_DT; et++) {
108739213Sgibbs		if ((softc->sc_firsts[et] <= eaddr)
108839213Sgibbs		    && ((softc->sc_firsts[et] + softc->sc_counts[et])
108939213Sgibbs			> eaddr)) {
109039213Sgibbs			ces->ces_addr = eaddr - softc->sc_firsts[et];
109139213Sgibbs			ces->ces_type = et;
109239213Sgibbs			break;
109339213Sgibbs		}
109439213Sgibbs	}
109539213Sgibbs
109639213Sgibbs	ces->ces_flags = desc->flags1;
109739213Sgibbs
109839213Sgibbs	ces->ces_sensecode = desc->sense_code;
109939213Sgibbs	ces->ces_sensequal = desc->sense_qual;
110039213Sgibbs
110139213Sgibbs	if (desc->flags2 & READ_ELEMENT_STATUS_INVERT)
110239213Sgibbs		ces->ces_flags |= CES_INVERT;
110339213Sgibbs
110439213Sgibbs	if (desc->flags2 & READ_ELEMENT_STATUS_SVALID) {
110539213Sgibbs
110639213Sgibbs		eaddr = scsi_2btoul(desc->ssea);
110739213Sgibbs
110839213Sgibbs		/* convert source address to logical format */
110939213Sgibbs		for (et = CHET_MT; et <= CHET_DT; et++) {
111039213Sgibbs			if ((softc->sc_firsts[et] <= eaddr)
111139213Sgibbs			    && ((softc->sc_firsts[et] + softc->sc_counts[et])
111239213Sgibbs				> eaddr)) {
1113249658Sken				ces->ces_source_addr =
111439213Sgibbs					eaddr - softc->sc_firsts[et];
111539213Sgibbs				ces->ces_source_type = et;
111639213Sgibbs				ces->ces_flags |= CES_SOURCE_VALID;
111739213Sgibbs				break;
111839213Sgibbs			}
111939213Sgibbs		}
112039213Sgibbs
112139213Sgibbs		if (!(ces->ces_flags & CES_SOURCE_VALID))
112239213Sgibbs			printf("ch: warning: could not map element source "
112358921Sken			       "address %ud to a valid element type\n",
112439213Sgibbs			       eaddr);
112539213Sgibbs	}
112639213Sgibbs
1127249658Sken	/*
1128249658Sken	 * pvoltag and avoltag are common between SCSI-2 and later versions
1129249658Sken	 */
113039213Sgibbs	if (flags & READ_ELEMENT_STATUS_PVOLTAG)
1131249658Sken		pvol_tag = &desc->voltag_devid.pvoltag;
113239213Sgibbs	if (flags & READ_ELEMENT_STATUS_AVOLTAG)
1133249658Sken		avol_tag = (flags & READ_ELEMENT_STATUS_PVOLTAG) ?
1134249658Sken		    &desc->voltag_devid.voltag[1] :&desc->voltag_devid.pvoltag;
1135249658Sken	/*
1136249658Sken	 * For SCSI-3 and later, element status can carry designator and
1137249658Sken	 * other information.
1138249658Sken	 */
1139249658Sken	if (scsi_version >= SCSI_REV_SPC) {
1140249658Sken		if ((flags & READ_ELEMENT_STATUS_PVOLTAG) ^
1141249658Sken		    (flags & READ_ELEMENT_STATUS_AVOLTAG))
1142249658Sken			devid = &desc->voltag_devid.pvol_and_devid.devid;
1143249658Sken		else if (!(flags & READ_ELEMENT_STATUS_PVOLTAG) &&
1144249658Sken			 !(flags & READ_ELEMENT_STATUS_AVOLTAG))
1145249658Sken			devid = &desc->voltag_devid.devid;
1146249658Sken		else /* Have both PVOLTAG and AVOLTAG */
1147249658Sken			devid = &desc->voltag_devid.vol_tags_and_devid.devid;
1148249658Sken	}
114939213Sgibbs
1150249658Sken	if (pvol_tag)
1151249658Sken		copy_voltag(&(ces->ces_pvoltag), pvol_tag);
1152249658Sken	if (avol_tag)
1153249658Sken		copy_voltag(&(ces->ces_pvoltag), avol_tag);
1154249658Sken	if (devid != NULL) {
1155249658Sken		if (devid->designator_length > 0) {
1156249658Sken			bcopy((void *)devid->designator,
1157249658Sken			      (void *)ces->ces_designator,
1158249658Sken			      devid->designator_length);
1159249658Sken			ces->ces_designator_length = devid->designator_length;
1160249658Sken			/*
1161249658Sken			 * Make sure we are always NUL terminated.  The
1162249701Sken			 * This won't matter for the binary code set,
1163249701Sken			 * since the user will only pay attention to the
1164249701Sken			 * length field.
1165249658Sken			 */
1166249701Sken			ces->ces_designator[devid->designator_length]= '\0';
1167249658Sken		}
1168249658Sken		if (devid->piv_assoc_designator_type &
1169249658Sken		    READ_ELEMENT_STATUS_PIV_SET) {
1170249658Sken			ces->ces_flags |= CES_PIV;
1171249658Sken			ces->ces_protocol_id =
1172249658Sken			    READ_ELEMENT_STATUS_PROTOCOL_ID(
1173249658Sken			    devid->prot_code_set);
1174249658Sken		}
1175249658Sken		ces->ces_code_set =
1176249658Sken		    READ_ELEMENT_STATUS_CODE_SET(devid->prot_code_set);
1177249658Sken		ces->ces_assoc = READ_ELEMENT_STATUS_ASSOCIATION(
1178249658Sken		    devid->piv_assoc_designator_type);
1179249658Sken		ces->ces_designator_type = READ_ELEMENT_STATUS_DESIGNATOR_TYPE(
1180249658Sken		    devid->piv_assoc_designator_type);
1181249658Sken	} else if (scsi_version > SCSI_REV_2) {
1182249658Sken		/* SCSI-SPC and No devid, no designator */
1183249658Sken		ces->ces_designator_length = 0;
1184249658Sken		ces->ces_designator[0] = '\0';
1185249658Sken		ces->ces_protocol_id = CES_PROTOCOL_ID_FCP_4;
118639213Sgibbs	}
118739213Sgibbs
1188249658Sken	if (scsi_version <= SCSI_REV_2) {
1189249658Sken		if (desc->dt_or_obsolete.scsi_2.dt_scsi_flags &
1190249658Sken		    READ_ELEMENT_STATUS_DT_IDVALID) {
1191249658Sken			ces->ces_flags |= CES_SCSIID_VALID;
1192249658Sken			ces->ces_scsi_id =
1193249658Sken			    desc->dt_or_obsolete.scsi_2.dt_scsi_addr;
1194249658Sken		}
1195249658Sken
1196249658Sken		if (desc->dt_or_obsolete.scsi_2.dt_scsi_addr &
1197249658Sken		    READ_ELEMENT_STATUS_DT_LUVALID) {
1198249658Sken			ces->ces_flags |= CES_LUN_VALID;
1199249658Sken			ces->ces_scsi_lun =
1200249658Sken			    desc->dt_or_obsolete.scsi_2.dt_scsi_flags &
1201249658Sken			    READ_ELEMENT_STATUS_DT_LUNMASK;
1202249658Sken		}
120339213Sgibbs	}
120439213Sgibbs}
120539213Sgibbs
120639213Sgibbsstatic int
1207249658Skenchgetelemstatus(struct cam_periph *periph, int scsi_version, u_long cmd,
120839213Sgibbs		struct changer_element_status_request *cesr)
120939213Sgibbs{
121039213Sgibbs	struct read_element_status_header *st_hdr;
121139213Sgibbs	struct read_element_status_page_header *pg_hdr;
121239213Sgibbs	struct read_element_status_descriptor *desc;
121339213Sgibbs	caddr_t data = NULL;
121439213Sgibbs	size_t size, desclen;
121539213Sgibbs	int avail, i, error = 0;
1216253274Sken	int curdata, dvcid, sense_flags;
1217253274Sken	int try_no_dvcid = 0;
121839213Sgibbs	struct changer_element_status *user_data = NULL;
121939213Sgibbs	struct ch_softc *softc;
122039213Sgibbs	union ccb *ccb;
122139213Sgibbs	int chet = cesr->cesr_element_type;
122239213Sgibbs	int want_voltags = (cesr->cesr_flags & CESR_VOLTAGS) ? 1 : 0;
122339213Sgibbs
122439213Sgibbs	softc = (struct ch_softc *)periph->softc;
122539213Sgibbs
122639213Sgibbs	/* perform argument checking */
122739213Sgibbs
122839213Sgibbs	/*
122939213Sgibbs	 * Perform a range check on the cesr_element_{base,count}
123039213Sgibbs	 * request argument fields.
123139213Sgibbs	 */
123239213Sgibbs	if ((softc->sc_counts[chet] - cesr->cesr_element_base) <= 0
123339213Sgibbs	    || (cesr->cesr_element_base + cesr->cesr_element_count)
123439213Sgibbs	        > softc->sc_counts[chet])
123539213Sgibbs		return (EINVAL);
123639213Sgibbs
123739213Sgibbs	/*
123839213Sgibbs	 * Request one descriptor for the given element type.  This
123939213Sgibbs	 * is used to determine the size of the descriptor so that
124039213Sgibbs	 * we can allocate enough storage for all of them.  We assume
124139213Sgibbs	 * that the first one can fit into 1k.
124239213Sgibbs	 */
1243168752Sscottl	cam_periph_unlock(periph);
1244111119Simp	data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK);
124539213Sgibbs
1246168752Sscottl	cam_periph_lock(periph);
1247198382Smav	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
124839213Sgibbs
1249253274Sken	sense_flags = SF_RETRY_UA;
1250253274Sken	if (softc->quirks & CH_Q_NO_DVCID) {
1251253274Sken		dvcid = 0;
1252253274Sken		curdata = 0;
1253253274Sken	} else {
1254253274Sken		dvcid = 1;
1255253274Sken		curdata = 1;
1256253274Sken		/*
1257253274Sken		 * Don't print anything for an Illegal Request, because
1258253274Sken		 * these flags can cause some changers to complain.  We'll
1259253274Sken		 * retry without them if we get an error.
1260253274Sken		 */
1261253274Sken		sense_flags |= SF_QUIET_IR;
1262253274Sken	}
1263253274Sken
1264253274Skenretry_einval:
1265253274Sken
126639213Sgibbs	scsi_read_element_status(&ccb->csio,
126739213Sgibbs				 /* retries */ 1,
126839213Sgibbs				 /* cbfcnp */ chdone,
126939213Sgibbs				 /* tag_action */ MSG_SIMPLE_Q_TAG,
127039213Sgibbs				 /* voltag */ want_voltags,
127139213Sgibbs				 /* sea */ softc->sc_firsts[chet],
1272253368Sken				 /* curdata */ curdata,
1273253274Sken				 /* dvcid */ dvcid,
127439213Sgibbs				 /* count */ 1,
127539213Sgibbs				 /* data_ptr */ data,
127639213Sgibbs				 /* dxfer_len */ 1024,
127739213Sgibbs				 /* sense_len */ SSD_FULL_SIZE,
127839213Sgibbs				 /* timeout */ CH_TIMEOUT_READ_ELEMENT_STATUS);
127939213Sgibbs
128074840Sken	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ CAM_RETRY_SELTO,
1281253274Sken				  /*sense_flags*/ sense_flags,
1282112006Sphk				  softc->device_stats);
128339213Sgibbs
1284253274Sken	/*
1285253274Sken	 * An Illegal Request sense key (only used if there is no asc/ascq)
1286253274Sken	 * or 0x24,0x00 for an ASC/ASCQ both map to EINVAL.  If dvcid or
1287253274Sken	 * curdata are set (we set both or neither), try turning them off
1288253274Sken	 * and see if the command is successful.
1289253274Sken	 */
1290253274Sken	if ((error == EINVAL)
1291253274Sken	 && (dvcid || curdata))  {
1292253274Sken		dvcid = 0;
1293253274Sken		curdata = 0;
1294253274Sken		error = 0;
1295253274Sken		/* At this point we want to report any Illegal Request */
1296253274Sken		sense_flags &= ~SF_QUIET_IR;
1297253274Sken		try_no_dvcid = 1;
1298253274Sken		goto retry_einval;
1299253274Sken	}
1300253274Sken
1301253274Sken	/*
1302253274Sken	 * In this case, we tried a read element status with dvcid and
1303253274Sken	 * curdata set, and it failed.  We retried without those bits, and
1304253274Sken	 * it succeeded.  Suggest to the user that he set a quirk, so we
1305253274Sken	 * don't go through the retry process the first time in the future.
1306253274Sken	 * This should only happen on changers that claim SCSI-3 or higher,
1307253274Sken	 * but don't support these bits.
1308253274Sken	 */
1309253274Sken	if ((try_no_dvcid != 0)
1310253274Sken	 && (error == 0))
1311253274Sken		softc->quirks |= CH_Q_NO_DVCID;
1312253274Sken
131339213Sgibbs	if (error)
131439213Sgibbs		goto done;
1315168752Sscottl	cam_periph_unlock(periph);
131639213Sgibbs
131739213Sgibbs	st_hdr = (struct read_element_status_header *)data;
131850270Sbde	pg_hdr = (struct read_element_status_page_header *)((uintptr_t)st_hdr +
131939213Sgibbs		  sizeof(struct read_element_status_header));
132039213Sgibbs	desclen = scsi_2btoul(pg_hdr->edl);
132139213Sgibbs
132239213Sgibbs	size = sizeof(struct read_element_status_header) +
132339213Sgibbs	       sizeof(struct read_element_status_page_header) +
132439213Sgibbs	       (desclen * cesr->cesr_element_count);
132539213Sgibbs	/*
132639213Sgibbs	 * Reallocate storage for descriptors and get them from the
132739213Sgibbs	 * device.
132839213Sgibbs	 */
132939213Sgibbs	free(data, M_DEVBUF);
1330111119Simp	data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK);
133139213Sgibbs
1332168752Sscottl	cam_periph_lock(periph);
133339213Sgibbs	scsi_read_element_status(&ccb->csio,
133439213Sgibbs				 /* retries */ 1,
133539213Sgibbs				 /* cbfcnp */ chdone,
133639213Sgibbs				 /* tag_action */ MSG_SIMPLE_Q_TAG,
133739213Sgibbs				 /* voltag */ want_voltags,
133839213Sgibbs				 /* sea */ softc->sc_firsts[chet]
133939213Sgibbs				 + cesr->cesr_element_base,
1340253368Sken				 /* curdata */ curdata,
1341253274Sken				 /* dvcid */ dvcid,
134239213Sgibbs				 /* count */ cesr->cesr_element_count,
134339213Sgibbs				 /* data_ptr */ data,
134439213Sgibbs				 /* dxfer_len */ size,
134539213Sgibbs				 /* sense_len */ SSD_FULL_SIZE,
134639213Sgibbs				 /* timeout */ CH_TIMEOUT_READ_ELEMENT_STATUS);
1347249658Sken
134874840Sken	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ CAM_RETRY_SELTO,
134974840Sken				  /*sense_flags*/ SF_RETRY_UA,
1350112006Sphk				  softc->device_stats);
135139213Sgibbs
135239213Sgibbs	if (error)
135339213Sgibbs		goto done;
1354168752Sscottl	cam_periph_unlock(periph);
135539213Sgibbs
135639213Sgibbs	/*
135739213Sgibbs	 * Fill in the user status array.
135839213Sgibbs	 */
135939213Sgibbs	st_hdr = (struct read_element_status_header *)data;
1360144132Sken	pg_hdr = (struct read_element_status_page_header *)((uintptr_t)st_hdr +
1361144132Sken		  sizeof(struct read_element_status_header));
136239213Sgibbs	avail = scsi_2btoul(st_hdr->count);
136339213Sgibbs
136439213Sgibbs	if (avail != cesr->cesr_element_count) {
1365164906Smjacob		xpt_print(periph->path,
1366164906Smjacob		    "warning, READ ELEMENT STATUS avail != count\n");
136739213Sgibbs	}
136839213Sgibbs
136939213Sgibbs	user_data = (struct changer_element_status *)
137039213Sgibbs		malloc(avail * sizeof(struct changer_element_status),
1371111119Simp		       M_DEVBUF, M_WAITOK | M_ZERO);
137239213Sgibbs
137350270Sbde	desc = (struct read_element_status_descriptor *)((uintptr_t)data +
137439213Sgibbs		sizeof(struct read_element_status_header) +
137539213Sgibbs		sizeof(struct read_element_status_page_header));
137639213Sgibbs	/*
137739213Sgibbs	 * Set up the individual element status structures
137839213Sgibbs	 */
137939213Sgibbs	for (i = 0; i < avail; ++i) {
1380249658Sken		struct changer_element_status *ces;
138139213Sgibbs
1382249658Sken		/*
1383249658Sken		 * In the changer_element_status structure, fields from
1384249658Sken		 * the beginning to the field of ces_scsi_lun are common
1385249658Sken		 * between SCSI-2 and SCSI-3, while all the rest are new
1386249658Sken		 * from SCSI-3. In order to maintain backward compatibility
1387249658Sken		 * of the chio command, the ces pointer, below, is computed
1388249658Sken		 * such that it lines up with the structure boundary
1389249658Sken		 * corresponding to the SCSI version.
1390249658Sken		 */
1391249658Sken		ces = cmd == OCHIOGSTATUS ?
1392249658Sken		    (struct changer_element_status *)
1393249658Sken		    ((unsigned char *)user_data + i *
1394249658Sken		     (offsetof(struct changer_element_status,ces_scsi_lun)+1)):
1395249658Sken		    &user_data[i];
139639213Sgibbs
1397249658Sken		copy_element_status(softc, pg_hdr->flags, desc,
1398249658Sken				    ces, scsi_version);
1399249658Sken
140050270Sbde		desc = (struct read_element_status_descriptor *)
1401249658Sken		       ((unsigned char *)desc + desclen);
140239213Sgibbs	}
140339213Sgibbs
140439213Sgibbs	/* Copy element status structures out to userspace. */
1405249658Sken	if (cmd == OCHIOGSTATUS)
1406249658Sken		error = copyout(user_data,
1407249658Sken				cesr->cesr_element_status,
1408249658Sken				avail* (offsetof(struct changer_element_status,
1409249658Sken				ces_scsi_lun) + 1));
1410249658Sken	else
1411249658Sken		error = copyout(user_data,
1412249658Sken				cesr->cesr_element_status,
1413249658Sken				avail * sizeof(struct changer_element_status));
1414249658Sken
1415168752Sscottl	cam_periph_lock(periph);
141639213Sgibbs
141739213Sgibbs done:
141839213Sgibbs	xpt_release_ccb(ccb);
141939213Sgibbs
142039213Sgibbs	if (data != NULL)
142139213Sgibbs		free(data, M_DEVBUF);
142239213Sgibbs	if (user_data != NULL)
142339213Sgibbs		free(user_data, M_DEVBUF);
142439213Sgibbs
142539213Sgibbs	return (error);
142639213Sgibbs}
142739213Sgibbs
142839213Sgibbsstatic int
142939213Sgibbschielem(struct cam_periph *periph,
143039213Sgibbs	unsigned int timeout)
143139213Sgibbs{
143239213Sgibbs	union ccb *ccb;
143339213Sgibbs	struct ch_softc *softc;
143439213Sgibbs	int error;
143539213Sgibbs
143639213Sgibbs	if (!timeout) {
143739213Sgibbs		timeout = CH_TIMEOUT_INITIALIZE_ELEMENT_STATUS;
143839213Sgibbs	} else {
143939213Sgibbs		timeout *= 1000;
144039213Sgibbs	}
144139213Sgibbs
144239213Sgibbs	error = 0;
144339213Sgibbs	softc = (struct ch_softc *)periph->softc;
144439213Sgibbs
1445198382Smav	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
144639213Sgibbs
144739213Sgibbs	scsi_initialize_element_status(&ccb->csio,
144839213Sgibbs				      /* retries */ 1,
144939213Sgibbs				      /* cbfcnp */ chdone,
145039213Sgibbs				      /* tag_action */ MSG_SIMPLE_Q_TAG,
145139213Sgibbs				      /* sense_len */ SSD_FULL_SIZE,
145239213Sgibbs				      /* timeout */ timeout);
145339213Sgibbs
145474840Sken	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ CAM_RETRY_SELTO,
145574840Sken				  /*sense_flags*/ SF_RETRY_UA,
1456112006Sphk				  softc->device_stats);
145739213Sgibbs
145839213Sgibbs	xpt_release_ccb(ccb);
145939213Sgibbs
146039213Sgibbs	return(error);
146139213Sgibbs}
146239213Sgibbs
146339213Sgibbsstatic int
146439213Sgibbschsetvoltag(struct cam_periph *periph,
146539213Sgibbs	    struct changer_set_voltag_request *csvr)
146639213Sgibbs{
146739213Sgibbs	union ccb *ccb;
146839213Sgibbs	struct ch_softc *softc;
146939213Sgibbs	u_int16_t ea;
147039213Sgibbs	u_int8_t sac;
147139213Sgibbs	struct scsi_send_volume_tag_parameters ssvtp;
147239213Sgibbs	int error;
147339213Sgibbs	int i;
147439213Sgibbs
147539213Sgibbs	error = 0;
147639213Sgibbs	softc = (struct ch_softc *)periph->softc;
147739213Sgibbs
147839213Sgibbs	bzero(&ssvtp, sizeof(ssvtp));
147939213Sgibbs	for (i=0; i<sizeof(ssvtp.vitf); i++) {
148039213Sgibbs		ssvtp.vitf[i] = ' ';
148139213Sgibbs	}
148239213Sgibbs
148339213Sgibbs	/*
148439213Sgibbs	 * Check arguments.
148539213Sgibbs	 */
148639213Sgibbs	if (csvr->csvr_type > CHET_DT)
148739213Sgibbs		return EINVAL;
148839213Sgibbs	if (csvr->csvr_addr > (softc->sc_counts[csvr->csvr_type] - 1))
148939213Sgibbs		return ENODEV;
149039213Sgibbs
149139213Sgibbs	ea = softc->sc_firsts[csvr->csvr_type] + csvr->csvr_addr;
149239213Sgibbs
149339213Sgibbs	if (csvr->csvr_flags & CSVR_ALTERNATE) {
149439213Sgibbs		switch (csvr->csvr_flags & CSVR_MODE_MASK) {
149539213Sgibbs		case CSVR_MODE_SET:
149639213Sgibbs			sac = SEND_VOLUME_TAG_ASSERT_ALTERNATE;
149739213Sgibbs			break;
149839213Sgibbs		case CSVR_MODE_REPLACE:
149939213Sgibbs			sac = SEND_VOLUME_TAG_REPLACE_ALTERNATE;
150039213Sgibbs			break;
150139213Sgibbs		case CSVR_MODE_CLEAR:
150239213Sgibbs			sac = SEND_VOLUME_TAG_UNDEFINED_ALTERNATE;
150339213Sgibbs			break;
150439213Sgibbs		default:
150539213Sgibbs			error = EINVAL;
150639213Sgibbs			goto out;
150739213Sgibbs		}
150839213Sgibbs	} else {
150939213Sgibbs		switch (csvr->csvr_flags & CSVR_MODE_MASK) {
151039213Sgibbs		case CSVR_MODE_SET:
151139213Sgibbs			sac = SEND_VOLUME_TAG_ASSERT_PRIMARY;
151239213Sgibbs			break;
151339213Sgibbs		case CSVR_MODE_REPLACE:
151439213Sgibbs			sac = SEND_VOLUME_TAG_REPLACE_PRIMARY;
151539213Sgibbs			break;
151639213Sgibbs		case CSVR_MODE_CLEAR:
151739213Sgibbs			sac = SEND_VOLUME_TAG_UNDEFINED_PRIMARY;
151839213Sgibbs			break;
151939213Sgibbs		default:
152039213Sgibbs			error = EINVAL;
152139213Sgibbs			goto out;
152239213Sgibbs		}
152339213Sgibbs	}
152439213Sgibbs
152539213Sgibbs	memcpy(ssvtp.vitf, csvr->csvr_voltag.cv_volid,
152639213Sgibbs	       min(strlen(csvr->csvr_voltag.cv_volid), sizeof(ssvtp.vitf)));
152739213Sgibbs	scsi_ulto2b(csvr->csvr_voltag.cv_serial, ssvtp.minvsn);
152839213Sgibbs
1529198382Smav	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
153039213Sgibbs
153139213Sgibbs	scsi_send_volume_tag(&ccb->csio,
153239213Sgibbs			     /* retries */ 1,
153339213Sgibbs			     /* cbfcnp */ chdone,
153439213Sgibbs			     /* tag_action */ MSG_SIMPLE_Q_TAG,
153539213Sgibbs			     /* element_address */ ea,
153639213Sgibbs			     /* send_action_code */ sac,
153739213Sgibbs			     /* parameters */ &ssvtp,
153839213Sgibbs			     /* sense_len */ SSD_FULL_SIZE,
153939213Sgibbs			     /* timeout */ CH_TIMEOUT_SEND_VOLTAG);
154039213Sgibbs
154174840Sken	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ CAM_RETRY_SELTO,
154274840Sken				  /*sense_flags*/ SF_RETRY_UA,
1543112006Sphk				  softc->device_stats);
154439213Sgibbs
154539213Sgibbs	xpt_release_ccb(ccb);
154639213Sgibbs
154739213Sgibbs out:
154839213Sgibbs	return error;
154939213Sgibbs}
155039213Sgibbs
155139213Sgibbsstatic int
155239213Sgibbschgetparams(struct cam_periph *periph)
155339213Sgibbs{
155439213Sgibbs	union ccb *ccb;
155539213Sgibbs	struct ch_softc *softc;
155639885Sken	void *mode_buffer;
155739885Sken	int mode_buffer_len;
155839885Sken	struct page_element_address_assignment *ea;
155939885Sken	struct page_device_capabilities *cap;
156039885Sken	int error, from, dbd;
156139213Sgibbs	u_int8_t *moves, *exchanges;
156239213Sgibbs
156339213Sgibbs	error = 0;
156439213Sgibbs
156539213Sgibbs	softc = (struct ch_softc *)periph->softc;
156639213Sgibbs
1567198382Smav	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
156839213Sgibbs
156939885Sken	/*
157039885Sken	 * The scsi_mode_sense_data structure is just a convenience
157139885Sken	 * structure that allows us to easily calculate the worst-case
157239885Sken	 * storage size of the mode sense buffer.
157339885Sken	 */
157439885Sken	mode_buffer_len = sizeof(struct scsi_mode_sense_data);
157539885Sken
1576169562Sscottl	mode_buffer = malloc(mode_buffer_len, M_SCSICH, M_NOWAIT);
157739885Sken
157839885Sken	if (mode_buffer == NULL) {
157939213Sgibbs		printf("chgetparams: couldn't malloc mode sense data\n");
158039213Sgibbs		return(ENOSPC);
158139213Sgibbs	}
158239213Sgibbs
158339885Sken	bzero(mode_buffer, mode_buffer_len);
158439213Sgibbs
158539885Sken	if (softc->quirks & CH_Q_NO_DBD)
158639885Sken		dbd = FALSE;
158739885Sken	else
158839885Sken		dbd = TRUE;
158939885Sken
159039213Sgibbs	/*
159139213Sgibbs	 * Get the element address assignment page.
159239213Sgibbs	 */
159339213Sgibbs	scsi_mode_sense(&ccb->csio,
159439213Sgibbs			/* retries */ 1,
159539213Sgibbs			/* cbfcnp */ chdone,
159639213Sgibbs			/* tag_action */ MSG_SIMPLE_Q_TAG,
159739885Sken			/* dbd */ dbd,
159839213Sgibbs			/* page_code */ SMS_PAGE_CTRL_CURRENT,
159939213Sgibbs			/* page */ CH_ELEMENT_ADDR_ASSIGN_PAGE,
160039885Sken			/* param_buf */ (u_int8_t *)mode_buffer,
160139885Sken			/* param_len */ mode_buffer_len,
160239213Sgibbs			/* sense_len */ SSD_FULL_SIZE,
160339213Sgibbs			/* timeout */ CH_TIMEOUT_MODE_SENSE);
160439213Sgibbs
160574840Sken	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ CAM_RETRY_SELTO,
160674840Sken				  /* sense_flags */ SF_RETRY_UA|SF_NO_PRINT,
1607112006Sphk				  softc->device_stats);
160839213Sgibbs
160939213Sgibbs	if (error) {
161039885Sken		if (dbd) {
161139885Sken			struct scsi_mode_sense_6 *sms;
161239885Sken
161339885Sken			sms = (struct scsi_mode_sense_6 *)
161439885Sken				ccb->csio.cdb_io.cdb_bytes;
161539885Sken
161639885Sken			sms->byte2 &= ~SMS_DBD;
161774840Sken			error = cam_periph_runccb(ccb, cherror,
161874840Sken						  /*cam_flags*/ CAM_RETRY_SELTO,
161974840Sken				  		  /*sense_flags*/ SF_RETRY_UA,
1620112006Sphk						  softc->device_stats);
162139885Sken		} else {
162239885Sken			/*
162339885Sken			 * Since we disabled sense printing above, print
162439885Sken			 * out the sense here since we got an error.
162539885Sken			 */
162639885Sken			scsi_sense_print(&ccb->csio);
162739885Sken		}
162839885Sken
162939885Sken		if (error) {
1630164906Smjacob			xpt_print(periph->path,
1631164906Smjacob			    "chgetparams: error getting element "
1632164906Smjacob			    "address page\n");
163339885Sken			xpt_release_ccb(ccb);
1634169562Sscottl			free(mode_buffer, M_SCSICH);
163539885Sken			return(error);
163639885Sken		}
163739213Sgibbs	}
163839213Sgibbs
163939885Sken	ea = (struct page_element_address_assignment *)
164039885Sken		find_mode_page_6((struct scsi_mode_header_6 *)mode_buffer);
164139213Sgibbs
164239885Sken	softc->sc_firsts[CHET_MT] = scsi_2btoul(ea->mtea);
164339885Sken	softc->sc_counts[CHET_MT] = scsi_2btoul(ea->nmte);
164439885Sken	softc->sc_firsts[CHET_ST] = scsi_2btoul(ea->fsea);
164539885Sken	softc->sc_counts[CHET_ST] = scsi_2btoul(ea->nse);
164639885Sken	softc->sc_firsts[CHET_IE] = scsi_2btoul(ea->fieea);
164739885Sken	softc->sc_counts[CHET_IE] = scsi_2btoul(ea->niee);
164839885Sken	softc->sc_firsts[CHET_DT] = scsi_2btoul(ea->fdtea);
164939885Sken	softc->sc_counts[CHET_DT] = scsi_2btoul(ea->ndte);
165039213Sgibbs
165139885Sken	bzero(mode_buffer, mode_buffer_len);
165239885Sken
165339213Sgibbs	/*
165439213Sgibbs	 * Now get the device capabilities page.
165539213Sgibbs	 */
165639213Sgibbs	scsi_mode_sense(&ccb->csio,
165739213Sgibbs			/* retries */ 1,
165839213Sgibbs			/* cbfcnp */ chdone,
165939213Sgibbs			/* tag_action */ MSG_SIMPLE_Q_TAG,
166039885Sken			/* dbd */ dbd,
166139213Sgibbs			/* page_code */ SMS_PAGE_CTRL_CURRENT,
166239213Sgibbs			/* page */ CH_DEVICE_CAP_PAGE,
166339885Sken			/* param_buf */ (u_int8_t *)mode_buffer,
166439885Sken			/* param_len */ mode_buffer_len,
166539213Sgibbs			/* sense_len */ SSD_FULL_SIZE,
166639213Sgibbs			/* timeout */ CH_TIMEOUT_MODE_SENSE);
166739213Sgibbs
166874840Sken	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ CAM_RETRY_SELTO,
166974840Sken				  /* sense_flags */ SF_RETRY_UA | SF_NO_PRINT,
1670112006Sphk				  softc->device_stats);
167139213Sgibbs
167239885Sken	if (error) {
167339885Sken		if (dbd) {
167439885Sken			struct scsi_mode_sense_6 *sms;
167539213Sgibbs
167639885Sken			sms = (struct scsi_mode_sense_6 *)
167739885Sken				ccb->csio.cdb_io.cdb_bytes;
167839885Sken
167939885Sken			sms->byte2 &= ~SMS_DBD;
168074840Sken			error = cam_periph_runccb(ccb, cherror,
168174840Sken						  /*cam_flags*/ CAM_RETRY_SELTO,
168274840Sken				  		  /*sense_flags*/ SF_RETRY_UA,
1683112006Sphk						  softc->device_stats);
168439885Sken		} else {
168539885Sken			/*
168639885Sken			 * Since we disabled sense printing above, print
168739885Sken			 * out the sense here since we got an error.
168839885Sken			 */
168939885Sken			scsi_sense_print(&ccb->csio);
169039885Sken		}
169139885Sken
169239885Sken		if (error) {
1693164906Smjacob			xpt_print(periph->path,
1694164906Smjacob			    "chgetparams: error getting device "
1695164906Smjacob			    "capabilities page\n");
169639885Sken			xpt_release_ccb(ccb);
1697169562Sscottl			free(mode_buffer, M_SCSICH);
169839885Sken			return(error);
169939885Sken		}
170039213Sgibbs	}
170139213Sgibbs
170239885Sken	xpt_release_ccb(ccb);
170339213Sgibbs
170439885Sken	cap = (struct page_device_capabilities *)
170539885Sken		find_mode_page_6((struct scsi_mode_header_6 *)mode_buffer);
170639885Sken
170739213Sgibbs	bzero(softc->sc_movemask, sizeof(softc->sc_movemask));
170839213Sgibbs	bzero(softc->sc_exchangemask, sizeof(softc->sc_exchangemask));
1709115464Sphk	moves = cap->move_from;
1710115464Sphk	exchanges = cap->exchange_with;
1711115464Sphk	for (from = CHET_MT; from <= CHET_MAX; ++from) {
171239213Sgibbs		softc->sc_movemask[from] = moves[from];
171339213Sgibbs		softc->sc_exchangemask[from] = exchanges[from];
171439213Sgibbs	}
171539213Sgibbs
1716169562Sscottl	free(mode_buffer, M_SCSICH);
171739885Sken
171839213Sgibbs	return(error);
171939213Sgibbs}
172039213Sgibbs
1721249658Skenstatic int
1722249658Skenchscsiversion(struct cam_periph *periph)
1723249658Sken{
1724249658Sken	struct scsi_inquiry_data *inq_data;
1725249658Sken	struct ccb_getdev *cgd;
1726249658Sken	int dev_scsi_version;
1727249658Sken	struct cam_sim *sim;
1728249658Sken
1729249658Sken	sim = xpt_path_sim(periph->path);
1730249658Sken	mtx_assert(sim->mtx, MA_OWNED);
1731249658Sken	if ((cgd = (struct ccb_getdev *)xpt_alloc_ccb_nowait()) == NULL)
1732249658Sken		return (-1);
1733249658Sken	/*
1734249658Sken	 * Get the device information.
1735249658Sken	 */
1736249658Sken	xpt_setup_ccb(&cgd->ccb_h,
1737249658Sken		      periph->path,
1738249658Sken		      CAM_PRIORITY_NORMAL);
1739249658Sken	cgd->ccb_h.func_code = XPT_GDEV_TYPE;
1740249658Sken	xpt_action((union ccb *)cgd);
1741249658Sken
1742249658Sken	if (cgd->ccb_h.status != CAM_REQ_CMP) {
1743249658Sken		xpt_free_ccb((union ccb *)cgd);
1744249658Sken		return -1;
1745249658Sken	}
1746249658Sken
1747249658Sken	inq_data = &cgd->inq_data;
1748249658Sken	dev_scsi_version = inq_data->version;
1749249658Sken	xpt_free_ccb((union ccb *)cgd);
1750249658Sken
1751249658Sken	return dev_scsi_version;
1752249658Sken}
1753249658Sken
175439213Sgibbsvoid
175539213Sgibbsscsi_move_medium(struct ccb_scsiio *csio, u_int32_t retries,
175639213Sgibbs		 void (*cbfcnp)(struct cam_periph *, union ccb *),
175739213Sgibbs		 u_int8_t tag_action, u_int32_t tea, u_int32_t src,
175839213Sgibbs		 u_int32_t dst, int invert, u_int8_t sense_len,
175939213Sgibbs		 u_int32_t timeout)
176039213Sgibbs{
176139213Sgibbs	struct scsi_move_medium *scsi_cmd;
176239213Sgibbs
176339213Sgibbs	scsi_cmd = (struct scsi_move_medium *)&csio->cdb_io.cdb_bytes;
176439213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
176539213Sgibbs
176639213Sgibbs	scsi_cmd->opcode = MOVE_MEDIUM;
176739213Sgibbs
176839213Sgibbs	scsi_ulto2b(tea, scsi_cmd->tea);
176939213Sgibbs	scsi_ulto2b(src, scsi_cmd->src);
177039213Sgibbs	scsi_ulto2b(dst, scsi_cmd->dst);
177139213Sgibbs
177239213Sgibbs	if (invert)
177339213Sgibbs		scsi_cmd->invert |= MOVE_MEDIUM_INVERT;
177439213Sgibbs
177539213Sgibbs	cam_fill_csio(csio,
177639213Sgibbs		      retries,
177739213Sgibbs		      cbfcnp,
177839213Sgibbs		      /*flags*/ CAM_DIR_NONE,
177939213Sgibbs		      tag_action,
178039213Sgibbs		      /*data_ptr*/ NULL,
178139213Sgibbs		      /*dxfer_len*/ 0,
178239213Sgibbs		      sense_len,
178339213Sgibbs		      sizeof(*scsi_cmd),
178439213Sgibbs		      timeout);
178539213Sgibbs}
178639213Sgibbs
178739213Sgibbsvoid
178839213Sgibbsscsi_exchange_medium(struct ccb_scsiio *csio, u_int32_t retries,
178939213Sgibbs		     void (*cbfcnp)(struct cam_periph *, union ccb *),
179039213Sgibbs		     u_int8_t tag_action, u_int32_t tea, u_int32_t src,
179139213Sgibbs		     u_int32_t dst1, u_int32_t dst2, int invert1,
179239213Sgibbs		     int invert2, u_int8_t sense_len, u_int32_t timeout)
179339213Sgibbs{
179439213Sgibbs	struct scsi_exchange_medium *scsi_cmd;
179539213Sgibbs
179639213Sgibbs	scsi_cmd = (struct scsi_exchange_medium *)&csio->cdb_io.cdb_bytes;
179739213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
179839213Sgibbs
179939213Sgibbs	scsi_cmd->opcode = EXCHANGE_MEDIUM;
180039213Sgibbs
180139213Sgibbs	scsi_ulto2b(tea, scsi_cmd->tea);
180239213Sgibbs	scsi_ulto2b(src, scsi_cmd->src);
180339213Sgibbs	scsi_ulto2b(dst1, scsi_cmd->fdst);
180439213Sgibbs	scsi_ulto2b(dst2, scsi_cmd->sdst);
180539213Sgibbs
180639213Sgibbs	if (invert1)
180739213Sgibbs		scsi_cmd->invert |= EXCHANGE_MEDIUM_INV1;
180839213Sgibbs
180939213Sgibbs	if (invert2)
181039213Sgibbs		scsi_cmd->invert |= EXCHANGE_MEDIUM_INV2;
181139213Sgibbs
181239213Sgibbs	cam_fill_csio(csio,
181339213Sgibbs		      retries,
181439213Sgibbs		      cbfcnp,
181539213Sgibbs		      /*flags*/ CAM_DIR_NONE,
181639213Sgibbs		      tag_action,
181739213Sgibbs		      /*data_ptr*/ NULL,
181839213Sgibbs		      /*dxfer_len*/ 0,
181939213Sgibbs		      sense_len,
182039213Sgibbs		      sizeof(*scsi_cmd),
182139213Sgibbs		      timeout);
182239213Sgibbs}
182339213Sgibbs
182439213Sgibbsvoid
182539213Sgibbsscsi_position_to_element(struct ccb_scsiio *csio, u_int32_t retries,
182639213Sgibbs			 void (*cbfcnp)(struct cam_periph *, union ccb *),
182739213Sgibbs			 u_int8_t tag_action, u_int32_t tea, u_int32_t dst,
182839213Sgibbs			 int invert, u_int8_t sense_len, u_int32_t timeout)
182939213Sgibbs{
183039213Sgibbs	struct scsi_position_to_element *scsi_cmd;
183139213Sgibbs
183239213Sgibbs	scsi_cmd = (struct scsi_position_to_element *)&csio->cdb_io.cdb_bytes;
183339213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
183439213Sgibbs
183539213Sgibbs	scsi_cmd->opcode = POSITION_TO_ELEMENT;
183639213Sgibbs
183739213Sgibbs	scsi_ulto2b(tea, scsi_cmd->tea);
183839213Sgibbs	scsi_ulto2b(dst, scsi_cmd->dst);
183939213Sgibbs
184039213Sgibbs	if (invert)
184139213Sgibbs		scsi_cmd->invert |= POSITION_TO_ELEMENT_INVERT;
184239213Sgibbs
184339213Sgibbs	cam_fill_csio(csio,
184439213Sgibbs		      retries,
184539213Sgibbs		      cbfcnp,
184639213Sgibbs		      /*flags*/ CAM_DIR_NONE,
184739213Sgibbs		      tag_action,
184839213Sgibbs		      /*data_ptr*/ NULL,
184939213Sgibbs		      /*dxfer_len*/ 0,
185039213Sgibbs		      sense_len,
185139213Sgibbs		      sizeof(*scsi_cmd),
185239213Sgibbs		      timeout);
185339213Sgibbs}
185439213Sgibbs
185539213Sgibbsvoid
185639213Sgibbsscsi_read_element_status(struct ccb_scsiio *csio, u_int32_t retries,
185739213Sgibbs			 void (*cbfcnp)(struct cam_periph *, union ccb *),
185839213Sgibbs			 u_int8_t tag_action, int voltag, u_int32_t sea,
1859249658Sken			 int curdata, int dvcid,
186039213Sgibbs			 u_int32_t count, u_int8_t *data_ptr,
186139213Sgibbs			 u_int32_t dxfer_len, u_int8_t sense_len,
186239213Sgibbs			 u_int32_t timeout)
186339213Sgibbs{
186439213Sgibbs	struct scsi_read_element_status *scsi_cmd;
186539213Sgibbs
186639213Sgibbs	scsi_cmd = (struct scsi_read_element_status *)&csio->cdb_io.cdb_bytes;
186739213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
186839213Sgibbs
186939213Sgibbs	scsi_cmd->opcode = READ_ELEMENT_STATUS;
187039213Sgibbs
187139213Sgibbs	scsi_ulto2b(sea, scsi_cmd->sea);
187239213Sgibbs	scsi_ulto2b(count, scsi_cmd->count);
187339213Sgibbs	scsi_ulto3b(dxfer_len, scsi_cmd->len);
1874249658Sken	if (dvcid)
1875249658Sken		scsi_cmd->flags |= READ_ELEMENT_STATUS_DVCID;
1876249658Sken	if (curdata)
1877249658Sken		scsi_cmd->flags |= READ_ELEMENT_STATUS_CURDATA;
187839213Sgibbs
187939213Sgibbs	if (voltag)
188039213Sgibbs		scsi_cmd->byte2 |= READ_ELEMENT_STATUS_VOLTAG;
188139213Sgibbs
188239213Sgibbs	cam_fill_csio(csio,
188339213Sgibbs		      retries,
188439213Sgibbs		      cbfcnp,
188539213Sgibbs		      /*flags*/ CAM_DIR_IN,
188639213Sgibbs		      tag_action,
188739213Sgibbs		      data_ptr,
188839213Sgibbs		      dxfer_len,
188939213Sgibbs		      sense_len,
189039213Sgibbs		      sizeof(*scsi_cmd),
189139213Sgibbs		      timeout);
189239213Sgibbs}
189339213Sgibbs
189439213Sgibbsvoid
189539213Sgibbsscsi_initialize_element_status(struct ccb_scsiio *csio, u_int32_t retries,
189639213Sgibbs			       void (*cbfcnp)(struct cam_periph *, union ccb *),
189739213Sgibbs			       u_int8_t tag_action, u_int8_t sense_len,
189839213Sgibbs			       u_int32_t timeout)
189939213Sgibbs{
190039213Sgibbs	struct scsi_initialize_element_status *scsi_cmd;
190139213Sgibbs
190239213Sgibbs	scsi_cmd = (struct scsi_initialize_element_status *)
190339213Sgibbs		    &csio->cdb_io.cdb_bytes;
190439213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
190539213Sgibbs
190639213Sgibbs	scsi_cmd->opcode = INITIALIZE_ELEMENT_STATUS;
190739213Sgibbs
190839213Sgibbs	cam_fill_csio(csio,
190939213Sgibbs		      retries,
191039213Sgibbs		      cbfcnp,
191139213Sgibbs		      /*flags*/ CAM_DIR_NONE,
191239213Sgibbs		      tag_action,
191339213Sgibbs		      /* data_ptr */ NULL,
191439213Sgibbs		      /* dxfer_len */ 0,
191539213Sgibbs		      sense_len,
191639213Sgibbs		      sizeof(*scsi_cmd),
191739213Sgibbs		      timeout);
191839213Sgibbs}
191939213Sgibbs
192039213Sgibbsvoid
192139213Sgibbsscsi_send_volume_tag(struct ccb_scsiio *csio, u_int32_t retries,
192239213Sgibbs		     void (*cbfcnp)(struct cam_periph *, union ccb *),
192339213Sgibbs		     u_int8_t tag_action,
192439213Sgibbs		     u_int16_t element_address,
192539213Sgibbs		     u_int8_t send_action_code,
192639213Sgibbs		     struct scsi_send_volume_tag_parameters *parameters,
192739213Sgibbs		     u_int8_t sense_len, u_int32_t timeout)
192839213Sgibbs{
192939213Sgibbs	struct scsi_send_volume_tag *scsi_cmd;
193039213Sgibbs
193139213Sgibbs	scsi_cmd = (struct scsi_send_volume_tag *) &csio->cdb_io.cdb_bytes;
193239213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
193339213Sgibbs
193439213Sgibbs	scsi_cmd->opcode = SEND_VOLUME_TAG;
193539213Sgibbs	scsi_ulto2b(element_address, scsi_cmd->ea);
193639213Sgibbs	scsi_cmd->sac = send_action_code;
193739213Sgibbs	scsi_ulto2b(sizeof(*parameters), scsi_cmd->pll);
193839213Sgibbs
193939213Sgibbs	cam_fill_csio(csio,
194039213Sgibbs		      retries,
194139213Sgibbs		      cbfcnp,
194239213Sgibbs		      /*flags*/ CAM_DIR_OUT,
194339213Sgibbs		      tag_action,
194439213Sgibbs		      /* data_ptr */ (u_int8_t *) parameters,
194539213Sgibbs		      sizeof(*parameters),
194639213Sgibbs		      sense_len,
194739213Sgibbs		      sizeof(*scsi_cmd),
194839213Sgibbs		      timeout);
194939213Sgibbs}
1950