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