ch.c revision 1.32
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
60struct 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
98int	chmatch __P((struct device *, void *, void *));
99#else
100int	chmatch __P((struct device *, struct cfdata *, void *));
101#endif
102void	chattach __P((struct device *, struct device *, void *));
103
104struct cfattach ch_ca = {
105	sizeof(struct ch_softc), chmatch, chattach
106};
107
108extern struct cfdriver ch_cd;
109
110struct scsipi_inquiry_pattern ch_patterns[] = {
111	{T_CHANGER, T_REMOV,
112	 "",		"",		""},
113};
114
115/* SCSI glue */
116struct scsipi_device ch_switch = {
117	NULL, NULL, NULL, NULL
118};
119
120int	ch_move __P((struct ch_softc *, struct changer_move *));
121int	ch_exchange __P((struct ch_softc *, struct changer_exchange *));
122int	ch_position __P((struct ch_softc *, struct changer_position *));
123int	ch_ielem __P((struct ch_softc *));
124int	ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *));
125int	ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t));
126int	ch_get_params __P((struct ch_softc *, int));
127void	ch_get_quirks __P((struct ch_softc *,
128	    struct scsipi_inquiry_pattern *));
129
130/*
131 * SCSI changer quirks.
132 */
133struct chquirk {
134	struct	scsipi_inquiry_pattern cq_match; /* device id pattern */
135	int	cq_settledelay;	/* settle delay, in seconds */
136};
137
138struct chquirk chquirks[] = {
139	{{T_CHANGER, T_REMOV,
140	  "SPECTRA",	"9000",		"0200"},
141	 75},
142};
143
144int
145chmatch(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
164void
165chattach(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
227int
228chopen(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
272int
273chclose(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
284int
285chioctl(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
366int
367ch_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
414int
415ch_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
471int
472ch_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 */
515int
516ch_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
598int
599ch_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
625int
626ch_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 */
649int
650ch_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
731void
732ch_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