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