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