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