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