diff options
author | joerg <joerg@FreeBSD.org> | 1997-03-06 15:36:45 +0000 |
---|---|---|
committer | joerg <joerg@FreeBSD.org> | 1997-03-06 15:36:45 +0000 |
commit | 5daf619db7d05ff5b3e4b471d5e9c273ed0b7308 (patch) | |
tree | d2360bd505a7513abde2fce6317aeeced5325869 /sys/scsi/ch.c | |
parent | cf12351a8546bd190c653cb29759d3dc794107c6 (diff) | |
download | FreeBSD-src-5daf619db7d05ff5b3e4b471d5e9c273ed0b7308.zip FreeBSD-src-5daf619db7d05ff5b3e4b471d5e9c273ed0b7308.tar.gz |
Merge Jason Thorpe's updated changer stuff into the actual system.
Many things have been changing in the kernel since mid-1996, so there's
quite some amount of diffs here already. It compiles, but i cannot
test it anywhere here.
2.2 candidate?
Closes PR # 1201.
Diffstat (limited to 'sys/scsi/ch.c')
-rw-r--r-- | sys/scsi/ch.c | 980 |
1 files changed, 592 insertions, 388 deletions
diff --git a/sys/scsi/ch.c b/sys/scsi/ch.c index 0901373..9a0ac19 100644 --- a/sys/scsi/ch.c +++ b/sys/scsi/ch.c @@ -1,13 +1,42 @@ /* - * Written by grefen@convex.com (probably moved by now) - * Based on scsi drivers by Julian Elischer (julian@tfs.com) + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> + * All rights reserved. * - * $Id$ + * 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * 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 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * 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. + * + * $Id: ch.c,v 1.36 1997/02/22 09:44:25 peter Exp $ */ -#include <sys/types.h> -#include <ch.h> -#include "opt_scsi.h" +#include "opt_scsi.h" #include <sys/param.h> #include <sys/systm.h> @@ -16,9 +45,8 @@ #include <sys/buf.h> #include <sys/proc.h> #include <sys/chio.h> -#include <sys/malloc.h> -#include <sys/conf.h> #include <sys/kernel.h> +#include <sys/malloc.h> #ifdef DEVFS #include <sys/devfsext.h> #endif /*DEVFS*/ @@ -27,52 +55,56 @@ #include <scsi/scsi_changer.h> #include <scsi/scsiconf.h> -#define CHRETRIES 2 - -#define CHUNIT(DEV) ((minor(DEV)&0xF0) >> 4) /* 4 bit unit. */ -#define CHSETUNIT(DEV, U) makedev(major(DEV), ((U) << 4)) - -#define MODE(z) ( (minor(z) & 0x0F) ) - -#define ESUCCESS 0 - -struct scsi_data { - u_int32_t flags; - 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 opertaions */ - u_int16_t lsterr; /* details of lasterror */ - u_char stor; /* posible Storage locations */ +#include "ch.h" + +#define CHRETRIES 2 +#define CHUNIT(x) (minor((x))) +#define CHSETUNIT(x, y) makedev(major(x), y) + +struct ch_softc { + /* + * Human-readable external name. FreeBSD doesn't have a + * generic hook for this, so we make it look NetBSD-like. See + * comment in chattach(). + */ + struct { + char dv_xname[16]; + } sc_dev; + + /* + * Pointer back to the scsi_link. See comment in chattach(). + */ + struct scsi_link *sc_link; + + 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 */ #ifdef DEVFS - void *c_devfs_token; - void *ctl_devfs_token; + void *c_devfs_token; + void *ctl_devfs_token; #endif }; -static errval ch_getelem __P((u_int32_t unit, short *stat, int type, u_int32_t from, - void *data, u_int32_t flags)); -static errval ch_move __P((u_int32_t unit, short *stat, u_int32_t chm, u_int32_t from, - u_int32_t to, u_int32_t flags)); -static errval ch_mode_sense __P((u_int32_t unit, u_int32_t flags)); -static errval ch_position __P((u_int32_t unit, short *stat, u_int32_t chm, - u_int32_t to, u_int32_t flags)); - -static int chunit(dev_t dev) { return CHUNIT(dev); } -static dev_t chsetunit(dev_t dev, int unit) { return CHSETUNIT(dev, unit); } - -static errval ch_open(dev_t dev, int flags, int fmt, struct proc *p, - struct scsi_link *sc_link); -static errval ch_ioctl(dev_t dev, int cmd, caddr_t addr, int flag, - struct proc *p, struct scsi_link *sc_link); -static errval ch_close(dev_t dev, int flag, int fmt, struct proc *p, - struct scsi_link *sc_link); +/* sc_flags */ +#define CHF_ROTATE 0x01 /* picker can rotate */ static d_open_t chopen; static d_close_t chclose; @@ -84,426 +116,600 @@ static struct cdevsw ch_cdevsw = chioctl, nostop, nullreset, nodevtotty,/* ch */ noselect, nommap, nostrat, "ch", NULL, -1 }; +/* + * SCSI glue. + */ + +/* + * Under FreeBSD, this macro sets up a bunch of trampoline + * functions that indirect through the SCSI subsystem. + */ SCSI_DEVICE_ENTRIES(ch) -static struct scsi_device ch_switch = -{ - NULL, - NULL, - NULL, - NULL, - "ch", - 0, - {0, 0}, - 0, /* Link flags */ - chattach, - "Medium-Changer", - chopen, - sizeof(struct scsi_data), - T_CHANGER, - chunit, - chsetunit, - ch_open, - ch_ioctl, - ch_close, - 0, +static int chunit __P((dev_t)); +static dev_t chsetunit __P((dev_t, int)); + +/* So, like, why not "int"? */ +static errval ch_devopen __P((dev_t, int, int, struct proc *, + struct scsi_link *)); +static errval ch_devioctl __P((dev_t, int, caddr_t, int, struct proc *, + struct scsi_link *)); +static errval ch_devclose __P((dev_t, int, int, struct proc *, + struct scsi_link *)); + +static struct scsi_device ch_switch = { + NULL, /* (*err_handler) */ + NULL, /* (*start) */ + NULL, /* (*async) */ + NULL, /* (*done) */ + "ch", /* name */ + 0, /* flags */ + { 0, 0 }, /* spare[2] */ + 0, /* link_flags */ + chattach, /* (*attach) */ + "Medium-Changer", /* desc */ + chopen, /* (*open) */ + sizeof(struct ch_softc), /* sizeof_scsi_data */ + T_CHANGER, /* type */ + chunit, /* (*getunit) */ + chsetunit, /* (*setunit) */ + ch_devopen, /* (*dev_open) */ + ch_devioctl, /* (*dev_ioctl) */ + ch_devclose, /* (*dev_close) */ }; -#define CH_OPEN 0x01 +static int ch_move __P((struct ch_softc *, struct changer_move *)); +static int ch_exchange __P((struct ch_softc *, struct changer_exchange *)); +static int ch_position __P((struct ch_softc *, struct changer_position *)); +static int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *)); +static int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t)); +static int ch_get_params __P((struct ch_softc *, int)); -/* - * The routine called by the low level scsi routine when it discovers - * a device suitable for this driver. - */ -static errval -chattach(struct scsi_link *sc_link) +static errval +chattach(link) + struct scsi_link *link; { - u_int32_t unit; + struct ch_softc *sc = (struct ch_softc *)(link->sd); + u_int32_t unit = link->dev_unit; - struct scsi_data *ch = sc_link->sd; + /* + * FreeBSD doesn't have any common way of carrying + * around a device's external name (i.e. <name><unit>), + * so emulate the structure used by NetBSD to keep the + * diffs lower. + */ + bzero(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname)); + sprintf(sc->sc_dev.dv_xname, "%s%d", ch_switch.name, unit); - unit = sc_link->dev_unit; + /* + * FreeBSD gets "softc" info for a device from the + * scsi_link argument passed to indirect entry point functions. + * NetBSD get scsi_link info from softcs that are + * obtained from indexes passed to direct entry point functions. + * We emulate the NetBSD behavior here to keep the diffs + * lower. + */ + sc->sc_link = link; /* - * 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. */ - if ((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT */ ))) { + if (ch_get_params(sc, SCSI_NOSLEEP|SCSI_NOMASK)) printf("offline"); - } else { - printf("%d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s)", - ch->slots, ch->drives, ch->chms, ch->imexs); + else { + printf("%d slot%s, %d drive%s, %d picker%s", + 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" : ""); + if (bootverbose) { + printf("\n"); /* This will probably look ugly ... bummer. */ + 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]); + } } + /* Default the current picker. */ + sc->sc_picker = sc->sc_firsts[CHET_MT]; + #ifdef DEVFS - ch->c_devfs_token = devfs_add_devswf(&ch_cdevsw, unit << 4, DV_CHR, + sc->c_devfs_token = devfs_add_devswf(&ch_cdevsw, unit << 4, DV_CHR, UID_ROOT, GID_OPERATOR, 0600, "ch%d", unit); - ch->ctl_devfs_token = devfs_add_devswf(&ch_cdevsw, + sc->ctl_devfs_token = devfs_add_devswf(&ch_cdevsw, (unit << 4) | SCSI_CONTROL_MASK, DV_CHR, UID_ROOT, GID_OPERATOR, 0600, "ch%d.ctl", unit); #endif - return 0; + return (0); } -/* - * open the device. - */ -static errval -ch_open(dev_t dev, int flags, int fmt, struct proc *p, - struct scsi_link *sc_link) +static errval +ch_devopen(dev, flags, fmt, p, link) + dev_t dev; + int flags, fmt; + struct proc *p; + struct scsi_link *link; { - errval errcode = 0; - u_int32_t unit, mode; - struct scsi_data *cd; + struct ch_softc *sc = (struct ch_softc *)(link->sd); + errval error = 0; + int unit; unit = CHUNIT(dev); - mode = MODE(dev); - cd = sc_link->sd; - /* - * Only allow one at a time - */ - if (cd->flags & CH_OPEN) { - printf("ch%ld: already open\n", unit); - return EBUSY; - } /* - * Catch any unit attention errors. + * Only allow one open at a time. */ - scsi_test_unit_ready(sc_link, SCSI_SILENT); + if (link->flags & SDEV_OPEN) + return (EBUSY); + + link->flags |= SDEV_OPEN; - sc_link->flags |= SDEV_OPEN; /* - * Check that it is still responding and ok. + * Absorb any unit attention errors. Ignore "not ready" + * since this might occur if e.g. a tape isn't actually + * loaded in the drive. */ - if ( (errcode = (scsi_test_unit_ready(sc_link, 0))) ) { - printf("ch%ld: not ready\n", unit); - sc_link->flags &= ~SDEV_OPEN; - return errcode; - } + (void)scsi_test_unit_ready(link, SCSI_SILENT); + /* - * Make sure data is loaded + * Make sure our parameters are up to date. */ - if ( (errcode = (ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK))) ) { - printf("ch%ld: scsi changer :- offline\n", unit); - sc_link->flags &= ~SDEV_OPEN; - return (errcode); - } - cd->flags = CH_OPEN; - return 0; + if (error = ch_get_params(sc, 0)) + goto bad; + + return (0); + + bad: + link->flags &= ~SDEV_OPEN; + return (error); } -/* - * close the device.. only called if we are the LAST - * occurence of an open device - */ -static errval -ch_close(dev_t dev, int flag, int fmt, struct proc *p, - struct scsi_link *sc_link) +static errval +ch_devclose(dev, flags, fmt, p, link) + dev_t dev; + int flags, fmt; + struct proc *p; + struct scsi_link *link; { - sc_link->sd->flags = 0; - sc_link->flags &= ~SDEV_OPEN; + + link->flags &= ~SDEV_OPEN; return (0); } -/* - * Perform special action on behalf of the user - * Knows about the internals of this device - */ -static errval -ch_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc *p, - struct scsi_link *sc_link) +static errval +ch_devioctl(dev, cmd, data, flags, p, link) + dev_t dev; + int cmd; + caddr_t data; + int flags; + struct proc *p; + struct scsi_link *link; { - /* struct ch_cmd_buf *args; */ - unsigned char unit; - u_int32_t flags; - errval ret; - struct scsi_data *cd; + struct ch_softc *sc = (struct ch_softc *)(link->sd); + caddr_t elemdata; + int error = 0; + + switch (cmd) { + 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? */ - /* - * Find the device that the user is talking about - */ - flags = 0; /* give error messages, act on errors etc. */ - unit = CHUNIT(dev); - cd = sc_link->sd; - - switch ((int)cmd) { - case CHIOOP:{ - struct chop *ch = (struct chop *) arg; - SC_DEBUG(sc_link, SDEV_DB2, - ("[chtape_chop: %x]\n", ch->ch_op)); - - switch ((short) (ch->ch_op)) { - case CHGETPARAM: - ch->u.getparam.chmo = cd->chmo; - ch->u.getparam.chms = cd->chms; - ch->u.getparam.sloto = cd->sloto; - ch->u.getparam.slots = cd->slots; - ch->u.getparam.imexo = cd->imexo; - ch->u.getparam.imexs = cd->imexs; - ch->u.getparam.driveo = cd->driveo; - ch->u.getparam.drives = cd->drives; - ch->u.getparam.rot = cd->rot; - ch->result = 0; - return 0; - break; - case CHPOSITION: - return ch_position(unit, &ch->result, ch->u.position.chm, - ch->u.position.to, - flags); - case CHMOVE: - return ch_move(unit, &ch->result, ch->u.position.chm, - ch->u.move.from, ch->u.move.to, - flags); - case CHGETELEM: - return ch_getelem(unit, &ch->result, ch->u.get_elem_stat.type, - ch->u.get_elem_stat.from, &ch->u.get_elem_stat.elem_data, - flags); - default: - return EINVAL; - } - } default: - return scsi_do_ioctl(dev, cmd, arg, mode, p, sc_link); + error = scsi_do_ioctl(dev, cmd, data, flags, p, link); + break; } - return (ret ? ESUCCESS : EIO); + + return (error); } -static errval -ch_getelem(unit, stat, type, from, data, flags) - u_int32_t unit, from, flags; - int type; - short *stat; - void *data; /* XXX `struct untagged *' - see chio.h */ +static int +ch_move(sc, cm) + struct ch_softc *sc; + struct changer_move *cm; { - struct scsi_read_element_status scsi_cmd; - char elbuf[32]; - errval ret; - struct scsi_link *sc_link; + struct scsi_move_medium cmd; + u_int16_t fromelem, toelem; - if ((sc_link = SCSI_LINK(&ch_switch, unit)) == 0) - return ENXIO; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = 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; - - if ((ret = scsi_scsi_cmd(sc_link, - (struct scsi_generic *) &scsi_cmd, - sizeof(scsi_cmd), - (u_char *) elbuf, - 32, - CHRETRIES, - 100000, - NULL, - SCSI_DATA_IN | flags) != ESUCCESS)) { - *stat = sc_link->sd->lsterr; - bcopy(elbuf + 16, data, 16); - return ret; - } - bcopy(elbuf + 16, data, 16); /*Just a hack sh */ - return ret; + /* + * 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; + scsi_uto2b(sc->sc_picker, cmd.tea); + scsi_uto2b(fromelem, cmd.src); + scsi_uto2b(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)); } -static errval -ch_move(unit, stat, chm, from, to, flags) - u_int32_t unit, chm, from, to, flags; - short *stat; +static int +ch_exchange(sc, ce) + struct ch_softc *sc; + struct changer_exchange *ce; { - struct scsi_move_medium scsi_cmd; - errval ret; - struct scsi_link *sc_link; + struct scsi_exchange_medium cmd; + u_int16_t src, dst1, dst2; - if ((sc_link = SCSI_LINK(&ch_switch, unit)) == 0) - return ENXIO; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = 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; - if ((ret = scsi_scsi_cmd(sc_link, - (struct scsi_generic *) &scsi_cmd, - sizeof(scsi_cmd), - NULL, - 0, - CHRETRIES, - 100000, - NULL, - flags) != ESUCCESS)) { - *stat = sc_link->sd->lsterr; - return ret; - } - return ret; + /* + * 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; + scsi_uto2b(sc->sc_picker, cmd.tea); + scsi_uto2b(src, cmd.src); + scsi_uto2b(dst1, cmd.fdst); + scsi_uto2b(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)); } -static errval -ch_position(unit, stat, chm, to, flags) - u_int32_t unit, chm, to, flags; - short *stat; +static int +ch_position(sc, cp) + struct ch_softc *sc; + struct changer_position *cp; { - struct scsi_position_to_element scsi_cmd; - errval ret; - struct scsi_link *sc_link; + struct scsi_position_to_element cmd; + u_int16_t dst; - if ((sc_link = SCSI_LINK(&ch_switch, unit)) == 0) - return ENXIO; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = 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; - if ((ret = scsi_scsi_cmd(sc_link, - (struct scsi_generic *) &scsi_cmd, - sizeof(scsi_cmd), - NULL, - 0, - CHRETRIES, - 100000, - NULL, - flags) != ESUCCESS)) { - *stat = sc_link->sd->lsterr; - return ret; - } - return ret; -} + /* + * Check arguments. + */ + if (cp->cp_type > CHET_DT) + return (EINVAL); + if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1)) + return (ENODEV); -#ifdef __STDC__ -#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) -#else -#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 ) -#endif + /* + * 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; + scsi_uto2b(sc->sc_picker, cmd.tea); + scsi_uto2b(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). */ -static errval -ch_mode_sense(unit, flags) - u_int32_t unit, flags; +static int +ch_usergetelemstatus(sc, chet, uptr) + struct ch_softc *sc; + int chet; + u_int8_t *uptr; { - 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; - int32_t i, l; - errval errcode; - struct scsi_data *cd; - struct scsi_link *sc_link; + 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; - if ((sc_link = SCSI_LINK(&ch_switch, unit)) == 0) - return ENXIO; + 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 = scsi_2btou(pg_hdr->edl); - cd = sc_link->sd; + size = sizeof(struct read_element_status_header) + + sizeof(struct read_element_status_page_header) + + (desclen * sc->sc_counts[chet]); /* - * First check if we have it all loaded + * Reallocate storage for descriptors and get them from the + * device. */ - if (sc_link->flags & SDEV_MEDIA_LOADED) - return 0; + 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; /* - * First do a mode sense + * Fill in the user status array. */ - /* sc_link->flags &= ~SDEV_MEDIA_LOADED; *//*XXX */ - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = MODE_SENSE; - scsi_cmd.byte2 = SMS_DBD; - scsi_cmd.page = 0x3f; /* All Pages */ - scsi_cmd.length = sizeof(scsi_sense); + st_hdr = (struct read_element_status_header *)data; + avail = scsi_2btou(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); +} + +static int +ch_getelemstatus(sc, first, count, data, datalen) + struct ch_softc *sc; + int first, count; + caddr_t data; + size_t datalen; +{ + struct scsi_read_element_status cmd; + + /* + * Build SCSI command. + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = READ_ELEMENT_STATUS; + scsi_uto2b(first, cmd.sea); + scsi_uto2b(count, cmd.count); + scsi_uto3b(datalen, cmd.len); + + /* + * Send command to changer. + */ + 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. + */ +static 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. */ - if ( (errcode = scsi_scsi_cmd(sc_link, - (struct scsi_generic *) &scsi_cmd, - sizeof(struct scsi_mode_sense), - (u_char *) & scsi_sense, - sizeof (scsi_sense), - CHRETRIES, - 5000, - NULL, - flags | SCSI_DATA_IN) != 0) ) { - if (!(flags & SCSI_SILENT)) - printf("ch%ld: could not mode sense\n", unit); - return (errcode); + bzero(&cmd, sizeof(cmd)); + bzero(&sense_data, sizeof(sense_data)); + cmd.op_code = 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 sense element address page\n", + sc->sc_dev.dv_xname); + return (error); } - sc_link->flags |= SDEV_MEDIA_LOADED; - l = scsi_sense[0] - 3; - b = &scsi_sense[4]; + + sc->sc_firsts[CHET_MT] = scsi_2btou(sense_data.pages.ea.mtea); + sc->sc_counts[CHET_MT] = scsi_2btou(sense_data.pages.ea.nmte); + sc->sc_firsts[CHET_ST] = scsi_2btou(sense_data.pages.ea.fsea); + sc->sc_counts[CHET_ST] = scsi_2btou(sense_data.pages.ea.nse); + sc->sc_firsts[CHET_IE] = scsi_2btou(sense_data.pages.ea.fieea); + sc->sc_counts[CHET_IE] = scsi_2btou(sense_data.pages.ea.niee); + sc->sc_firsts[CHET_DT] = scsi_2btou(sense_data.pages.ea.fdtea); + sc->sc_counts[CHET_DT] = scsi_2btou(sense_data.pages.ea.ndte); + + /* XXX ask for page trasport geom */ /* - * To avoid alignment problems + * Grab info from the capabilities page. */ -/* XXX - 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_int32_t pc = (*b++) & 0x3f; - u_int32_t pl = *b++; - u_char *bb = b; - switch ((int)pc) { - case 0x1d: - cd->chmo = p2copy(bb); - cd->chms = p2copy(bb); - cd->sloto = p2copy(bb); - cd->slots = p2copy(bb); - cd->imexo = p2copy(bb); - cd->imexs = p2copy(bb); - cd->driveo = p2copy(bb); - cd->drives = p2copy(bb); - break; - case 0x1e: - cd->rot = (*b) & 1; - break; - case 0x1f: - cd->stor = *b & 0xf; - bb += 2; - cd->stor = p4copy(bb); - break; - default: - break; - } - b += pl; - i += pl + 2; + bzero(&cmd, sizeof(cmd)); + bzero(&sense_data, sizeof(sense_data)); + cmd.op_code = 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", + sc->sc_dev.dv_xname); + return (error); } - SC_DEBUG(sc_link, SDEV_DB2, - (" cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n", - cd->chmo, cd->chms, - cd->sloto, cd->slots, - cd->imexo, cd->imexs, - cd->driveo, cd->drives, - cd->rot ? "can" : "can't")); + + 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->sc_link->flags |= SDEV_MEDIA_LOADED; return (0); } +static int +chunit(dev) + dev_t dev; +{ + + return (CHUNIT(dev)); +} + +static dev_t +chsetunit(dev, unit) + dev_t dev; + int unit; +{ + + return (CHSETUNIT(dev, unit)); +} + static ch_devsw_installed = 0; -static void ch_drvinit(void *unused) +static void +ch_drvinit(void *unused) { dev_t dev; @@ -515,5 +721,3 @@ static void ch_drvinit(void *unused) } SYSINIT(chdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ch_drvinit,NULL) - - |