diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1996-04-21 22:33:19 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1996-04-21 22:33:19 +0000 |
commit | 67d88b0a9910a68bb666b448d2dac29cb4d3d8c2 (patch) | |
tree | 967b89f6e07398a22bd8c76d30179b648776542d /sys/scsi/ch.c | |
parent | ba95d3c1d69cdb251d15a12ebf70f50b0ea2019b (diff) |
partial sync with netbsd 960418, more to come
Diffstat (limited to 'sys/scsi/ch.c')
-rw-r--r-- | sys/scsi/ch.c | 829 |
1 files changed, 493 insertions, 336 deletions
diff --git a/sys/scsi/ch.c b/sys/scsi/ch.c index a99dc2ad7b5..811bb057fb2 100644 --- a/sys/scsi/ch.c +++ b/sys/scsi/ch.c @@ -1,8 +1,13 @@ -/* $OpenBSD: ch.c,v 1.3 1996/04/19 16:10:12 niklas Exp $ */ -/* $NetBSD: ch.c,v 1.16 1996/03/05 00:15:09 thorpej Exp $ */ +/* $OpenBSD: ch.c,v 1.4 1996/04/21 22:30:45 deraadt Exp $ */ +/* $NetBSD: ch.c,v 1.20 1996/04/03 00:25:39 thorpej Exp $ */ /* - * Copyright (c) 1994 Charles Hannum. All rights reserved. + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> + * All rights reserved. + * + * Partially based on an autochanger driver written by Stefan Grefen + * and on an autochanger driver written by the Systems Programming Group + * at the University of Utah Computer Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -13,8 +18,9 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Charles Hannum. + * must display the following acknowledgements: + * This product includes software developed by Jason R. Thorpe + * for And Communications, http://www.and.com/ * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * @@ -22,20 +28,15 @@ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Originally written by grefen@????? - * Based on scsi drivers by Julian Elischer (julian@tfs.com) + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ -#include <sys/types.h> #include <sys/param.h> #include <sys/systm.h> #include <sys/errno.h> @@ -43,63 +44,76 @@ #include <sys/buf.h> #include <sys/proc.h> #include <sys/user.h> -#include <sys/chio.h> +#include <sys/chio.h> #include <sys/device.h> +#include <sys/malloc.h> #include <scsi/scsi_all.h> #include <scsi/scsi_changer.h> #include <scsi/scsiconf.h> -#include <scsi/scsi_conf.h> -#define CHRETRIES 2 - -#define CHMODE(z) (minor(z) & 0x0f) -#define CHUNIT(z) (minor(z) >> 4) +#define CHRETRIES 2 +#define CHUNIT(x) (minor((x))) struct ch_softc { - struct device sc_dev; - - struct scsi_link *sc_link; /* all the inter level info */ - u_int16_t chmo; /* Offset of first CHM */ - u_int16_t chms; /* No. of CHM */ - u_int16_t slots; /* No. of Storage Elements */ - u_int16_t sloto; /* Offset of first SE */ - u_int16_t imexs; /* No. of Import/Export Slots */ - u_int16_t imexo; /* Offset of first IM/EX */ - u_int16_t drives; /* No. of CTS */ - u_int16_t driveo; /* Offset of first CTS */ - u_int16_t rot; /* CHM can rotate */ - u_long op_matrix; /* possible operations */ - u_int16_t lsterr; /* details of lasterror */ - u_char stor; /* posible Storage locations */ + struct device sc_dev; /* generic device info */ + struct scsi_link *sc_link; /* link in the SCSI bus */ + + int sc_picker; /* current picker */ + + /* + * The following information is obtained from the + * element address assignment page. + */ + int sc_firsts[4]; /* firsts, indexed by CHET_* */ + int sc_counts[4]; /* counts, indexed by CHET_* */ + + /* + * The following mask defines the legal combinations + * of elements for the MOVE MEDIUM command. + */ + u_int8_t sc_movemask[4]; + + /* + * As above, but for EXCHANGE MEDIUM. + */ + u_int8_t sc_exchangemask[4]; + + int flags; /* misc. info */ }; +/* sc_flags */ +#define CHF_ROTATE 0x01 /* picker can rotate */ + +/* Autoconfiguration glue */ int chmatch __P((struct device *, void *, void *)); void chattach __P((struct device *, struct device *, void *)); -int ch_getelem __P((struct ch_softc *, short *, int, int , char *, int)); -int ch_move __P((struct ch_softc *, short *, int, int , int , int )); -int ch_position __P((struct ch_softc *, short *, int, int , int )); -int ch_mode_sense __P((struct ch_softc *, int)); -struct cfdriver chcd = { - NULL, "ch", chmatch, chattach, DV_DULL, sizeof(struct ch_softc) +struct cfattach ch_ca = { + sizeof(struct ch_softc), chmatch, chattach }; -/* - * This driver is so simple it uses all the default services - */ -struct scsi_device ch_switch = { - NULL, - NULL, - NULL, - NULL, +struct cfdriver ch_cd = { + NULL, "ch", DV_DULL }; struct scsi_inquiry_pattern ch_patterns[] = { {T_CHANGER, T_REMOV, - "", "", ""}, + "", "", ""}, }; +/* SCSI glue */ +struct scsi_device ch_switch = { + NULL, NULL, NULL, NULL +}; + +int ch_move __P((struct ch_softc *, struct changer_move *)); +int ch_exchange __P((struct ch_softc *, struct changer_exchange *)); +int ch_position __P((struct ch_softc *, struct changer_position *)); +int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *)); +int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t)); +int ch_get_params __P((struct ch_softc *, int)); + int chmatch(parent, match, aux) struct device *parent; @@ -111,366 +125,509 @@ chmatch(parent, match, aux) (void)scsi_inqmatch(sa->sa_inqbuf, (caddr_t)ch_patterns, sizeof(ch_patterns)/sizeof(ch_patterns[0]), sizeof(ch_patterns[0]), &priority); + return (priority); } -/* - * The routine called by the low level scsi routine when it discovers - * a device suitable for this driver. - */ -void +void chattach(parent, self, aux) struct device *parent, *self; void *aux; { - struct ch_softc *ch = (void *)self; + struct ch_softc *sc = (struct ch_softc *)self; struct scsibus_attach_args *sa = aux; - struct scsi_link *sc_link = sa->sa_sc_link; + struct scsi_link *link = sa->sa_sc_link; - SC_DEBUG(sc_link, SDEV_DB2, ("chattach: ")); + /* Glue into the SCSI bus */ + sc->sc_link = link; + link->device = &ch_switch; + link->device_softc = sc; + link->openings = 1; - /* - * Store information needed to contact our base driver - */ - ch->sc_link = sc_link; - sc_link->device = &ch_switch; - sc_link->device_softc = ch; - sc_link->openings = 1; + printf("\n"); /* - * Use the subdriver to request information regarding - * the drive. We cannot use interrupts yet, so the - * request must specify this. + * Get information about the device. Note we can't use + * interrupts yet. */ - printf("\n"); - printf("%s: ", ch->sc_dev.dv_xname); - if (ch_mode_sense(ch, SCSI_AUTOCONF) != 0) - printf("offline\n"); - else - printf("%d slot(s), %d drive(s), %d arm(s), %d i/e-slot(s)\n", - ch->slots, ch->drives, ch->chms, ch->imexs); + if (ch_get_params(sc, SCSI_AUTOCONF)) + printf("%s: offline\n", sc->sc_dev.dv_xname); + else { + printf("%s: %d slot%s, %d drive%s, %d picker%s", + sc->sc_dev.dv_xname, + sc->sc_counts[CHET_ST], (sc->sc_counts[CHET_ST] > 1) ? + "s" : "", + sc->sc_counts[CHET_DT], (sc->sc_counts[CHET_DT] > 1) ? + "s" : "", + sc->sc_counts[CHET_MT], (sc->sc_counts[CHET_MT] > 1) ? + "s" : ""); + if (sc->sc_counts[CHET_IE]) + printf(", %d portal%s", sc->sc_counts[CHET_IE], + (sc->sc_counts[CHET_IE] > 1) ? "s" : ""); + printf("\n"); +#ifdef CHANGER_DEBUG + printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n", + sc->sc_dev.dv_xname, + sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST], + sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]); + printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n", + sc->sc_dev.dv_xname, + sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST], + sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]); +#endif /* CHANGER_DEBUG */ + } + + /* Default the current picker. */ + sc->sc_picker = sc->sc_firsts[CHET_MT]; } -/* - * open the device. - */ -int -chopen(dev, flags, mode, p) +int +chopen(dev, flags, fmt, p) dev_t dev; - int flags; - int mode; + int flags, fmt; struct proc *p; { - int error = 0; - int unit; - struct ch_softc *ch; - struct scsi_link *sc_link; + struct ch_softc *sc; + int unit, error = 0; unit = CHUNIT(dev); - if (unit >= chcd.cd_ndevs) - return ENXIO; - ch = chcd.cd_devs[unit]; - if (!ch) - return ENXIO; - - sc_link = ch->sc_link; - - SC_DEBUG(sc_link, SDEV_DB1, - ("chopen: dev=0x%x (unit %d (of %d))\n", dev, unit, chcd.cd_ndevs)); + if ((unit >= ch_cd.cd_ndevs) || + ((sc = ch_cd.cd_devs[unit]) == NULL)) + return (ENXIO); /* - * Only allow one at a time + * Only allow one open at a time. */ - if (sc_link->flags & SDEV_OPEN) { - printf("%s: already open\n", ch->sc_dev.dv_xname); - return EBUSY; - } + if (sc->sc_link->flags & SDEV_OPEN) + return (EBUSY); + + sc->sc_link->flags |= SDEV_OPEN; /* - * Catch any unit attention errors. + * Absorb any unit attention errors. Ignore "not ready" + * since this might occur if e.g. a tape isn't actually + * loaded in the drive. */ - error = scsi_test_unit_ready(sc_link, SCSI_IGNORE_MEDIA_CHANGE); - if (error) + if (error = scsi_test_unit_ready(sc->sc_link, + SCSI_IGNORE_NOT_READY|SCSI_IGNORE_MEDIA_CHANGE)) goto bad; - sc_link->flags |= SDEV_OPEN; /* unit attn are now errors */ - /* - * Make sure data is loaded + * Make sure our parameters are up to date. */ - if ((error = ch_mode_sense(ch, 0)) != 0) { - printf("%s: offline\n", ch->sc_dev.dv_xname); + if (error = ch_get_params(sc, 0)) goto bad; - } - SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n")); - return 0; + return (0); -bad: - sc_link->flags &= ~SDEV_OPEN; - return error; + bad: + sc->sc_link->flags &= ~SDEV_OPEN; + return (error); } -/* - * close the device.. only called if we are the LAST - * occurence of an open device - */ -int -chclose(dev, flags, mode, p) +int +chclose(dev, flags, fmt, p) dev_t dev; - int flags; - int mode; + int flags, fmt; struct proc *p; { - struct ch_softc *ch = chcd.cd_devs[CHUNIT(dev)]; - - SC_DEBUG(ch->sc_link, SDEV_DB1, ("closing\n")); - ch->sc_link->flags &= ~SDEV_OPEN; + struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; - return 0; + sc->sc_link->flags &= ~SDEV_OPEN; + return (0); } -/* - * Perform special action on behalf of the user - * Knows about the internals of this device - */ -int -chioctl(dev, cmd, arg, mode, p) +int +chioctl(dev, cmd, data, flags, p) dev_t dev; u_long cmd; - caddr_t arg; - int mode; + caddr_t data; + int flags; struct proc *p; { - struct ch_softc *ch = chcd.cd_devs[CHUNIT(dev)]; - struct scsi_link *sc_link = ch->sc_link; - int flags; - - /* - * Find the device that the user is talking about - */ - flags = 0; /* give error messages, act on errors etc. */ + struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)]; + caddr_t elemdata; + int error = 0; switch (cmd) { - case CHIOOP: { - struct chop *chop = (struct chop *) arg; - SC_DEBUG(sc_link, SDEV_DB2, ("[chtape_chop: %x]\n", - chop->ch_op)); - - switch (chop->ch_op) { - case CHGETPARAM: - chop->u.getparam.chmo = ch->chmo; - chop->u.getparam.chms = ch->chms; - chop->u.getparam.sloto = ch->sloto; - chop->u.getparam.slots = ch->slots; - chop->u.getparam.imexo = ch->imexo; - chop->u.getparam.imexs = ch->imexs; - chop->u.getparam.driveo = ch->driveo; - chop->u.getparam.drives = ch->drives; - chop->u.getparam.rot = ch->rot; - chop->result = 0; - return 0; - break; - case CHPOSITION: - return ch_position(ch, &chop->result, - chop->u.position.chm, chop->u.position.to, flags); - case CHMOVE: - return ch_move(ch, &chop->result, chop->u.position.chm, - chop->u.move.from, chop->u.move.to, flags); - case CHGETELEM: - return ch_getelem(ch, &chop->result, - chop->u.get_elem_stat.type, - chop->u.get_elem_stat.from, - (char *) &chop->u.get_elem_stat.elem_data, flags); - default: - return EINVAL; - } - } + case CHIOMOVE: + error = ch_move(sc, (struct changer_move *)data); + break; + + case CHIOEXCHANGE: + error = ch_exchange(sc, (struct changer_exchange *)data); + break; + + case CHIOPOSITION: + error = ch_position(sc, (struct changer_position *)data); + break; + + case CHIOGPICKER: + *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT]; + break; + + case CHIOSPICKER: { + int new_picker = *(int *)data; + + if (new_picker > (sc->sc_counts[CHET_MT] - 1)) + return (EINVAL); + sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker; + break; } + + case CHIOGPARAMS: { + struct changer_params *cp = (struct changer_params *)data; + + cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT]; + cp->cp_npickers = sc->sc_counts[CHET_MT]; + cp->cp_nslots = sc->sc_counts[CHET_ST]; + cp->cp_nportals = sc->sc_counts[CHET_IE]; + cp->cp_ndrives = sc->sc_counts[CHET_DT]; + break; } + + case CHIOGSTATUS: { + struct changer_element_status *ces = + (struct changer_element_status *)data; + + error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data); + break; } + + /* Implement prevent/allow? */ + default: - return scsi_do_ioctl(sc_link, dev, cmd, arg, mode, p); + error = scsi_do_ioctl(sc->sc_link, dev, cmd, data, flags, p); + break; } -#ifdef DIAGNOSTIC - panic("chioctl: impossible"); -#endif + + return (error); } -int -ch_getelem(ch, stat, type, from, data, flags) - struct ch_softc *ch; - short *stat; - int type, from; - char *data; - int flags; +int +ch_move(sc, cm) + struct ch_softc *sc; + struct changer_move *cm; { - struct scsi_read_element_status scsi_cmd; - char elbuf[32]; - int error; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.opcode = READ_ELEMENT_STATUS; - scsi_cmd.byte2 = type; - scsi_cmd.starting_element_addr[0] = (from >> 8) & 0xff; - scsi_cmd.starting_element_addr[1] = from & 0xff; - scsi_cmd.number_of_elements[1] = 1; - scsi_cmd.allocation_length[2] = 32; - - error = scsi_scsi_cmd(ch->sc_link, (struct scsi_generic *) &scsi_cmd, - sizeof(scsi_cmd), (u_char *) elbuf, 32, CHRETRIES, 100000, NULL, - SCSI_DATA_IN | flags); - if (error) - *stat = ch->lsterr; - else - *stat = 0; - bcopy(elbuf + 16, data, 16); - return error; + struct scsi_move_medium cmd; + u_int16_t fromelem, toelem; + + /* + * Check arguments. + */ + if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT)) + return (EINVAL); + if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) || + (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1))) + return (ENODEV); + + /* + * Check the request against the changer's capabilities. + */ + if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0) + return (EINVAL); + + /* + * Calculate the source and destination elements. + */ + fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit; + toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit; + + /* + * Build the SCSI command. + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = MOVE_MEDIUM; + _lto2b(sc->sc_picker, cmd.tea); + _lto2b(fromelem, cmd.src); + _lto2b(toelem, cmd.dst); + if (cm->cm_flags & CM_INVERT) + cmd.flags |= MOVE_MEDIUM_INVERT; + + /* + * Send command to changer. + */ + return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); } -int -ch_move(ch, stat, chm, from, to, flags) - struct ch_softc *ch; - short *stat; - int chm, from, to, flags; +int +ch_exchange(sc, ce) + struct ch_softc *sc; + struct changer_exchange *ce; { - struct scsi_move_medium scsi_cmd; - int error; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.opcode = MOVE_MEDIUM; - scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff; - scsi_cmd.transport_element_address[1] = chm & 0xff; - scsi_cmd.source_address[0] = (from >> 8) & 0xff; - scsi_cmd.source_address[1] = from & 0xff; - scsi_cmd.destination_address[0] = (to >> 8) & 0xff; - scsi_cmd.destination_address[1] = to & 0xff; - scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0; - error = scsi_scsi_cmd(ch->sc_link, (struct scsi_generic *) &scsi_cmd, - sizeof(scsi_cmd), NULL, 0, CHRETRIES, 100000, NULL, flags); - if (error) - *stat = ch->lsterr; - else - *stat = 0; - return error; + struct scsi_exchange_medium cmd; + u_int16_t src, dst1, dst2; + + /* + * Check arguments. + */ + if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) || + (ce->ce_sdsttype > CHET_DT)) + return (EINVAL); + if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) || + (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) || + (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1))) + return (ENODEV); + + /* + * Check the request against the changer's capabilities. + */ + if (((sc->sc_exchangemask[ce->ce_srctype] & + (1 << ce->ce_fdsttype)) == 0) || + ((sc->sc_exchangemask[ce->ce_fdsttype] & + (1 << ce->ce_sdsttype)) == 0)) + return (EINVAL); + + /* + * Calculate the source and destination elements. + */ + src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit; + dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit; + dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit; + + /* + * Build the SCSI command. + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = EXCHANGE_MEDIUM; + _lto2b(sc->sc_picker, cmd.tea); + _lto2b(src, cmd.src); + _lto2b(dst1, cmd.fdst); + _lto2b(dst2, cmd.sdst); + if (ce->ce_flags & CE_INVERT1) + cmd.flags |= EXCHANGE_MEDIUM_INV1; + if (ce->ce_flags & CE_INVERT2) + cmd.flags |= EXCHANGE_MEDIUM_INV2; + + /* + * Send command to changer. + */ + return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); } -int -ch_position(ch, stat, chm, to, flags) - struct ch_softc *ch; - short *stat; - int chm, to, flags; +int +ch_position(sc, cp) + struct ch_softc *sc; + struct changer_position *cp; { - struct scsi_position_to_element scsi_cmd; - int error; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.opcode = POSITION_TO_ELEMENT; - scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff; - scsi_cmd.transport_element_address[1] = chm & 0xff; - scsi_cmd.source_address[0] = (to >> 8) & 0xff; - scsi_cmd.source_address[1] = to & 0xff; - scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0; - error = scsi_scsi_cmd(ch->sc_link, (struct scsi_generic *) &scsi_cmd, - sizeof(scsi_cmd), NULL, 0, CHRETRIES, 100000, NULL, flags); - if (error) - *stat = ch->lsterr; - else - *stat = 0; - return error; + struct scsi_position_to_element cmd; + u_int16_t dst; + + /* + * Check arguments. + */ + if (cp->cp_type > CHET_DT) + return (EINVAL); + if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1)) + return (ENODEV); + + /* + * Calculate the destination element. + */ + dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit; + + /* + * Build the SCSI command. + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = POSITION_TO_ELEMENT; + _lto2b(sc->sc_picker, cmd.tea); + _lto2b(dst, cmd.dst); + if (cp->cp_flags & CP_INVERT) + cmd.flags |= POSITION_TO_ELEMENT_INVERT; + + /* + * Send command to changer. + */ + return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); } /* - * Get the scsi driver to send a full inquiry to the - * device and use the results to fill out the global - * parameter structure. + * Perform a READ ELEMENT STATUS on behalf of the user, and return to + * the user only the data the user is interested in (i.e. an array of + * flags bytes). */ -int -ch_mode_sense(ch, flags) - struct ch_softc *ch; - int flags; +int +ch_usergetelemstatus(sc, chet, uptr) + struct ch_softc *sc; + int chet; + u_int8_t *uptr; +{ + struct read_element_status_header *st_hdr; + struct read_element_status_page_header *pg_hdr; + struct read_element_status_descriptor *desc; + caddr_t data = NULL; + size_t size, desclen; + int avail, i, error = 0; + u_int8_t *user_data = NULL; + + /* + * If there are no elements of the requested type in the changer, + * the request is invalid. + */ + if (sc->sc_counts[chet] == 0) + return (EINVAL); + + /* + * Request one descriptor for the given element type. This + * is used to determine the size of the descriptor so that + * we can allocate enough storage for all of them. We assume + * that the first one can fit into 1k. + */ + data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK); + if (error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024)) + goto done; + + st_hdr = (struct read_element_status_header *)data; + pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr + + sizeof(struct read_element_status_header)); + desclen = _2btol(pg_hdr->edl); + + size = sizeof(struct read_element_status_header) + + sizeof(struct read_element_status_page_header) + + (desclen * sc->sc_counts[chet]); + + /* + * Reallocate storage for descriptors and get them from the + * device. + */ + free(data, M_DEVBUF); + data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK); + if (error = ch_getelemstatus(sc, sc->sc_firsts[chet], + sc->sc_counts[chet], data, size)) + goto done; + + /* + * Fill in the user status array. + */ + st_hdr = (struct read_element_status_header *)data; + avail = _2btol(st_hdr->count); + if (avail != sc->sc_counts[chet]) + printf("%s: warning, READ ELEMENT STATUS avail != count\n", + sc->sc_dev.dv_xname); + + user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK); + + desc = (struct read_element_status_descriptor *)((u_long)data + + sizeof(struct read_element_status_header) + + sizeof(struct read_element_status_page_header)); + for (i = 0; i < avail; ++i) { + user_data[i] = desc->flags1; + (u_long)desc += desclen; + } + + /* Copy flags array out to userspace. */ + error = copyout(user_data, uptr, avail); + + done: + if (data != NULL) + free(data, M_DEVBUF); + if (user_data != NULL) + free(user_data, M_DEVBUF); + return (error); +} + +int +ch_getelemstatus(sc, first, count, data, datalen) + struct ch_softc *sc; + int first, count; + caddr_t data; + size_t datalen; { - struct scsi_mode_sense scsi_cmd; - u_char scsi_sense[128]; /* Can't use scsi_mode_sense_data because of - * missing block descriptor. - */ - u_char *b; - int i, l; - int error; - struct scsi_link *sc_link = ch->sc_link; + struct scsi_read_element_status cmd; /* - * First check if we have it all loaded + * Build SCSI command. */ - if (sc_link->flags & SDEV_MEDIA_LOADED) - return 0; + bzero(&cmd, sizeof(cmd)); + cmd.opcode = READ_ELEMENT_STATUS; + _lto2b(first, cmd.sea); + _lto2b(count, cmd.count); + _lto3b(datalen, cmd.len); /* - * First do a mode sense + * Send command to changer. */ - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.opcode = MODE_SENSE; - scsi_cmd.byte2 = SMS_DBD; - scsi_cmd.page = 0x3f; /* All Pages */ - scsi_cmd.length = sizeof(scsi_sense); + return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), (u_char *)data, datalen, CHRETRIES, 100000, NULL, 0)); +} + + +/* + * Ask the device about itself and fill in the parameters in our + * softc. + */ +int +ch_get_params(sc, scsiflags) + struct ch_softc *sc; + int scsiflags; +{ + struct scsi_mode_sense cmd; + struct scsi_mode_sense_data { + struct scsi_mode_header header; + union { + struct page_element_address_assignment ea; + struct page_transport_geometry_parameters tg; + struct page_device_capabilities cap; + } pages; + } sense_data; + int error, from; + u_int8_t *moves, *exchanges; /* - * Read in the pages + * Grab info from the element address assignment page. */ - error = scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, - sizeof(scsi_cmd), (u_char *) &scsi_sense, - sizeof(scsi_sense), CHRETRIES, 5000, NULL, - flags | SCSI_DATA_IN); + bzero(&cmd, sizeof(cmd)); + bzero(&sense_data, sizeof(sense_data)); + cmd.opcode = MODE_SENSE; + cmd.byte2 |= 0x08; /* disable block descriptors */ + cmd.page = 0x1d; + cmd.length = (sizeof(sense_data) & 0xff); + error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES, + 6000, NULL, scsiflags | SCSI_DATA_IN); if (error) { - printf("%s: could not mode sense\n", ch->sc_dev.dv_xname); - return error; + printf("%s: could not sense element address page\n"); + return (error); } - sc_link->flags |= SDEV_MEDIA_LOADED; - l = scsi_sense[0] - 3; - b = &scsi_sense[4]; + sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea); + sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte); + sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea); + sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse); + sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea); + sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee); + sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea); + sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte); + + /* XXX ask for page trasport geom */ /* - * To avoid alignment problems + * Grab info from the capabilities page. */ -/* XXXX - FIX THIS FOR MSB */ -#define p2copy(valp) (valp[1] | (valp[0]<<8)); valp+=2 -#define p4copy(valp) (valp[3] | (valp[2]<<8) | (valp[1]<<16) | (valp[0]<<24)); valp+=4 -#if 0 - printf("\nmode_sense %d\n", l); - for (i = 0; i < l + 4; i++) - printf("%x%c", scsi_sense[i], i % 8 == 7 ? '\n' : ':'); - printf("\n"); -#endif - for (i = 0; i < l;) { - u_char pc = (*b++) & 0x3f; - u_char pl = *b++; - u_char *bb = b; - switch (pc) { - case 0x1d: - ch->chmo = p2copy(bb); - ch->chms = p2copy(bb); - ch->sloto = p2copy(bb); - ch->slots = p2copy(bb); - ch->imexo = p2copy(bb); - ch->imexs = p2copy(bb); - ch->driveo = p2copy(bb); - ch->drives = p2copy(bb); - break; - case 0x1e: - ch->rot = *b & 0x1; - break; - case 0x1f: - ch->stor = *b & 0xf; - bb += 2; - ch->stor = p4copy(bb); - break; - default: - break; - } - b += pl; - i += pl + 2; + bzero(&cmd, sizeof(cmd)); + bzero(&sense_data, sizeof(sense_data)); + cmd.opcode = MODE_SENSE; + cmd.byte2 |= 0x08; /* disable block descriptors */ + cmd.page = 0x1f; + cmd.length = (sizeof(sense_data) & 0xff); + error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES, + 6000, NULL, scsiflags | SCSI_DATA_IN); + if (error) { + printf("%s: could not sense capabilities page\n"); + return (error); + } + + bzero(sc->sc_movemask, sizeof(sc->sc_movemask)); + bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask)); + moves = &sense_data.pages.cap.move_from_mt; + exchanges = &sense_data.pages.cap.exchange_with_mt; + for (from = CHET_MT; from <= CHET_DT; ++from) { + sc->sc_movemask[from] = moves[from]; + sc->sc_exchangemask[from] = exchanges[from]; } - SC_DEBUG(sc_link, SDEV_DB2, - (" cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n", - ch->chmo, ch->chms, ch->sloto, ch->slots, ch->imexo, ch->imexs, - ch->driveo, ch->drives, ch->rot ? "can" : "can't")); - return 0; + + sc->sc_link->flags |= SDEV_MEDIA_LOADED; + return (0); } |