Home | History | Annotate | Line # | Download | only in scsipi
ch.c revision 1.25.6.1
      1 /*	$NetBSD: ch.c,v 1.25.6.1 1997/03/12 21:25:40 is Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej (at) 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