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