xref: /openbsd/sys/scsi/ch.c (revision 404b540a)
1 /*	$OpenBSD: ch.c,v 1.37 2009/01/10 21:36:49 beck Exp $	*/
2 /*	$NetBSD: ch.c,v 1.26 1997/02/21 22:06:52 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej@and.com>
6  * All rights reserved.
7  *
8  * Partially based on an autochanger driver written by Stefan Grefen
9  * and on an autochanger driver written by the Systems Programming Group
10  * at the University of Utah Computer Science Department.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgements:
22  *	This product includes software developed by Jason R. Thorpe
23  *	for And Communications, http://www.and.com/
24  * 4. The name of the author may not be used to endorse or promote products
25  *    derived from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
28  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
31  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
32  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
34  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
35  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/errno.h>
43 #include <sys/ioctl.h>
44 #include <sys/buf.h>
45 #include <sys/proc.h>
46 #include <sys/user.h>
47 #include <sys/chio.h>
48 #include <sys/device.h>
49 #include <sys/malloc.h>
50 #include <sys/conf.h>
51 #include <sys/fcntl.h>
52 
53 #include <scsi/scsi_all.h>
54 #include <scsi/scsi_changer.h>
55 #include <scsi/scsiconf.h>
56 
57 #define CHRETRIES	2
58 #define CHUNIT(x)	(minor((x)))
59 
60 struct ch_softc {
61 	struct device	sc_dev;		/* generic device info */
62 	struct scsi_link *sc_link;	/* link in the SCSI bus */
63 
64 	int		sc_picker;	/* current picker */
65 
66 	/*
67 	 * The following information is obtained from the
68 	 * element address assignment page.
69 	 */
70 	int		sc_firsts[4];	/* firsts, indexed by CHET_* */
71 	int		sc_counts[4];	/* counts, indexed by CHET_* */
72 
73 	/*
74 	 * The following mask defines the legal combinations
75 	 * of elements for the MOVE MEDIUM command.
76 	 */
77 	u_int8_t	sc_movemask[4];
78 
79 	/*
80 	 * As above, but for EXCHANGE MEDIUM.
81 	 */
82 	u_int8_t	sc_exchangemask[4];
83 
84 	int		flags;		/* misc. info */
85 
86 	/*
87 	 * Quirks; see below.
88 	 */
89 	int		sc_settledelay; /* delay for settle */
90 
91 };
92 
93 /* sc_flags */
94 #define CHF_ROTATE	0x01		/* picker can rotate */
95 
96 /* Autoconfiguration glue */
97 int	chmatch(struct device *, void *, void *);
98 void	chattach(struct device *, struct device *, void *);
99 
100 struct cfattach ch_ca = {
101 	sizeof(struct ch_softc), chmatch, chattach
102 };
103 
104 struct cfdriver ch_cd = {
105 	NULL, "ch", DV_DULL
106 };
107 
108 const struct scsi_inquiry_pattern ch_patterns[] = {
109 	{T_CHANGER, T_REMOV,
110 	 "",		"",		""},
111 };
112 
113 int	ch_move(struct ch_softc *, struct changer_move *);
114 int	ch_exchange(struct ch_softc *, struct changer_exchange *);
115 int	ch_position(struct ch_softc *, struct changer_position *);
116 int	ch_usergetelemstatus(struct ch_softc *,
117     struct changer_element_status_request *);
118 int	ch_getelemstatus(struct ch_softc *, int, int, caddr_t, size_t, int);
119 int	ch_get_params(struct ch_softc *, int);
120 int	ch_interpret_sense(struct scsi_xfer *xs);
121 void	ch_get_quirks(struct ch_softc *, struct scsi_inquiry_data *);
122 
123 /* SCSI glue */
124 struct scsi_device ch_switch = {
125 	ch_interpret_sense,
126 	NULL,
127 	NULL,
128 	NULL
129 };
130 
131 /*
132  * SCSI changer quirks.
133  */
134 struct chquirk {
135 	struct	scsi_inquiry_pattern cq_match; /* device id pattern */
136 	int	cq_settledelay;	/* settle delay, in seconds */
137 };
138 
139 struct chquirk chquirks[] = {
140 	{{T_CHANGER, T_REMOV,
141 	  "SPECTRA",	"9000",		"0200"},
142 	 75},
143 };
144 
145 int
146 chmatch(parent, match, aux)
147 	struct device *parent;
148 	void *match, *aux;
149 {
150 	struct scsi_attach_args *sa = aux;
151 	int priority;
152 
153 	(void)scsi_inqmatch(sa->sa_inqbuf,
154 	    ch_patterns, sizeof(ch_patterns)/sizeof(ch_patterns[0]),
155 	    sizeof(ch_patterns[0]), &priority);
156 
157 	return (priority);
158 }
159 
160 void
161 chattach(parent, self, aux)
162 	struct device *parent, *self;
163 	void *aux;
164 {
165 	struct ch_softc *sc = (struct ch_softc *)self;
166 	struct scsi_attach_args *sa = aux;
167 	struct scsi_link *link = sa->sa_sc_link;
168 
169 	/* Glue into the SCSI bus */
170 	sc->sc_link = link;
171 	link->device = &ch_switch;
172 	link->device_softc = sc;
173 	link->openings = 1;
174 
175 	printf("\n");
176 
177 	/*
178 	 * Store our our device's quirks.
179 	 */
180 	ch_get_quirks(sc, sa->sa_inqbuf);
181 
182 }
183 
184 int
185 chopen(dev, flags, fmt, p)
186 	dev_t dev;
187 	int flags, fmt;
188 	struct proc *p;
189 {
190 	struct ch_softc *sc;
191 	int oldcounts[4];
192 	int i, unit, error = 0;
193 
194 	unit = CHUNIT(dev);
195 	if ((unit >= ch_cd.cd_ndevs) ||
196 	    ((sc = ch_cd.cd_devs[unit]) == NULL))
197 		return (ENXIO);
198 
199 	/*
200 	 * Only allow one open at a time.
201 	 */
202 	if (sc->sc_link->flags & SDEV_OPEN)
203 		return (EBUSY);
204 
205 	sc->sc_link->flags |= SDEV_OPEN;
206 
207 	/*
208 	 * Absorb any unit attention errors. We must notice
209 	 * "Not ready" errors as a changer will report "In the
210 	 * process of getting ready" any time it must rescan
211 	 * itself to determine the state of the changer.
212 	 */
213 	error = scsi_test_unit_ready(sc->sc_link, TEST_READY_RETRIES,
214 	    SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);
215 	if (error)
216 		goto bad;
217 
218 	/*
219 	 * Get information about the device. Save old information
220 	 * so we can decide whether to be verbose about new parameters.
221 	 */
222 	for (i = 0; i < 4; i++) {
223 		oldcounts[i] = sc->sc_counts[i];
224 	}
225 	error = ch_get_params(sc, scsi_autoconf);
226 	if (error)
227 		goto bad;
228 
229 	for (i = 0; i < 4; i++) {
230 		if (oldcounts[i] != sc->sc_counts[i]) {
231 			break;
232 		}
233 	}
234 	if (i < 4) {
235 #ifdef CHANGER_DEBUG
236 #define PLURAL(c)	(c) == 1 ? "" : "s"
237 		printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n",
238 		    sc->sc_dev.dv_xname,
239 		    sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]),
240 		    sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]),
241 		    sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]),
242 		    sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE]));
243 #undef PLURAL
244 		printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n",
245 		    sc->sc_dev.dv_xname,
246 		    sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST],
247 		    sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]);
248 		printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n",
249 		    sc->sc_dev.dv_xname,
250 		    sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST],
251 		    sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]);
252 #endif /* CHANGER_DEBUG */
253 	}
254 
255 	/* Default the current picker. */
256 	sc->sc_picker = sc->sc_firsts[CHET_MT];
257 
258 	return (0);
259 
260  bad:
261 	sc->sc_link->flags &= ~SDEV_OPEN;
262 	return (error);
263 }
264 
265 int
266 chclose(dev, flags, fmt, p)
267 	dev_t dev;
268 	int flags, fmt;
269 	struct proc *p;
270 {
271 	struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
272 
273 	sc->sc_link->flags &= ~SDEV_OPEN;
274 	return (0);
275 }
276 
277 int
278 chioctl(dev, cmd, data, flags, p)
279 	dev_t dev;
280 	u_long cmd;
281 	caddr_t data;
282 	int flags;
283 	struct proc *p;
284 {
285 	struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
286 	int error = 0;
287 
288 	/*
289 	 * If this command can change the device's state, we must
290 	 * have the device open for writing.
291 	 */
292 	switch (cmd) {
293 	case CHIOGPICKER:
294 	case CHIOGPARAMS:
295 	case CHIOGSTATUS:
296 		break;
297 
298 	default:
299 		if ((flags & FWRITE) == 0)
300 			return (EBADF);
301 	}
302 
303 	switch (cmd) {
304 	case CHIOMOVE:
305 		error = ch_move(sc, (struct changer_move *)data);
306 		break;
307 
308 	case CHIOEXCHANGE:
309 		error = ch_exchange(sc, (struct changer_exchange *)data);
310 		break;
311 
312 	case CHIOPOSITION:
313 		error = ch_position(sc, (struct changer_position *)data);
314 		break;
315 
316 	case CHIOGPICKER:
317 		*(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT];
318 		break;
319 
320 	case CHIOSPICKER:	{
321 		int new_picker = *(int *)data;
322 
323 		if (new_picker > (sc->sc_counts[CHET_MT] - 1))
324 			return (EINVAL);
325 		sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker;
326 		break;		}
327 
328 	case CHIOGPARAMS:	{
329 		struct changer_params *cp = (struct changer_params *)data;
330 
331 		cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT];
332 		cp->cp_npickers = sc->sc_counts[CHET_MT];
333 		cp->cp_nslots = sc->sc_counts[CHET_ST];
334 		cp->cp_nportals = sc->sc_counts[CHET_IE];
335 		cp->cp_ndrives = sc->sc_counts[CHET_DT];
336 		break;		}
337 
338 	case CHIOGSTATUS:	{
339 		struct changer_element_status_request *cesr =
340 		    (struct changer_element_status_request *)data;
341 
342 		error = ch_usergetelemstatus(sc, cesr);
343 		break;		}
344 
345 	/* Implement prevent/allow? */
346 
347 	default:
348 		error = scsi_do_ioctl(sc->sc_link, dev, cmd, data,
349 		    flags, p);
350 		break;
351 	}
352 
353 	return (error);
354 }
355 
356 int
357 ch_move(sc, cm)
358 	struct ch_softc *sc;
359 	struct changer_move *cm;
360 {
361 	struct scsi_move_medium cmd;
362 	u_int16_t fromelem, toelem;
363 
364 	/*
365 	 * Check arguments.
366 	 */
367 	if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
368 		return (EINVAL);
369 	if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) ||
370 	    (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1)))
371 		return (ENODEV);
372 
373 	/*
374 	 * Check the request against the changer's capabilities.
375 	 */
376 	if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
377 		return (EINVAL);
378 
379 	/*
380 	 * Calculate the source and destination elements.
381 	 */
382 	fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
383 	toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
384 
385 	/*
386 	 * Build the SCSI command.
387 	 */
388 	bzero(&cmd, sizeof(cmd));
389 	cmd.opcode = MOVE_MEDIUM;
390 	_lto2b(sc->sc_picker, cmd.tea);
391 	_lto2b(fromelem, cmd.src);
392 	_lto2b(toelem, cmd.dst);
393 	if (cm->cm_flags & CM_INVERT)
394 		cmd.flags |= MOVE_MEDIUM_INVERT;
395 
396 	/*
397 	 * Send command to changer.
398 	 */
399 	return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
400 	    sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
401 }
402 
403 int
404 ch_exchange(sc, ce)
405 	struct ch_softc *sc;
406 	struct changer_exchange *ce;
407 {
408 	struct scsi_exchange_medium cmd;
409 	u_int16_t src, dst1, dst2;
410 
411 	/*
412 	 * Check arguments.
413 	 */
414 	if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
415 	    (ce->ce_sdsttype > CHET_DT))
416 		return (EINVAL);
417 	if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) ||
418 	    (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) ||
419 	    (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1)))
420 		return (ENODEV);
421 
422 	/*
423 	 * Check the request against the changer's capabilities.
424 	 */
425 	if (((sc->sc_exchangemask[ce->ce_srctype] &
426 	    (1 << ce->ce_fdsttype)) == 0) ||
427 	    ((sc->sc_exchangemask[ce->ce_fdsttype] &
428 	    (1 << ce->ce_sdsttype)) == 0))
429 		return (EINVAL);
430 
431 	/*
432 	 * Calculate the source and destination elements.
433 	 */
434 	src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
435 	dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
436 	dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
437 
438 	/*
439 	 * Build the SCSI command.
440 	 */
441 	bzero(&cmd, sizeof(cmd));
442 	cmd.opcode = EXCHANGE_MEDIUM;
443 	_lto2b(sc->sc_picker, cmd.tea);
444 	_lto2b(src, cmd.src);
445 	_lto2b(dst1, cmd.fdst);
446 	_lto2b(dst2, cmd.sdst);
447 	if (ce->ce_flags & CE_INVERT1)
448 		cmd.flags |= EXCHANGE_MEDIUM_INV1;
449 	if (ce->ce_flags & CE_INVERT2)
450 		cmd.flags |= EXCHANGE_MEDIUM_INV2;
451 
452 	/*
453 	 * Send command to changer.
454 	 */
455 	return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
456 	    sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
457 }
458 
459 int
460 ch_position(sc, cp)
461 	struct ch_softc *sc;
462 	struct changer_position *cp;
463 {
464 	struct scsi_position_to_element cmd;
465 	u_int16_t dst;
466 
467 	/*
468 	 * Check arguments.
469 	 */
470 	if (cp->cp_type > CHET_DT)
471 		return (EINVAL);
472 	if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1))
473 		return (ENODEV);
474 
475 	/*
476 	 * Calculate the destination element.
477 	 */
478 	dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit;
479 
480 	/*
481 	 * Build the SCSI command.
482 	 */
483 	bzero(&cmd, sizeof(cmd));
484 	cmd.opcode = POSITION_TO_ELEMENT;
485 	_lto2b(sc->sc_picker, cmd.tea);
486 	_lto2b(dst, cmd.dst);
487 	if (cp->cp_flags & CP_INVERT)
488 		cmd.flags |= POSITION_TO_ELEMENT_INVERT;
489 
490 	/*
491 	 * Send command to changer.
492 	 */
493 	return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
494 	    sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
495 }
496 
497 /*
498  * Copy a volume tag to a volume_tag struct, converting SCSI byte order
499  * to host native byte order in the volume serial number.  The volume
500  * label as returned by the changer is transferred to user mode as
501  * nul-terminated string.  Volume labels are truncated at the first
502  * space, as suggested by SCSI-2.
503  */
504 static  void
505 copy_voltag(struct changer_voltag *uvoltag, struct volume_tag *voltag)
506 {
507 	int i;
508 
509 	for (i=0; i<CH_VOLTAG_MAXLEN; i++) {
510 		char c = voltag->vif[i];
511 		if (c && c != ' ')
512 			uvoltag->cv_volid[i] = c;
513 		else
514 			break;
515 	}
516 	uvoltag->cv_volid[i] = '\0';
517 	uvoltag->cv_serial = _2btol(voltag->vsn);
518 }
519 
520 /*
521  * Copy an an element status descriptor to a user-mode
522  * changer_element_status structure.
523  */
524 static void
525 copy_element_status(int flags,	struct read_element_status_descriptor *desc,
526     struct changer_element_status *ces)
527 {
528 	ces->ces_flags = desc->flags1;
529 
530 	if (flags & READ_ELEMENT_STATUS_PVOLTAG)
531 		copy_voltag(&ces->ces_pvoltag, &desc->pvoltag);
532 	if (flags & READ_ELEMENT_STATUS_AVOLTAG)
533 		copy_voltag(&ces->ces_avoltag, &desc->avoltag);
534 }
535 
536 /*
537  * Perform a READ ELEMENT STATUS on behalf of the user, and return to
538  * the user only the data the user is interested in (i.e. an array of
539  * changer_element_status structures)
540  */
541 int
542 ch_usergetelemstatus(sc, cesr)
543 	struct ch_softc *sc;
544 	struct changer_element_status_request *cesr;
545 {
546 	struct changer_element_status *user_data = NULL;
547 	struct read_element_status_header *st_hdr;
548 	struct read_element_status_page_header *pg_hdr;
549 	caddr_t desc;
550 	caddr_t data = NULL;
551 	size_t size, desclen, udsize;
552 	int chet = cesr->cesr_type;
553 	int avail, i, error = 0;
554 	int want_voltags = (cesr->cesr_flags & CESR_VOLTAGS) ? 1 : 0;
555 
556 	/*
557 	 * If there are no elements of the requested type in the changer,
558 	 * the request is invalid.
559 	 */
560 	if (sc->sc_counts[chet] == 0)
561 		return (EINVAL);
562 
563 	/*
564 	 * Request one descriptor for the given element type.  This
565 	 * is used to determine the size of the descriptor so that
566 	 * we can allocate enough storage for all of them.  We assume
567 	 * that the first one can fit into 1k.
568 	 */
569 	data = malloc(1024, M_DEVBUF, M_WAITOK);
570 	error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024,
571 	    want_voltags);
572 	if (error)
573 		goto done;
574 
575 	st_hdr = (struct read_element_status_header *)data;
576 	pg_hdr = (struct read_element_status_page_header *) (st_hdr + 1);
577 	desclen = _2btol(pg_hdr->edl);
578 
579 	size = sizeof(struct read_element_status_header) +
580 	    sizeof(struct read_element_status_page_header) +
581 	    (desclen * sc->sc_counts[chet]);
582 
583 	/*
584 	 * Reallocate storage for descriptors and get them from the
585 	 * device.
586 	 */
587 	free(data, M_DEVBUF);
588 	data = malloc(size, M_DEVBUF, M_WAITOK);
589 	error = ch_getelemstatus(sc, sc->sc_firsts[chet],
590 	    sc->sc_counts[chet], data, size, want_voltags);
591 	if (error)
592 		goto done;
593 
594 	/*
595 	 * Fill in the user status array.
596 	 */
597 	st_hdr = (struct read_element_status_header *)data;
598 	pg_hdr = (struct read_element_status_page_header *) (st_hdr + 1);
599 
600 	avail = _2btol(st_hdr->count);
601 	if (avail != sc->sc_counts[chet]) {
602 		error = EINVAL;
603 		goto done;
604 	}
605 	udsize = avail * sizeof(struct changer_element_status);
606 
607 	user_data = malloc(udsize, M_DEVBUF, M_WAITOK | M_ZERO);
608 
609 	desc = (caddr_t)(pg_hdr + 1);
610 	for (i = 0; i < avail; ++i) {
611 		struct changer_element_status *ces = &(user_data[i]);
612 		copy_element_status(pg_hdr->flags,
613 		    (struct read_element_status_descriptor *)desc, ces);
614 		desc += desclen;
615 	}
616 
617 	/* Copy array out to userspace. */
618 	error = copyout(user_data, cesr->cesr_data, udsize);
619 
620  done:
621 	if (data != NULL)
622 		free(data, M_DEVBUF);
623 	if (user_data != NULL)
624 		free(user_data, M_DEVBUF);
625 	return (error);
626 }
627 
628 int
629 ch_getelemstatus(sc, first, count, data, datalen, voltag)
630 	struct ch_softc *sc;
631 	int first;
632 	int count;
633 	caddr_t data;
634 	size_t datalen;
635 	int voltag;
636 {
637 	struct scsi_read_element_status cmd;
638 
639 	/*
640 	 * Build SCSI command.
641 	 */
642 	bzero(&cmd, sizeof(cmd));
643 	cmd.opcode = READ_ELEMENT_STATUS;
644 	_lto2b(first, cmd.sea);
645 	_lto2b(count, cmd.count);
646 	_lto3b(datalen, cmd.len);
647 	if (voltag)
648 		cmd.byte2 |= READ_ELEMENT_STATUS_VOLTAG;
649 
650 	/*
651 	 * Send command to changer.
652 	 */
653 	return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
654 	    sizeof(cmd), (u_char *)data, datalen, CHRETRIES, 100000, NULL,
655 	    SCSI_DATA_IN));
656 }
657 
658 
659 /*
660  * Ask the device about itself and fill in the parameters in our
661  * softc.
662  */
663 int
664 ch_get_params(sc, flags)
665 	struct ch_softc *sc;
666 	int flags;
667 {
668 	union scsi_mode_sense_buf *data;
669 	struct page_element_address_assignment *ea;
670 	struct page_device_capabilities *cap;
671 	int error, from;
672 	u_int8_t *moves, *exchanges;
673 
674 	data = malloc(sizeof(*data), M_TEMP, M_NOWAIT);
675 	if (data == NULL)
676 		return (ENOMEM);
677 
678 	/*
679 	 * Grab info from the element address assignment page (0x1d).
680 	 */
681 	error = scsi_do_mode_sense(sc->sc_link, 0x1d, data,
682 	    (void **)&ea, NULL, NULL, NULL, sizeof(*ea), flags, NULL);
683 	if (error == 0 && ea == NULL)
684 		error = EIO;
685 	if (error != 0) {
686 #ifdef CHANGER_DEBUG
687 		printf("%s: could not sense element address page\n",
688 		    sc->sc_dev.dv_xname);
689 #endif
690 		free(data, M_TEMP);
691 		return (error);
692 	}
693 
694 	sc->sc_firsts[CHET_MT] = _2btol(ea->mtea);
695 	sc->sc_counts[CHET_MT] = _2btol(ea->nmte);
696 	sc->sc_firsts[CHET_ST] = _2btol(ea->fsea);
697 	sc->sc_counts[CHET_ST] = _2btol(ea->nse);
698 	sc->sc_firsts[CHET_IE] = _2btol(ea->fieea);
699 	sc->sc_counts[CHET_IE] = _2btol(ea->niee);
700 	sc->sc_firsts[CHET_DT] = _2btol(ea->fdtea);
701 	sc->sc_counts[CHET_DT] = _2btol(ea->ndte);
702 
703 	/* XXX Ask for transport geometry page. */
704 
705 	/*
706 	 * Grab info from the capabilities page (0x1f).
707 	 */
708 	error = scsi_do_mode_sense(sc->sc_link, 0x1f, data,
709 	    (void **)&cap, NULL, NULL, NULL, sizeof(*cap), flags, NULL);
710 	if (cap == NULL)
711 		error = EIO;
712 	if (error != 0) {
713 #ifdef CHANGER_DEBUG
714 		printf("%s: could not sense capabilities page\n",
715 		    sc->sc_dev.dv_xname);
716 #endif
717 		free(data, M_TEMP);
718 		return (error);
719 	}
720 
721 	bzero(sc->sc_movemask, sizeof(sc->sc_movemask));
722 	bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask));
723 	moves = &cap->move_from_mt;
724 	exchanges = &cap->exchange_with_mt;
725 	for (from = CHET_MT; from <= CHET_DT; ++from) {
726 		sc->sc_movemask[from] = moves[from];
727 		sc->sc_exchangemask[from] = exchanges[from];
728 	}
729 
730 	sc->sc_link->flags |= SDEV_MEDIA_LOADED;
731 	free(data, M_TEMP);
732 	return (0);
733 }
734 
735 void
736 ch_get_quirks(sc, inqbuf)
737 	struct ch_softc *sc;
738 	struct scsi_inquiry_data *inqbuf;
739 {
740 	const struct chquirk *match;
741 	int priority;
742 
743 	sc->sc_settledelay = 0;
744 
745 	match = (const struct chquirk *)scsi_inqmatch(inqbuf,
746 	    (caddr_t)chquirks,
747 	    sizeof(chquirks) / sizeof(chquirks[0]),
748 	    sizeof(chquirks[0]), &priority);
749 	if (priority != 0) {
750 		sc->sc_settledelay = match->cq_settledelay;
751 	}
752 }
753 
754 /*
755  * Look at the returned sense and act on the error and detirmine
756  * The unix error number to pass back... (0 = report no error)
757  *                            (-1 = continue processing)
758  */
759 int
760 ch_interpret_sense(xs)
761 	struct scsi_xfer *xs;
762 {
763 	struct scsi_sense_data *sense = &xs->sense;
764 	struct scsi_link *sc_link = xs->sc_link;
765 	u_int8_t serr = sense->error_code & SSD_ERRCODE;
766 	u_int8_t skey = sense->flags & SSD_KEY;
767 
768 	if (((sc_link->flags & SDEV_OPEN) == 0) ||
769 	    (serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED))
770 		return (EJUSTRETURN); /* let the generic code handle it */
771 
772 	switch (skey) {
773 
774 	/*
775 	 * We do custom processing in ch for the unit becoming ready case.
776 	 * in this case we do not allow xs->retries to be decremented
777 	 * only on the "Unit Becoming Ready" case. This is because tape
778 	 * changers report "Unit Becoming Ready" when they rescan their
779 	 * state (i.e. when the door got opened) and can take a long time
780 	 * for large units. Rather than having a massive timeout for
781 	 * all operations (which would cause other problems) we allow
782 	 * changers to wait (but be interruptable with Ctrl-C) forever
783 	 * as long as they are reporting that they are becoming ready.
784 	 * all other cases are handled as per the default.
785 	 */
786 	case SKEY_NOT_READY:
787 		if ((xs->flags & SCSI_IGNORE_NOT_READY) != 0)
788 			return (0);
789 		switch (ASC_ASCQ(sense)) {
790 		case SENSE_NOT_READY_BECOMING_READY:
791 			SC_DEBUG(sc_link, SDEV_DB1, ("not ready: busy (%#x)\n",
792 			    sense->add_sense_code_qual));
793 			/* don't count this as a retry */
794 			xs->retries++;
795 			return (scsi_delay(xs, 1));
796 		default:
797 			return (EJUSTRETURN);
798 	}
799 	default:
800 		return (EJUSTRETURN);
801 	}
802 }
803