xref: /netbsd-src/sys/dev/scsipi/ch.c (revision dc306354b0b29af51801a7632f1e95265a68cd81)
1 /*	$NetBSD: ch.c,v 1.37 1998/12/17 22:28:07 gibbs Exp $	*/
2 
3 /*
4  * Copyright (c) 1996, 1997, 1998 Jason R. Thorpe <thorpej@and.com>
5  * All rights reserved.
6  *
7  * Partially based on an autochanger driver written by Stefan Grefen
8  * and on an autochanger driver written by the Systems Programming Group
9  * at the University of Utah Computer Science Department.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgements:
21  *	This product includes software developed by Jason R. Thorpe
22  *	for And Communications, http://www.and.com/
23  * 4. The name of the author may not be used to endorse or promote products
24  *    derived from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/errno.h>
42 #include <sys/ioctl.h>
43 #include <sys/buf.h>
44 #include <sys/proc.h>
45 #include <sys/user.h>
46 #include <sys/chio.h>
47 #include <sys/device.h>
48 #include <sys/malloc.h>
49 #include <sys/conf.h>
50 #include <sys/fcntl.h>
51 
52 #include <dev/scsipi/scsipi_all.h>
53 #include <dev/scsipi/scsi_all.h>
54 #include <dev/scsipi/scsi_changer.h>
55 #include <dev/scsipi/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 scsipi_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 __P((struct device *, struct cfdata *, void *));
98 void	chattach __P((struct device *, struct device *, void *));
99 
100 struct cfattach ch_ca = {
101 	sizeof(struct ch_softc), chmatch, chattach
102 };
103 
104 extern struct cfdriver ch_cd;
105 
106 struct scsipi_inquiry_pattern ch_patterns[] = {
107 	{T_CHANGER, T_REMOV,
108 	 "",		"",		""},
109 };
110 
111 /* SCSI glue */
112 struct scsipi_device ch_switch = {
113 	NULL, NULL, NULL, NULL
114 };
115 
116 int	ch_move __P((struct ch_softc *, struct changer_move *));
117 int	ch_exchange __P((struct ch_softc *, struct changer_exchange *));
118 int	ch_position __P((struct ch_softc *, struct changer_position *));
119 int	ch_ielem __P((struct ch_softc *));
120 int	ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *));
121 int	ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t));
122 int	ch_get_params __P((struct ch_softc *, int));
123 void	ch_get_quirks __P((struct ch_softc *,
124 	    struct scsipi_inquiry_pattern *));
125 
126 /*
127  * SCSI changer quirks.
128  */
129 struct chquirk {
130 	struct	scsipi_inquiry_pattern cq_match; /* device id pattern */
131 	int	cq_settledelay;	/* settle delay, in seconds */
132 };
133 
134 struct chquirk chquirks[] = {
135 	{{T_CHANGER, T_REMOV,
136 	  "SPECTRA",	"9000",		"0200"},
137 	 75},
138 };
139 
140 int
141 chmatch(parent, match, aux)
142 	struct device *parent;
143 	struct cfdata *match;
144 	void *aux;
145 {
146 	struct scsipibus_attach_args *sa = aux;
147 	int priority;
148 
149 	(void)scsipi_inqmatch(&sa->sa_inqbuf,
150 	    (caddr_t)ch_patterns, sizeof(ch_patterns) / sizeof(ch_patterns[0]),
151 	    sizeof(ch_patterns[0]), &priority);
152 
153 	return (priority);
154 }
155 
156 void
157 chattach(parent, self, aux)
158 	struct device *parent, *self;
159 	void *aux;
160 {
161 	struct ch_softc *sc = (struct ch_softc *)self;
162 	struct scsipibus_attach_args *sa = aux;
163 	struct scsipi_link *link = sa->sa_sc_link;
164 
165 	/* Glue into the SCSI bus */
166 	sc->sc_link = link;
167 	link->device = &ch_switch;
168 	link->device_softc = sc;
169 	link->openings = 1;
170 
171 	printf("\n");
172 
173 	/*
174 	 * Find out our device's quirks.
175 	 */
176 	ch_get_quirks(sc, &sa->sa_inqbuf);
177 
178 	/*
179 	 * Some changers require a long time to settle out, to do
180 	 * tape inventory, for instance.
181 	 */
182 	if (sc->sc_settledelay) {
183 		printf("%s: waiting %d seconds for changer to settle...\n",
184 		    sc->sc_dev.dv_xname, sc->sc_settledelay);
185 		delay(1000000 * sc->sc_settledelay);
186 	}
187 
188 	/*
189 	 * Get information about the device.  Note we can't use
190 	 * interrupts yet.
191 	 */
192 	if (ch_get_params(sc, SCSI_AUTOCONF))
193 		printf("%s: offline\n", sc->sc_dev.dv_xname);
194 	else {
195 #define PLURAL(c)	(c) == 1 ? "" : "s"
196 		printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n",
197 		    sc->sc_dev.dv_xname,
198 		    sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]),
199 		    sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]),
200 		    sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]),
201 		    sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE]));
202 #undef PLURAL
203 #ifdef CHANGER_DEBUG
204 		printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n",
205 		    sc->sc_dev.dv_xname,
206 		    sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST],
207 		    sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]);
208 		printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n",
209 		    sc->sc_dev.dv_xname,
210 		    sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST],
211 		    sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]);
212 #endif /* CHANGER_DEBUG */
213 	}
214 
215 	/* Default the current picker. */
216 	sc->sc_picker = sc->sc_firsts[CHET_MT];
217 }
218 
219 int
220 chopen(dev, flags, fmt, p)
221 	dev_t dev;
222 	int flags, fmt;
223 	struct proc *p;
224 {
225 	struct ch_softc *sc;
226 	int unit, error;
227 
228 	unit = CHUNIT(dev);
229 	if ((unit >= ch_cd.cd_ndevs) ||
230 	    ((sc = ch_cd.cd_devs[unit]) == NULL))
231 		return (ENXIO);
232 
233 	/*
234 	 * Only allow one open at a time.
235 	 */
236 	if (sc->sc_link->flags & SDEV_OPEN)
237 		return (EBUSY);
238 
239 	if ((error = scsipi_adapter_addref(sc->sc_link)) != 0)
240 		return (error);
241 
242 	sc->sc_link->flags |= SDEV_OPEN;
243 
244 	/*
245 	 * Absorb any unit attention errors.  Ignore "not ready"
246 	 * since this might occur if e.g. a tape isn't actually
247 	 * loaded in the drive.
248 	 */
249 	error = scsipi_test_unit_ready(sc->sc_link,
250 	    SCSI_IGNORE_NOT_READY|SCSI_IGNORE_MEDIA_CHANGE);
251 	if (error)
252 		goto bad;
253 
254 	/*
255 	 * Make sure our parameters are up to date.
256 	 */
257 	if ((error = ch_get_params(sc, 0)) != 0)
258 		goto bad;
259 
260 	return (0);
261 
262  bad:
263 	scsipi_adapter_delref(sc->sc_link);
264 	sc->sc_link->flags &= ~SDEV_OPEN;
265 	return (error);
266 }
267 
268 int
269 chclose(dev, flags, fmt, p)
270 	dev_t dev;
271 	int flags, fmt;
272 	struct proc *p;
273 {
274 	struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
275 
276 	scsipi_wait_drain(sc->sc_link);
277 
278 	scsipi_adapter_delref(sc->sc_link);
279 	sc->sc_link->flags &= ~SDEV_OPEN;
280 	return (0);
281 }
282 
283 int
284 chioctl(dev, cmd, data, flags, p)
285 	dev_t dev;
286 	u_long cmd;
287 	caddr_t data;
288 	int flags;
289 	struct proc *p;
290 {
291 	struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
292 	int error = 0;
293 
294 	/*
295 	 * If this command can change the device's state, we must
296 	 * have the device open for writing.
297 	 */
298 	switch (cmd) {
299 	case CHIOGPICKER:
300 	case CHIOGPARAMS:
301 	case CHIOGSTATUS:
302 		break;
303 
304 	default:
305 		if ((flags & FWRITE) == 0)
306 			return (EBADF);
307 	}
308 
309 	switch (cmd) {
310 	case CHIOMOVE:
311 		error = ch_move(sc, (struct changer_move *)data);
312 		break;
313 
314 	case CHIOEXCHANGE:
315 		error = ch_exchange(sc, (struct changer_exchange *)data);
316 		break;
317 
318 	case CHIOPOSITION:
319 		error = ch_position(sc, (struct changer_position *)data);
320 		break;
321 
322 	case CHIOGPICKER:
323 		*(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT];
324 		break;
325 
326 	case CHIOSPICKER:	{
327 		int new_picker = *(int *)data;
328 
329 		if (new_picker > (sc->sc_counts[CHET_MT] - 1))
330 			return (EINVAL);
331 		sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker;
332 		break;		}
333 
334 	case CHIOGPARAMS:	{
335 		struct changer_params *cp = (struct changer_params *)data;
336 
337 		cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT];
338 		cp->cp_npickers = sc->sc_counts[CHET_MT];
339 		cp->cp_nslots = sc->sc_counts[CHET_ST];
340 		cp->cp_nportals = sc->sc_counts[CHET_IE];
341 		cp->cp_ndrives = sc->sc_counts[CHET_DT];
342 		break;		}
343 
344 	case CHIOIELEM:
345 		error = ch_ielem(sc);
346 		break;
347 
348 	case CHIOGSTATUS:	{
349 		struct changer_element_status *ces =
350 		    (struct changer_element_status *)data;
351 
352 		error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data);
353 		break;		}
354 
355 	/* Implement prevent/allow? */
356 
357 	default:
358 		error = scsipi_do_ioctl(sc->sc_link, dev, cmd, data, flags, p);
359 		break;
360 	}
361 
362 	return (error);
363 }
364 
365 int
366 ch_move(sc, cm)
367 	struct ch_softc *sc;
368 	struct changer_move *cm;
369 {
370 	struct scsi_move_medium cmd;
371 	u_int16_t fromelem, toelem;
372 
373 	/*
374 	 * Check arguments.
375 	 */
376 	if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
377 		return (EINVAL);
378 	if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) ||
379 	    (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1)))
380 		return (ENODEV);
381 
382 	/*
383 	 * Check the request against the changer's capabilities.
384 	 */
385 	if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
386 		return (ENODEV);
387 
388 	/*
389 	 * Calculate the source and destination elements.
390 	 */
391 	fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
392 	toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
393 
394 	/*
395 	 * Build the SCSI command.
396 	 */
397 	bzero(&cmd, sizeof(cmd));
398 	cmd.opcode = MOVE_MEDIUM;
399 	_lto2b(sc->sc_picker, cmd.tea);
400 	_lto2b(fromelem, cmd.src);
401 	_lto2b(toelem, cmd.dst);
402 	if (cm->cm_flags & CM_INVERT)
403 		cmd.flags |= MOVE_MEDIUM_INVERT;
404 
405 	/*
406 	 * Send command to changer.
407 	 */
408 	return (scsipi_command(sc->sc_link,
409 	    (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES,
410 	    100000, NULL, 0));
411 }
412 
413 int
414 ch_exchange(sc, ce)
415 	struct ch_softc *sc;
416 	struct changer_exchange *ce;
417 {
418 	struct scsi_exchange_medium cmd;
419 	u_int16_t src, dst1, dst2;
420 
421 	/*
422 	 * Check arguments.
423 	 */
424 	if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
425 	    (ce->ce_sdsttype > CHET_DT))
426 		return (EINVAL);
427 	if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) ||
428 	    (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) ||
429 	    (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1)))
430 		return (ENODEV);
431 
432 	/*
433 	 * Check the request against the changer's capabilities.
434 	 */
435 	if (((sc->sc_exchangemask[ce->ce_srctype] &
436 	     (1 << ce->ce_fdsttype)) == 0) ||
437 	    ((sc->sc_exchangemask[ce->ce_fdsttype] &
438 	     (1 << ce->ce_sdsttype)) == 0))
439 		return (ENODEV);
440 
441 	/*
442 	 * Calculate the source and destination elements.
443 	 */
444 	src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
445 	dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
446 	dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
447 
448 	/*
449 	 * Build the SCSI command.
450 	 */
451 	bzero(&cmd, sizeof(cmd));
452 	cmd.opcode = EXCHANGE_MEDIUM;
453 	_lto2b(sc->sc_picker, cmd.tea);
454 	_lto2b(src, cmd.src);
455 	_lto2b(dst1, cmd.fdst);
456 	_lto2b(dst2, cmd.sdst);
457 	if (ce->ce_flags & CE_INVERT1)
458 		cmd.flags |= EXCHANGE_MEDIUM_INV1;
459 	if (ce->ce_flags & CE_INVERT2)
460 		cmd.flags |= EXCHANGE_MEDIUM_INV2;
461 
462 	/*
463 	 * Send command to changer.
464 	 */
465 	return (scsipi_command(sc->sc_link,
466 	    (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES,
467 	    100000, NULL, 0));
468 }
469 
470 int
471 ch_position(sc, cp)
472 	struct ch_softc *sc;
473 	struct changer_position *cp;
474 {
475 	struct scsi_position_to_element cmd;
476 	u_int16_t dst;
477 
478 	/*
479 	 * Check arguments.
480 	 */
481 	if (cp->cp_type > CHET_DT)
482 		return (EINVAL);
483 	if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1))
484 		return (ENODEV);
485 
486 	/*
487 	 * Calculate the destination element.
488 	 */
489 	dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit;
490 
491 	/*
492 	 * Build the SCSI command.
493 	 */
494 	bzero(&cmd, sizeof(cmd));
495 	cmd.opcode = POSITION_TO_ELEMENT;
496 	_lto2b(sc->sc_picker, cmd.tea);
497 	_lto2b(dst, cmd.dst);
498 	if (cp->cp_flags & CP_INVERT)
499 		cmd.flags |= POSITION_TO_ELEMENT_INVERT;
500 
501 	/*
502 	 * Send command to changer.
503 	 */
504 	return (scsipi_command(sc->sc_link,
505 	    (struct scsipi_generic *)&cmd, sizeof(cmd), NULL, 0, CHRETRIES,
506 	    100000, NULL, 0));
507 }
508 
509 /*
510  * Perform a READ ELEMENT STATUS on behalf of the user, and return to
511  * the user only the data the user is interested in (i.e. an array of
512  * flags bytes).
513  */
514 int
515 ch_usergetelemstatus(sc, chet, uptr)
516 	struct ch_softc *sc;
517 	int chet;
518 	u_int8_t *uptr;
519 {
520 	struct read_element_status_header *st_hdr;
521 	struct read_element_status_page_header *pg_hdr;
522 	struct read_element_status_descriptor *desc;
523 	caddr_t data = NULL;
524 	size_t size, desclen;
525 	int avail, i, error = 0;
526 	u_int8_t *user_data = NULL;
527 
528 	/*
529 	 * If there are no elements of the requested type in the changer,
530 	 * the request is invalid.
531 	 */
532 	if (sc->sc_counts[chet] == 0)
533 		return (EINVAL);
534 
535 	/*
536 	 * Request one descriptor for the given element type.  This
537 	 * is used to determine the size of the descriptor so that
538 	 * we can allocate enough storage for all of them.  We assume
539 	 * that the first one can fit into 1k.
540 	 */
541 	data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK);
542 	error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024);
543 	if (error)
544 		goto done;
545 
546 	st_hdr = (struct read_element_status_header *)data;
547 	pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr +
548 	    sizeof(struct read_element_status_header));
549 	desclen = _2btol(pg_hdr->edl);
550 
551 	size = sizeof(struct read_element_status_header) +
552 	    sizeof(struct read_element_status_page_header) +
553 	    (desclen * sc->sc_counts[chet]);
554 
555 	/*
556 	 * Reallocate storage for descriptors and get them from the
557 	 * device.
558 	 */
559 	free(data, M_DEVBUF);
560 	data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK);
561 	error = ch_getelemstatus(sc, sc->sc_firsts[chet],
562 	    sc->sc_counts[chet], data, size);
563 	if (error)
564 		goto done;
565 
566 	/*
567 	 * Fill in the user status array.
568 	 */
569 	st_hdr = (struct read_element_status_header *)data;
570 	avail = _2btol(st_hdr->count);
571 
572 	if (avail != sc->sc_counts[chet])
573 		printf("%s: warning, READ ELEMENT STATUS avail != count\n",
574 		    sc->sc_dev.dv_xname);
575 
576 	user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK);
577 
578 	desc = (struct read_element_status_descriptor *)((u_long)data +
579 	    sizeof(struct read_element_status_header) +
580 	    sizeof(struct read_element_status_page_header));
581 	for (i = 0; i < avail; ++i) {
582 		user_data[i] = desc->flags1;
583 		(u_long)desc += desclen;
584 	}
585 
586 	/* Copy flags array out to userspace. */
587 	error = copyout(user_data, uptr, avail);
588 
589  done:
590 	if (data != NULL)
591 		free(data, M_DEVBUF);
592 	if (user_data != NULL)
593 		free(user_data, M_DEVBUF);
594 	return (error);
595 }
596 
597 int
598 ch_getelemstatus(sc, first, count, data, datalen)
599 	struct ch_softc *sc;
600 	int first, count;
601 	caddr_t data;
602 	size_t datalen;
603 {
604 	struct scsi_read_element_status cmd;
605 
606 	/*
607 	 * Build SCSI command.
608 	 */
609 	bzero(&cmd, sizeof(cmd));
610 	cmd.opcode = READ_ELEMENT_STATUS;
611 	_lto2b(first, cmd.sea);
612 	_lto2b(count, cmd.count);
613 	_lto3b(datalen, cmd.len);
614 
615 	/*
616 	 * Send command to changer.
617 	 */
618 	return (scsipi_command(sc->sc_link,
619 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
620 	    (u_char *)data, datalen, CHRETRIES, 100000, NULL, SCSI_DATA_IN));
621 }
622 
623 
624 int
625 ch_ielem(sc)
626 	struct ch_softc *sc;
627 {
628 	int tmo;
629 	struct scsi_initialize_element_status cmd;
630 
631 	/*
632 	 * Build SCSI command.
633 	 */
634 	bzero(&cmd, sizeof(cmd));
635 	cmd.opcode = INITIALIZE_ELEMENT_STATUS;
636 
637 	/*
638 	 * Send command to changer.
639 	 *
640 	 * The problem is, how long to allow for the command?
641 	 * It can take a *really* long time, and also depends
642 	 * on unknowable factors such as whether there are
643 	 * *almost* readable labels on tapes that a barcode
644 	 * reader is trying to decipher.
645 	 *
646 	 * I'm going to make this long enough to allow 5 minutes
647 	 * per element plus an initial 10 minute wait.
648 	 */
649 	tmo =	sc->sc_counts[CHET_MT] +
650 		sc->sc_counts[CHET_ST] +
651 		sc->sc_counts[CHET_IE] +
652 		sc->sc_counts[CHET_DT];
653 	tmo *= 5 * 1000;
654 	tmo += (10 * 60 * 1000);
655 
656 	return (scsipi_command(sc->sc_link,
657 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
658 	    NULL, 0, CHRETRIES, tmo, NULL, 0));
659 }
660 
661 /*
662  * Ask the device about itself and fill in the parameters in our
663  * softc.
664  */
665 int
666 ch_get_params(sc, scsiflags)
667 	struct ch_softc *sc;
668 	int scsiflags;
669 {
670 	struct scsi_mode_sense cmd;
671 	struct scsi_mode_sense_data {
672 		struct scsi_mode_header header;
673 		union {
674 			struct page_element_address_assignment ea;
675 			struct page_transport_geometry_parameters tg;
676 			struct page_device_capabilities cap;
677 		} pages;
678 	} sense_data;
679 	int error, from;
680 	u_int8_t *moves, *exchanges;
681 
682 	/*
683 	 * Grab info from the element address assignment page.
684 	 */
685 	bzero(&cmd, sizeof(cmd));
686 	bzero(&sense_data, sizeof(sense_data));
687 	cmd.opcode = SCSI_MODE_SENSE;
688 	cmd.byte2 |= 0x08;	/* disable block descriptors */
689 	cmd.page = 0x1d;
690 	cmd.length = (sizeof(sense_data) & 0xff);
691 	error = scsipi_command(sc->sc_link,
692 	    (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&sense_data,
693 	    sizeof(sense_data), CHRETRIES, 6000, NULL,
694 	    scsiflags | SCSI_DATA_IN);
695 	if (error) {
696 		printf("%s: could not sense element address page\n",
697 		    sc->sc_dev.dv_xname);
698 		return (error);
699 	}
700 
701 	sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea);
702 	sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte);
703 	sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea);
704 	sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse);
705 	sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea);
706 	sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee);
707 	sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea);
708 	sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte);
709 
710 	/* XXX ask for page trasport geom */
711 
712 	/*
713 	 * Grab info from the capabilities page.
714 	 */
715 	bzero(&cmd, sizeof(cmd));
716 	bzero(&sense_data, sizeof(sense_data));
717 	cmd.opcode = SCSI_MODE_SENSE;
718 	/*
719 	 * XXX: Note: not all changers can deal with disabled block descriptors
720 	 */
721 	cmd.byte2 = 0x08;	/* disable block descriptors */
722 	cmd.page = 0x1f;
723 	cmd.length = (sizeof(sense_data) & 0xff);
724 	error = scsipi_command(sc->sc_link,
725 	    (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&sense_data,
726 	    sizeof(sense_data), CHRETRIES, 6000, NULL,
727 	    scsiflags | SCSI_DATA_IN);
728 	if (error) {
729 		printf("%s: could not sense capabilities page\n",
730 		    sc->sc_dev.dv_xname);
731 		return (error);
732 	}
733 
734 	bzero(sc->sc_movemask, sizeof(sc->sc_movemask));
735 	bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask));
736 	moves = &sense_data.pages.cap.move_from_mt;
737 	exchanges = &sense_data.pages.cap.exchange_with_mt;
738 	for (from = CHET_MT; from <= CHET_DT; ++from) {
739 		sc->sc_movemask[from] = moves[from];
740 		sc->sc_exchangemask[from] = exchanges[from];
741 	}
742 
743 	sc->sc_link->flags |= SDEV_MEDIA_LOADED;
744 	return (0);
745 }
746 
747 void
748 ch_get_quirks(sc, inqbuf)
749 	struct ch_softc *sc;
750 	struct scsipi_inquiry_pattern *inqbuf;
751 {
752 	struct chquirk *match;
753 	int priority;
754 
755 	sc->sc_settledelay = 0;
756 
757 	match = (struct chquirk *)scsipi_inqmatch(inqbuf,
758 	    (caddr_t)chquirks,
759 	    sizeof(chquirks) / sizeof(chquirks[0]),
760 	    sizeof(chquirks[0]), &priority);
761 	if (priority != 0)
762 		sc->sc_settledelay = match->cq_settledelay;
763 }
764