xref: /freebsd/sys/cam/scsi/scsi_ch.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
1 /*
2  * Copyright (c) 1997 Justin T. Gibbs.
3  * Copyright (c) 1997, 1998 Kenneth D. Merry.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer,
11  *    without modification, immediately at the beginning of the file.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *      $Id: scsi_ch.c,v 1.1 1998/05/17 13:08:49 hans Exp hans $
28  */
29 /*
30  * Derived from the NetBSD SCSI changer driver.
31  *
32  *	$NetBSD: ch.c,v 1.32 1998/01/12 09:49:12 thorpej Exp $
33  *
34  */
35 /*
36  * Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej@and.com>
37  * All rights reserved.
38  *
39  * Partially based on an autochanger driver written by Stefan Grefen
40  * and on an autochanger driver written by the Systems Programming Group
41  * at the University of Utah Computer Science Department.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgements:
53  *	This product includes software developed by Jason R. Thorpe
54  *	for And Communications, http://www.and.com/
55  * 4. The name of the author may not be used to endorse or promote products
56  *    derived from this software without specific prior written permission.
57  *
58  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
59  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
60  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
61  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
62  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
63  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
64  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
65  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
66  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
67  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68  * SUCH DAMAGE.
69  */
70 
71 #include <sys/param.h>
72 #include <sys/queue.h>
73 #include <sys/systm.h>
74 #include <sys/kernel.h>
75 #include <sys/types.h>
76 #include <sys/dkbad.h>
77 #include <sys/malloc.h>
78 #include <sys/fcntl.h>
79 #include <sys/stat.h>
80 #include <sys/conf.h>
81 #include <sys/buf.h>
82 #include <sys/chio.h>
83 #include <sys/errno.h>
84 #include <sys/devicestat.h>
85 
86 #include <cam/cam.h>
87 #include <cam/cam_ccb.h>
88 #include <cam/cam_extend.h>
89 #include <cam/cam_periph.h>
90 #include <cam/cam_xpt_periph.h>
91 #include <cam/cam_queue.h>
92 #include <cam/cam_debug.h>
93 
94 #include <cam/scsi/scsi_all.h>
95 #include <cam/scsi/scsi_message.h>
96 #include <cam/scsi/scsi_ch.h>
97 
98 /*
99  * Timeout definitions for various changer related commands.  They may
100  * be too short for some devices (especially the timeout for INITIALIZE
101  * ELEMENT STATUS).
102  */
103 
104 const u_int32_t	CH_TIMEOUT_MODE_SENSE                = 6000;
105 const u_int32_t	CH_TIMEOUT_MOVE_MEDIUM               = 100000;
106 const u_int32_t	CH_TIMEOUT_EXCHANGE_MEDIUM           = 100000;
107 const u_int32_t	CH_TIMEOUT_POSITION_TO_ELEMENT       = 100000;
108 const u_int32_t	CH_TIMEOUT_READ_ELEMENT_STATUS       = 10000;
109 const u_int32_t	CH_TIMEOUT_SEND_VOLTAG		     = 10000;
110 const u_int32_t	CH_TIMEOUT_INITIALIZE_ELEMENT_STATUS = 500000;
111 
112 typedef enum {
113 	CH_FLAG_INVALID		= 0x001,
114 	CH_FLAG_OPEN		= 0x002
115 } ch_flags;
116 
117 typedef enum {
118 	CH_STATE_PROBE,
119 	CH_STATE_NORMAL
120 } ch_state;
121 
122 typedef enum {
123 	CH_CCB_PROBE,
124 	CH_CCB_WAITING
125 } ch_ccb_types;
126 
127 #define ccb_state	ppriv_field0
128 #define ccb_bp		ppriv_ptr1
129 
130 struct scsi_mode_sense_data {
131 	struct scsi_mode_header_6 header;
132 	union {
133 		struct page_element_address_assignment ea;
134 		struct page_transport_geometry_parameters tg;
135 		struct page_device_capabilities cap;
136 	} pages;
137 };
138 
139 struct ch_softc {
140 	ch_flags	flags;
141 	ch_state	state;
142 	union ccb	saved_ccb;
143 	struct devstat	device_stats;
144 
145 	int		sc_picker;	/* current picker */
146 
147 	/*
148 	 * The following information is obtained from the
149 	 * element address assignment page.
150 	 */
151 	int		sc_firsts[4];	/* firsts, indexed by CHET_* */
152 	int		sc_counts[4];	/* counts, indexed by CHET_* */
153 
154 	/*
155 	 * The following mask defines the legal combinations
156 	 * of elements for the MOVE MEDIUM command.
157 	 */
158 	u_int8_t	sc_movemask[4];
159 
160 	/*
161 	 * As above, but for EXCHANGE MEDIUM.
162 	 */
163 	u_int8_t	sc_exchangemask[4];
164 
165 	/*
166 	 * Quirks; see below.  XXX KDM not implemented yet
167 	 */
168 	int		sc_settledelay;	/* delay for settle */
169 };
170 
171 #define CHUNIT(x)       (minor((x)))
172 #define CH_CDEV_MAJOR	17
173 
174 static	d_open_t	chopen;
175 static	d_close_t	chclose;
176 static	d_ioctl_t	chioctl;
177 static	periph_init_t	chinit;
178 static  periph_ctor_t	chregister;
179 static  periph_dtor_t   chcleanup;
180 static  periph_start_t  chstart;
181 static	void		chasync(void *callback_arg, u_int32_t code,
182 				struct cam_path *path, void *arg);
183 static	void		chdone(struct cam_periph *periph,
184 			       union ccb *done_ccb);
185 static	int		cherror(union ccb *ccb, u_int32_t cam_flags,
186 				u_int32_t sense_flags);
187 static	int		chmove(struct cam_periph *periph,
188 			       struct changer_move *cm);
189 static	int		chexchange(struct cam_periph *periph,
190 				   struct changer_exchange *ce);
191 static	int		chposition(struct cam_periph *periph,
192 				   struct changer_position *cp);
193 static	int		chgetelemstatus(struct cam_periph *periph,
194 				struct changer_element_status_request *csr);
195 static	int		chsetvoltag(struct cam_periph *periph,
196 				    struct changer_set_voltag_request *csvr);
197 static	int		chielem(struct cam_periph *periph,
198 				unsigned int timeout);
199 static	int		chgetparams(struct cam_periph *periph);
200 
201 static struct periph_driver chdriver =
202 {
203 	chinit, "ch",
204 	TAILQ_HEAD_INITIALIZER(chdriver.units), /* generation */ 0
205 };
206 
207 DATA_SET(periphdriver_set, chdriver);
208 
209 static struct cdevsw ch_cdevsw =
210 {
211 	/*d_open*/	chopen,
212 	/*d_close*/	chclose,
213 	/*d_read*/	noread,
214 	/*d_write*/	nowrite,
215 	/*d_ioctl*/	chioctl,
216 	/*d_stop*/	nostop,
217 	/*d_reset*/	noreset,
218 	/*d_devtotty*/	nodevtotty,
219 	/*d_poll*/	seltrue,
220 	/*d_mmap*/	nommap,
221 	/*d_strategy*/	nostrategy,
222 	/*d_name*/	"ch",
223 	/*d_spare*/	NULL,
224 	/*d_maj*/	-1,
225 	/*d_dump*/	nodump,
226 	/*d_psize*/	nopsize,
227 	/*d_flags*/	0,
228 	/*d_maxio*/	0,
229 	/*b_maj*/	-1
230 };
231 
232 static struct extend_array *chperiphs;
233 
234 void
235 chinit(void)
236 {
237 	cam_status status;
238 	struct cam_path *path;
239 
240 	/*
241 	 * Create our extend array for storing the devices we attach to.
242 	 */
243 	chperiphs = cam_extend_new();
244 	if (chperiphs == NULL) {
245 		printf("ch: Failed to alloc extend array!\n");
246 		return;
247 	}
248 
249 	/*
250 	 * Install a global async callback.  This callback will
251 	 * receive async callbacks like "new device found".
252 	 */
253 	status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
254 				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
255 
256 	if (status == CAM_REQ_CMP) {
257 		struct ccb_setasync csa;
258 
259                 xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
260                 csa.ccb_h.func_code = XPT_SASYNC_CB;
261                 csa.event_enable = AC_FOUND_DEVICE;
262                 csa.callback = chasync;
263                 csa.callback_arg = NULL;
264                 xpt_action((union ccb *)&csa);
265 		status = csa.ccb_h.status;
266                 xpt_free_path(path);
267         }
268 
269 	if (status != CAM_REQ_CMP) {
270 		printf("ch: Failed to attach master async callback "
271 		       "due to status 0x%x!\n", status);
272 	} else {
273 		dev_t dev;
274 
275 		/* If we were successfull, register our devsw */
276 		dev = makedev(CH_CDEV_MAJOR, 0);
277 		cdevsw_add(&dev, &ch_cdevsw, NULL);
278 	}
279 }
280 
281 static void
282 chcleanup(struct cam_periph *periph)
283 {
284 
285         cam_extend_release(chperiphs, periph->unit_number);
286         xpt_print_path(periph->path);
287         printf("removing device entry\n");
288         free(periph->softc, M_DEVBUF);
289 }
290 
291 static void
292 chasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
293 {
294 	struct cam_periph *periph;
295 
296 	periph = (struct cam_periph *)callback_arg;
297 
298 	switch(code) {
299 	case AC_FOUND_DEVICE:
300 	{
301 		struct ccb_getdev *cgd;
302 		cam_status status;
303 
304 		cgd = (struct ccb_getdev *)arg;
305 
306 		if (cgd->pd_type != T_CHANGER)
307 			break;
308 
309 		/*
310 		 * Allocate a peripheral instance for
311 		 * this device and start the probe
312 		 * process.
313 		 */
314 		status = cam_periph_alloc(chregister, chcleanup, chstart,
315 					  "ch", CAM_PERIPH_BIO, cgd->ccb_h.path,
316 					  chasync, AC_FOUND_DEVICE, cgd);
317 
318 		if (status != CAM_REQ_CMP
319 		 && status != CAM_REQ_INPROG)
320 			printf("chasync: Unable to probe new device "
321 			       "due to status 0x%x\n", status);
322 
323 		break;
324 
325 	}
326 	case AC_LOST_DEVICE:
327 	{
328 		int s;
329 		struct ch_softc *softc;
330 		struct ccb_setasync csa;
331 
332 		softc = (struct ch_softc *)periph->softc;
333 
334 		/*
335 		 * Insure that no other async callbacks that
336 		 * might affect this peripheral can come through.
337 		 */
338 		s = splcam();
339 
340 		/*
341 		 * De-register any async callbacks.
342 		 */
343 		xpt_setup_ccb(&csa.ccb_h, periph->path,
344 			      /* priority */ 5);
345 		csa.ccb_h.func_code = XPT_SASYNC_CB;
346 		csa.event_enable = 0;
347 		csa.callback = chasync;
348 		csa.callback_arg = periph;
349 		xpt_action((union ccb *)&csa);
350 
351 		softc->flags |= CH_FLAG_INVALID;
352 
353 		devstat_remove_entry(&softc->device_stats);
354 
355 		xpt_print_path(periph->path);
356 		printf("lost device\n");
357 
358 		splx(s);
359 
360 		cam_periph_invalidate(periph);
361 		break;
362 	}
363 	case AC_TRANSFER_NEG:
364 	case AC_SENT_BDR:
365 	case AC_SCSI_AEN:
366 	case AC_UNSOL_RESEL:
367 	case AC_BUS_RESET:
368 	default:
369 		break;
370 	}
371 }
372 
373 static cam_status
374 chregister(struct cam_periph *periph, void *arg)
375 {
376 	int s;
377 	struct ch_softc *softc;
378 	struct ccb_setasync csa;
379 	struct ccb_getdev *cgd;
380 
381 	cgd = (struct ccb_getdev *)arg;
382 	if (periph == NULL) {
383 		printf("chregister: periph was NULL!!\n");
384 		return(CAM_REQ_CMP_ERR);
385 	}
386 
387 	if (cgd == NULL) {
388 		printf("chregister: no getdev CCB, can't register device\n");
389 		return(CAM_REQ_CMP_ERR);
390 	}
391 
392 	softc = (struct ch_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
393 
394 	if (softc == NULL) {
395 		printf("chregister: Unable to probe new device. "
396 		       "Unable to allocate softc\n");
397 		return(CAM_REQ_CMP_ERR);
398 	}
399 
400 	bzero(softc, sizeof(*softc));
401 	softc->state = CH_STATE_PROBE;
402 	periph->softc = softc;
403 	cam_extend_set(chperiphs, periph->unit_number, periph);
404 
405 	/*
406 	 * Changers don't have a blocksize, and obviously don't support
407 	 * tagged queueing.
408 	 */
409 	devstat_add_entry(&softc->device_stats, "ch",
410 			  periph->unit_number, 0,
411 			  DEVSTAT_NO_BLOCKSIZE | DEVSTAT_NO_ORDERED_TAGS,
412 			  cgd->pd_type | DEVSTAT_TYPE_IF_SCSI);
413 
414 	/*
415 	 * Add an async callback so that we get
416 	 * notified if this device goes away.
417 	 */
418 	xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);
419 	csa.ccb_h.func_code = XPT_SASYNC_CB;
420 	csa.event_enable = AC_LOST_DEVICE;
421 	csa.callback = chasync;
422 	csa.callback_arg = periph;
423 	xpt_action((union ccb *)&csa);
424 
425 	/*
426 	 * Lock this peripheral until we are setup.
427 	 * This first call can't block
428 	 */
429 	(void)cam_periph_lock(periph, PRIBIO);
430 	xpt_schedule(periph, /*priority*/5);
431 
432 	return(CAM_REQ_CMP);
433 }
434 
435 static int
436 chopen(dev_t dev, int flags, int fmt, struct proc *p)
437 {
438 	struct cam_periph *periph;
439 	struct ch_softc *softc;
440 	int unit, error;
441 
442 	unit = CHUNIT(dev);
443 	periph = cam_extend_get(chperiphs, unit);
444 
445 	if (periph == NULL)
446 		return(ENXIO);
447 
448 	softc = (struct ch_softc *)periph->softc;
449 
450 	if (softc->flags & CH_FLAG_INVALID)
451 		return(ENXIO);
452 
453 	if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0)
454 		return (error);
455 
456 	if ((softc->flags & CH_FLAG_OPEN) == 0) {
457 		if (cam_periph_acquire(periph) != CAM_REQ_CMP)
458 			return(ENXIO);
459 		softc->flags |= CH_FLAG_OPEN;
460 	}
461 
462 	/*
463 	 * Load information about this changer device into the softc.
464 	 */
465 	if ((error = chgetparams(periph)) != 0) {
466 		softc->flags &= ~CH_FLAG_OPEN;
467 		cam_periph_unlock(periph);
468 		cam_periph_release(periph);
469 		return(error);
470 	}
471 
472 	cam_periph_unlock(periph);
473 
474 	return(error);
475 }
476 
477 static int
478 chclose(dev_t dev, int flag, int fmt, struct proc *p)
479 {
480 	struct	cam_periph *periph;
481 	struct	ch_softc *softc;
482 	int	unit, error;
483 
484 	error = 0;
485 
486 	unit = CHUNIT(dev);
487 	periph = cam_extend_get(chperiphs, unit);
488 	if (periph == NULL)
489 		return(ENXIO);
490 
491 	softc = (struct ch_softc *)periph->softc;
492 
493 	if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
494 		return(error);
495 
496 	softc->flags &= ~CH_FLAG_OPEN;
497 
498 	cam_periph_unlock(periph);
499 	cam_periph_release(periph);
500 
501 	return(0);
502 }
503 
504 static void
505 chstart(struct cam_periph *periph, union ccb *start_ccb)
506 {
507 	struct ch_softc *softc;
508 	int s;
509 
510 	softc = (struct ch_softc *)periph->softc;
511 
512 	switch (softc->state) {
513 	case CH_STATE_NORMAL:
514 	{
515 		s = splbio();
516 		if (periph->immediate_priority <= periph->pinfo.priority){
517 			start_ccb->ccb_h.ccb_state = CH_CCB_WAITING;
518 
519 			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
520 					  periph_links.sle);
521 			periph->immediate_priority = CAM_PRIORITY_NONE;
522 			splx(s);
523 			wakeup(&periph->ccb_list);
524 		} else
525 			splx(s);
526 		break;
527 	}
528 	case CH_STATE_PROBE:
529 	{
530 		struct scsi_mode_sense_data *sense_data;
531 
532 		sense_data = (struct scsi_mode_sense_data *)malloc(
533 							   sizeof(*sense_data),
534 							   M_TEMP, M_NOWAIT);
535 
536 		if (sense_data == NULL) {
537 			printf("chstart: couldn't malloc mode sense data\n");
538 			break;
539 		}
540 		bzero(sense_data, sizeof(*sense_data));
541 
542 		/*
543 		 * Get the element address assignment page.
544 		 */
545 		scsi_mode_sense(&start_ccb->csio,
546 				/* retries */ 1,
547 				/* cbfcnp */ chdone,
548 				/* tag_action */ MSG_SIMPLE_Q_TAG,
549 				/* dbd */ TRUE,
550 				/* page_code */ SMS_PAGE_CTRL_CURRENT,
551 				/* page */ CH_ELEMENT_ADDR_ASSIGN_PAGE,
552 				/* param_buf */ (u_int8_t *)sense_data,
553 				/* param_len */ sizeof(*sense_data),
554 				/* sense_len */ SSD_FULL_SIZE,
555 				/* timeout */ CH_TIMEOUT_MODE_SENSE);
556 
557 		start_ccb->ccb_h.ccb_bp = NULL;
558 		start_ccb->ccb_h.ccb_state = CH_CCB_PROBE;
559 		xpt_action(start_ccb);
560 		break;
561 	}
562 	}
563 }
564 
565 static void
566 chdone(struct cam_periph *periph, union ccb *done_ccb)
567 {
568 	struct ch_softc *softc;
569 	struct ccb_scsiio *csio;
570 
571 	softc = (struct ch_softc *)periph->softc;
572 	csio = &done_ccb->csio;
573 
574 	switch(done_ccb->ccb_h.ccb_state) {
575 	case CH_CCB_PROBE:
576 	{
577 		struct scsi_mode_sense_data *sense_data;
578 		char announce_buf[80];
579 
580 		sense_data = (struct scsi_mode_sense_data *)csio->data_ptr;
581 
582 		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP){
583 
584 			softc->sc_firsts[CHET_MT] =
585 				scsi_2btoul(sense_data->pages.ea.mtea);
586 			softc->sc_counts[CHET_MT] =
587 				scsi_2btoul(sense_data->pages.ea.nmte);
588 			softc->sc_firsts[CHET_ST] =
589 				scsi_2btoul(sense_data->pages.ea.fsea);
590 			softc->sc_counts[CHET_ST] =
591 				scsi_2btoul(sense_data->pages.ea.nse);
592 			softc->sc_firsts[CHET_IE] =
593 				scsi_2btoul(sense_data->pages.ea.fieea);
594 			softc->sc_counts[CHET_IE] =
595 				scsi_2btoul(sense_data->pages.ea.niee);
596 			softc->sc_firsts[CHET_DT] =
597 				scsi_2btoul(sense_data->pages.ea.fdtea);
598 			softc->sc_counts[CHET_DT] =
599 				scsi_2btoul(sense_data->pages.ea.ndte);
600 			softc->sc_picker = softc->sc_firsts[CHET_MT];
601 
602 #define PLURAL(c)	(c) == 1 ? "" : "s"
603 			sprintf(announce_buf, "%d slot%s, %d drive%s, "
604 				"%d picker%s, %d portal%s",
605 		    		softc->sc_counts[CHET_ST],
606 				PLURAL(softc->sc_counts[CHET_ST]),
607 		    		softc->sc_counts[CHET_DT],
608 				PLURAL(softc->sc_counts[CHET_DT]),
609 		    		softc->sc_counts[CHET_MT],
610 				PLURAL(softc->sc_counts[CHET_MT]),
611 		    		softc->sc_counts[CHET_IE],
612 				PLURAL(softc->sc_counts[CHET_IE]));
613 #undef PLURAL
614 		} else {
615 			int error;
616 
617 			error = cherror(done_ccb, 0, SF_RETRY_UA);
618 			/*
619 			 * Retry any UNIT ATTENTION type errors.  They
620 			 * are expected at boot.
621 			 */
622 			if (error == ERESTART) {
623 				/*
624 				 * A retry was scheuled, so
625 				 * just return.
626 				 */
627 				return;
628 			} else if (error != 0) {
629 				/* Don't wedge this device's queue */
630 				cam_release_devq(done_ccb->ccb_h.path,
631 						 /*relsim_flags*/0,
632 						 /*reduction*/0,
633 						 /*timeout*/0,
634 						 /*getcount_only*/0);
635 				sprintf(announce_buf,
636 					"Attempt to query device parameters"
637 					" failed");
638 			}
639 		}
640 		xpt_announce_periph(periph, announce_buf);
641 		softc->state = CH_STATE_NORMAL;
642 		free(sense_data, M_TEMP);
643 		cam_periph_unlock(periph);
644 		break;
645 	}
646 	case CH_CCB_WAITING:
647 	{
648 		/* Caller will release the CCB */
649 		wakeup(&done_ccb->ccb_h.cbfcnp);
650 		return;
651 	}
652 	}
653 	xpt_release_ccb(done_ccb);
654 }
655 
656 static int
657 cherror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
658 {
659 	struct ch_softc *softc;
660 	struct cam_periph *periph;
661 
662 	periph = xpt_path_periph(ccb->ccb_h.path);
663 	softc = (struct ch_softc *)periph->softc;
664 
665 	return (cam_periph_error(ccb, cam_flags, sense_flags,
666 				 &softc->saved_ccb));
667 }
668 
669 static int
670 chioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
671 {
672 	struct cam_periph *periph;
673 	struct ch_softc *softc;
674 	u_int8_t unit;
675 	int error;
676 
677 	unit = CHUNIT(dev);
678 
679 	periph = cam_extend_get(chperiphs, unit);
680 	if (periph == NULL)
681 		return(ENXIO);
682 
683 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering chioctl\n"));
684 
685 	softc = (struct ch_softc *)periph->softc;
686 
687 	error = 0;
688 
689 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
690 		  ("trying to do ioctl %#x\n", cmd));
691 
692 	/*
693 	 * If this command can change the device's state, we must
694 	 * have the device open for writing.
695 	 */
696 	switch (cmd) {
697 	case CHIOGPICKER:
698 	case CHIOGPARAMS:
699 	case CHIOGSTATUS:
700 		break;
701 
702 	default:
703 		if ((flag & FWRITE) == 0)
704 			return (EBADF);
705 	}
706 
707 	switch (cmd) {
708 	case CHIOMOVE:
709 		error = chmove(periph, (struct changer_move *)addr);
710 		break;
711 
712 	case CHIOEXCHANGE:
713 		error = chexchange(periph, (struct changer_exchange *)addr);
714 		break;
715 
716 	case CHIOPOSITION:
717 		error = chposition(periph, (struct changer_position *)addr);
718 		break;
719 
720 	case CHIOGPICKER:
721 		*(int *)addr = softc->sc_picker - softc->sc_firsts[CHET_MT];
722 		break;
723 
724 	case CHIOSPICKER:
725 	{
726 		int new_picker = *(int *)addr;
727 
728 		if (new_picker > (softc->sc_counts[CHET_MT] - 1))
729 			return (EINVAL);
730 		softc->sc_picker = softc->sc_firsts[CHET_MT] + new_picker;
731 		break;
732 	}
733 	case CHIOGPARAMS:
734 	{
735 		struct changer_params *cp = (struct changer_params *)addr;
736 
737 		cp->cp_npickers = softc->sc_counts[CHET_MT];
738 		cp->cp_nslots = softc->sc_counts[CHET_ST];
739 		cp->cp_nportals = softc->sc_counts[CHET_IE];
740 		cp->cp_ndrives = softc->sc_counts[CHET_DT];
741 		break;
742 	}
743 	case CHIOIELEM:
744 		error = chielem(periph, *(unsigned int *)addr);
745 		break;
746 
747 	case CHIOGSTATUS:
748 	{
749 		error = chgetelemstatus(periph,
750 			       (struct changer_element_status_request *) addr);
751 		break;
752 	}
753 
754 	case CHIOSETVOLTAG:
755 	{
756 		error = chsetvoltag(periph,
757 				    (struct changer_set_voltag_request *) addr);
758 		break;
759 	}
760 
761 	/* Implement prevent/allow? */
762 
763 	default:
764 		error = cam_periph_ioctl(periph, cmd, addr, cherror);
765 		break;
766 	}
767 
768 	return (error);
769 }
770 
771 static int
772 chmove(struct cam_periph *periph, struct changer_move *cm)
773 {
774 	struct ch_softc *softc;
775 	u_int16_t fromelem, toelem;
776 	union ccb *ccb;
777 	int error;
778 
779 	error = 0;
780 	softc = (struct ch_softc *)periph->softc;
781 
782 	/*
783 	 * Check arguments.
784 	 */
785 	if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
786 		return (EINVAL);
787 	if ((cm->cm_fromunit > (softc->sc_counts[cm->cm_fromtype] - 1)) ||
788 	    (cm->cm_tounit > (softc->sc_counts[cm->cm_totype] - 1)))
789 		return (ENODEV);
790 
791 	/*
792 	 * Check the request against the changer's capabilities.
793 	 */
794 	if ((softc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
795 		return (EINVAL);
796 
797 	/*
798 	 * Calculate the source and destination elements.
799 	 */
800 	fromelem = softc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
801 	toelem = softc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
802 
803 	ccb = cam_periph_getccb(periph, /*priority*/ 1);
804 
805 	scsi_move_medium(&ccb->csio,
806 			 /* retries */ 1,
807 			 /* cbfcnp */ chdone,
808 			 /* tag_action */ MSG_SIMPLE_Q_TAG,
809 			 /* tea */ softc->sc_picker,
810 			 /* src */ fromelem,
811 			 /* dst */ toelem,
812 			 /* invert */ (cm->cm_flags & CM_INVERT) ? TRUE : FALSE,
813 			 /* sense_len */ SSD_FULL_SIZE,
814 			 /* timeout */ CH_TIMEOUT_MOVE_MEDIUM);
815 
816 	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/0,
817 				  /*sense_flags*/ SF_RETRY_UA,
818 				  &softc->device_stats);
819 
820 	xpt_release_ccb(ccb);
821 
822 	return(error);
823 }
824 
825 static int
826 chexchange(struct cam_periph *periph, struct changer_exchange *ce)
827 {
828 	struct ch_softc *softc;
829 	u_int16_t src, dst1, dst2;
830 	union ccb *ccb;
831 	int error;
832 
833 	error = 0;
834 	softc = (struct ch_softc *)periph->softc;
835 	/*
836 	 * Check arguments.
837 	 */
838 	if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
839 	    (ce->ce_sdsttype > CHET_DT))
840 		return (EINVAL);
841 	if ((ce->ce_srcunit > (softc->sc_counts[ce->ce_srctype] - 1)) ||
842 	    (ce->ce_fdstunit > (softc->sc_counts[ce->ce_fdsttype] - 1)) ||
843 	    (ce->ce_sdstunit > (softc->sc_counts[ce->ce_sdsttype] - 1)))
844 		return (ENODEV);
845 
846 	/*
847 	 * Check the request against the changer's capabilities.
848 	 */
849 	if (((softc->sc_exchangemask[ce->ce_srctype] &
850 	     (1 << ce->ce_fdsttype)) == 0) ||
851 	    ((softc->sc_exchangemask[ce->ce_fdsttype] &
852 	     (1 << ce->ce_sdsttype)) == 0))
853 		return (EINVAL);
854 
855 	/*
856 	 * Calculate the source and destination elements.
857 	 */
858 	src = softc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
859 	dst1 = softc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
860 	dst2 = softc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
861 
862 	ccb = cam_periph_getccb(periph, /*priority*/ 1);
863 
864 	scsi_exchange_medium(&ccb->csio,
865 			     /* retries */ 1,
866 			     /* cbfcnp */ chdone,
867 			     /* tag_action */ MSG_SIMPLE_Q_TAG,
868 			     /* tea */ softc->sc_picker,
869 			     /* src */ src,
870 			     /* dst1 */ dst1,
871 			     /* dst2 */ dst2,
872 			     /* invert1 */ (ce->ce_flags & CE_INVERT1) ?
873 			                   TRUE : FALSE,
874 			     /* invert2 */ (ce->ce_flags & CE_INVERT2) ?
875 			                   TRUE : FALSE,
876 			     /* sense_len */ SSD_FULL_SIZE,
877 			     /* timeout */ CH_TIMEOUT_EXCHANGE_MEDIUM);
878 
879 	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/0,
880 				  /*sense_flags*/ SF_RETRY_UA,
881 				  &softc->device_stats);
882 
883 	xpt_release_ccb(ccb);
884 
885 	return(error);
886 }
887 
888 static int
889 chposition(struct cam_periph *periph, struct changer_position *cp)
890 {
891 	struct ch_softc *softc;
892 	u_int16_t dst;
893 	union ccb *ccb;
894 	int error;
895 
896 	error = 0;
897 	softc = (struct ch_softc *)periph->softc;
898 
899 	/*
900 	 * Check arguments.
901 	 */
902 	if (cp->cp_type > CHET_DT)
903 		return (EINVAL);
904 	if (cp->cp_unit > (softc->sc_counts[cp->cp_type] - 1))
905 		return (ENODEV);
906 
907 	/*
908 	 * Calculate the destination element.
909 	 */
910 	dst = softc->sc_firsts[cp->cp_type] + cp->cp_unit;
911 
912 	ccb = cam_periph_getccb(periph, /*priority*/ 1);
913 
914 	scsi_position_to_element(&ccb->csio,
915 				 /* retries */ 1,
916 				 /* cbfcnp */ chdone,
917 				 /* tag_action */ MSG_SIMPLE_Q_TAG,
918 				 /* tea */ softc->sc_picker,
919 				 /* dst */ dst,
920 				 /* invert */ (cp->cp_flags & CP_INVERT) ?
921 					      TRUE : FALSE,
922 				 /* sense_len */ SSD_FULL_SIZE,
923 				 /* timeout */ CH_TIMEOUT_POSITION_TO_ELEMENT);
924 
925 	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
926 				  /*sense_flags*/ SF_RETRY_UA,
927 				  &softc->device_stats);
928 
929 	xpt_release_ccb(ccb);
930 
931 	return(error);
932 }
933 
934 /*
935  * Copy a volume tag to a volume_tag struct, converting SCSI byte order
936  * to host native byte order in the volume serial number.  The volume
937  * label as returned by the changer is transferred to user mode as
938  * nul-terminated string.  Volume labels are truncated at the first
939  * space, as suggested by SCSI-2.
940  */
941 static	void
942 copy_voltag(struct changer_voltag *uvoltag, struct volume_tag *voltag)
943 {
944 	int i;
945 	for (i=0; i<CH_VOLTAG_MAXLEN; i++) {
946 		char c = voltag->vif[i];
947 		if (c && c != ' ')
948 			uvoltag->cv_volid[i] = c;
949 	        else
950 			break;
951 	}
952 	uvoltag->cv_serial = scsi_2btoul(voltag->vsn);
953 }
954 
955 /*
956  * Copy an an element status descriptor to a user-mode
957  * changer_element_status structure.
958  */
959 
960 static	void
961 copy_element_status(struct ch_softc *softc,
962 		    u_int16_t flags,
963 		    struct read_element_status_descriptor *desc,
964 		    struct changer_element_status *ces)
965 {
966 	u_int16_t eaddr = scsi_2btoul(desc->eaddr);
967 	u_int16_t et;
968 
969 	ces->ces_int_addr = eaddr;
970 	/* set up logical address in element status */
971 	for (et = CHET_MT; et <= CHET_DT; et++) {
972 		if ((softc->sc_firsts[et] <= eaddr)
973 		    && ((softc->sc_firsts[et] + softc->sc_counts[et])
974 			> eaddr)) {
975 			ces->ces_addr = eaddr - softc->sc_firsts[et];
976 			ces->ces_type = et;
977 			break;
978 		}
979 	}
980 
981 	ces->ces_flags = desc->flags1;
982 
983 	ces->ces_sensecode = desc->sense_code;
984 	ces->ces_sensequal = desc->sense_qual;
985 
986 	if (desc->flags2 & READ_ELEMENT_STATUS_INVERT)
987 		ces->ces_flags |= CES_INVERT;
988 
989 	if (desc->flags2 & READ_ELEMENT_STATUS_SVALID) {
990 
991 		eaddr = scsi_2btoul(desc->ssea);
992 
993 		/* convert source address to logical format */
994 		for (et = CHET_MT; et <= CHET_DT; et++) {
995 			if ((softc->sc_firsts[et] <= eaddr)
996 			    && ((softc->sc_firsts[et] + softc->sc_counts[et])
997 				> eaddr)) {
998 				ces->ces_source_addr =
999 					eaddr - softc->sc_firsts[et];
1000 				ces->ces_source_type = et;
1001 				ces->ces_flags |= CES_SOURCE_VALID;
1002 				break;
1003 			}
1004 		}
1005 
1006 		if (!(ces->ces_flags & CES_SOURCE_VALID))
1007 			printf("ch: warning: could not map element source "
1008 			       "address %ud to a valid element type",
1009 			       eaddr);
1010 	}
1011 
1012 
1013 	if (flags & READ_ELEMENT_STATUS_PVOLTAG)
1014 		copy_voltag(&(ces->ces_pvoltag), &(desc->pvoltag));
1015 	if (flags & READ_ELEMENT_STATUS_AVOLTAG)
1016 		copy_voltag(&(ces->ces_avoltag), &(desc->avoltag));
1017 
1018 	if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_IDVALID) {
1019 		ces->ces_flags |= CES_SCSIID_VALID;
1020 		ces->ces_scsi_id = desc->dt_scsi_addr;
1021 	}
1022 
1023 	if (desc->dt_scsi_addr & READ_ELEMENT_STATUS_DT_LUVALID) {
1024 		ces->ces_flags |= CES_LUN_VALID;
1025 		ces->ces_scsi_lun =
1026 			desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_LUNMASK;
1027 	}
1028 }
1029 
1030 static int
1031 chgetelemstatus(struct cam_periph *periph,
1032 		struct changer_element_status_request *cesr)
1033 {
1034 	struct read_element_status_header *st_hdr;
1035 	struct read_element_status_page_header *pg_hdr;
1036 	struct read_element_status_descriptor *desc;
1037 	caddr_t data = NULL;
1038 	size_t size, desclen;
1039 	int avail, i, error = 0;
1040 	struct changer_element_status *user_data = NULL;
1041 	struct ch_softc *softc;
1042 	union ccb *ccb;
1043 	int chet = cesr->cesr_element_type;
1044 	int want_voltags = (cesr->cesr_flags & CESR_VOLTAGS) ? 1 : 0;
1045 
1046 	softc = (struct ch_softc *)periph->softc;
1047 
1048 	/* perform argument checking */
1049 
1050 	/*
1051 	 * Perform a range check on the cesr_element_{base,count}
1052 	 * request argument fields.
1053 	 */
1054 	if ((softc->sc_counts[chet] - cesr->cesr_element_base) <= 0
1055 	    || (cesr->cesr_element_base + cesr->cesr_element_count)
1056 	        > softc->sc_counts[chet])
1057 		return (EINVAL);
1058 
1059 	/*
1060 	 * Request one descriptor for the given element type.  This
1061 	 * is used to determine the size of the descriptor so that
1062 	 * we can allocate enough storage for all of them.  We assume
1063 	 * that the first one can fit into 1k.
1064 	 */
1065 	data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK);
1066 
1067 	ccb = cam_periph_getccb(periph, /*priority*/ 1);
1068 
1069 	scsi_read_element_status(&ccb->csio,
1070 				 /* retries */ 1,
1071 				 /* cbfcnp */ chdone,
1072 				 /* tag_action */ MSG_SIMPLE_Q_TAG,
1073 				 /* voltag */ want_voltags,
1074 				 /* sea */ softc->sc_firsts[chet],
1075 				 /* count */ 1,
1076 				 /* data_ptr */ data,
1077 				 /* dxfer_len */ 1024,
1078 				 /* sense_len */ SSD_FULL_SIZE,
1079 				 /* timeout */ CH_TIMEOUT_READ_ELEMENT_STATUS);
1080 
1081 	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
1082 				  /* sense_flags */ SF_RETRY_UA,
1083 				  &softc->device_stats);
1084 
1085 	if (error)
1086 		goto done;
1087 
1088 	st_hdr = (struct read_element_status_header *)data;
1089 	pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr +
1090 		  sizeof(struct read_element_status_header));
1091 	desclen = scsi_2btoul(pg_hdr->edl);
1092 
1093 	size = sizeof(struct read_element_status_header) +
1094 	       sizeof(struct read_element_status_page_header) +
1095 	       (desclen * cesr->cesr_element_count);
1096 
1097 	/*
1098 	 * Reallocate storage for descriptors and get them from the
1099 	 * device.
1100 	 */
1101 	free(data, M_DEVBUF);
1102 	data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK);
1103 
1104 	scsi_read_element_status(&ccb->csio,
1105 				 /* retries */ 1,
1106 				 /* cbfcnp */ chdone,
1107 				 /* tag_action */ MSG_SIMPLE_Q_TAG,
1108 				 /* voltag */ want_voltags,
1109 				 /* sea */ softc->sc_firsts[chet]
1110 				 + cesr->cesr_element_base,
1111 				 /* count */ cesr->cesr_element_count,
1112 				 /* data_ptr */ data,
1113 				 /* dxfer_len */ size,
1114 				 /* sense_len */ SSD_FULL_SIZE,
1115 				 /* timeout */ CH_TIMEOUT_READ_ELEMENT_STATUS);
1116 
1117 	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
1118 				  /* sense_flags */ SF_RETRY_UA,
1119 				  &softc->device_stats);
1120 
1121 	if (error)
1122 		goto done;
1123 
1124 	/*
1125 	 * Fill in the user status array.
1126 	 */
1127 	st_hdr = (struct read_element_status_header *)data;
1128 	avail = scsi_2btoul(st_hdr->count);
1129 
1130 	if (avail != cesr->cesr_element_count) {
1131 		xpt_print_path(periph->path);
1132 		printf("warning, READ ELEMENT STATUS avail != count\n");
1133 	}
1134 
1135 	user_data = (struct changer_element_status *)
1136 		malloc(avail * sizeof(struct changer_element_status),
1137 		       M_DEVBUF, M_WAITOK);
1138 	bzero(user_data, avail * sizeof(struct changer_element_status));
1139 
1140 	desc = (struct read_element_status_descriptor *)((u_long)data +
1141 		sizeof(struct read_element_status_header) +
1142 		sizeof(struct read_element_status_page_header));
1143 	/*
1144 	 * Set up the individual element status structures
1145 	 */
1146 	for (i = 0; i < avail; ++i) {
1147 		struct changer_element_status *ces = &(user_data[i]);
1148 
1149 		copy_element_status(softc, pg_hdr->flags, desc, ces);
1150 
1151 		(u_long)desc += desclen;
1152 	}
1153 
1154 	/* Copy element status structures out to userspace. */
1155 	error = copyout(user_data,
1156 			cesr->cesr_element_status,
1157 			avail * sizeof(struct changer_element_status));
1158 
1159  done:
1160 	xpt_release_ccb(ccb);
1161 
1162 	if (data != NULL)
1163 		free(data, M_DEVBUF);
1164 	if (user_data != NULL)
1165 		free(user_data, M_DEVBUF);
1166 
1167 	return (error);
1168 }
1169 
1170 static int
1171 chielem(struct cam_periph *periph,
1172 	unsigned int timeout)
1173 {
1174 	union ccb *ccb;
1175 	struct ch_softc *softc;
1176 	int error;
1177 
1178 	if (!timeout) {
1179 		timeout = CH_TIMEOUT_INITIALIZE_ELEMENT_STATUS;
1180 	} else {
1181 		timeout *= 1000;
1182 	}
1183 
1184 	error = 0;
1185 	softc = (struct ch_softc *)periph->softc;
1186 
1187 	ccb = cam_periph_getccb(periph, /*priority*/ 1);
1188 
1189 	scsi_initialize_element_status(&ccb->csio,
1190 				      /* retries */ 1,
1191 				      /* cbfcnp */ chdone,
1192 				      /* tag_action */ MSG_SIMPLE_Q_TAG,
1193 				      /* sense_len */ SSD_FULL_SIZE,
1194 				      /* timeout */ timeout);
1195 
1196 	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
1197 				  /* sense_flags */ SF_RETRY_UA,
1198 				  &softc->device_stats);
1199 
1200 	xpt_release_ccb(ccb);
1201 
1202 	return(error);
1203 }
1204 
1205 static int
1206 chsetvoltag(struct cam_periph *periph,
1207 	    struct changer_set_voltag_request *csvr)
1208 {
1209 	union ccb *ccb;
1210 	struct ch_softc *softc;
1211 	u_int16_t ea;
1212 	u_int8_t sac;
1213 	struct scsi_send_volume_tag_parameters ssvtp;
1214 	int error;
1215 	int i;
1216 
1217 	error = 0;
1218 	softc = (struct ch_softc *)periph->softc;
1219 
1220 	bzero(&ssvtp, sizeof(ssvtp));
1221 	for (i=0; i<sizeof(ssvtp.vitf); i++) {
1222 		ssvtp.vitf[i] = ' ';
1223 	}
1224 
1225 	/*
1226 	 * Check arguments.
1227 	 */
1228 	if (csvr->csvr_type > CHET_DT)
1229 		return EINVAL;
1230 	if (csvr->csvr_addr > (softc->sc_counts[csvr->csvr_type] - 1))
1231 		return ENODEV;
1232 
1233 	ea = softc->sc_firsts[csvr->csvr_type] + csvr->csvr_addr;
1234 
1235 	if (csvr->csvr_flags & CSVR_ALTERNATE) {
1236 		switch (csvr->csvr_flags & CSVR_MODE_MASK) {
1237 		case CSVR_MODE_SET:
1238 			sac = SEND_VOLUME_TAG_ASSERT_ALTERNATE;
1239 			break;
1240 		case CSVR_MODE_REPLACE:
1241 			sac = SEND_VOLUME_TAG_REPLACE_ALTERNATE;
1242 			break;
1243 		case CSVR_MODE_CLEAR:
1244 			sac = SEND_VOLUME_TAG_UNDEFINED_ALTERNATE;
1245 			break;
1246 		default:
1247 			error = EINVAL;
1248 			goto out;
1249 		}
1250 	} else {
1251 		switch (csvr->csvr_flags & CSVR_MODE_MASK) {
1252 		case CSVR_MODE_SET:
1253 			sac = SEND_VOLUME_TAG_ASSERT_PRIMARY;
1254 			break;
1255 		case CSVR_MODE_REPLACE:
1256 			sac = SEND_VOLUME_TAG_REPLACE_PRIMARY;
1257 			break;
1258 		case CSVR_MODE_CLEAR:
1259 			sac = SEND_VOLUME_TAG_UNDEFINED_PRIMARY;
1260 			break;
1261 		default:
1262 			error = EINVAL;
1263 			goto out;
1264 		}
1265 	}
1266 
1267 	memcpy(ssvtp.vitf, csvr->csvr_voltag.cv_volid,
1268 	       min(strlen(csvr->csvr_voltag.cv_volid), sizeof(ssvtp.vitf)));
1269 	scsi_ulto2b(csvr->csvr_voltag.cv_serial, ssvtp.minvsn);
1270 
1271 	ccb = cam_periph_getccb(periph, /*priority*/ 1);
1272 
1273 	scsi_send_volume_tag(&ccb->csio,
1274 			     /* retries */ 1,
1275 			     /* cbfcnp */ chdone,
1276 			     /* tag_action */ MSG_SIMPLE_Q_TAG,
1277 			     /* element_address */ ea,
1278 			     /* send_action_code */ sac,
1279 			     /* parameters */ &ssvtp,
1280 			     /* sense_len */ SSD_FULL_SIZE,
1281 			     /* timeout */ CH_TIMEOUT_SEND_VOLTAG);
1282 
1283 	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
1284 				  /*sense_flags*/ SF_RETRY_UA,
1285 				  &softc->device_stats);
1286 
1287 	xpt_release_ccb(ccb);
1288 
1289  out:
1290 	return error;
1291 }
1292 
1293 static int
1294 chgetparams(struct cam_periph *periph)
1295 {
1296 	union ccb *ccb;
1297 	struct ch_softc *softc;
1298 	struct scsi_mode_sense_data *sense_data;
1299 	int error, from;
1300 	u_int8_t *moves, *exchanges;
1301 
1302 	error = 0;
1303 
1304 	softc = (struct ch_softc *)periph->softc;
1305 
1306 	ccb = cam_periph_getccb(periph, /*priority*/ 1);
1307 
1308 	sense_data = (struct scsi_mode_sense_data *)malloc(sizeof(*sense_data),
1309 							   M_TEMP, M_NOWAIT);
1310 	if (sense_data == NULL) {
1311 		printf("chgetparams: couldn't malloc mode sense data\n");
1312 		return(ENOSPC);
1313 	}
1314 
1315 	bzero(sense_data, sizeof(*sense_data));
1316 
1317 	/*
1318 	 * Get the element address assignment page.
1319 	 */
1320 	scsi_mode_sense(&ccb->csio,
1321 			/* retries */ 1,
1322 			/* cbfcnp */ chdone,
1323 			/* tag_action */ MSG_SIMPLE_Q_TAG,
1324 			/* dbd */ TRUE,
1325 			/* page_code */ SMS_PAGE_CTRL_CURRENT,
1326 			/* page */ CH_ELEMENT_ADDR_ASSIGN_PAGE,
1327 			/* param_buf */ (u_int8_t *)sense_data,
1328 			/* param_len */ sizeof(*sense_data),
1329 			/* sense_len */ SSD_FULL_SIZE,
1330 			/* timeout */ CH_TIMEOUT_MODE_SENSE);
1331 
1332 	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
1333 				  /* sense_flags */ SF_RETRY_UA,
1334 				  &softc->device_stats);
1335 
1336 	if (error) {
1337 		xpt_print_path(periph->path);
1338 		printf("chgetparams: error getting element address page\n");
1339 		xpt_release_ccb(ccb);
1340 		return(error);
1341 	}
1342 
1343 	softc->sc_firsts[CHET_MT] = scsi_2btoul(sense_data->pages.ea.mtea);
1344 	softc->sc_counts[CHET_MT] = scsi_2btoul(sense_data->pages.ea.nmte);
1345 	softc->sc_firsts[CHET_ST] = scsi_2btoul(sense_data->pages.ea.fsea);
1346 	softc->sc_counts[CHET_ST] = scsi_2btoul(sense_data->pages.ea.nse);
1347 	softc->sc_firsts[CHET_IE] = scsi_2btoul(sense_data->pages.ea.fieea);
1348 	softc->sc_counts[CHET_IE] = scsi_2btoul(sense_data->pages.ea.niee);
1349 	softc->sc_firsts[CHET_DT] = scsi_2btoul(sense_data->pages.ea.fdtea);
1350 	softc->sc_counts[CHET_DT] = scsi_2btoul(sense_data->pages.ea.ndte);
1351 
1352 	bzero(sense_data, sizeof(*sense_data));
1353 
1354 	/*
1355 	 * Now get the device capabilities page.
1356 	 */
1357 	scsi_mode_sense(&ccb->csio,
1358 			/* retries */ 1,
1359 			/* cbfcnp */ chdone,
1360 			/* tag_action */ MSG_SIMPLE_Q_TAG,
1361 			/* dbd */ TRUE,
1362 			/* page_code */ SMS_PAGE_CTRL_CURRENT,
1363 			/* page */ CH_DEVICE_CAP_PAGE,
1364 			/* param_buf */ (u_int8_t *)sense_data,
1365 			/* param_len */ sizeof(*sense_data),
1366 			/* sense_len */ SSD_FULL_SIZE,
1367 			/* timeout */ CH_TIMEOUT_MODE_SENSE);
1368 
1369 	error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
1370 				  /* sense_flags */ SF_RETRY_UA,
1371 				  &softc->device_stats);
1372 
1373 	xpt_release_ccb(ccb);
1374 
1375 	if (error) {
1376 		xpt_print_path(periph->path);
1377 		printf("chgetparams: error getting device capabilities page\n");
1378 		return(error);
1379 	}
1380 
1381 
1382 	bzero(softc->sc_movemask, sizeof(softc->sc_movemask));
1383 	bzero(softc->sc_exchangemask, sizeof(softc->sc_exchangemask));
1384 	moves = &sense_data->pages.cap.move_from_mt;
1385 	exchanges = &sense_data->pages.cap.exchange_with_mt;
1386 	for (from = CHET_MT; from <= CHET_DT; ++from) {
1387 		softc->sc_movemask[from] = moves[from];
1388 		softc->sc_exchangemask[from] = exchanges[from];
1389 	}
1390 
1391 	return(error);
1392 }
1393 
1394 void
1395 scsi_move_medium(struct ccb_scsiio *csio, u_int32_t retries,
1396 		 void (*cbfcnp)(struct cam_periph *, union ccb *),
1397 		 u_int8_t tag_action, u_int32_t tea, u_int32_t src,
1398 		 u_int32_t dst, int invert, u_int8_t sense_len,
1399 		 u_int32_t timeout)
1400 {
1401 	struct scsi_move_medium *scsi_cmd;
1402 
1403 	scsi_cmd = (struct scsi_move_medium *)&csio->cdb_io.cdb_bytes;
1404 	bzero(scsi_cmd, sizeof(*scsi_cmd));
1405 
1406 	scsi_cmd->opcode = MOVE_MEDIUM;
1407 
1408 	scsi_ulto2b(tea, scsi_cmd->tea);
1409 	scsi_ulto2b(src, scsi_cmd->src);
1410 	scsi_ulto2b(dst, scsi_cmd->dst);
1411 
1412 	if (invert)
1413 		scsi_cmd->invert |= MOVE_MEDIUM_INVERT;
1414 
1415 	cam_fill_csio(csio,
1416 		      retries,
1417 		      cbfcnp,
1418 		      /*flags*/ CAM_DIR_NONE,
1419 		      tag_action,
1420 		      /*data_ptr*/ NULL,
1421 		      /*dxfer_len*/ 0,
1422 		      sense_len,
1423 		      sizeof(*scsi_cmd),
1424 		      timeout);
1425 }
1426 
1427 void
1428 scsi_exchange_medium(struct ccb_scsiio *csio, u_int32_t retries,
1429 		     void (*cbfcnp)(struct cam_periph *, union ccb *),
1430 		     u_int8_t tag_action, u_int32_t tea, u_int32_t src,
1431 		     u_int32_t dst1, u_int32_t dst2, int invert1,
1432 		     int invert2, u_int8_t sense_len, u_int32_t timeout)
1433 {
1434 	struct scsi_exchange_medium *scsi_cmd;
1435 
1436 	scsi_cmd = (struct scsi_exchange_medium *)&csio->cdb_io.cdb_bytes;
1437 	bzero(scsi_cmd, sizeof(*scsi_cmd));
1438 
1439 	scsi_cmd->opcode = EXCHANGE_MEDIUM;
1440 
1441 	scsi_ulto2b(tea, scsi_cmd->tea);
1442 	scsi_ulto2b(src, scsi_cmd->src);
1443 	scsi_ulto2b(dst1, scsi_cmd->fdst);
1444 	scsi_ulto2b(dst2, scsi_cmd->sdst);
1445 
1446 	if (invert1)
1447 		scsi_cmd->invert |= EXCHANGE_MEDIUM_INV1;
1448 
1449 	if (invert2)
1450 		scsi_cmd->invert |= EXCHANGE_MEDIUM_INV2;
1451 
1452 	cam_fill_csio(csio,
1453 		      retries,
1454 		      cbfcnp,
1455 		      /*flags*/ CAM_DIR_NONE,
1456 		      tag_action,
1457 		      /*data_ptr*/ NULL,
1458 		      /*dxfer_len*/ 0,
1459 		      sense_len,
1460 		      sizeof(*scsi_cmd),
1461 		      timeout);
1462 }
1463 
1464 void
1465 scsi_position_to_element(struct ccb_scsiio *csio, u_int32_t retries,
1466 			 void (*cbfcnp)(struct cam_periph *, union ccb *),
1467 			 u_int8_t tag_action, u_int32_t tea, u_int32_t dst,
1468 			 int invert, u_int8_t sense_len, u_int32_t timeout)
1469 {
1470 	struct scsi_position_to_element *scsi_cmd;
1471 
1472 	scsi_cmd = (struct scsi_position_to_element *)&csio->cdb_io.cdb_bytes;
1473 	bzero(scsi_cmd, sizeof(*scsi_cmd));
1474 
1475 	scsi_cmd->opcode = POSITION_TO_ELEMENT;
1476 
1477 	scsi_ulto2b(tea, scsi_cmd->tea);
1478 	scsi_ulto2b(dst, scsi_cmd->dst);
1479 
1480 	if (invert)
1481 		scsi_cmd->invert |= POSITION_TO_ELEMENT_INVERT;
1482 
1483 	cam_fill_csio(csio,
1484 		      retries,
1485 		      cbfcnp,
1486 		      /*flags*/ CAM_DIR_NONE,
1487 		      tag_action,
1488 		      /*data_ptr*/ NULL,
1489 		      /*dxfer_len*/ 0,
1490 		      sense_len,
1491 		      sizeof(*scsi_cmd),
1492 		      timeout);
1493 }
1494 
1495 void
1496 scsi_read_element_status(struct ccb_scsiio *csio, u_int32_t retries,
1497 			 void (*cbfcnp)(struct cam_periph *, union ccb *),
1498 			 u_int8_t tag_action, int voltag, u_int32_t sea,
1499 			 u_int32_t count, u_int8_t *data_ptr,
1500 			 u_int32_t dxfer_len, u_int8_t sense_len,
1501 			 u_int32_t timeout)
1502 {
1503 	struct scsi_read_element_status *scsi_cmd;
1504 
1505 	scsi_cmd = (struct scsi_read_element_status *)&csio->cdb_io.cdb_bytes;
1506 	bzero(scsi_cmd, sizeof(*scsi_cmd));
1507 
1508 	scsi_cmd->opcode = READ_ELEMENT_STATUS;
1509 
1510 	scsi_ulto2b(sea, scsi_cmd->sea);
1511 	scsi_ulto2b(count, scsi_cmd->count);
1512 	scsi_ulto3b(dxfer_len, scsi_cmd->len);
1513 
1514 	if (voltag)
1515 		scsi_cmd->byte2 |= READ_ELEMENT_STATUS_VOLTAG;
1516 
1517 	cam_fill_csio(csio,
1518 		      retries,
1519 		      cbfcnp,
1520 		      /*flags*/ CAM_DIR_IN,
1521 		      tag_action,
1522 		      data_ptr,
1523 		      dxfer_len,
1524 		      sense_len,
1525 		      sizeof(*scsi_cmd),
1526 		      timeout);
1527 }
1528 
1529 void
1530 scsi_initialize_element_status(struct ccb_scsiio *csio, u_int32_t retries,
1531 			       void (*cbfcnp)(struct cam_periph *, union ccb *),
1532 			       u_int8_t tag_action, u_int8_t sense_len,
1533 			       u_int32_t timeout)
1534 {
1535 	struct scsi_initialize_element_status *scsi_cmd;
1536 
1537 	scsi_cmd = (struct scsi_initialize_element_status *)
1538 		    &csio->cdb_io.cdb_bytes;
1539 	bzero(scsi_cmd, sizeof(*scsi_cmd));
1540 
1541 	scsi_cmd->opcode = INITIALIZE_ELEMENT_STATUS;
1542 
1543 	cam_fill_csio(csio,
1544 		      retries,
1545 		      cbfcnp,
1546 		      /*flags*/ CAM_DIR_NONE,
1547 		      tag_action,
1548 		      /* data_ptr */ NULL,
1549 		      /* dxfer_len */ 0,
1550 		      sense_len,
1551 		      sizeof(*scsi_cmd),
1552 		      timeout);
1553 }
1554 
1555 void
1556 scsi_send_volume_tag(struct ccb_scsiio *csio, u_int32_t retries,
1557 		     void (*cbfcnp)(struct cam_periph *, union ccb *),
1558 		     u_int8_t tag_action,
1559 		     u_int16_t element_address,
1560 		     u_int8_t send_action_code,
1561 		     struct scsi_send_volume_tag_parameters *parameters,
1562 		     u_int8_t sense_len, u_int32_t timeout)
1563 {
1564 	struct scsi_send_volume_tag *scsi_cmd;
1565 
1566 	scsi_cmd = (struct scsi_send_volume_tag *) &csio->cdb_io.cdb_bytes;
1567 	bzero(scsi_cmd, sizeof(*scsi_cmd));
1568 
1569 	scsi_cmd->opcode = SEND_VOLUME_TAG;
1570 	scsi_ulto2b(element_address, scsi_cmd->ea);
1571 	scsi_cmd->sac = send_action_code;
1572 	scsi_ulto2b(sizeof(*parameters), scsi_cmd->pll);
1573 
1574 	cam_fill_csio(csio,
1575 		      retries,
1576 		      cbfcnp,
1577 		      /*flags*/ CAM_DIR_OUT,
1578 		      tag_action,
1579 		      /* data_ptr */ (u_int8_t *) parameters,
1580 		      sizeof(*parameters),
1581 		      sense_len,
1582 		      sizeof(*scsi_cmd),
1583 		      timeout);
1584 }
1585