Home | History | Annotate | Line # | Download | only in scsipi
ch.c revision 1.33
      1 /*	$NetBSD: ch.c,v 1.33 1998/07/03 19:11:25 mjacob 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 <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 	int tmo;
    630 	struct scsi_initialize_element_status cmd;
    631 
    632 	/*
    633 	 * Build SCSI command.
    634 	 */
    635 	bzero(&cmd, sizeof(cmd));
    636 	cmd.opcode = INITIALIZE_ELEMENT_STATUS;
    637 
    638 	/*
    639 	 * Send command to changer.
    640 	 *
    641 	 * The problem is, how long to allow for the command?
    642 	 * It can take a *really* long time, and also depends
    643 	 * on unknowable factors such as whether there are
    644 	 * *almost* readable labels on tapes that a barcode
    645 	 * reader is trying to decipher.
    646 	 *
    647 	 * I'm going to make this long enough to allow 5 minutes
    648 	 * per element plus an initial 10 minute wait.
    649 	 */
    650 	tmo =	sc->sc_counts[CHET_MT] +
    651 		sc->sc_counts[CHET_ST] +
    652 		sc->sc_counts[CHET_IE] +
    653 		sc->sc_counts[CHET_DT];
    654 	tmo *= 5 * 1000;
    655 	tmo += (10 * 60 * 1000);
    656 
    657 	return (scsipi_command(sc->sc_link,
    658 	    (struct scsipi_generic *)&cmd, sizeof(cmd),
    659 	    NULL, 0, CHRETRIES, tmo, NULL, 0));
    660 }
    661 
    662 /*
    663  * Ask the device about itself and fill in the parameters in our
    664  * softc.
    665  */
    666 int
    667 ch_get_params(sc, scsiflags)
    668 	struct ch_softc *sc;
    669 	int scsiflags;
    670 {
    671 	struct scsi_mode_sense cmd;
    672 	struct scsi_mode_sense_data {
    673 		struct scsi_mode_header header;
    674 		union {
    675 			struct page_element_address_assignment ea;
    676 			struct page_transport_geometry_parameters tg;
    677 			struct page_device_capabilities cap;
    678 		} pages;
    679 	} sense_data;
    680 	int error, from;
    681 	u_int8_t *moves, *exchanges;
    682 
    683 	/*
    684 	 * Grab info from the element address assignment page.
    685 	 */
    686 	bzero(&cmd, sizeof(cmd));
    687 	bzero(&sense_data, sizeof(sense_data));
    688 	cmd.opcode = SCSI_MODE_SENSE;
    689 	cmd.byte2 |= 0x08;	/* disable block descriptors */
    690 	cmd.page = 0x1d;
    691 	cmd.length = (sizeof(sense_data) & 0xff);
    692 	error = scsipi_command(sc->sc_link,
    693 	    (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&sense_data,
    694 	    sizeof(sense_data), CHRETRIES, 6000, NULL,
    695 	    scsiflags | SCSI_DATA_IN);
    696 	if (error) {
    697 		printf("%s: could not sense element address page\n",
    698 		    sc->sc_dev.dv_xname);
    699 		return (error);
    700 	}
    701 
    702 	sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea);
    703 	sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte);
    704 	sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea);
    705 	sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse);
    706 	sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea);
    707 	sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee);
    708 	sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea);
    709 	sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte);
    710 
    711 	/* XXX ask for page trasport geom */
    712 
    713 	/*
    714 	 * Grab info from the capabilities page.
    715 	 */
    716 	bzero(&cmd, sizeof(cmd));
    717 	bzero(&sense_data, sizeof(sense_data));
    718 	cmd.opcode = SCSI_MODE_SENSE;
    719 	/*
    720 	 * XXX: Note: not all changers can deal with disabled block descriptors
    721 	 */
    722 	cmd.byte2 = 0x08;	/* disable block descriptors */
    723 	cmd.page = 0x1f;
    724 	cmd.length = (sizeof(sense_data) & 0xff);
    725 	error = scsipi_command(sc->sc_link,
    726 	    (struct scsipi_generic *)&cmd, sizeof(cmd), (u_char *)&sense_data,
    727 	    sizeof(sense_data), CHRETRIES, 6000, NULL,
    728 	    scsiflags | SCSI_DATA_IN);
    729 	if (error) {
    730 		printf("%s: could not sense capabilities page\n",
    731 		    sc->sc_dev.dv_xname);
    732 		return (error);
    733 	}
    734 
    735 	bzero(sc->sc_movemask, sizeof(sc->sc_movemask));
    736 	bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask));
    737 	moves = &sense_data.pages.cap.move_from_mt;
    738 	exchanges = &sense_data.pages.cap.exchange_with_mt;
    739 	for (from = CHET_MT; from <= CHET_DT; ++from) {
    740 		sc->sc_movemask[from] = moves[from];
    741 		sc->sc_exchangemask[from] = exchanges[from];
    742 	}
    743 
    744 	sc->sc_link->flags |= SDEV_MEDIA_LOADED;
    745 	return (0);
    746 }
    747 
    748 void
    749 ch_get_quirks(sc, inqbuf)
    750 	struct ch_softc *sc;
    751 	struct scsipi_inquiry_pattern *inqbuf;
    752 {
    753 	struct chquirk *match;
    754 	int priority;
    755 
    756 	sc->sc_settledelay = 0;
    757 
    758 	match = (struct chquirk *)scsipi_inqmatch(inqbuf,
    759 	    (caddr_t)chquirks,
    760 	    sizeof(chquirks) / sizeof(chquirks[0]),
    761 	    sizeof(chquirks[0]), &priority);
    762 	if (priority != 0)
    763 		sc->sc_settledelay = match->cq_settledelay;
    764 }
    765