summaryrefslogtreecommitdiff
path: root/sys/scsi/ch.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1996-04-21 22:33:19 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1996-04-21 22:33:19 +0000
commit67d88b0a9910a68bb666b448d2dac29cb4d3d8c2 (patch)
tree967b89f6e07398a22bd8c76d30179b648776542d /sys/scsi/ch.c
parentba95d3c1d69cdb251d15a12ebf70f50b0ea2019b (diff)
partial sync with netbsd 960418, more to come
Diffstat (limited to 'sys/scsi/ch.c')
-rw-r--r--sys/scsi/ch.c829
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);
}