xref: /netbsd-src/sys/dev/scsipi/ch.c (revision 1394f01b4a9e99092957ca5d824d67219565d9b5)
1 /*	$NetBSD: ch.c,v 1.26 1997/02/21 22:06:52 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 <scsi/scsi_all.h>
53 #include <scsi/scsi_changer.h>
54 #include <scsi/scsiconf.h>
55 
56 #define CHRETRIES	2
57 #define CHUNIT(x)	(minor((x)))
58 
59 struct ch_softc {
60 	struct device	sc_dev;		/* generic device info */
61 	struct scsi_link *sc_link;	/* link in the SCSI bus */
62 
63 	int		sc_picker;	/* current picker */
64 
65 	/*
66 	 * The following information is obtained from the
67 	 * element address assignment page.
68 	 */
69 	int		sc_firsts[4];	/* firsts, indexed by CHET_* */
70 	int		sc_counts[4];	/* counts, indexed by CHET_* */
71 
72 	/*
73 	 * The following mask defines the legal combinations
74 	 * of elements for the MOVE MEDIUM command.
75 	 */
76 	u_int8_t	sc_movemask[4];
77 
78 	/*
79 	 * As above, but for EXCHANGE MEDIUM.
80 	 */
81 	u_int8_t	sc_exchangemask[4];
82 
83 	int		flags;		/* misc. info */
84 
85 	/*
86 	 * Quirks; see below.
87 	 */
88 	int		sc_settledelay;	/* delay for settle */
89 
90 };
91 
92 /* sc_flags */
93 #define CHF_ROTATE	0x01		/* picker can rotate */
94 
95 /* Autoconfiguration glue */
96 #ifdef __BROKEN_INDIRECT_CONFIG
97 int	chmatch __P((struct device *, void *, void *));
98 #else
99 int	chmatch __P((struct device *, struct cfdata *, void *));
100 #endif
101 void	chattach __P((struct device *, struct device *, void *));
102 
103 struct cfattach ch_ca = {
104 	sizeof(struct ch_softc), chmatch, chattach
105 };
106 
107 struct cfdriver ch_cd = {
108 	NULL, "ch", DV_DULL
109 };
110 
111 struct scsi_inquiry_pattern ch_patterns[] = {
112 	{T_CHANGER, T_REMOV,
113 	 "",		"",		""},
114 };
115 
116 /* SCSI glue */
117 struct scsi_device ch_switch = {
118 	NULL, NULL, NULL, NULL
119 };
120 
121 int	ch_move __P((struct ch_softc *, struct changer_move *));
122 int	ch_exchange __P((struct ch_softc *, struct changer_exchange *));
123 int	ch_position __P((struct ch_softc *, struct changer_position *));
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 *, struct scsi_inquiry_data *));
128 
129 /*
130  * SCSI changer quirks.
131  */
132 struct chquirk {
133 	struct	scsi_inquiry_pattern cq_match; /* device id pattern */
134 	int	cq_settledelay;	/* settle delay, in seconds */
135 };
136 
137 struct chquirk chquirks[] = {
138 	{{T_CHANGER, T_REMOV,
139 	  "SPECTRA",	"9000",		"0200"},
140 	 75},
141 };
142 
143 int
144 chmatch(parent, match, aux)
145 	struct device *parent;
146 #ifdef __BROKEN_INDIRECT_CONFIG
147 	void *match;
148 #else
149 	struct cfdata *match;
150 #endif
151 	void *aux;
152 {
153 	struct scsibus_attach_args *sa = aux;
154 	int priority;
155 
156 	(void)scsi_inqmatch(sa->sa_inqbuf,
157 	    (caddr_t)ch_patterns, sizeof(ch_patterns)/sizeof(ch_patterns[0]),
158 	    sizeof(ch_patterns[0]), &priority);
159 
160 	return (priority);
161 }
162 
163 void
164 chattach(parent, self, aux)
165 	struct device *parent, *self;
166 	void *aux;
167 {
168 	struct ch_softc *sc = (struct ch_softc *)self;
169 	struct scsibus_attach_args *sa = aux;
170 	struct scsi_link *link = sa->sa_sc_link;
171 
172 	/* Glue into the SCSI bus */
173 	sc->sc_link = link;
174 	link->device = &ch_switch;
175 	link->device_softc = sc;
176 	link->openings = 1;
177 
178 	printf("\n");
179 
180 	/*
181 	 * Find out our device's quirks.
182 	 */
183 	ch_get_quirks(sc, sa->sa_inqbuf);
184 
185 	/*
186 	 * Some changers require a long time to settle out, to do
187 	 * tape inventory, for instance.
188 	 */
189 	if (sc->sc_settledelay) {
190 		printf("%s: waiting %d seconds for changer to settle...\n",
191 		    sc->sc_dev.dv_xname, sc->sc_settledelay);
192 		delay(1000000 * sc->sc_settledelay);
193 	}
194 
195 	/*
196 	 * Get information about the device.  Note we can't use
197 	 * interrupts yet.
198 	 */
199 	if (ch_get_params(sc, SCSI_AUTOCONF))
200 		printf("%s: offline\n", sc->sc_dev.dv_xname);
201 	else {
202 #define PLURAL(c)	(c) == 1 ? "" : "s"
203 		printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n",
204 		    sc->sc_dev.dv_xname,
205 		    sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]),
206 		    sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]),
207 		    sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]),
208 		    sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE]));
209 #undef PLURAL
210 #ifdef CHANGER_DEBUG
211 		printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n",
212 		    sc->sc_dev.dv_xname,
213 		    sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST],
214 		    sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]);
215 		printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n",
216 		    sc->sc_dev.dv_xname,
217 		    sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST],
218 		    sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]);
219 #endif /* CHANGER_DEBUG */
220 	}
221 
222 	/* Default the current picker. */
223 	sc->sc_picker = sc->sc_firsts[CHET_MT];
224 }
225 
226 int
227 chopen(dev, flags, fmt, p)
228 	dev_t dev;
229 	int flags, fmt;
230 	struct proc *p;
231 {
232 	struct ch_softc *sc;
233 	int unit, error = 0;
234 
235 	unit = CHUNIT(dev);
236 	if ((unit >= ch_cd.cd_ndevs) ||
237 	    ((sc = ch_cd.cd_devs[unit]) == NULL))
238 		return (ENXIO);
239 
240 	/*
241 	 * Only allow one open at a time.
242 	 */
243 	if (sc->sc_link->flags & SDEV_OPEN)
244 		return (EBUSY);
245 
246 	sc->sc_link->flags |= SDEV_OPEN;
247 
248 	/*
249 	 * Absorb any unit attention errors.  Ignore "not ready"
250 	 * since this might occur if e.g. a tape isn't actually
251 	 * loaded in the drive.
252 	 */
253 	error = scsi_test_unit_ready(sc->sc_link,
254 	    SCSI_IGNORE_NOT_READY|SCSI_IGNORE_MEDIA_CHANGE);
255 	if (error)
256 		goto bad;
257 
258 	/*
259 	 * Make sure our parameters are up to date.
260 	 */
261 	if ((error = ch_get_params(sc, 0)) != 0)
262 		goto bad;
263 
264 	return (0);
265 
266  bad:
267 	sc->sc_link->flags &= ~SDEV_OPEN;
268 	return (error);
269 }
270 
271 int
272 chclose(dev, flags, fmt, p)
273 	dev_t dev;
274 	int flags, fmt;
275 	struct proc *p;
276 {
277 	struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
278 
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 CHIOGSTATUS:	{
345 		struct changer_element_status *ces =
346 		    (struct changer_element_status *)data;
347 
348 		error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data);
349 		break;		}
350 
351 	/* Implement prevent/allow? */
352 
353 	default:
354 		error = scsi_do_ioctl(sc->sc_link, dev, cmd, data, flags, p);
355 		break;
356 	}
357 
358 	return (error);
359 }
360 
361 int
362 ch_move(sc, cm)
363 	struct ch_softc *sc;
364 	struct changer_move *cm;
365 {
366 	struct scsi_move_medium cmd;
367 	u_int16_t fromelem, toelem;
368 
369 	/*
370 	 * Check arguments.
371 	 */
372 	if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
373 		return (EINVAL);
374 	if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) ||
375 	    (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1)))
376 		return (ENODEV);
377 
378 	/*
379 	 * Check the request against the changer's capabilities.
380 	 */
381 	if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
382 		return (EINVAL);
383 
384 	/*
385 	 * Calculate the source and destination elements.
386 	 */
387 	fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
388 	toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
389 
390 	/*
391 	 * Build the SCSI command.
392 	 */
393 	bzero(&cmd, sizeof(cmd));
394 	cmd.opcode = MOVE_MEDIUM;
395 	_lto2b(sc->sc_picker, cmd.tea);
396 	_lto2b(fromelem, cmd.src);
397 	_lto2b(toelem, cmd.dst);
398 	if (cm->cm_flags & CM_INVERT)
399 		cmd.flags |= MOVE_MEDIUM_INVERT;
400 
401 	/*
402 	 * Send command to changer.
403 	 */
404 	return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
405 	    sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
406 }
407 
408 int
409 ch_exchange(sc, ce)
410 	struct ch_softc *sc;
411 	struct changer_exchange *ce;
412 {
413 	struct scsi_exchange_medium cmd;
414 	u_int16_t src, dst1, dst2;
415 
416 	/*
417 	 * Check arguments.
418 	 */
419 	if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
420 	    (ce->ce_sdsttype > CHET_DT))
421 		return (EINVAL);
422 	if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) ||
423 	    (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) ||
424 	    (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1)))
425 		return (ENODEV);
426 
427 	/*
428 	 * Check the request against the changer's capabilities.
429 	 */
430 	if (((sc->sc_exchangemask[ce->ce_srctype] &
431 	     (1 << ce->ce_fdsttype)) == 0) ||
432 	    ((sc->sc_exchangemask[ce->ce_fdsttype] &
433 	     (1 << ce->ce_sdsttype)) == 0))
434 		return (EINVAL);
435 
436 	/*
437 	 * Calculate the source and destination elements.
438 	 */
439 	src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
440 	dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
441 	dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
442 
443 	/*
444 	 * Build the SCSI command.
445 	 */
446 	bzero(&cmd, sizeof(cmd));
447 	cmd.opcode = EXCHANGE_MEDIUM;
448 	_lto2b(sc->sc_picker, cmd.tea);
449 	_lto2b(src, cmd.src);
450 	_lto2b(dst1, cmd.fdst);
451 	_lto2b(dst2, cmd.sdst);
452 	if (ce->ce_flags & CE_INVERT1)
453 		cmd.flags |= EXCHANGE_MEDIUM_INV1;
454 	if (ce->ce_flags & CE_INVERT2)
455 		cmd.flags |= EXCHANGE_MEDIUM_INV2;
456 
457 	/*
458 	 * Send command to changer.
459 	 */
460 	return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
461 	    sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
462 }
463 
464 int
465 ch_position(sc, cp)
466 	struct ch_softc *sc;
467 	struct changer_position *cp;
468 {
469 	struct scsi_position_to_element cmd;
470 	u_int16_t dst;
471 
472 	/*
473 	 * Check arguments.
474 	 */
475 	if (cp->cp_type > CHET_DT)
476 		return (EINVAL);
477 	if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1))
478 		return (ENODEV);
479 
480 	/*
481 	 * Calculate the destination element.
482 	 */
483 	dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit;
484 
485 	/*
486 	 * Build the SCSI command.
487 	 */
488 	bzero(&cmd, sizeof(cmd));
489 	cmd.opcode = POSITION_TO_ELEMENT;
490 	_lto2b(sc->sc_picker, cmd.tea);
491 	_lto2b(dst, cmd.dst);
492 	if (cp->cp_flags & CP_INVERT)
493 		cmd.flags |= POSITION_TO_ELEMENT_INVERT;
494 
495 	/*
496 	 * Send command to changer.
497 	 */
498 	return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
499 	    sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0));
500 }
501 
502 /*
503  * Perform a READ ELEMENT STATUS on behalf of the user, and return to
504  * the user only the data the user is interested in (i.e. an array of
505  * flags bytes).
506  */
507 int
508 ch_usergetelemstatus(sc, chet, uptr)
509 	struct ch_softc *sc;
510 	int chet;
511 	u_int8_t *uptr;
512 {
513 	struct read_element_status_header *st_hdr;
514 	struct read_element_status_page_header *pg_hdr;
515 	struct read_element_status_descriptor *desc;
516 	caddr_t data = NULL;
517 	size_t size, desclen;
518 	int avail, i, error = 0;
519 	u_int8_t *user_data = NULL;
520 
521 	/*
522 	 * If there are no elements of the requested type in the changer,
523 	 * the request is invalid.
524 	 */
525 	if (sc->sc_counts[chet] == 0)
526 		return (EINVAL);
527 
528 	/*
529 	 * Request one descriptor for the given element type.  This
530 	 * is used to determine the size of the descriptor so that
531 	 * we can allocate enough storage for all of them.  We assume
532 	 * that the first one can fit into 1k.
533 	 */
534 	data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK);
535 	error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024);
536 	if (error)
537 		goto done;
538 
539 	st_hdr = (struct read_element_status_header *)data;
540 	pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr +
541 	    sizeof(struct read_element_status_header));
542 	desclen = _2btol(pg_hdr->edl);
543 
544 	size = sizeof(struct read_element_status_header) +
545 	    sizeof(struct read_element_status_page_header) +
546 	    (desclen * sc->sc_counts[chet]);
547 
548 	/*
549 	 * Reallocate storage for descriptors and get them from the
550 	 * device.
551 	 */
552 	free(data, M_DEVBUF);
553 	data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK);
554 	error = ch_getelemstatus(sc, sc->sc_firsts[chet],
555 	    sc->sc_counts[chet], data, size);
556 	if (error)
557 		goto done;
558 
559 	/*
560 	 * Fill in the user status array.
561 	 */
562 	st_hdr = (struct read_element_status_header *)data;
563 	avail = _2btol(st_hdr->count);
564 	if (avail != sc->sc_counts[chet])
565 		printf("%s: warning, READ ELEMENT STATUS avail != count\n",
566 		    sc->sc_dev.dv_xname);
567 
568 	user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK);
569 
570 	desc = (struct read_element_status_descriptor *)((u_long)data +
571 	    sizeof(struct read_element_status_header) +
572 	    sizeof(struct read_element_status_page_header));
573 	for (i = 0; i < avail; ++i) {
574 		user_data[i] = desc->flags1;
575 		(u_long)desc += desclen;
576 	}
577 
578 	/* Copy flags array out to userspace. */
579 	error = copyout(user_data, uptr, avail);
580 
581  done:
582 	if (data != NULL)
583 		free(data, M_DEVBUF);
584 	if (user_data != NULL)
585 		free(user_data, M_DEVBUF);
586 	return (error);
587 }
588 
589 int
590 ch_getelemstatus(sc, first, count, data, datalen)
591 	struct ch_softc *sc;
592 	int first, count;
593 	caddr_t data;
594 	size_t datalen;
595 {
596 	struct scsi_read_element_status cmd;
597 
598 	/*
599 	 * Build SCSI command.
600 	 */
601 	bzero(&cmd, sizeof(cmd));
602 	cmd.opcode = READ_ELEMENT_STATUS;
603 	_lto2b(first, cmd.sea);
604 	_lto2b(count, cmd.count);
605 	_lto3b(datalen, cmd.len);
606 
607 	/*
608 	 * Send command to changer.
609 	 */
610 	return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
611 	    sizeof(cmd), (u_char *)data, datalen, CHRETRIES, 100000, NULL, 0));
612 }
613 
614 
615 /*
616  * Ask the device about itself and fill in the parameters in our
617  * softc.
618  */
619 int
620 ch_get_params(sc, scsiflags)
621 	struct ch_softc *sc;
622 	int scsiflags;
623 {
624 	struct scsi_mode_sense cmd;
625 	struct scsi_mode_sense_data {
626 		struct scsi_mode_header header;
627 		union {
628 			struct page_element_address_assignment ea;
629 			struct page_transport_geometry_parameters tg;
630 			struct page_device_capabilities cap;
631 		} pages;
632 	} sense_data;
633 	int error, from;
634 	u_int8_t *moves, *exchanges;
635 
636 	/*
637 	 * Grab info from the element address assignment page.
638 	 */
639 	bzero(&cmd, sizeof(cmd));
640 	bzero(&sense_data, sizeof(sense_data));
641 	cmd.opcode = MODE_SENSE;
642 	cmd.byte2 |= 0x08;	/* disable block descriptors */
643 	cmd.page = 0x1d;
644 	cmd.length = (sizeof(sense_data) & 0xff);
645 	error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
646 	    sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES,
647 	    6000, NULL, scsiflags | SCSI_DATA_IN);
648 	if (error) {
649 		printf("%s: could not sense element address page\n",
650 		    sc->sc_dev.dv_xname);
651 		return (error);
652 	}
653 
654 	sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea);
655 	sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte);
656 	sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea);
657 	sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse);
658 	sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea);
659 	sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee);
660 	sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea);
661 	sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte);
662 
663 	/* XXX ask for page trasport geom */
664 
665 	/*
666 	 * Grab info from the capabilities page.
667 	 */
668 	bzero(&cmd, sizeof(cmd));
669 	bzero(&sense_data, sizeof(sense_data));
670 	cmd.opcode = MODE_SENSE;
671 	cmd.byte2 |= 0x08;	/* disable block descriptors */
672 	cmd.page = 0x1f;
673 	cmd.length = (sizeof(sense_data) & 0xff);
674 	error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
675 	    sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES,
676 	    6000, NULL, scsiflags | SCSI_DATA_IN);
677 	if (error) {
678 		printf("%s: could not sense capabilities page\n",
679 		    sc->sc_dev.dv_xname);
680 		return (error);
681 	}
682 
683 	bzero(sc->sc_movemask, sizeof(sc->sc_movemask));
684 	bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask));
685 	moves = &sense_data.pages.cap.move_from_mt;
686 	exchanges = &sense_data.pages.cap.exchange_with_mt;
687 	for (from = CHET_MT; from <= CHET_DT; ++from) {
688 		sc->sc_movemask[from] = moves[from];
689 		sc->sc_exchangemask[from] = exchanges[from];
690 	}
691 
692 	sc->sc_link->flags |= SDEV_MEDIA_LOADED;
693 	return (0);
694 }
695 
696 void
697 ch_get_quirks(sc, inqbuf)
698 	struct ch_softc *sc;
699 	struct scsi_inquiry_data *inqbuf;
700 {
701 	struct chquirk *match;
702 	int priority;
703 
704 	sc->sc_settledelay = 0;
705 
706 	match = (struct chquirk *)scsi_inqmatch(inqbuf,
707 	    (caddr_t)chquirks,
708 	    sizeof(chquirks) / sizeof(chquirks[0]),
709 	    sizeof(chquirks[0]), &priority);
710 	if (priority != 0) {
711 		sc->sc_settledelay = match->cq_settledelay;
712 	}
713 }
714