summaryrefslogtreecommitdiff
path: root/sys/scsi
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sys/scsi
initial import of NetBSD tree
Diffstat (limited to 'sys/scsi')
-rw-r--r--sys/scsi/README43
-rw-r--r--sys/scsi/cd.c1169
-rw-r--r--sys/scsi/ch.c462
-rw-r--r--sys/scsi/files.scsi26
-rw-r--r--sys/scsi/scsi_all.h316
-rw-r--r--sys/scsi/scsi_base.c848
-rw-r--r--sys/scsi/scsi_cd.h212
-rw-r--r--sys/scsi/scsi_changer.h94
-rw-r--r--sys/scsi/scsi_debug.h42
-rw-r--r--sys/scsi/scsi_disk.h208
-rw-r--r--sys/scsi/scsi_ioctl.c361
-rw-r--r--sys/scsi/scsi_message.h33
-rw-r--r--sys/scsi/scsi_tape.h209
-rw-r--r--sys/scsi/scsiconf.c576
-rw-r--r--sys/scsi/scsiconf.h281
-rw-r--r--sys/scsi/sd.c996
-rw-r--r--sys/scsi/st.c1786
-rw-r--r--sys/scsi/su.c5
-rw-r--r--sys/scsi/uk.c174
19 files changed, 7841 insertions, 0 deletions
diff --git a/sys/scsi/README b/sys/scsi/README
new file mode 100644
index 00000000000..b0c51b69992
--- /dev/null
+++ b/sys/scsi/README
@@ -0,0 +1,43 @@
+/* $NetBSD: README,v 1.6 1995/06/02 14:04:36 mycroft Exp $ */
+
+This release consists of the following files
+
+share/man/man4/scsi.4 <-useful general info
+share/man/man4/uk.4
+share/man/man4/su.4
+share/man/man4/ch.4
+share/man/man4/cd.4
+share/man/man4/sd.4
+share/man/man4/st.4 <--READ THIS IF YOU USE TAPES!
+sbin/scsi/procargs.c
+sbin/scsi/scsi.c
+sbin/scsi/scsi.1
+sbin/scsi/Makefile
+sbin/st/Makefile
+sbin/st/st.1
+sbin/st/st.c
+
+
+################## Using the scsi system ##################
+--------------making devices------------
+see st(1) and st(4) for info on tape devices.
+
+---------ioctl definitions-------------
+User accessable structures (e.g. ioctl definitions) have been
+placed in sys/cdio, sys/sgio and sys/chio (based after sys/mtio for
+the ioctls for mag tapes (including st).
+General scsi ioctls are found in sys/scsiio.h.
+
+-----------cd-rom-----------------
+Cdrom audio is only suported at all for cdroms that use SCSI2 audio
+definitions.
+
+-------------media changer---------------
+Once again courtesy of grefen@convex.com (in germany)
+I have not tested this but he assures me it's ready for testing.
+If anyone has an exabyte tape changer or similar,
+contact the author for information regarding the control interface
+and program.
+
+WARNING: This has not been tested for a LONG TIME!
+
diff --git a/sys/scsi/cd.c b/sys/scsi/cd.c
new file mode 100644
index 00000000000..3dbe5c741f7
--- /dev/null
+++ b/sys/scsi/cd.c
@@ -0,0 +1,1169 @@
+/* $NetBSD: cd.c,v 1.76 1995/10/10 02:52:56 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
+ *
+ * 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 acknowledgement:
+ * This product includes software developed by Charles M. Hannum.
+ * 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.
+ */
+
+/*
+ * Originally written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/disk.h>
+#include <sys/cdio.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_cd.h>
+#include <scsi/scsi_disk.h> /* rw_big and start_stop come from there */
+#include <scsi/scsiconf.h>
+
+#define CDOUTSTANDING 2
+#define CDRETRIES 1
+
+#define CDUNIT(z) DISKUNIT(z)
+#define CDPART(z) DISKPART(z)
+#define MAKECDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part)
+
+struct cd_softc {
+ struct device sc_dev;
+ struct dkdevice sc_dk;
+
+ int flags;
+#define CDF_LOCKED 0x01
+#define CDF_WANTED 0x02
+#define CDF_WLABEL 0x04 /* label is writable */
+#define CDF_LABELLING 0x08 /* writing label */
+ struct scsi_link *sc_link; /* contains our targ, lun, etc. */
+ struct cd_parms {
+ int blksize;
+ u_long disksize; /* total number sectors */
+ } params;
+ struct buf buf_queue;
+};
+
+int cdmatch __P((struct device *, void *, void *));
+void cdattach __P((struct device *, struct device *, void *));
+
+struct cfdriver cdcd = {
+ NULL, "cd", cdmatch, cdattach, DV_DISK, sizeof(struct cd_softc)
+};
+
+void cdgetdisklabel __P((struct cd_softc *));
+int cd_get_parms __P((struct cd_softc *, int));
+void cdstrategy __P((struct buf *));
+void cdstart __P((struct cd_softc *));
+
+struct dkdriver cddkdriver = { cdstrategy };
+
+struct scsi_device cd_switch = {
+ NULL, /* use default error handler */
+ cdstart, /* we have a queue, which is started by this */
+ NULL, /* we do not have an async handler */
+ NULL, /* use default 'done' routine */
+};
+
+struct scsi_inquiry_pattern cd_patterns[] = {
+ {T_CDROM, T_REMOV,
+ "", "", ""},
+#if 0
+ {T_CDROM, T_REMOV, /* more luns */
+ "PIONEER ", "CD-ROM DRM-600 ", ""},
+#endif
+};
+
+int
+cdmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct scsibus_attach_args *sa = aux;
+ int priority;
+
+ (void)scsi_inqmatch(sa->sa_inqbuf,
+ (caddr_t)cd_patterns, sizeof(cd_patterns)/sizeof(cd_patterns[0]),
+ sizeof(cd_patterns[0]), &priority);
+ return (priority);
+}
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * A device suitable for this driver
+ */
+void
+cdattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct cd_softc *cd = (void *)self;
+ struct cd_parms *dp = &cd->params;
+ struct scsibus_attach_args *sa = aux;
+ struct scsi_link *sc_link = sa->sa_sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("cdattach: "));
+
+ /*
+ * Store information needed to contact our base driver
+ */
+ cd->sc_link = sc_link;
+ sc_link->device = &cd_switch;
+ sc_link->device_softc = cd;
+ if (sc_link->openings > CDOUTSTANDING)
+ sc_link->openings = CDOUTSTANDING;
+
+ cd->sc_dk.dk_driver = &cddkdriver;
+#if !defined(i386) || defined(NEWCONFIG)
+ dk_establish(&cd->sc_dk, &cd->sc_dev);
+#endif
+
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ if (scsi_start(cd->sc_link, SSS_START,
+ SCSI_AUTOCONF | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT) ||
+ cd_get_parms(cd, SCSI_AUTOCONF) != 0)
+ printf(": drive empty\n");
+ else
+ printf(": cd present, %d x %d byte records\n",
+ cd->params.disksize, cd->params.blksize);
+}
+
+/*
+ * Wait interruptibly for an exclusive lock.
+ *
+ * XXX
+ * Several drivers do this; it should be abstracted and made MP-safe.
+ */
+int
+cdlock(cd)
+ struct cd_softc *cd;
+{
+ int error;
+
+ while ((cd->flags & CDF_LOCKED) != 0) {
+ cd->flags |= CDF_WANTED;
+ if ((error = tsleep(cd, PRIBIO | PCATCH, "cdlck", 0)) != 0)
+ return error;
+ }
+ cd->flags |= CDF_LOCKED;
+ return 0;
+}
+
+/*
+ * Unlock and wake up any waiters.
+ */
+void
+cdunlock(cd)
+ struct cd_softc *cd;
+{
+
+ cd->flags &= ~CDF_LOCKED;
+ if ((cd->flags & CDF_WANTED) != 0) {
+ cd->flags &= ~CDF_WANTED;
+ wakeup(cd);
+ }
+}
+
+/*
+ * open the device. Make sure the partition info is a up-to-date as can be.
+ */
+int
+cdopen(dev, flag, fmt)
+ dev_t dev;
+ int flag, fmt;
+{
+ struct cd_softc *cd;
+ struct scsi_link *sc_link;
+ int unit, part;
+ int error;
+
+ unit = CDUNIT(dev);
+ if (unit >= cdcd.cd_ndevs)
+ return ENXIO;
+ cd = cdcd.cd_devs[unit];
+ if (!cd)
+ return ENXIO;
+
+ sc_link = cd->sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1,
+ ("cdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit,
+ cdcd.cd_ndevs, part));
+
+ if (error = cdlock(cd))
+ return error;
+
+ if (cd->sc_dk.dk_openmask != 0) {
+ /*
+ * If any partition is open, but the disk has been invalidated,
+ * disallow further opens.
+ */
+ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ error = EIO;
+ goto bad3;
+ }
+ } else {
+ /* Check that it is still responding and ok. */
+ if (error = scsi_test_unit_ready(sc_link,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_IGNORE_NOT_READY))
+ goto bad3;
+
+ /* Start the pack spinning if necessary. */
+ if (error = scsi_start(sc_link, SSS_START,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT))
+ goto bad3;
+
+ sc_link->flags |= SDEV_OPEN;
+
+ /* Lock the pack in. */
+ if (error = scsi_prevent(sc_link, PR_PREVENT,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE))
+ goto bad;
+
+ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ sc_link->flags |= SDEV_MEDIA_LOADED;
+
+ /* Load the physical device parameters. */
+ if (cd_get_parms(cd, 0) != 0) {
+ error = ENXIO;
+ goto bad2;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded "));
+
+ /* Fabricate a disk label. */
+ cdgetdisklabel(cd);
+ SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel fabricated "));
+ }
+ }
+
+ part = CDPART(dev);
+
+ /* Check that the partition exists. */
+ if (part != RAW_PART &&
+ (part >= cd->sc_dk.dk_label.d_npartitions ||
+ cd->sc_dk.dk_label.d_partitions[part].p_fstype == FS_UNUSED)) {
+ error = ENXIO;
+ goto bad;
+ }
+
+ /* Insure only one open at a time. */
+ switch (fmt) {
+ case S_IFCHR:
+ cd->sc_dk.dk_copenmask |= (1 << part);
+ break;
+ case S_IFBLK:
+ cd->sc_dk.dk_bopenmask |= (1 << part);
+ break;
+ }
+ cd->sc_dk.dk_openmask = cd->sc_dk.dk_copenmask | cd->sc_dk.dk_bopenmask;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n"));
+ cdunlock(cd);
+ return 0;
+
+bad2:
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+
+bad:
+ if (cd->sc_dk.dk_openmask == 0) {
+ scsi_prevent(sc_link, PR_ALLOW,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);
+ sc_link->flags &= ~SDEV_OPEN;
+ }
+
+bad3:
+ cdunlock(cd);
+ return error;
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+int
+cdclose(dev, flag, fmt)
+ dev_t dev;
+ int flag, fmt;
+{
+ struct cd_softc *cd = cdcd.cd_devs[CDUNIT(dev)];
+ int part = CDPART(dev);
+ int error;
+
+ if (error = cdlock(cd))
+ return error;
+
+ switch (fmt) {
+ case S_IFCHR:
+ cd->sc_dk.dk_copenmask &= ~(1 << part);
+ break;
+ case S_IFBLK:
+ cd->sc_dk.dk_bopenmask &= ~(1 << part);
+ break;
+ }
+ cd->sc_dk.dk_openmask = cd->sc_dk.dk_copenmask | cd->sc_dk.dk_bopenmask;
+
+ if (cd->sc_dk.dk_openmask == 0) {
+ /* XXXX Must wait for I/O to complete! */
+
+ scsi_prevent(cd->sc_link, PR_ALLOW,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY);
+ cd->sc_link->flags &= ~SDEV_OPEN;
+ }
+
+ cdunlock(cd);
+ return 0;
+}
+
+/*
+ * Actually translate the requested transfer into one the physical driver can
+ * understand. The transfer is described by a buf and will include only one
+ * physical transfer.
+ */
+void
+cdstrategy(bp)
+ struct buf *bp;
+{
+ struct cd_softc *cd = cdcd.cd_devs[CDUNIT(bp->b_dev)];
+ int opri;
+
+ SC_DEBUG(cd->sc_link, SDEV_DB2, ("cdstrategy "));
+ SC_DEBUG(cd->sc_link, SDEV_DB1,
+ ("%d bytes @ blk %d\n", bp->b_bcount, bp->b_blkno));
+ /*
+ * The transfer must be a whole number of blocks.
+ */
+ if ((bp->b_bcount % cd->sc_dk.dk_label.d_secsize) != 0) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+ /*
+ * If the device has been made invalid, error out
+ * maybe the media changed
+ */
+ if ((cd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /*
+ * If it's a null transfer, return immediately
+ */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ /*
+ * Do bounds checking, adjust transfer. if error, process.
+ * If end of partition, just return.
+ */
+ if (CDPART(bp->b_dev) != RAW_PART &&
+ bounds_check_with_label(bp, &cd->sc_dk.dk_label,
+ (cd->flags & (CDF_WLABEL|CDF_LABELLING)) != 0) <= 0)
+ goto done;
+
+ opri = splbio();
+
+ /*
+ * Place it in the queue of disk activities for this disk
+ */
+ disksort(&cd->buf_queue, bp);
+
+ /*
+ * Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion
+ */
+ cdstart(cd);
+
+ splx(opri);
+ return;
+
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ /*
+ * Correctly set the buf to indicate a completed xfer
+ */
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+}
+
+/*
+ * cdstart looks to see if there is a buf waiting for the device
+ * and that the device is not already busy. If both are true,
+ * It deques the buf and creates a scsi command to perform the
+ * transfer in the buf. The transfer request will call scsi_done
+ * on completion, which will in turn call this routine again
+ * so that the next queued transfer is performed.
+ * The bufs are queued by the strategy routine (cdstrategy)
+ *
+ * This routine is also called after other non-queued requests
+ * have been made of the scsi driver, to ensure that the queue
+ * continues to be drained.
+ *
+ * must be called at the correct (highish) spl level
+ * cdstart() is called at splbio from cdstrategy and scsi_done
+ */
+void
+cdstart(cd)
+ register struct cd_softc *cd;
+{
+ register struct scsi_link *sc_link = cd->sc_link;
+ struct buf *bp = 0;
+ struct buf *dp;
+ struct scsi_rw_big cmd;
+ int blkno, nblks;
+ struct partition *p;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("cdstart "));
+ /*
+ * Check if the device has room for another command
+ */
+ while (sc_link->openings > 0) {
+ /*
+ * there is excess capacity, but a special waits
+ * It'll need the adapter as soon as we clear out of the
+ * way and let it run (user level wait).
+ */
+ if (sc_link->flags & SDEV_WAITING) {
+ sc_link->flags &= ~SDEV_WAITING;
+ wakeup((caddr_t)sc_link);
+ return;
+ }
+
+ /*
+ * See if there is a buf with work for us to do..
+ */
+ dp = &cd->buf_queue;
+ if ((bp = dp->b_actf) == NULL) /* yes, an assign */
+ return;
+ dp->b_actf = bp->b_actf;
+
+ /*
+ * If the deivce has become invalid, abort all the
+ * reads and writes until all files have been closed and
+ * re-opened
+ */
+ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ continue;
+ }
+
+ /*
+ * We have a buf, now we should make a command
+ *
+ * First, translate the block to absolute and put it in terms
+ * of the logical blocksize of the device.
+ */
+ blkno =
+ bp->b_blkno / (cd->sc_dk.dk_label.d_secsize / DEV_BSIZE);
+ if (CDPART(bp->b_dev) != RAW_PART) {
+ p = &cd->sc_dk.dk_label.d_partitions[CDPART(bp->b_dev)];
+ blkno += p->p_offset;
+ }
+ nblks = howmany(bp->b_bcount, cd->sc_dk.dk_label.d_secsize);
+
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = (bp->b_flags & B_READ) ? READ_BIG : WRITE_BIG;
+ cmd.addr_3 = (blkno >> 24) & 0xff;
+ cmd.addr_2 = (blkno >> 16) & 0xff;
+ cmd.addr_1 = (blkno >> 8) & 0xff;
+ cmd.addr_0 = blkno & 0xff;
+ cmd.length2 = (nblks >> 8) & 0xff;
+ cmd.length1 = nblks & 0xff;
+
+ /*
+ * Call the routine that chats with the adapter.
+ * Note: we cannot sleep as we may be an interrupt
+ */
+ if (scsi_scsi_cmd(sc_link, (struct scsi_generic *)&cmd,
+ sizeof(cmd), (u_char *) bp->b_data, bp->b_bcount,
+ CDRETRIES, 30000, bp, SCSI_NOSLEEP |
+ ((bp->b_flags & B_READ) ? SCSI_DATA_IN : SCSI_DATA_OUT)))
+ printf("%s: not queued", cd->sc_dev.dv_xname);
+ }
+}
+
+int
+cdread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ struct cd_softc *cd = cdcd.cd_devs[CDUNIT(dev)];
+
+ return (physio(cdstrategy, NULL, dev, B_READ,
+ cd->sc_link->adapter->scsi_minphys, uio));
+}
+
+int
+cdwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ struct cd_softc *cd = cdcd.cd_devs[CDUNIT(dev)];
+
+ return (physio(cdstrategy, NULL, dev, B_WRITE,
+ cd->sc_link->adapter->scsi_minphys, uio));
+}
+
+/*
+ * Perform special action on behalf of the user.
+ * Knows about the internals of this device
+ */
+int
+cdioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ struct cd_softc *cd = cdcd.cd_devs[CDUNIT(dev)];
+ int error;
+
+ SC_DEBUG(cd->sc_link, SDEV_DB2, ("cdioctl 0x%lx ", cmd));
+
+ /*
+ * If the device is not valid.. abandon ship
+ */
+ if ((cd->sc_link->flags & SDEV_MEDIA_LOADED) == 0)
+ return EIO;
+
+ switch (cmd) {
+ case DIOCGDINFO:
+ *(struct disklabel *)addr = cd->sc_dk.dk_label;
+ return 0;
+
+ case DIOCGPART:
+ ((struct partinfo *)addr)->disklab = &cd->sc_dk.dk_label;
+ ((struct partinfo *)addr)->part =
+ &cd->sc_dk.dk_label.d_partitions[CDPART(dev)];
+ return 0;
+
+ case DIOCWDINFO:
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+
+ if (error = cdlock(cd))
+ return error;
+ cd->flags |= CDF_LABELLING;
+
+ error = setdisklabel(&cd->sc_dk.dk_label,
+ (struct disklabel *)addr, /*cd->sc_dk.dk_openmask : */0,
+ &cd->sc_dk.dk_cpulabel);
+ if (error == 0) {
+ }
+
+ cd->flags &= ~CDF_LABELLING;
+ cdunlock(cd);
+ return error;
+
+ case DIOCWLABEL:
+ return EBADF;
+
+ case CDIOCPLAYTRACKS: {
+ struct ioc_play_track *args = (struct ioc_play_track *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(cd, &data))
+ return error;
+ return cd_play_tracks(cd, args->start_track, args->start_index,
+ args->end_track, args->end_index);
+ }
+ case CDIOCPLAYMSF: {
+ struct ioc_play_msf *args
+ = (struct ioc_play_msf *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(cd, &data))
+ return error;
+ return cd_play_msf(cd, args->start_m, args->start_s,
+ args->start_f, args->end_m, args->end_s, args->end_f);
+ }
+ case CDIOCPLAYBLOCKS: {
+ struct ioc_play_blocks *args
+ = (struct ioc_play_blocks *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(cd, &data))
+ return error;
+ return cd_play(cd, args->blk, args->len);
+ }
+ case CDIOCREADSUBCHANNEL: {
+ struct ioc_read_subchannel *args
+ = (struct ioc_read_subchannel *)addr;
+ struct cd_sub_channel_info data;
+ int len = args->data_len;
+ if (len > sizeof(data) ||
+ len < sizeof(struct cd_sub_channel_header))
+ return EINVAL;
+ if (error = cd_read_subchannel(cd, args->address_format,
+ args->data_format, args->track, &data, len))
+ return error;
+ len = min(len, ((data.header.data_len[0] << 8) +
+ data.header.data_len[1] +
+ sizeof(struct cd_sub_channel_header)));
+ return copyout(&data, args->data, len);
+ }
+ case CDIOREADTOCHEADER: {
+ struct ioc_toc_header th;
+ if (error = cd_read_toc(cd, 0, 0, &th, sizeof(th)))
+ return error;
+ th.len = ntohs(th.len);
+ bcopy(&th, addr, sizeof(th));
+ return 0;
+ }
+ case CDIOREADTOCENTRYS: {
+ struct cd_toc {
+ struct ioc_toc_header header;
+ struct cd_toc_entry entries[65];
+ } data;
+ struct ioc_read_toc_entry *te =
+ (struct ioc_read_toc_entry *)addr;
+ struct ioc_toc_header *th;
+ int len = te->data_len;
+ th = &data.header;
+
+ if (len > sizeof(data.entries) ||
+ len < sizeof(struct cd_toc_entry))
+ return EINVAL;
+ if (error = cd_read_toc(cd, te->address_format,
+ te->starting_track, (struct cd_toc_entry *)&data,
+ len + sizeof(struct ioc_toc_header)))
+ return error;
+ len = min(len, ntohs(th->len) - (sizeof(th->starting_track) +
+ sizeof(th->ending_track)));
+ return copyout(data.entries, te->data, len);
+ }
+ case CDIOCSETPATCH: {
+ struct ioc_patch *arg = (struct ioc_patch *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = arg->patch[0];
+ data.page.audio.port[RIGHT_PORT].channels = arg->patch[1];
+ data.page.audio.port[2].channels = arg->patch[2];
+ data.page.audio.port[3].channels = arg->patch[3];
+ return cd_set_mode(cd, &data);
+ }
+ case CDIOCGETVOL: {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ arg->vol[LEFT_PORT] = data.page.audio.port[LEFT_PORT].volume;
+ arg->vol[RIGHT_PORT] = data.page.audio.port[RIGHT_PORT].volume;
+ arg->vol[2] = data.page.audio.port[2].volume;
+ arg->vol[3] = data.page.audio.port[3].volume;
+ return 0;
+ }
+ case CDIOCSETVOL: {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = CHANNEL_0;
+ data.page.audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT];
+ data.page.audio.port[RIGHT_PORT].channels = CHANNEL_1;
+ data.page.audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT];
+ data.page.audio.port[2].volume = arg->vol[2];
+ data.page.audio.port[3].volume = arg->vol[3];
+ return cd_set_mode(cd, &data);
+ }
+ case CDIOCSETMONO: {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels =
+ LEFT_CHANNEL | RIGHT_CHANNEL | 4 | 8;
+ data.page.audio.port[RIGHT_PORT].channels =
+ LEFT_CHANNEL | RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+ case CDIOCSETSTEREO: {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+ case CDIOCSETMUTE: {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = 0;
+ data.page.audio.port[RIGHT_PORT].channels = 0;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+ case CDIOCSETLEFT: {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = LEFT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+ case CDIOCSETRIGHT: {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+ return error;
+ data.page.audio.port[LEFT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL;
+ data.page.audio.port[2].channels = 0;
+ data.page.audio.port[3].channels = 0;
+ return cd_set_mode(cd, &data);
+ }
+ case CDIOCRESUME:
+ return cd_pause(cd, 1);
+ case CDIOCPAUSE:
+ return cd_pause(cd, 0);
+ case CDIOCSTART:
+ return scsi_start(cd->sc_link, SSS_START, 0);
+ case CDIOCSTOP:
+ return scsi_start(cd->sc_link, SSS_STOP, 0);
+ case CDIOCEJECT:
+ return scsi_start(cd->sc_link, SSS_STOP|SSS_LOEJ, 0);
+ case CDIOCALLOW:
+ return scsi_prevent(cd->sc_link, PR_ALLOW, 0);
+ case CDIOCPREVENT:
+ return scsi_prevent(cd->sc_link, PR_PREVENT, 0);
+ case CDIOCSETDEBUG:
+ cd->sc_link->flags |= (SDEV_DB1 | SDEV_DB2);
+ return 0;
+ case CDIOCCLRDEBUG:
+ cd->sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2);
+ return 0;
+ case CDIOCRESET:
+ return cd_reset(cd);
+
+ default:
+ if (CDPART(dev) != RAW_PART)
+ return ENOTTY;
+ return scsi_do_ioctl(cd->sc_link, dev, cmd, addr, flag, p);
+ }
+
+#ifdef DIAGNOSTIC
+ panic("cdioctl: impossible");
+#endif
+}
+
+/*
+ * Load the label information on the named device
+ * Actually fabricate a disklabel
+ *
+ * EVENTUALLY take information about different
+ * data tracks from the TOC and put it in the disklabel
+ */
+void
+cdgetdisklabel(cd)
+ struct cd_softc *cd;
+{
+ struct disklabel *lp = &cd->sc_dk.dk_label;
+
+ bzero(lp, sizeof(struct disklabel));
+ bzero(&cd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
+
+ lp->d_secsize = cd->params.blksize;
+ lp->d_ntracks = 1;
+ lp->d_nsectors = 100;
+ lp->d_ncylinders = (cd->params.disksize / 100) + 1;
+ lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
+
+ strncpy(lp->d_typename, "SCSI CD-ROM", 16);
+ lp->d_type = DTYPE_SCSI;
+ strncpy(lp->d_packname, "fictitious", 16);
+ lp->d_secperunit = cd->params.disksize;
+ lp->d_rpm = 300;
+ lp->d_interleave = 1;
+ lp->d_flags = D_REMOVABLE;
+
+ lp->d_partitions[0].p_offset = 0;
+ lp->d_partitions[0].p_size =
+ lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
+ lp->d_partitions[0].p_fstype = FS_ISO9660;
+ lp->d_partitions[RAW_PART].p_offset = 0;
+ lp->d_partitions[RAW_PART].p_size =
+ lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
+ lp->d_partitions[RAW_PART].p_fstype = FS_ISO9660;
+ lp->d_npartitions = RAW_PART + 1;
+
+ lp->d_magic = DISKMAGIC;
+ lp->d_magic2 = DISKMAGIC;
+ lp->d_checksum = dkcksum(lp);
+}
+
+/*
+ * Find out from the device what it's capacity is
+ */
+u_long
+cd_size(cd, flags)
+ struct cd_softc *cd;
+ int flags;
+{
+ struct scsi_read_cd_cap_data rdcap;
+ struct scsi_read_cd_capacity scsi_cmd;
+ int blksize;
+ u_long size;
+
+ /*
+ * make up a scsi command and ask the scsi driver to do
+ * it for you.
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = READ_CD_CAPACITY;
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks and a blocksize
+ */
+ if (scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)&rdcap, sizeof(rdcap), CDRETRIES,
+ 2000, NULL, flags | SCSI_DATA_IN) != 0)
+ return 0;
+
+ blksize = (rdcap.length_3 << 24) + (rdcap.length_2 << 16) +
+ (rdcap.length_1 << 8) + rdcap.length_0;
+ if (blksize < 512)
+ blksize = 2048; /* some drives lie ! */
+ cd->params.blksize = blksize;
+
+ size = (rdcap.addr_3 << 24) + (rdcap.addr_2 << 16) +
+ (rdcap.addr_1 << 8) + rdcap.addr_0 + 1;
+ if (size < 100)
+ size = 400000; /* ditto */
+ cd->params.disksize = size;
+
+ return size;
+}
+
+/*
+ * Get the requested page into the buffer given
+ */
+int
+cd_get_mode(cd, data, page)
+ struct cd_softc *cd;
+ struct cd_mode_data *data;
+ int page;
+{
+ struct scsi_mode_sense scsi_cmd;
+ int error;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ bzero(data, sizeof(*data));
+ scsi_cmd.opcode = MODE_SENSE;
+ scsi_cmd.page = page;
+ scsi_cmd.length = sizeof(*data) & 0xff;
+ return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)data, sizeof(*data), CDRETRIES, 20000,
+ NULL, SCSI_DATA_IN);
+}
+
+/*
+ * Get the requested page into the buffer given
+ */
+int
+cd_set_mode(cd, data)
+ struct cd_softc *cd;
+ struct cd_mode_data *data;
+{
+ struct scsi_mode_select scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = MODE_SELECT;
+ scsi_cmd.byte2 |= SMS_PF;
+ scsi_cmd.length = sizeof(*data) & 0xff;
+ data->header.data_length = 0;
+ return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)data, sizeof(*data), CDRETRIES, 20000,
+ NULL, SCSI_DATA_OUT);
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+int
+cd_play(cd, blkno, nblks)
+ struct cd_softc *cd;
+ int blkno, nblks;
+{
+ struct scsi_play scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY;
+ scsi_cmd.blk_addr[0] = (blkno >> 24) & 0xff;
+ scsi_cmd.blk_addr[1] = (blkno >> 16) & 0xff;
+ scsi_cmd.blk_addr[2] = (blkno >> 8) & 0xff;
+ scsi_cmd.blk_addr[3] = blkno & 0xff;
+ scsi_cmd.xfer_len[0] = (nblks >> 8) & 0xff;
+ scsi_cmd.xfer_len[1] = nblks & 0xff;
+ return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 200000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+int
+cd_play_big(cd, blkno, nblks)
+ struct cd_softc *cd;
+ int blkno, nblks;
+{
+ struct scsi_play_big scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY_BIG;
+ scsi_cmd.blk_addr[0] = (blkno >> 24) & 0xff;
+ scsi_cmd.blk_addr[1] = (blkno >> 16) & 0xff;
+ scsi_cmd.blk_addr[2] = (blkno >> 8) & 0xff;
+ scsi_cmd.blk_addr[3] = blkno & 0xff;
+ scsi_cmd.xfer_len[0] = (nblks >> 24) & 0xff;
+ scsi_cmd.xfer_len[1] = (nblks >> 16) & 0xff;
+ scsi_cmd.xfer_len[2] = (nblks >> 8) & 0xff;
+ scsi_cmd.xfer_len[3] = nblks & 0xff;
+ return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 20000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+int
+cd_play_tracks(cd, strack, sindex, etrack, eindex)
+ struct cd_softc *cd;
+ int strack, sindex, etrack, eindex;
+{
+ struct scsi_play_track scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY_TRACK;
+ scsi_cmd.start_track = strack;
+ scsi_cmd.start_index = sindex;
+ scsi_cmd.end_track = etrack;
+ scsi_cmd.end_index = eindex;
+ return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 20000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "play msf" command
+ */
+int
+cd_play_msf(cd, startm, starts, startf, endm, ends, endf)
+ struct cd_softc *cd;
+ int startm, starts, startf, endm, ends, endf;
+{
+ struct scsi_play_msf scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PLAY_MSF;
+ scsi_cmd.start_m = startm;
+ scsi_cmd.start_s = starts;
+ scsi_cmd.start_f = startf;
+ scsi_cmd.end_m = endm;
+ scsi_cmd.end_s = ends;
+ scsi_cmd.end_f = endf;
+ return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 2000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "start up" command
+ */
+int
+cd_pause(cd, go)
+ struct cd_softc *cd;
+ int go;
+{
+ struct scsi_pause scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PAUSE;
+ scsi_cmd.resume = go;
+ return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, CDRETRIES, 2000, NULL, 0);
+}
+
+/*
+ * Get scsi driver to send a "RESET" command
+ */
+int
+cd_reset(cd)
+ struct cd_softc *cd;
+{
+
+ return scsi_scsi_cmd(cd->sc_link, 0, 0, 0, 0, CDRETRIES, 2000, NULL,
+ SCSI_RESET);
+}
+
+/*
+ * Read subchannel
+ */
+int
+cd_read_subchannel(cd, mode, format, track, data, len)
+ struct cd_softc *cd;
+ int mode, format, len;
+ struct cd_sub_channel_info *data;
+{
+ struct scsi_read_subchannel scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = READ_SUBCHANNEL;
+ if (mode == CD_MSF_FORMAT)
+ scsi_cmd.byte2 |= CD_MSF;
+ scsi_cmd.byte3 = SRS_SUBQ;
+ scsi_cmd.subchan_format = format;
+ scsi_cmd.track = track;
+ scsi_cmd.data_len[0] = (len >> 8) & 0xff;
+ scsi_cmd.data_len[1] = len & 0xff;
+ return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(struct scsi_read_subchannel), (u_char *)data, len,
+ CDRETRIES, 5000, NULL, SCSI_DATA_IN);
+}
+
+/*
+ * Read table of contents
+ */
+int
+cd_read_toc(cd, mode, start, data, len)
+ struct cd_softc *cd;
+ int mode, start, len;
+ struct cd_toc_entry *data;
+{
+ struct scsi_read_toc scsi_cmd;
+ int ntoc;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ /*if (len!=sizeof(struct ioc_toc_header))
+ * ntoc=((len)-sizeof(struct ioc_toc_header))/sizeof(struct cd_toc_entry);
+ * else */
+ ntoc = len;
+ scsi_cmd.opcode = READ_TOC;
+ if (mode == CD_MSF_FORMAT)
+ scsi_cmd.byte2 |= CD_MSF;
+ scsi_cmd.from_track = start;
+ scsi_cmd.data_len[0] = (ntoc >> 8) & 0xff;
+ scsi_cmd.data_len[1] = ntoc & 0xff;
+ return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(struct scsi_read_toc), (u_char *)data, len, CDRETRIES,
+ 5000, NULL, SCSI_DATA_IN);
+}
+
+/*
+ * Get the scsi driver to send a full inquiry to the device and use the
+ * results to fill out the disk parameter structure.
+ */
+int
+cd_get_parms(cd, flags)
+ struct cd_softc *cd;
+ int flags;
+{
+
+ /*
+ * give a number of sectors so that sec * trks * cyls
+ * is <= disk_size
+ */
+ if (cd_size(cd, flags) == 0)
+ return ENXIO;
+
+ return 0;
+}
+
+int
+cdsize(dev)
+ dev_t dev;
+{
+
+ /* CD-ROMs are read-only. */
+ return -1;
+}
+
+int
+cddump(dev, blkno, va, size)
+ dev_t dev;
+ daddr_t blkno;
+ caddr_t va;
+ size_t size;
+{
+
+ /* Not implemented. */
+ return ENXIO;
+}
diff --git a/sys/scsi/ch.c b/sys/scsi/ch.c
new file mode 100644
index 00000000000..8f9cb9e17a9
--- /dev/null
+++ b/sys/scsi/ch.c
@@ -0,0 +1,462 @@
+/* $NetBSD: ch.c,v 1.14 1995/01/16 21:31:38 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1994 Charles Hannum. All rights reserved.
+ *
+ * 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 acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 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.
+ */
+
+/*
+ * Originally written by grefen@?????
+ * Based on scsi drivers by Julian Elischer (julian@tfs.com)
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/chio.h>
+#include <sys/device.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_changer.h>
+#include <scsi/scsiconf.h>
+
+#define CHRETRIES 2
+
+#define CHMODE(z) (minor(z) & 0x0f)
+#define CHUNIT(z) (minor(z) >> 4)
+
+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 */
+};
+
+int chmatch __P((struct device *, void *, void *));
+void chattach __P((struct device *, struct device *, void *));
+
+struct cfdriver chcd = {
+ NULL, "ch", chmatch, chattach, DV_DULL, sizeof(struct ch_softc)
+};
+
+/*
+ * This driver is so simple it uses all the default services
+ */
+struct scsi_device ch_switch = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+struct scsi_inquiry_pattern ch_patterns[] = {
+ {T_CHANGER, T_REMOV,
+ "", "", ""},
+};
+
+int
+chmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct scsibus_attach_args *sa = aux;
+ int priority;
+
+ (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
+chattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct ch_softc *ch = (void *)self;
+ struct scsibus_attach_args *sa = aux;
+ struct scsi_link *sc_link = sa->sa_sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("chattach: "));
+
+ /*
+ * 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;
+
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ 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);
+}
+
+/*
+ * open the device.
+ */
+int
+chopen(dev)
+ dev_t dev;
+{
+ int error = 0;
+ int unit, mode;
+ struct ch_softc *ch;
+ struct scsi_link *sc_link;
+
+ unit = CHUNIT(dev);
+ if (unit >= chcd.cd_ndevs)
+ return ENXIO;
+ ch = chcd.cd_devs[unit];
+ if (!ch)
+ return ENXIO;
+
+ mode = CHMODE(dev);
+ 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));
+
+ /*
+ * Only allow one at a time
+ */
+ if (sc_link->flags & SDEV_OPEN) {
+ printf("%s: already open\n", ch->sc_dev.dv_xname);
+ return EBUSY;
+ }
+
+ /*
+ * Catch any unit attention errors.
+ */
+ if (error = scsi_test_unit_ready(sc_link, SCSI_IGNORE_MEDIA_CHANGE))
+ goto bad;
+
+ sc_link->flags |= SDEV_OPEN; /* unit attn are now errors */
+
+ /*
+ * Make sure data is loaded
+ */
+ if (error = ch_mode_sense(ch, 0)) {
+ printf("%s: offline\n", ch->sc_dev.dv_xname);
+ goto bad;
+ }
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n"));
+ return 0;
+
+bad:
+ 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)
+ dev_t dev;
+{
+ struct ch_softc *ch = chcd.cd_devs[CHUNIT(dev)];
+
+ SC_DEBUG(ch->sc_link, SDEV_DB1, ("closing\n"));
+ ch->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)
+ dev_t dev;
+ u_long cmd;
+ caddr_t arg;
+ int mode;
+ struct proc *p;
+{
+ struct ch_softc *ch = chcd.cd_devs[CHUNIT(dev)];
+ struct scsi_link *sc_link = ch->sc_link;
+ int number;
+ int flags;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ flags = 0; /* give error messages, act on errors etc. */
+
+ 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,
+ &chop->u.get_elem_stat.elem_data, flags);
+ default:
+ return EINVAL;
+ }
+ }
+ default:
+ return scsi_do_ioctl(sc_link, dev, cmd, arg, mode, p);
+ }
+#ifdef DIAGNOSTIC
+ panic("chioctl: impossible");
+#endif
+}
+
+int
+ch_getelem(ch, stat, type, from, data, flags)
+ struct ch_softc *ch;
+ short *stat;
+ int type, from;
+ char *data;
+ int flags;
+{
+ 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;
+}
+
+int
+ch_move(ch, stat, chm, from, to, flags)
+ struct ch_softc *ch;
+ short *stat;
+ int chm, from, to, flags;
+{
+ 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;
+}
+
+int
+ch_position(ch, stat, chm, to, flags)
+ struct ch_softc *ch;
+ short *stat;
+ int chm, to, flags;
+{
+ 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;
+}
+
+/*
+ * Get the scsi driver to send a full inquiry to the
+ * device and use the results to fill out the global
+ * parameter structure.
+ */
+int
+ch_mode_sense(ch, flags)
+ struct ch_softc *ch;
+ int flags;
+{
+ 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;
+
+ /*
+ * First check if we have it all loaded
+ */
+ if (sc_link->flags & SDEV_MEDIA_LOADED)
+ return 0;
+
+ /*
+ * First do a mode sense
+ */
+ 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);
+
+ /*
+ * Read in the pages
+ */
+ if (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)) {
+ printf("%s: could not mode sense\n", ch->sc_dev.dv_xname);
+ return error;
+ }
+
+ sc_link->flags |= SDEV_MEDIA_LOADED;
+ l = scsi_sense[0] - 3;
+ b = &scsi_sense[4];
+
+ /*
+ * To avoid alignment problems
+ */
+/* 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;
+ }
+ 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;
+}
diff --git a/sys/scsi/files.scsi b/sys/scsi/files.scsi
new file mode 100644
index 00000000000..c1060f0c18a
--- /dev/null
+++ b/sys/scsi/files.scsi
@@ -0,0 +1,26 @@
+# $NetBSD: files.scsi,v 1.1 1995/04/17 07:15:40 cgd Exp $
+#
+# Config.new file and device description for machine-independent SCSI code.
+# Included by ports that need it. Ports that usee it must provide
+# their own "major" declarations for the appropriate devices.
+
+define scsi {}
+file scsi/scsi_base.c scsi
+file scsi/scsi_ioctl.c scsi
+file scsi/scsiconf.c scsi
+
+device scsibus at scsi {target = -1, lun = -1}
+
+device cd at scsibus: disk
+file scsi/cd.c cd needs-flag
+device ch at scsibus: disk
+file scsi/ch.c ch needs-flag
+device sd at scsibus: disk
+file scsi/sd.c sd needs-flag
+device st at scsibus: tape
+file scsi/st.c st needs-flag
+device su at scsibus: disk
+file scsi/su.c su needs-flag
+device uk at scsibus: disk
+file scsi/uk.c uk needs-flag
+
diff --git a/sys/scsi/scsi_all.h b/sys/scsi/scsi_all.h
new file mode 100644
index 00000000000..646826c872b
--- /dev/null
+++ b/sys/scsi/scsi_all.h
@@ -0,0 +1,316 @@
+/* $NetBSD: scsi_all.h,v 1.6 1994/12/28 19:42:54 mycroft Exp $ */
+
+/*
+ * SCSI general interface description
+ */
+
+/*
+ * Largely written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+#ifndef _SCSI_SCSI_ALL_H
+#define _SCSI_SCSI_ALL_H 1
+
+/*
+ * SCSI command format
+ */
+
+/*
+ * Define dome bits that are in ALL (or a lot of) scsi commands
+ */
+#define SCSI_CTL_LINK 0x01
+#define SCSI_CTL_FLAG 0x02
+#define SCSI_CTL_VENDOR 0xC0
+#define SCSI_CMD_LUN 0xA0 /* these two should not be needed */
+#define SCSI_CMD_LUN_SHIFT 5 /* LUN in the cmd is no longer SCSI */
+
+
+struct scsi_generic {
+ u_int8_t opcode;
+ u_int8_t bytes[11];
+};
+
+struct scsi_test_unit_ready {
+ u_int8_t opcode;
+ u_int8_t byte2;
+ u_int8_t unused[3];
+ u_int8_t control;
+};
+
+struct scsi_send_diag {
+ u_int8_t opcode;
+ u_int8_t byte2;
+#define SSD_UOL 0x01
+#define SSD_DOL 0x02
+#define SSD_SELFTEST 0x04
+#define SSD_PF 0x10
+ u_int8_t unused[1];
+ u_int8_t paramlen[2];
+ u_int8_t control;
+};
+
+struct scsi_sense {
+ u_int8_t opcode;
+ u_int8_t byte2;
+ u_int8_t unused[2];
+ u_int8_t length;
+ u_int8_t control;
+};
+
+struct scsi_inquiry {
+ u_int8_t opcode;
+ u_int8_t byte2;
+ u_int8_t unused[2];
+ u_int8_t length;
+ u_int8_t control;
+};
+
+struct scsi_mode_sense {
+ u_int8_t opcode;
+ u_int8_t byte2;
+#define SMS_DBD 0x08
+ u_int8_t page;
+#define SMS_PAGE_CODE 0x3F
+#define SMS_PAGE_CTRL 0xC0
+#define SMS_PAGE_CTRL_CURRENT 0x00
+#define SMS_PAGE_CTRL_CHANGEABLE 0x40
+#define SMS_PAGE_CTRL_DEFAULT 0x80
+#define SMS_PAGE_CTRL_SAVED 0xC0
+ u_int8_t unused;
+ u_int8_t length;
+ u_int8_t control;
+};
+
+struct scsi_mode_sense_big {
+ u_int8_t opcode;
+ u_int8_t byte2; /* same bits as small version */
+ u_int8_t page; /* same bits as small version */
+ u_int8_t unused[4];
+ u_int8_t length[2];
+ u_int8_t control;
+};
+
+struct scsi_mode_select {
+ u_int8_t opcode;
+ u_int8_t byte2;
+#define SMS_SP 0x01
+#define SMS_PF 0x10
+ u_int8_t unused[2];
+ u_int8_t length;
+ u_int8_t control;
+};
+
+struct scsi_mode_select_big {
+ u_int8_t opcode;
+ u_int8_t byte2; /* same bits as small version */
+ u_int8_t unused[5];
+ u_int8_t length[2];
+ u_int8_t control;
+};
+
+struct scsi_reserve {
+ u_int8_t opcode;
+ u_int8_t byte2;
+ u_int8_t unused[2];
+ u_int8_t length;
+ u_int8_t control;
+};
+
+struct scsi_release {
+ u_int8_t opcode;
+ u_int8_t byte2;
+ u_int8_t unused[2];
+ u_int8_t length;
+ u_int8_t control;
+};
+
+struct scsi_prevent {
+ u_int8_t opcode;
+ u_int8_t byte2;
+ u_int8_t unused[2];
+ u_int8_t how;
+ u_int8_t control;
+};
+#define PR_PREVENT 0x01
+#define PR_ALLOW 0x00
+
+struct scsi_changedef {
+ u_int8_t opcode;
+ u_int8_t byte2;
+ u_int8_t unused1;
+ u_int8_t how;
+ u_int8_t unused[4];
+ u_int8_t datalen;
+ u_int8_t control;
+};
+#define SC_SCSI_1 0x01
+#define SC_SCSI_2 0x03
+
+/*
+ * Opcodes
+ */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define INQUIRY 0x12
+#define MODE_SELECT 0x15
+#define MODE_SENSE 0x1a
+#define START_STOP 0x1b
+#define RESERVE 0x16
+#define RELEASE 0x17
+#define PREVENT_ALLOW 0x1e
+#define POSITION_TO_ELEMENT 0x2b
+#define CHANGE_DEFINITION 0x40
+#define MODE_SENSE_BIG 0x54
+#define MODE_SELECT_BIG 0x55
+#define MOVE_MEDIUM 0xa5
+#define READ_ELEMENT_STATUS 0xb8
+
+/*
+ * sense data format
+ */
+#define T_DIRECT 0
+#define T_SEQUENTIAL 1
+#define T_PRINTER 2
+#define T_PROCESSOR 3
+#define T_WORM 4
+#define T_CDROM 5
+#define T_SCANNER 6
+#define T_OPTICAL 7
+#define T_NODEVICE 0x1F
+
+#define T_CHANGER 8
+#define T_COMM 9
+
+#define T_REMOV 1
+#define T_FIXED 0
+
+struct scsi_inquiry_data {
+ u_int8_t device;
+#define SID_TYPE 0x1F
+#define SID_QUAL 0xE0
+#define SID_QUAL_LU_OK 0x00
+#define SID_QUAL_LU_OFFLINE 0x20
+#define SID_QUAL_RSVD 0x40
+#define SID_QUAL_BAD_LU 0x60
+ u_int8_t dev_qual2;
+#define SID_QUAL2 0x7F
+#define SID_REMOVABLE 0x80
+ u_int8_t version;
+#define SID_ANSII 0x07
+#define SID_ECMA 0x38
+#define SID_ISO 0xC0
+ u_int8_t response_format;
+ u_int8_t additional_length;
+ u_int8_t unused[2];
+ u_int8_t flags;
+#define SID_SftRe 0x01
+#define SID_CmdQue 0x02
+#define SID_Linked 0x08
+#define SID_Sync 0x10
+#define SID_WBus16 0x20
+#define SID_WBus32 0x40
+#define SID_RelAdr 0x80
+ char vendor[8];
+ char product[16];
+ char revision[4];
+ u_int8_t extra[8];
+};
+
+/*
+ * This looks bad, and it is. However it fixes padding problems
+ * caused by using unions. This *needs* to be an array, if this code
+ * is to work on any architecture.
+ */
+struct scsi_sense_data {
+/* 1*/ u_int8_t error_code; /* same bits as new version */
+#define XXX_unextended_blockhi extended_segment
+#define XXX_unextended_blockmed extended_flags
+#define XXX_unextended_blocklow extended_info[0]
+/* 2*/ u_int8_t extended_segment;
+/* 3*/ u_int8_t extended_flags; /* same bits as new version */
+/* 7*/ u_int8_t extended_info[4];
+/* 8*/ u_int8_t extended_extra_len;
+ /*
+ * allocate enough room to hold new stuff
+ * (by increasing 16 to 24 below)
+ */
+/*32*/ u_int8_t extended_extra_bytes[24];
+}; /* total of 32 bytes */
+
+struct scsi_sense_data_new {
+/* 1*/ u_int8_t error_code;
+#define SSD_ERRCODE 0x7F
+#define SSD_ERRCODE_VALID 0x80
+ union {
+ struct { /* this is deprecated, the standard says "DON'T"*/
+/* 2*/ u_int8_t blockhi;
+/* 3*/ u_int8_t blockmed;
+/* 4*/ u_int8_t blocklow;
+ } unextended;
+ struct {
+/* 2*/ u_int8_t segment;
+/* 3*/ u_int8_t flags;
+#define SSD_KEY 0x0F
+#define SSD_ILI 0x20
+#define SSD_EOM 0x40
+#define SSD_FILEMARK 0x80
+/* 7*/ u_int8_t info[4];
+/* 8*/ u_int8_t extra_len;
+/*12*/ u_int8_t cmd_spec_info[4];
+/*13*/ u_int8_t add_sense_code;
+/*14*/ u_int8_t add_sense_code_qual;
+/*15*/ u_int8_t fru;
+/*16*/ u_int8_t sense_key_spec_1;
+#define SSD_SCS_VALID 0x80
+/*17*/ u_int8_t sense_key_spec_2;
+/*18*/ u_int8_t sense_key_spec_3;
+/*32*/ u_int8_t extra_bytes[14];
+ } extended;
+ } ext;
+}; /* total of 32 bytes */
+
+struct scsi_blk_desc {
+ u_int8_t density;
+ u_int8_t nblocks[3];
+ u_int8_t reserved;
+ u_int8_t blklen[3];
+};
+
+struct scsi_mode_header {
+ u_int8_t data_length; /* Sense data length */
+ u_int8_t medium_type;
+ u_int8_t dev_spec;
+ u_int8_t blk_desc_len;
+};
+
+struct scsi_mode_header_big {
+ u_int8_t data_length[2]; /* Sense data length */
+ u_int8_t medium_type;
+ u_int8_t dev_spec;
+ u_int8_t unused[2];
+ u_int8_t blk_desc_len[2];
+};
+
+
+/*
+ * Status Byte
+ */
+#define SCSI_OK 0x00
+#define SCSI_CHECK 0x02
+#define SCSI_BUSY 0x08
+#define SCSI_INTERM 0x10
+
+#endif /* _SCSI_SCSI_ALL_H */
diff --git a/sys/scsi/scsi_base.c b/sys/scsi/scsi_base.c
new file mode 100644
index 00000000000..7a61c55237f
--- /dev/null
+++ b/sys/scsi/scsi_base.c
@@ -0,0 +1,848 @@
+/* $NetBSD: scsi_base.c,v 1.30 1995/09/26 19:26:55 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Charles Hannum. All rights reserved.
+ *
+ * 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 acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 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.
+ */
+
+/*
+ * Originally written by Julian Elischer (julian@dialix.oz.au)
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+
+void scsi_error __P((struct scsi_xfer *, int));
+
+LIST_HEAD(xs_free_list, scsi_xfer) xs_free_list;
+
+/*
+ * Get a scsi transfer structure for the caller. Charge the structure
+ * to the device that is referenced by the sc_link structure. If the
+ * sc_link structure has no 'credits' then the device already has the
+ * maximum number or outstanding operations under way. In this stage,
+ * wait on the structure so that when one is freed, we are awoken again
+ * If the SCSI_NOSLEEP flag is set, then do not wait, but rather, return
+ * a NULL pointer, signifying that no slots were available
+ * Note in the link structure, that we are waiting on it.
+ */
+
+struct scsi_xfer *
+scsi_get_xs(sc_link, flags)
+ struct scsi_link *sc_link; /* who to charge the xs to */
+ int flags; /* if this call can sleep */
+{
+ struct scsi_xfer *xs;
+ int s;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("scsi_get_xs\n"));
+ s = splbio();
+ while (sc_link->openings <= 0) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("sleeping\n"));
+ if ((flags & SCSI_NOSLEEP) != 0) {
+ splx(s);
+ return 0;
+ }
+ sc_link->flags |= SDEV_WAITING;
+ (void) tsleep(sc_link, PRIBIO, "getxs", 0);
+ }
+ sc_link->openings--;
+ if (xs = xs_free_list.lh_first) {
+ LIST_REMOVE(xs, free_list);
+ splx(s);
+ } else {
+ splx(s);
+ SC_DEBUG(sc_link, SDEV_DB3, ("making\n"));
+ xs = malloc(sizeof(*xs), M_DEVBUF,
+ ((flags & SCSI_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK));
+ if (!xs) {
+ sc_print_addr(sc_link);
+ printf("cannot allocate scsi xs\n");
+ return 0;
+ }
+ }
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("returning\n"));
+ xs->flags = INUSE | flags;
+ return xs;
+}
+
+/*
+ * Given a scsi_xfer struct, and a device (referenced through sc_link)
+ * return the struct to the free pool and credit the device with it
+ * If another process is waiting for an xs, do a wakeup, let it proceed
+ */
+void
+scsi_free_xs(xs, flags)
+ struct scsi_xfer *xs;
+ int flags;
+{
+ struct scsi_link *sc_link = xs->sc_link;
+
+ xs->flags &= ~INUSE;
+ LIST_INSERT_HEAD(&xs_free_list, xs, free_list);
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("scsi_free_xs\n"));
+ /* if was 0 and someone waits, wake them up */
+ sc_link->openings++;
+ if ((sc_link->flags & SDEV_WAITING) != 0) {
+ sc_link->flags &= ~SDEV_WAITING;
+ wakeup(sc_link);
+ } else {
+ if (sc_link->device->start) {
+ SC_DEBUG(sc_link, SDEV_DB2, ("calling private start()\n"));
+ (*(sc_link->device->start)) (sc_link->device_softc);
+ }
+ }
+}
+
+/*
+ * Make a scsi_xfer, and return a pointer to it.
+ */
+static __inline struct scsi_xfer *
+scsi_make_xs(sc_link, scsi_cmd, cmdlen, data_addr, datalen,
+ retries, timeout, bp, flags)
+ struct scsi_link *sc_link;
+ struct scsi_generic *scsi_cmd;
+ int cmdlen;
+ u_char *data_addr;
+ int datalen;
+ int retries;
+ int timeout;
+ struct buf *bp;
+ int flags;
+{
+ struct scsi_xfer *xs;
+
+ if ((xs = scsi_get_xs(sc_link, flags)) == NULL)
+ return NULL;
+
+ /*
+ * Fill out the scsi_xfer structure. We don't know whose context
+ * the cmd is in, so copy it.
+ */
+ xs->sc_link = sc_link;
+ bcopy(scsi_cmd, &xs->cmdstore, cmdlen);
+ xs->cmd = &xs->cmdstore;
+ xs->cmdlen = cmdlen;
+ xs->data = data_addr;
+ xs->datalen = datalen;
+ xs->retries = retries;
+ xs->timeout = timeout;
+ xs->bp = bp;
+
+ return xs;
+}
+
+/*
+ * Find out from the device what its capacity is.
+ */
+u_long
+scsi_size(sc_link, flags)
+ struct scsi_link *sc_link;
+ int flags;
+{
+ struct scsi_read_cap_data rdcap;
+ struct scsi_read_capacity scsi_cmd;
+ u_long size;
+
+ /*
+ * make up a scsi command and ask the scsi driver to do
+ * it for you.
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = READ_CAPACITY;
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks
+ */
+ if (scsi_scsi_cmd(sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)&rdcap, sizeof(rdcap),
+ 2, 20000, NULL, flags | SCSI_DATA_IN) != 0) {
+ sc_print_addr(sc_link);
+ printf("could not get size\n");
+ return 0;
+ } else {
+ size = rdcap.addr_0 + 1;
+ size += rdcap.addr_1 << 8;
+ size += rdcap.addr_2 << 16;
+ size += rdcap.addr_3 << 24;
+ }
+ return size;
+}
+
+/*
+ * Get scsi driver to send a "are you ready?" command
+ */
+int
+scsi_test_unit_ready(sc_link, flags)
+ struct scsi_link *sc_link;
+ int flags;
+{
+ struct scsi_test_unit_ready scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = TEST_UNIT_READY;
+
+ return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, 2, 10000, NULL, flags);
+}
+
+/*
+ * Do a scsi operation, asking a device to run as SCSI-II if it can.
+ */
+int
+scsi_change_def(sc_link, flags)
+ struct scsi_link *sc_link;
+ int flags;
+{
+ struct scsi_changedef scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = CHANGE_DEFINITION;
+ scsi_cmd.how = SC_SCSI_2;
+
+ return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, 2, 100000, NULL, flags);
+}
+
+/*
+ * Do a scsi operation asking a device what it is
+ * Use the scsi_cmd routine in the switch table.
+ */
+int
+scsi_inquire(sc_link, inqbuf, flags)
+ struct scsi_link *sc_link;
+ struct scsi_inquiry_data *inqbuf;
+ int flags;
+{
+ struct scsi_inquiry scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = INQUIRY;
+ scsi_cmd.length = sizeof(struct scsi_inquiry_data);
+
+ return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd), (u_char *) inqbuf,
+ sizeof(struct scsi_inquiry_data), 2, 10000, NULL,
+ SCSI_DATA_IN | flags);
+}
+
+/*
+ * Prevent or allow the user to remove the media
+ */
+int
+scsi_prevent(sc_link, type, flags)
+ struct scsi_link *sc_link;
+ int type, flags;
+{
+ struct scsi_prevent scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = PREVENT_ALLOW;
+ scsi_cmd.how = type;
+ return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, 2, 5000, NULL, flags);
+}
+
+/*
+ * Get scsi driver to send a "start up" command
+ */
+int
+scsi_start(sc_link, type, flags)
+ struct scsi_link *sc_link;
+ int type, flags;
+{
+ struct scsi_start_stop scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = START_STOP;
+ scsi_cmd.byte2 = 0x00;
+ scsi_cmd.how = type;
+ return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd), 0, 0, 2,
+ type == SSS_START ? 30000 : 10000, NULL, flags);
+}
+
+/*
+ * This routine is called by the scsi interrupt when the transfer is complete.
+ */
+void
+scsi_done(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *sc_link = xs->sc_link;
+ int error;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("scsi_done\n"));
+#ifdef SCSIDEBUG
+ if ((sc_link->flags & SDEV_DB1) != 0)
+ show_scsi_cmd(xs);
+#endif /* SCSIDEBUG */
+
+ /*
+ * If it's a user level request, bypass all usual completion processing,
+ * let the user work it out.. We take reponsibility for freeing the
+ * xs when the user returns. (and restarting the device's queue).
+ */
+ if ((xs->flags & SCSI_USER) != 0) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("calling user done()\n"));
+ scsi_user_done(xs); /* to take a copy of the sense etc. */
+ SC_DEBUG(sc_link, SDEV_DB3, ("returned from user done()\n "));
+
+ scsi_free_xs(xs, SCSI_NOSLEEP); /* restarts queue too */
+ SC_DEBUG(sc_link, SDEV_DB3, ("returning to adapter\n"));
+ return;
+ }
+
+ /*
+ * If the device has it's own done routine, call it first.
+ * If it returns a legit error value, return that, otherwise
+ * it wants us to continue with normal processing.
+ */
+ if (sc_link->device->done) {
+ SC_DEBUG(sc_link, SDEV_DB2, ("calling private done()\n"));
+ error = (*sc_link->device->done) (xs);
+ if (error == EJUSTRETURN)
+ goto done;
+ SC_DEBUG(sc_link, SDEV_DB3, ("continuing with generic done()\n"));
+ }
+ if (xs->bp == NULL) {
+ /*
+ * if it's a normal upper level request, then ask
+ * the upper level code to handle error checking
+ * rather than doing it here at interrupt time
+ */
+ wakeup(xs);
+ return;
+ }
+ /*
+ * Go and handle errors now.
+ * If it returns ERESTART then we should RETRY
+ */
+retry:
+ if (sc_err1(xs, 1) == ERESTART) {
+ switch ((*(sc_link->adapter->scsi_cmd)) (xs)) {
+ case SUCCESSFULLY_QUEUED:
+ return;
+
+ case TRY_AGAIN_LATER:
+ xs->error = XS_BUSY;
+ case COMPLETE:
+ goto retry;
+ }
+ }
+done:
+ scsi_free_xs(xs, SCSI_NOSLEEP);
+}
+
+int
+scsi_execute_xs(xs)
+ struct scsi_xfer *xs;
+{
+ int error;
+ int s;
+
+ xs->flags &= ~ITSDONE;
+ xs->error = XS_NOERROR;
+ xs->resid = xs->datalen;
+
+retry:
+ /*
+ * Do the transfer. If we are polling we will return:
+ * COMPLETE, Was poll, and scsi_done has been called
+ * TRY_AGAIN_LATER, Adapter short resources, try again
+ *
+ * if under full steam (interrupts) it will return:
+ * SUCCESSFULLY_QUEUED, will do a wakeup when complete
+ * TRY_AGAIN_LATER, (as for polling)
+ * After the wakeup, we must still check if it succeeded
+ *
+ * If we have a bp however, all the error proccessing
+ * and the buffer code both expect us to return straight
+ * to them, so as soon as the command is queued, return
+ */
+ switch ((*(xs->sc_link->adapter->scsi_cmd)) (xs)) {
+ case SUCCESSFULLY_QUEUED:
+ if (xs->bp)
+ return EJUSTRETURN;
+ s = splbio();
+ while ((xs->flags & ITSDONE) == 0)
+ tsleep(xs, PRIBIO + 1, "scsi_scsi_cmd", 0);
+ splx(s);
+ case COMPLETE: /* Polling command completed ok */
+ if (xs->bp)
+ return EJUSTRETURN;
+ doit:
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("back in cmd()\n"));
+ if ((error = sc_err1(xs, 0)) != ERESTART)
+ return error;
+ goto retry;
+
+ case TRY_AGAIN_LATER: /* adapter resource shortage */
+ xs->error = XS_BUSY;
+ goto doit;
+
+ default:
+ panic("scsi_execute_xs: invalid return code");
+ }
+
+#ifdef DIAGNOSTIC
+ panic("scsi_execute_xs: impossible");
+#endif
+}
+
+/*
+ * ask the scsi driver to perform a command for us.
+ * tell it where to read/write the data, and how
+ * long the data is supposed to be. If we have a buf
+ * to associate with the transfer, we need that too.
+ */
+int
+scsi_scsi_cmd(sc_link, scsi_cmd, cmdlen, data_addr, datalen,
+ retries, timeout, bp, flags)
+ struct scsi_link *sc_link;
+ struct scsi_generic *scsi_cmd;
+ int cmdlen;
+ u_char *data_addr;
+ int datalen;
+ int retries;
+ int timeout;
+ struct buf *bp;
+ int flags;
+{
+ struct scsi_xfer *xs;
+ int error;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("scsi_cmd\n"));
+
+#ifdef DIAGNOSTIC
+ if (bp != 0 && (flags & SCSI_NOSLEEP) == 0)
+ panic("scsi_scsi_cmd: buffer without nosleep");
+#endif
+
+ if ((xs = scsi_make_xs(sc_link, scsi_cmd, cmdlen, data_addr, datalen,
+ retries, timeout, bp, flags)) == NULL)
+ return ENOMEM;
+
+ if ((error = scsi_execute_xs(xs)) == EJUSTRETURN)
+ return 0;
+
+ /*
+ * we have finished with the xfer stuct, free it and
+ * check if anyone else needs to be started up.
+ */
+ scsi_free_xs(xs, flags);
+ return error;
+}
+
+int
+sc_err1(xs, async)
+ struct scsi_xfer *xs;
+ int async;
+{
+ int error;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("sc_err1,err = 0x%x \n", xs->error));
+
+ /*
+ * If it has a buf, we might be working with
+ * a request from the buffer cache or some other
+ * piece of code that requires us to process
+ * errors at inetrrupt time. We have probably
+ * been called by scsi_done()
+ */
+ switch (xs->error) {
+ case XS_NOERROR: /* nearly always hit this one */
+ error = 0;
+ break;
+
+ case XS_SENSE:
+ if ((error = scsi_interpret_sense(xs)) == ERESTART)
+ goto retry;
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("scsi_interpret_sense returned %d\n", error));
+ break;
+
+ case XS_BUSY:
+ if (xs->retries) {
+ if ((xs->flags & SCSI_POLL) != 0)
+ delay(1000000);
+ else if ((xs->flags & SCSI_NOSLEEP) == 0)
+ tsleep(&lbolt, PRIBIO, "scbusy", 0);
+ else
+#if 0
+ timeout(scsi_requeue, xs, hz);
+#else
+ goto lose;
+#endif
+ }
+ case XS_TIMEOUT:
+ retry:
+ if (xs->retries--) {
+ xs->error = XS_NOERROR;
+ xs->flags &= ~ITSDONE;
+ return ERESTART;
+ }
+ case XS_DRIVER_STUFFUP:
+ lose:
+ error = EIO;
+ break;
+
+ case XS_SELTIMEOUT:
+ /* XXX Disable device? */
+ error = EIO;
+ break;
+
+ default:
+ sc_print_addr(xs->sc_link);
+ printf("unknown error category from scsi driver\n");
+ error = EIO;
+ break;
+ }
+
+ scsi_error(xs, error);
+ return error;
+}
+
+void
+scsi_error(xs, error)
+ struct scsi_xfer *xs;
+ int error;
+{
+ struct buf *bp = xs->bp;
+
+ if (bp) {
+ if (error) {
+ bp->b_error = error;
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ } else {
+ bp->b_error = 0;
+ bp->b_resid = xs->resid;
+ }
+ biodone(bp);
+ }
+}
+
+/*
+ * Look at the returned sense and act on the error, determining
+ * the unix error number to pass back. (0 = report no error)
+ *
+ * THIS IS THE DEFAULT ERROR HANDLER
+ */
+int
+scsi_interpret_sense(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *sense;
+ struct scsi_link *sc_link = xs->sc_link;
+ u_int8_t key;
+ u_int32_t info;
+ int error;
+
+ static char *error_mes[] = {
+ "soft error (corrected)",
+ "not ready", "medium error",
+ "non-media hardware failure", "illegal request",
+ "unit attention", "readonly device",
+ "no data found", "vendor unique",
+ "copy aborted", "command aborted",
+ "search returned equal", "volume overflow",
+ "verify miscompare", "unknown error key"
+ };
+
+ sense = &xs->sense;
+#ifdef SCSIDEBUG
+ if ((sc_link->flags & SDEV_DB1) != 0) {
+ int count;
+ printf("code%x valid%x ",
+ sense->error_code & SSD_ERRCODE,
+ sense->error_code & SSD_ERRCODE_VALID ? 1 : 0);
+ printf("seg%x key%x ili%x eom%x fmark%x\n",
+ sense->extended_segment,
+ sense->extended_flags & SSD_KEY,
+ sense->extended_flags & SSD_ILI ? 1 : 0,
+ sense->extended_flags & SSD_EOM ? 1 : 0,
+ sense->extended_flags & SSD_FILEMARK ? 1 : 0);
+ printf("info: %x %x %x %x followed by %d extra bytes\n",
+ sense->extended_info[0],
+ sense->extended_info[1],
+ sense->extended_info[2],
+ sense->extended_info[3],
+ sense->extended_extra_len);
+ printf("extra: ");
+ for (count = 0; count < sense->extended_extra_len; count++)
+ printf("%x ", sense->extended_extra_bytes[count]);
+ printf("\n");
+ }
+#endif /*SCSIDEBUG */
+ /*
+ * If the device has it's own error handler, call it first.
+ * If it returns a legit error value, return that, otherwise
+ * it wants us to continue with normal error processing.
+ */
+ if (sc_link->device->err_handler) {
+ SC_DEBUG(sc_link, SDEV_DB2, ("calling private err_handler()\n"));
+ error = (*sc_link->device->err_handler) (xs);
+ if (error != -1)
+ return error; /* error >= 0 better ? */
+ }
+ /* otherwise use the default */
+ switch (sense->error_code & SSD_ERRCODE) {
+ /*
+ * If it's code 70, use the extended stuff and interpret the key
+ */
+ case 0x71: /* delayed error */
+ sc_print_addr(sc_link);
+ key = sense->extended_flags & SSD_KEY;
+ printf(" DELAYED ERROR, key = 0x%x\n", key);
+ case 0x70:
+ if ((sense->error_code & SSD_ERRCODE_VALID) != 0) {
+ bcopy(sense->extended_info, &info, sizeof info);
+ info = ntohl(info);
+ } else
+ info = 0;
+ key = sense->extended_flags & SSD_KEY;
+
+ switch (key) {
+ case 0x0: /* NO SENSE */
+ case 0x1: /* RECOVERED ERROR */
+ if (xs->resid == xs->datalen)
+ xs->resid = 0; /* not short read */
+ case 0xc: /* EQUAL */
+ error = 0;
+ break;
+ case 0x2: /* NOT READY */
+ if ((sc_link->flags & SDEV_REMOVABLE) != 0)
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ if ((xs->flags & SCSI_IGNORE_NOT_READY) != 0)
+ return 0;
+ if ((xs->flags & SCSI_SILENT) != 0)
+ return EIO;
+ error = EIO;
+ break;
+ case 0x5: /* ILLEGAL REQUEST */
+ if ((xs->flags & SCSI_IGNORE_ILLEGAL_REQUEST) != 0)
+ return 0;
+ error = EINVAL;
+ break;
+ case 0x6: /* UNIT ATTENTION */
+ if ((sc_link->flags & SDEV_REMOVABLE) != 0)
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ if ((xs->flags & SCSI_IGNORE_MEDIA_CHANGE) != 0 ||
+ /* XXX Should reupload any transient state. */
+ (sc_link->flags & SDEV_REMOVABLE) == 0)
+ return ERESTART;
+ if ((xs->flags & SCSI_SILENT) != 0)
+ return EIO;
+ error = EIO;
+ break;
+ case 0x7: /* DATA PROTECT */
+ error = EACCES;
+ break;
+ case 0x8: /* BLANK CHECK */
+ error = 0;
+ break;
+ case 0xd: /* VOLUME OVERFLOW */
+ error = ENOSPC;
+ break;
+ default:
+ error = EIO;
+ break;
+ }
+
+ if (key) {
+ sc_print_addr(sc_link);
+ printf("%s", error_mes[key - 1]);
+ if ((sense->error_code & SSD_ERRCODE_VALID) != 0) {
+ switch (key) {
+ case 0x2: /* NOT READY */
+ case 0x5: /* ILLEGAL REQUEST */
+ case 0x6: /* UNIT ATTENTION */
+ case 0x7: /* DATA PROTECT */
+ break;
+ case 0x8: /* BLANK CHECK */
+ printf(", requested size: %d (decimal)",
+ info);
+ break;
+ default:
+ printf(", info = %d (decimal)", info);
+ }
+ }
+ if (sense->extended_extra_len != 0) {
+ int n;
+ printf(", data =");
+ for (n = 0; n < sense->extended_extra_len; n++)
+ printf(" %02x", sense->extended_extra_bytes[n]);
+ }
+ printf("\n");
+ }
+ return error;
+
+ /*
+ * Not code 70, just report it
+ */
+ default:
+ sc_print_addr(sc_link);
+ printf("error code %d",
+ sense->error_code & SSD_ERRCODE);
+ if ((sense->error_code & SSD_ERRCODE_VALID) != 0) {
+ printf(" at block no. %d (decimal)",
+ (sense->XXX_unextended_blockhi << 16) +
+ (sense->XXX_unextended_blockmed << 8) +
+ (sense->XXX_unextended_blocklow));
+ }
+ printf("\n");
+ return EIO;
+ }
+}
+
+/*
+ * Utility routines often used in SCSI stuff
+ */
+
+/*
+ * convert a physical address to 3 bytes,
+ * MSB at the lowest address,
+ * LSB at the highest.
+ */
+void
+lto3b(val, bytes)
+ u_int32_t val;
+ u_int8_t *bytes;
+{
+
+ *bytes++ = (val >> 16) & 0xff;
+ *bytes++ = (val >> 8) & 0xff;
+ *bytes = val & 0xff;
+}
+
+/*
+ * The reverse of lto3b
+ */
+u_int32_t
+_3btol(bytes)
+ u_int8_t *bytes;
+{
+ u_int32_t rc;
+
+ rc = (*bytes++ << 16);
+ rc += (*bytes++ << 8);
+ rc += *bytes;
+ return (rc);
+}
+
+/*
+ * Print out the scsi_link structure's address info.
+ */
+void
+sc_print_addr(sc_link)
+ struct scsi_link *sc_link;
+{
+
+ printf("%s(%s:%d:%d): ",
+ sc_link->device_softc ?
+ ((struct device *)sc_link->device_softc)->dv_xname : "probe",
+ ((struct device *)sc_link->adapter_softc)->dv_xname,
+ sc_link->target, sc_link->lun);
+}
+
+#ifdef SCSIDEBUG
+/*
+ * Given a scsi_xfer, dump the request, in all it's glory
+ */
+void
+show_scsi_xs(xs)
+ struct scsi_xfer *xs;
+{
+ printf("xs(0x%x): ", xs);
+ printf("flg(0x%x)", xs->flags);
+ printf("sc_link(0x%x)", xs->sc_link);
+ printf("retr(0x%x)", xs->retries);
+ printf("timo(0x%x)", xs->timeout);
+ printf("cmd(0x%x)", xs->cmd);
+ printf("len(0x%x)", xs->cmdlen);
+ printf("data(0x%x)", xs->data);
+ printf("len(0x%x)", xs->datalen);
+ printf("res(0x%x)", xs->resid);
+ printf("err(0x%x)", xs->error);
+ printf("bp(0x%x)", xs->bp);
+ show_scsi_cmd(xs);
+}
+
+void
+show_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ u_char *b = (u_char *) xs->cmd;
+ int i = 0;
+
+ sc_print_addr(xs->sc_link);
+ printf("command: ");
+
+ if ((xs->flags & SCSI_RESET) == 0) {
+ while (i < xs->cmdlen) {
+ if (i)
+ printf(",");
+ printf("%x", b[i++]);
+ }
+ printf("-[%d bytes]\n", xs->datalen);
+ if (xs->datalen)
+ show_mem(xs->data, min(64, xs->datalen));
+ } else
+ printf("-RESET-\n");
+}
+
+void
+show_mem(address, num)
+ u_char *address;
+ int num;
+{
+ int x;
+
+ printf("------------------------------");
+ for (x = 0; x < num; x++) {
+ if ((x % 16) == 0)
+ printf("\n%03d: ", x);
+ printf("%02x ", *address++);
+ }
+ printf("\n------------------------------\n");
+}
+#endif /*SCSIDEBUG */
diff --git a/sys/scsi/scsi_cd.h b/sys/scsi/scsi_cd.h
new file mode 100644
index 00000000000..bbd15676e98
--- /dev/null
+++ b/sys/scsi/scsi_cd.h
@@ -0,0 +1,212 @@
+/* $NetBSD: scsi_cd.h,v 1.5 1994/12/28 19:42:58 mycroft Exp $ */
+
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+#ifndef _SCSI_SCSI_CD_H
+#define _SCSI_SCSI_CD_H 1
+
+/*
+ * Define two bits always in the same place in byte 2 (flag byte)
+ */
+#define CD_RELADDR 0x01
+#define CD_MSF 0x02
+
+/*
+ * SCSI command format
+ */
+
+struct scsi_read_capacity_cd {
+ u_int8_t opcode;
+ u_char byte2;
+ u_char addr_3; /* Most Significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* Least Significant */
+ u_char unused[3];
+ u_char control;
+};
+
+struct scsi_pause {
+ u_char opcode;
+ u_char byte2;
+ u_char unused[6];
+ u_char resume;
+ u_char control;
+};
+#define PA_PAUSE 1
+#define PA_RESUME 0
+
+struct scsi_play_msf {
+ u_char opcode;
+ u_char byte2;
+ u_char unused;
+ u_char start_m;
+ u_char start_s;
+ u_char start_f;
+ u_char end_m;
+ u_char end_s;
+ u_char end_f;
+ u_char control;
+};
+
+struct scsi_play_track {
+ u_char opcode;
+ u_char byte2;
+ u_char unused[2];
+ u_char start_track;
+ u_char start_index;
+ u_char unused1;
+ u_char end_track;
+ u_char end_index;
+ u_char control;
+};
+
+struct scsi_play {
+ u_char opcode;
+ u_char byte2;
+ u_char blk_addr[4];
+ u_char unused;
+ u_char xfer_len[2];
+ u_char control;
+};
+
+struct scsi_play_big {
+ u_char opcode;
+ u_char byte2; /* same as above */
+ u_char blk_addr[4];
+ u_char xfer_len[4];
+ u_char unused;
+ u_char control;
+};
+
+struct scsi_play_rel_big {
+ u_char opcode;
+ u_char byte2; /* same as above */
+ u_char blk_addr[4];
+ u_char xfer_len[4];
+ u_char track;
+ u_char control;
+};
+
+struct scsi_read_header {
+ u_char opcode;
+ u_char byte2;
+ u_char blk_addr[4];
+ u_char unused;
+ u_char data_len[2];
+ u_char control;
+};
+
+struct scsi_read_subchannel {
+ u_char opcode;
+ u_char byte2;
+ u_char byte3;
+#define SRS_SUBQ 0x40
+ u_char subchan_format;
+ u_char unused[2];
+ u_char track;
+ u_char data_len[2];
+ u_char control;
+};
+
+struct scsi_read_toc {
+ u_char opcode;
+ u_char byte2;
+ u_char unused[4];
+ u_char from_track;
+ u_char data_len[2];
+ u_char control;
+};
+;
+
+struct scsi_read_cd_capacity {
+ u_char opcode;
+ u_char byte2;
+ u_char addr_3; /* Most Significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* Least Significant */
+ u_char unused[3];
+ u_char control;
+};
+
+/*
+ * Opcodes
+ */
+
+#define READ_CD_CAPACITY 0x25 /* slightly different from disk */
+#define READ_SUBCHANNEL 0x42 /* cdrom read Subchannel */
+#define READ_TOC 0x43 /* cdrom read TOC */
+#define READ_HEADER 0x44 /* cdrom read header */
+#define PLAY 0x45 /* cdrom play 'play audio' mode */
+#define PLAY_MSF 0x47 /* cdrom play Min,Sec,Frames mode */
+#define PLAY_TRACK 0x48 /* cdrom play track/index mode */
+#define PLAY_TRACK_REL 0x49 /* cdrom play track/index mode */
+#define PAUSE 0x4b /* cdrom pause in 'play audio' mode */
+#define PLAY_BIG 0xa5 /* cdrom pause in 'play audio' mode */
+#define PLAY_TRACK_REL_BIG 0xa9 /* cdrom play track/index mode */
+
+
+struct scsi_read_cd_cap_data {
+ u_char addr_3; /* Most significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* Least significant */
+ u_char length_3; /* Most significant */
+ u_char length_2;
+ u_char length_1;
+ u_char length_0; /* Least significant */
+};
+
+union cd_pages {
+ struct audio_page {
+ u_char page_code;
+#define CD_PAGE_CODE 0x3F
+#define AUDIO_PAGE 0x0e
+#define CD_PAGE_PS 0x80
+ u_char param_len;
+ u_char flags;
+#define CD_PA_SOTC 0x02
+#define CD_PA_IMMED 0x04
+ u_char unused[2];
+ u_char format_lba;
+#define CD_PA_FORMAT_LBA 0x0F
+#define CD_PA_APR_VALID 0x80
+ u_char lb_per_sec[2];
+ struct port_control {
+ u_char channels;
+#define CHANNEL 0x0F
+#define CHANNEL_0 1
+#define CHANNEL_1 2
+#define CHANNEL_2 4
+#define CHANNEL_3 8
+#define LEFT_CHANNEL CHANNEL_0
+#define RIGHT_CHANNEL CHANNEL_1
+ u_char volume;
+ } port[4];
+#define LEFT_PORT 0
+#define RIGHT_PORT 1
+ } audio;
+};
+
+struct cd_mode_data {
+ struct scsi_mode_header header;
+ struct scsi_blk_desc blk_desc;
+ union cd_pages page;
+};
+#endif /*_SCSI_SCSI_CD_H*/
+
diff --git a/sys/scsi/scsi_changer.h b/sys/scsi/scsi_changer.h
new file mode 100644
index 00000000000..5161ce8e166
--- /dev/null
+++ b/sys/scsi/scsi_changer.h
@@ -0,0 +1,94 @@
+/* $NetBSD: scsi_changer.h,v 1.5 1994/12/28 19:42:59 mycroft Exp $ */
+
+/*
+ * SCSI changer interface description
+ */
+
+/*
+ * Written by Stefan Grefen (grefen@goofy.zdv.uni-mainz.de soon grefen@convex.com)
+ * based on the SCSI System by written Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+#ifndef _SCSI_SCSI_CHANGER_H
+#define _SCSI_SCSI_CHANGER_H 1
+
+/*
+ * SCSI command format
+ */
+struct scsi_read_element_status {
+ u_char opcode;
+ u_char byte2;
+#define SRES_ELEM_TYPE_CODE 0x0F
+#define SRES_ELEM_VOLTAG 0x10
+ u_char starting_element_addr[2];
+ u_char number_of_elements[2];
+ u_char resv1;
+ u_char allocation_length[3];
+ u_char resv2;
+ u_char control;
+};
+#define RE_ALL_ELEMENTS 0
+#define RE_MEDIUM_TRANSPORT_ELEMENT 1
+#define RE_STORAGE_ELEMENT 2
+#define RE_IMPORT_EXPORT 3
+#define RE_DATA_TRANSFER_ELEMENT 4
+
+struct scsi_move_medium {
+ u_char opcode;
+ u_char byte2;
+ u_char transport_element_address[2];
+ u_char source_address[2];
+ u_char destination_address[2];
+ u_char rsvd[2];
+ u_char invert;
+ u_char control;
+};
+
+struct scsi_position_to_element {
+ u_char opcode;
+ u_char byte2;
+ u_char transport_element_address[2];
+ u_char source_address[2];
+ u_char rsvd[2];
+ u_char invert;
+ u_char control;
+};
+
+/*
+ * Opcodes
+ */
+#define POSITION_TO_ELEMENT 0x2b
+#define MOVE_MEDIUM 0xa5
+#define READ_ELEMENT_STATUS 0xb8
+
+struct scsi_element_status_data {
+ u_char first_element_reported[2];
+ u_char number_of_elements_reported[2];
+ u_char rsvd;
+ u_char byte_count_of_report[3];
+};
+
+struct element_status_page {
+ u_char element_type_code;
+ u_char flags;
+#define ESP_AVOLTAG 0x40
+#define ESP_PVOLTAG 0x80
+ u_char element_descriptor_length[2];
+ u_char rsvd;
+ u_char byte_count_of_descriptor_data[3];
+};
+
+#endif /* _SCSI_SCSI_CHANGER_H */
+
diff --git a/sys/scsi/scsi_debug.h b/sys/scsi/scsi_debug.h
new file mode 100644
index 00000000000..ad2c3da29be
--- /dev/null
+++ b/sys/scsi/scsi_debug.h
@@ -0,0 +1,42 @@
+/* $NetBSD: scsi_debug.h,v 1.5 1994/12/28 19:43:00 mycroft Exp $ */
+
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ */
+#ifndef _SCSI_SCSI_DEBUG_H
+#define _SCSI_SCSI_DEBUG_H 1
+
+/*
+ * These are the new debug bits. (Sat Oct 2 12:46:46 WST 1993)
+ * the following DEBUG bits are defined to exist in the flags word of
+ * the scsi_link structure.
+ */
+#define SDEV_DB1 0x10 /* scsi commands, errors, data */
+#define SDEV_DB2 0x20 /* routine flow tracking */
+#define SDEV_DB3 0x40 /* internal to routine flows */
+#define SDEV_DB4 0x80 /* level 4 debugging for this dev */
+
+/* target and LUN we want to debug */
+#define DEBUGTARGET -1 /* -1 = disable */
+#define DEBUGLUN 0
+#define DEBUGLEVEL (SDEV_DB1|SDEV_DB2)
+
+/*
+ * This is the usual debug macro for use with the above bits
+ */
+#ifdef SCSIDEBUG
+#define SC_DEBUG(sc_link,Level,Printstuff) \
+ if ((sc_link)->flags & (Level)) { \
+ sc_print_addr(sc_link); \
+ printf Printstuff; \
+ }
+#define SC_DEBUGN(sc_link,Level,Printstuff) \
+ if ((sc_link)->flags & (Level)) { \
+ printf Printstuff; \
+ }
+#else
+#define SC_DEBUG(A,B,C)
+#define SC_DEBUGN(A,B,C)
+#endif
+
+#endif /* _SCSI_SCSI_DEBUG_H */
diff --git a/sys/scsi/scsi_disk.h b/sys/scsi/scsi_disk.h
new file mode 100644
index 00000000000..1039f58b88a
--- /dev/null
+++ b/sys/scsi/scsi_disk.h
@@ -0,0 +1,208 @@
+/* $NetBSD: scsi_disk.h,v 1.7 1994/12/28 19:43:02 mycroft Exp $ */
+
+/*
+ * SCSI interface description
+ */
+
+/*
+ * Some lines of this file come from a file of the name "scsi.h"
+ * distributed by OSF as part of mach2.5,
+ * so the following disclaimer has been kept.
+ *
+ * Copyright 1990 by Open Software Foundation,
+ * Grenoble, FRANCE
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OSF or Open Software
+ * Foundation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission.
+ *
+ * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+ * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Largely written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+/*
+ * SCSI command format
+ */
+
+#ifndef _SCSI_SCSI_DISK_H
+#define _SCSI_SCSI_DISK_H 1
+
+struct scsi_reassign_blocks {
+ u_char opcode;
+ u_char byte2;
+ u_char unused[3];
+ u_char control;
+};
+
+struct scsi_rw {
+ u_char opcode;
+ u_char addr_2; /* Most significant */
+#define SRW_TOPADDR 0x1F /* only 5 bits here */
+ u_char addr_1;
+ u_char addr_0; /* least significant */
+ u_char length;
+ u_char control;
+};
+
+struct scsi_rw_big {
+ u_char opcode;
+ u_char byte2;
+#define SRWB_RELADDR 0x01
+ u_char addr_3; /* Most significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* least significant */
+ u_char reserved;
+ u_char length2;
+ u_char length1;
+ u_char control;
+};
+
+struct scsi_read_capacity {
+ u_char opcode;
+ u_char byte2;
+ u_char addr_3; /* Most Significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* Least Significant */
+ u_char unused[3];
+ u_char control;
+};
+
+struct scsi_start_stop {
+ u_char opcode;
+ u_char byte2;
+ u_char unused[2];
+ u_char how;
+#define SSS_STOP 0x00
+#define SSS_START 0x01
+#define SSS_LOEJ 0x02
+ u_char control;
+};
+
+
+
+/*
+ * Opcodes
+ */
+
+#define REASSIGN_BLOCKS 0x07
+#define READ_COMMAND 0x08
+#define WRITE_COMMAND 0x0a
+#define MODE_SELECT 0x15
+#define MODE_SENSE 0x1a
+#define START_STOP 0x1b
+#define PREVENT_ALLOW 0x1e
+#define READ_CAPACITY 0x25
+#define READ_BIG 0x28
+#define WRITE_BIG 0x2a
+
+
+struct scsi_read_cap_data {
+ u_char addr_3; /* Most significant */
+ u_char addr_2;
+ u_char addr_1;
+ u_char addr_0; /* Least significant */
+ u_char length_3; /* Most significant */
+ u_char length_2;
+ u_char length_1;
+ u_char length_0; /* Least significant */
+};
+
+struct scsi_reassign_blocks_data {
+ u_char reserved[2];
+ u_char length_msb;
+ u_char length_lsb;
+ struct {
+ u_char dlbaddr_3; /* defect logical block address (MSB) */
+ u_char dlbaddr_2;
+ u_char dlbaddr_1;
+ u_char dlbaddr_0; /* defect logical block address (LSB) */
+ } defect_descriptor[1];
+};
+
+union disk_pages { /* this is the structure copied from osf */
+ struct page_disk_format {
+ u_char pg_code; /* page code (should be 3) */
+#define DISK_PGCODE 0x3F /* only 6 bits valid */
+ u_char pg_length; /* page length (should be 0x16) */
+ u_char trk_z_1; /* tracks per zone (MSB) */
+ u_char trk_z_0; /* tracks per zone (LSB) */
+ u_char alt_sec_1; /* alternate sectors per zone (MSB) */
+ u_char alt_sec_0; /* alternate sectors per zone (LSB) */
+ u_char alt_trk_z_1; /* alternate tracks per zone (MSB) */
+ u_char alt_trk_z_0; /* alternate tracks per zone (LSB) */
+ u_char alt_trk_v_1; /* alternate tracks per volume (MSB) */
+ u_char alt_trk_v_0; /* alternate tracks per volume (LSB) */
+ u_char ph_sec_t_1; /* physical sectors per track (MSB) */
+ u_char ph_sec_t_0; /* physical sectors per track (LSB) */
+ u_char bytes_s_1; /* bytes per sector (MSB) */
+ u_char bytes_s_0; /* bytes per sector (LSB) */
+ u_char interleave_1;/* interleave (MSB) */
+ u_char interleave_0;/* interleave (LSB) */
+ u_char trk_skew_1; /* track skew factor (MSB) */
+ u_char trk_skew_0; /* track skew factor (LSB) */
+ u_char cyl_skew_1; /* cylinder skew (MSB) */
+ u_char cyl_skew_0; /* cylinder skew (LSB) */
+ u_char flags; /* various */
+#define DISK_FMT_SURF 0x10
+#define DISK_FMT_RMB 0x20
+#define DISK_FMT_HSEC 0x40
+#define DISK_FMT_SSEC 0x80
+ u_char reserved2;
+ u_char reserved3;
+ } disk_format;
+ struct page_rigid_geometry {
+ u_char pg_code; /* page code (should be 4) */
+ u_char pg_length; /* page length (should be 0x16) */
+ u_char ncyl_2; /* number of cylinders (MSB) */
+ u_char ncyl_1; /* number of cylinders */
+ u_char ncyl_0; /* number of cylinders (LSB) */
+ u_char nheads; /* number of heads */
+ u_char st_cyl_wp_2; /* starting cyl., write precomp (MSB) */
+ u_char st_cyl_wp_1; /* starting cyl., write precomp */
+ u_char st_cyl_wp_0; /* starting cyl., write precomp (LSB) */
+ u_char st_cyl_rwc_2;/* starting cyl., red. write cur (MSB)*/
+ u_char st_cyl_rwc_1;/* starting cyl., red. write cur */
+ u_char st_cyl_rwc_0;/* starting cyl., red. write cur (LSB)*/
+ u_char driv_step_1; /* drive step rate (MSB) */
+ u_char driv_step_0; /* drive step rate (LSB) */
+ u_char land_zone_2; /* landing zone cylinder (MSB) */
+ u_char land_zone_1; /* landing zone cylinder */
+ u_char land_zone_0; /* landing zone cylinder (LSB) */
+ u_char reserved1;
+ u_char reserved2;
+ u_char reserved3;
+ } rigid_geometry;
+};
+
+#endif /* _SCSI_SCSI_DISK_H */
diff --git a/sys/scsi/scsi_ioctl.c b/sys/scsi/scsi_ioctl.c
new file mode 100644
index 00000000000..3bc77130a63
--- /dev/null
+++ b/sys/scsi/scsi_ioctl.c
@@ -0,0 +1,361 @@
+/* $NetBSD: scsi_ioctl.c,v 1.19 1995/09/26 19:26:58 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1994 Charles Hannum. All rights reserved.
+ *
+ * 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 acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 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.
+ */
+
+/*
+ * Contributed by HD Associates (hd@world.std.com).
+ * Copyright (c) 1992, 1993 HD Associates
+ *
+ * Berkeley style copyright.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <sys/scsiio.h>
+
+struct scsi_ioctl {
+ LIST_ENTRY(scsi_ioctl) si_list;
+ struct buf si_bp;
+ struct uio si_uio;
+ struct iovec si_iov;
+ scsireq_t si_screq;
+ struct scsi_link *si_sc_link;
+};
+
+LIST_HEAD(, scsi_ioctl) si_head;
+
+struct scsi_ioctl *
+si_get()
+{
+ struct scsi_ioctl *si;
+ int s;
+
+ si = malloc(sizeof(struct scsi_ioctl), M_TEMP, M_WAITOK);
+ bzero(si, sizeof(struct scsi_ioctl));
+ s = splbio();
+ LIST_INSERT_HEAD(&si_head, si, si_list);
+ splx(s);
+ return (si);
+}
+
+void
+si_free(si)
+ struct scsi_ioctl *si;
+{
+ int s;
+
+ s = splbio();
+ LIST_REMOVE(si, si_list);
+ splx(s);
+ free(si, M_TEMP);
+}
+
+struct scsi_ioctl *
+si_find(bp)
+ struct buf *bp;
+{
+ struct scsi_ioctl *si;
+ int s;
+
+ s = splbio();
+ for (si = si_head.lh_first; si != 0; si = si->si_list.le_next)
+ if (bp == &si->si_bp)
+ break;
+ splx(s);
+ return (si);
+}
+
+/*
+ * We let the user interpret his own sense in the generic scsi world.
+ * This routine is called at interrupt time if the SCSI_USER bit was set
+ * in the flags passed to scsi_scsi_cmd(). No other completion processing
+ * takes place, even if we are running over another device driver.
+ * The lower level routines that call us here, will free the xs and restart
+ * the device's queue if such exists.
+ */
+void
+scsi_user_done(xs)
+ struct scsi_xfer *xs;
+{
+ struct buf *bp;
+ struct scsi_ioctl *si;
+ scsireq_t *screq;
+ struct scsi_link *sc_link;
+
+ bp = xs->bp;
+ if (!bp) { /* ALL user requests must have a buf */
+ sc_print_addr(xs->sc_link);
+ printf("User command with no buf\n");
+ return;
+ }
+ si = si_find(bp);
+ if (!si) {
+ sc_print_addr(xs->sc_link);
+ printf("User command with no ioctl\n");
+ return;
+ }
+ screq = &si->si_screq;
+ sc_link = si->si_sc_link;
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("user-done\n"));
+
+ screq->retsts = 0;
+ screq->status = xs->status;
+ switch (xs->error) {
+ case XS_NOERROR:
+ SC_DEBUG(sc_link, SDEV_DB3, ("no error\n"));
+ screq->datalen_used = xs->datalen - xs->resid; /* probably rubbish */
+ screq->retsts = SCCMD_OK;
+ break;
+ case XS_SENSE:
+ SC_DEBUG(sc_link, SDEV_DB3, ("have sense\n"));
+ screq->senselen_used = min(sizeof(xs->sense), SENSEBUFLEN);
+ bcopy(&xs->sense, screq->sense, screq->senselen);
+ screq->retsts = SCCMD_SENSE;
+ break;
+ case XS_DRIVER_STUFFUP:
+ sc_print_addr(sc_link);
+ printf("host adapter code inconsistency\n");
+ screq->retsts = SCCMD_UNKNOWN;
+ break;
+ case XS_TIMEOUT:
+ SC_DEBUG(sc_link, SDEV_DB3, ("timeout\n"));
+ screq->retsts = SCCMD_TIMEOUT;
+ break;
+ case XS_BUSY:
+ SC_DEBUG(sc_link, SDEV_DB3, ("busy\n"));
+ screq->retsts = SCCMD_BUSY;
+ break;
+ default:
+ sc_print_addr(sc_link);
+ printf("unknown error category from host adapter code\n");
+ screq->retsts = SCCMD_UNKNOWN;
+ break;
+ }
+ biodone(bp); /* we're waiting on it in scsi_strategy() */
+}
+
+
+/* Pseudo strategy function
+ * Called by scsi_do_ioctl() via physio/physstrat if there is to
+ * be data transfered, and directly if there is no data transfer.
+ *
+ * Should I reorganize this so it returns to physio instead
+ * of sleeping in scsiio_scsi_cmd? Is there any advantage, other
+ * than avoiding the probable duplicate wakeup in iodone? [PD]
+ *
+ * No, seems ok to me... [JRE]
+ * (I don't see any duplicate wakeups)
+ *
+ * Can't be used with block devices or raw_read/raw_write directly
+ * from the cdevsw/bdevsw tables because they couldn't have added
+ * the screq structure. [JRE]
+ */
+void
+scsistrategy(bp)
+ struct buf *bp;
+{
+ struct scsi_ioctl *si;
+ scsireq_t *screq;
+ struct scsi_link *sc_link;
+ int error;
+ int flags = 0;
+ int s;
+
+ si = si_find(bp);
+ if (!si) {
+ printf("user_strat: No ioctl\n");
+ error = EINVAL;
+ goto bad;
+ }
+ screq = &si->si_screq;
+ sc_link = si->si_sc_link;
+ SC_DEBUG(sc_link, SDEV_DB2, ("user_strategy\n"));
+
+ /*
+ * We're in trouble if physio tried to break up the transfer.
+ */
+ if (bp->b_bcount != screq->datalen) {
+ sc_print_addr(sc_link);
+ printf("physio split the request.. cannot proceed\n");
+ error = EIO;
+ goto bad;
+ }
+
+ if (screq->timeout == 0) {
+ error = EINVAL;
+ goto bad;
+ }
+
+ if (screq->cmdlen > sizeof(struct scsi_generic)) {
+ sc_print_addr(sc_link);
+ printf("cmdlen too big\n");
+ error = EFAULT;
+ goto bad;
+ }
+
+ if (screq->flags & SCCMD_READ)
+ flags |= SCSI_DATA_IN;
+ if (screq->flags & SCCMD_WRITE)
+ flags |= SCSI_DATA_OUT;
+ if (screq->flags & SCCMD_TARGET)
+ flags |= SCSI_TARGET;
+ if (screq->flags & SCCMD_ESCAPE)
+ flags |= SCSI_ESCAPE;
+
+ error = scsi_scsi_cmd(sc_link, (struct scsi_generic *)screq->cmd,
+ screq->cmdlen, (u_char *)bp->b_data, screq->datalen,
+ 0, /* user must do the retries *//* ignored */
+ screq->timeout, bp, flags | SCSI_USER | SCSI_NOSLEEP);
+
+ /* because there is a bp, scsi_scsi_cmd will return immediatly */
+ if (error)
+ goto bad;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("about to sleep\n"));
+ s = splbio();
+ while ((bp->b_flags & B_DONE) == 0)
+ tsleep(bp, PRIBIO, "scistr", 0);
+ splx(s);
+ SC_DEBUG(sc_link, SDEV_DB3, ("back from sleep\n"));
+
+ return;
+
+bad:
+ bp->b_flags |= B_ERROR;
+ bp->b_error = error;
+ biodone(bp);
+}
+
+/*
+ * Something (e.g. another driver) has called us
+ * with an sc_link for a target/lun/adapter, and a scsi
+ * specific ioctl to perform, better try.
+ * If user-level type command, we must still be running
+ * in the context of the calling process
+ */
+int
+scsi_do_ioctl(sc_link, dev, cmd, addr, flag, p)
+ struct scsi_link *sc_link;
+ dev_t dev;
+ u_long cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ int error;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd));
+
+ switch(cmd) {
+ case SCIOCCOMMAND: {
+ scsireq_t *screq = (scsireq_t *)addr;
+ struct scsi_ioctl *si;
+ int len;
+
+ si = si_get();
+ si->si_screq = *screq;
+ si->si_sc_link = sc_link;
+ len = screq->datalen;
+ if (len) {
+ si->si_iov.iov_base = screq->databuf;
+ si->si_iov.iov_len = len;
+ si->si_uio.uio_iov = &si->si_iov;
+ si->si_uio.uio_iovcnt = 1;
+ si->si_uio.uio_resid = len;
+ si->si_uio.uio_offset = 0;
+ si->si_uio.uio_segflg = UIO_USERSPACE;
+ si->si_uio.uio_rw =
+ (screq->flags & SCCMD_READ) ? UIO_READ : UIO_WRITE;
+ si->si_uio.uio_procp = p;
+ error = physio(scsistrategy, &si->si_bp, dev,
+ (screq->flags & SCCMD_READ) ? B_READ : B_WRITE,
+ sc_link->adapter->scsi_minphys, &si->si_uio);
+ } else {
+ /* if no data, no need to translate it.. */
+ si->si_bp.b_flags = 0;
+ si->si_bp.b_data = 0;
+ si->si_bp.b_bcount = 0;
+ si->si_bp.b_dev = dev;
+ si->si_bp.b_proc = p;
+ scsistrategy(&si->si_bp);
+ error = si->si_bp.b_error;
+ }
+ *screq = si->si_screq;
+ si_free(si);
+ return error;
+ }
+ case SCIOCDEBUG: {
+ int level = *((int *)addr);
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("debug set to %d\n", level));
+ sc_link->flags &= ~SDEV_DBX; /* clear debug bits */
+ if (level & 1)
+ sc_link->flags |= SDEV_DB1;
+ if (level & 2)
+ sc_link->flags |= SDEV_DB2;
+ if (level & 4)
+ sc_link->flags |= SDEV_DB3;
+ if (level & 8)
+ sc_link->flags |= SDEV_DB4;
+ return 0;
+ }
+ case SCIOCREPROBE: {
+ struct scsi_addr *sca = (struct scsi_addr *)addr;
+
+ return scsi_probe_busses(sca->scbus, sca->target, sca->lun);
+ }
+ case SCIOCRECONFIG:
+ case SCIOCDECONFIG:
+ return EINVAL;
+ case SCIOCIDENTIFY: {
+ struct scsi_addr *sca = (struct scsi_addr *)addr;
+
+ sca->scbus = sc_link->scsibus;
+ sca->target = sc_link->target;
+ sca->lun = sc_link->lun;
+ return 0;
+ }
+ default:
+ return ENOTTY;
+ }
+
+#ifdef DIAGNOSTIC
+ panic("scsi_do_ioctl: impossible");
+#endif
+}
diff --git a/sys/scsi/scsi_message.h b/sys/scsi/scsi_message.h
new file mode 100644
index 00000000000..331a2d3ddea
--- /dev/null
+++ b/sys/scsi/scsi_message.h
@@ -0,0 +1,33 @@
+/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */
+#define MSG_CMDCOMPLETE 0x00 /* M/M */
+#define MSG_EXTENDED 0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER 0x02 /* O/O */
+#define MSG_RESTOREPOINTERS 0x03 /* O/O */
+#define MSG_DISCONNECT 0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */
+#define MSG_ABORT 0x06 /* O/M */
+#define MSG_MESSAGE_REJECT 0x07 /* M/M */
+#define MSG_NOOP 0x08 /* M/M */
+#define MSG_PARITY_ERROR 0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */
+#define MSG_BUS_DEV_RESET 0x0c /* O/M */
+#define MSG_ABORT_TAG 0x0d /* O/O */
+#define MSG_CLEAR_QUEUE 0x0e /* O/O */
+#define MSG_INIT_RECOVERY 0x0f /* O/O */
+#define MSG_REL_RECOVERY 0x10 /* O/O */
+#define MSG_TERM_IO_PROC 0x11 /* O/O */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */
+#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */
+#define MSG_ORDERED_Q_TAG 0x22 /* O/O */
+#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */
+
+/* Identify message */
+#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : 0x80) | (lun))
+#define MSG_ISIDENTIFY(m) ((m) & 0x80)
+
+/* Extended messages (opcode) */
+#define MSG_EXT_SDTR 0x01
+#define MSG_EXT_WDTR 0x03
diff --git a/sys/scsi/scsi_tape.h b/sys/scsi/scsi_tape.h
new file mode 100644
index 00000000000..4ff8042e75b
--- /dev/null
+++ b/sys/scsi/scsi_tape.h
@@ -0,0 +1,209 @@
+/* $NetBSD: scsi_tape.h,v 1.6 1994/12/28 19:43:08 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1994 Charles Hannum. All rights reserved.
+ *
+ * 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 acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 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.
+ */
+
+/*
+ * Originally written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+/*
+ * SCSI tape interface description
+ */
+
+#ifndef _SCSI_TAPE_H_
+#define _SCSI_TAPE_H_ 1
+
+/*
+ * SCSI command formats
+ */
+
+#define READ 0x08
+#define WRITE 0x0a
+struct scsi_rw_tape {
+ u_char opcode;
+ u_char byte2;
+#define SRW_FIXED 0x01
+ u_char len[3];
+ u_char control;
+};
+
+#define SPACE 0x11
+struct scsi_space {
+ u_char opcode;
+ u_char byte2;
+#define SS_CODE 0x03
+#define SP_BLKS 0x00
+#define SP_FILEMARKS 0x01
+#define SP_SEQ_FILEMARKS 0x02
+#define SP_EOM 0x03
+ u_char number[3];
+ u_char control;
+};
+
+#define WRITE_FILEMARKS 0x10
+struct scsi_write_filemarks {
+ u_char opcode;
+ u_char byte2;
+ u_char number[3];
+ u_char control;
+};
+
+#define REWIND 0x01
+struct scsi_rewind {
+ u_char opcode;
+ u_char byte2;
+#define SR_IMMED 0x01
+ u_char unused[3];
+ u_char control;
+};
+
+#define LOAD 0x1b
+struct scsi_load {
+ u_char opcode;
+ u_char byte2;
+#define SL_IMMED 0x01
+ u_char unused[2];
+ u_char how;
+#define LD_UNLOAD 0x00
+#define LD_LOAD 0x01
+#define LD_RETENSION 0x02
+ u_char control;
+};
+
+#define READ_BLOCK_LIMITS 0x05
+struct scsi_block_limits {
+ u_char opcode;
+ u_char byte2;
+ u_char unused[3];
+ u_char control;
+};
+
+struct scsi_block_limits_data {
+ u_char reserved;
+ u_char max_length_2; /* Most significant */
+ u_char max_length_1;
+ u_char max_length_0; /* Least significant */
+ u_char min_length_1; /* Most significant */
+ u_char min_length_0; /* Least significant */
+};
+
+/* defines for the device specific byte in the mode select/sense header */
+#define SMH_DSP_SPEED 0x0F
+#define SMH_DSP_BUFF_MODE 0x70
+#define SMH_DSP_BUFF_MODE_OFF 0x00
+#define SMH_DSP_BUFF_MODE_ON 0x10
+#define SMH_DSP_BUFF_MODE_MLTI 0x20
+#define SMH_DSP_WRITE_PROT 0x80
+
+/* A special for the CIPHER ST150S(old drive) */
+struct block_desc_cipher {
+ u_char density;
+ u_char nblocks[3];
+ u_char reserved;
+ u_char blklen[3];
+ u_char other;
+#define ST150_SEC 0x01 /* soft error count */
+#define SR150_AUI 0x02 /* autoload inhibit */
+};
+
+/**********************************************************************
+ from the scsi2 spec
+ Value Tracks Density(bpi) Code Type Reference Note
+ 0x1 9 800 NRZI R X3.22-1983 2
+ 0x2 9 1600 PE R X3.39-1986 2
+ 0x3 9 6250 GCR R X3.54-1986 2
+ 0x5 4/9 8000 GCR C X3.136-1986 1
+ 0x6 9 3200 PE R X3.157-1987 2
+ 0x7 4 6400 IMFM C X3.116-1986 1
+ 0x8 4 8000 GCR CS X3.158-1986 1
+ 0x9 18 37871 GCR C X3B5/87-099 2
+ 0xA 22 6667 MFM C X3B5/86-199 1
+ 0xB 4 1600 PE C X3.56-1986 1
+ 0xC 24 12690 GCR C HI-TC1 1,5
+ 0xD 24 25380 GCR C HI-TC2 1,5
+ 0xF 15 10000 GCR C QIC-120 1,5
+ 0x10 18 10000 GCR C QIC-150 1,5
+ 0x11 26 16000 GCR C QIC-320(525?) 1,5
+ 0x12 30 51667 RLL C QIC-1350 1,5
+ 0x13 1 61000 DDS CS X3B5/88-185A 4
+ 0x14 1 43245 RLL CS X3.202-1991 4
+ 0x15 1 45434 RLL CS ECMA TC17 4
+ 0x16 48 10000 MFM C X3.193-1990 1
+ 0x17 48 42500 MFM C X3B5/91-174 1
+
+ where Code means:
+ NRZI Non Return to Zero, change on ones
+ GCR Group Code Recording
+ PE Phase Encoded
+ IMFM Inverted Modified Frequency Modulation
+ MFM Modified Frequency Modulation
+ DDS Dat Data Storage
+ RLL Run Length Encoding
+
+ where Type means:
+ R Reel-to-Reel
+ C Cartridge
+ CS cassette
+
+ where Notes means:
+ 1 Serial Recorded
+ 2 Parallel Recorded
+ 3 Old format know as QIC-11
+ 4 Helical Scan
+ 5 Not ANSI standard, rather industry standard.
+********************************************************************/
+
+#define HALFINCH_800 0x01
+#define HALFINCH_1600 0x02
+#define HALFINCH_6250 0x03
+#define QIC_11 0x04 /* from Archive 150S Theory of Op. XXX */
+#define QIC_24 0x05 /* may be bad, works for CIPHER ST150S XXX */
+#define QIC_120 0x0f
+#define QIC_150 0x10
+#define QIC_320 0x11
+#define QIC_525 0x11
+#define QIC_1320 0x12
+#define DDS 0x13
+#define DAT_1 0x13
+
+#endif /* _SCSI_TAPE_H_ */
diff --git a/sys/scsi/scsiconf.c b/sys/scsi/scsiconf.c
new file mode 100644
index 00000000000..39e5fc060a0
--- /dev/null
+++ b/sys/scsi/scsiconf.c
@@ -0,0 +1,576 @@
+/* $NetBSD: scsiconf.c,v 1.43 1995/10/13 20:01:03 gwr Exp $ */
+
+/*
+ * Copyright (c) 1994 Charles Hannum. All rights reserved.
+ *
+ * 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 acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 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.
+ */
+
+/*
+ * Originally written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#if 0
+#if NCALS > 0
+ { T_PROCESSOR, T_FIXED, 1,
+ 0, 0, 0 },
+#endif /* NCALS */
+#if NBLL > 0
+ { T_PROCESSOR, T_FIXED, 1,
+ "AEG ", "READER ", "V1.0" },
+#endif /* NBLL */
+#if NKIL > 0
+ { T_SCANNER, T_FIXED, 0,
+ "KODAK ", "IL Scanner 900 ", 0 },
+#endif /* NKIL */
+#endif
+
+/*
+ * Declarations
+ */
+void scsi_probedev __P((struct scsibus_softc *, int, int));
+int scsi_probe_bus __P((int bus, int target, int lun));
+
+struct scsi_device probe_switch = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+int scsibusmatch __P((struct device *, void *, void *));
+void scsibusattach __P((struct device *, struct device *, void *));
+
+struct cfdriver scsibuscd = {
+ NULL, "scsibus", scsibusmatch, scsibusattach, DV_DULL,
+ sizeof(struct scsibus_softc)
+};
+
+int
+scsibusmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+
+ return 1;
+}
+
+/*
+ * The routine called by the adapter boards to get all their
+ * devices configured in.
+ */
+void
+scsibusattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct scsibus_softc *sb = (struct scsibus_softc *)self;
+ struct scsi_link *sc_link_proto = aux;
+
+ sc_link_proto->scsibus = sb->sc_dev.dv_unit;
+ sb->adapter_link = sc_link_proto;
+ printf("\n");
+
+#if defined(SCSI_DELAY) && SCSI_DELAY > 2
+ printf("%s: waiting for scsi devices to settle\n",
+ sb->sc_dev.dv_xname);
+#else /* SCSI_DELAY > 2 */
+#undef SCSI_DELAY
+#define SCSI_DELAY 2
+#endif /* SCSI_DELAY */
+ delay(1000000 * SCSI_DELAY);
+
+ scsi_probe_bus(sb->sc_dev.dv_unit, -1, -1);
+}
+
+int
+scsibussubmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct scsibus_attach_args *sa = aux;
+ struct scsi_link *sc_link = sa->sa_sc_link;
+
+ if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != sc_link->target)
+ return 0;
+ if (cf->cf_loc[1] != -1 && cf->cf_loc[1] != sc_link->lun)
+ return 0;
+ return ((*cf->cf_driver->cd_match)(parent, match, aux));
+}
+
+/*
+ * Probe the requested scsi bus. It must be already set up.
+ * -1 requests all set up scsi busses.
+ * target and lun optionally narrow the search if not -1
+ */
+int
+scsi_probe_busses(bus, target, lun)
+ int bus, target, lun;
+{
+
+ if (bus == -1) {
+ for (bus = 0; bus < scsibuscd.cd_ndevs; bus++)
+ if (scsibuscd.cd_devs[bus])
+ scsi_probe_bus(bus, target, lun);
+ return 0;
+ } else {
+ return scsi_probe_bus(bus, target, lun);
+ }
+}
+
+/*
+ * Probe the requested scsi bus. It must be already set up.
+ * target and lun optionally narrow the search if not -1
+ */
+int
+scsi_probe_bus(bus, target, lun)
+ int bus, target, lun;
+{
+ struct scsibus_softc *scsi;
+ int maxtarget, mintarget, maxlun, minlun;
+ u_int8_t scsi_addr;
+
+ if (bus < 0 || bus >= scsibuscd.cd_ndevs)
+ return ENXIO;
+ scsi = scsibuscd.cd_devs[bus];
+ if (!scsi)
+ return ENXIO;
+
+ scsi_addr = scsi->adapter_link->adapter_target;
+
+ if (target == -1) {
+ maxtarget = 7;
+ mintarget = 0;
+ } else {
+ if (target < 0 || target > 7)
+ return EINVAL;
+ maxtarget = mintarget = target;
+ }
+
+ if (lun == -1) {
+ maxlun = 7;
+ minlun = 0;
+ } else {
+ if (lun < 0 || lun > 7)
+ return EINVAL;
+ maxlun = minlun = lun;
+ }
+
+ for (target = mintarget; target <= maxtarget; target++) {
+ if (target == scsi_addr)
+ continue;
+ for (lun = minlun; lun <= maxlun; lun++) {
+ /*
+ * See if there's a device present, and configure it.
+ */
+ scsi_probedev(scsi, target, lun);
+ if ((scsi->moreluns & (1 << target)) == 0)
+ break;
+ /* otherwise something says we should look further */
+ }
+ }
+ return 0;
+}
+
+void
+scsi_strvis(dst, src, len)
+ u_char *dst, *src;
+ int len;
+{
+
+ /* Trim leading and trailing blanks. */
+ while (len > 0 && src[0] == ' ')
+ ++src, --len;
+ while (len > 0 && src[len-1] == ' ')
+ --len;
+
+ while (len > 0) {
+ if (*src < 0x20 || *src >= 0x80) {
+ /* non-printable characters */
+ *dst++ = '\\';
+ *dst++ = ((*src & 0300) >> 6) + '0';
+ *dst++ = ((*src & 0070) >> 3) + '0';
+ *dst++ = ((*src & 0007) >> 0) + '0';
+ } else if (*src == '\\') {
+ /* quote characters */
+ *dst++ = '\\';
+ *dst++ = '\\';
+ } else {
+ /* normal characters */
+ *dst++ = *src;
+ }
+ ++src, --len;
+ }
+
+ *dst++ = 0;
+}
+
+struct scsi_quirk_inquiry_pattern {
+ struct scsi_inquiry_pattern pattern;
+ u_int8_t quirks;
+};
+
+struct scsi_quirk_inquiry_pattern scsi_quirk_patterns[] = {
+ {T_CDROM, T_REMOV,
+ "CHINON ", "CD-ROM CDS-431 ", "", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "CHINON ", "CD-ROM CDS-535 ", "", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "DENON ", "DRD-25X ", "V", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "IMS ", "CDD521/10 ", "2.06", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "MEDIAVIS", "CDR-H93MV ", "1.31", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "NEC ", "CD-ROM DRIVE:55 ", "", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "NEC ", "CD-ROM DRIVE:83 ", "", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "NEC ", "CD-ROM DRIVE:84 ", "", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "NEC ", "CD-ROM DRIVE:841", "", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "SONY ", "CD-ROM CDU-541 ", "", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "SONY ", "CD-ROM CDU-55S ", "", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "SONY ", "CD-ROM CDU-8003A", "", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "SONY ", "CD-ROM CDU-8012 ", "", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "TEAC ", "CD-ROM ", "1.06", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "TEXEL ", "CD-ROM ", "1.06", SDEV_NOLUNS},
+ {T_CDROM, T_REMOV,
+ "TEXEL ", "CD-ROM DM-XX24 K", "1.10", SDEV_NOLUNS},
+
+ {T_DIRECT, T_FIXED,
+ "EMULEX ", "MD21/S2 ESDI", "A00", SDEV_FORCELUNS},
+ {T_DIRECT, T_FIXED,
+ "MAXTOR ", "XT-3280 ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "MAXTOR ", "XT-4380S ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "MAXTOR ", "MXT-1240S ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "MAXTOR ", "XT-4170S ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "MAXTOR ", "XT-8760S ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "MAXTOR ", "LXT-213S ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "MAXTOR ", "LXT-213S SUN0207", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "MST ", "SnapLink ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "QUANTUM ", "LPS525S ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "QUANTUM ", "P105S 910-10-94x", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "QUANTUM ", "PD1225S ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "RODIME ", "RO3000S ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "SEAGATE ", "ST157N ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "SEAGATE ", "ST296 ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "SEAGATE ", "ST296N ", "", SDEV_NOLUNS},
+ {T_DIRECT, T_FIXED,
+ "TOSHIBA ", "MK538FB ", "6027", SDEV_NOLUNS},
+
+ /* XXX: QIC-36 tape behind Emulex adapter. Very broken. */
+ {T_SEQUENTIAL, T_REMOV,
+ " ", " ", " ", SDEV_NOLUNS},
+ {T_SEQUENTIAL, T_REMOV,
+ "EXABYTE ", "EXB-8200 ", "", SDEV_NOLUNS},
+ {T_SEQUENTIAL, T_REMOV,
+ "SONY ", "SDT-2000 ", "2.09", SDEV_NOLUNS},
+ {T_SEQUENTIAL, T_REMOV,
+ "SONY ", "SDT-5000 ", "3.17", SDEV_NOSYNCWIDE},
+ {T_SEQUENTIAL, T_REMOV,
+ "TANDBERG", " TDC 3600 ", "", SDEV_NOLUNS},
+ {T_SEQUENTIAL, T_REMOV,
+ "WANGTEK ", "5099ES SCSI", "", SDEV_NOLUNS},
+ {T_SEQUENTIAL, T_REMOV,
+ "WANGTEK ", "5150ES SCSI", "", SDEV_NOLUNS},
+ {T_SEQUENTIAL, T_REMOV,
+ "WangDAT ", "Model 1300 ", "02.4", SDEV_NOSYNCWIDE},
+ {T_SEQUENTIAL, T_REMOV,
+ "WangDAT ", "Model 2600 ", "01.7", SDEV_NOSYNCWIDE},
+ {T_SEQUENTIAL, T_REMOV,
+ "WangDAT ", "Model 3200 ", "02.2", SDEV_NOSYNCWIDE},
+};
+
+/*
+ * given a target and lu, ask the device what
+ * it is, and find the correct driver table
+ * entry.
+ */
+void
+scsi_probedev(scsi, target, lun)
+ struct scsibus_softc *scsi;
+ int target, lun;
+{
+ struct scsi_link *sc_link;
+ static struct scsi_inquiry_data inqbuf;
+ struct scsi_quirk_inquiry_pattern *finger;
+ int priority;
+ u_int8_t type;
+ boolean removable;
+ char *dtype, *qtype;
+ char vendor[33], product[65], revision[17];
+ struct scsibus_attach_args sa;
+ struct cfdata *cf;
+
+ /* Skip this slot if it is already attached. */
+ if (scsi->sc_link[target][lun])
+ return;
+
+ sc_link = malloc(sizeof(*sc_link), M_DEVBUF, M_NOWAIT);
+ *sc_link = *scsi->adapter_link;
+ sc_link->target = target;
+ sc_link->lun = lun;
+ sc_link->device = &probe_switch;
+
+ /*
+ * Ask the device what it is
+ */
+#ifdef SCSIDEBUG
+ if (target == DEBUGTARGET && lun == DEBUGLUN)
+ sc_link->flags |= DEBUGLEVEL;
+#endif /* SCSIDEBUG */
+
+ (void) scsi_test_unit_ready(sc_link,
+ SCSI_AUTOCONF | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY | SCSI_IGNORE_MEDIA_CHANGE);
+
+#ifdef SCSI_2_DEF
+ /* some devices need to be told to go to SCSI2 */
+ /* However some just explode if you tell them this.. leave it out */
+ scsi_change_def(sc_link, SCSI_AUTOCONF | SCSI_SILENT);
+#endif /* SCSI_2_DEF */
+
+ /* Now go ask the device all about itself. */
+ bzero(&inqbuf, sizeof(inqbuf));
+ if (scsi_inquire(sc_link, &inqbuf, SCSI_AUTOCONF) != 0)
+ goto bad;
+
+ {
+ int len = inqbuf.additional_length;
+ while (len < 3)
+ inqbuf.unused[len++] = '\0';
+ while (len < 3 + 28)
+ inqbuf.unused[len++] = ' ';
+ }
+
+ finger = (struct scsi_quirk_inquiry_pattern *)scsi_inqmatch(&inqbuf,
+ (caddr_t)scsi_quirk_patterns,
+ sizeof(scsi_quirk_patterns)/sizeof(scsi_quirk_patterns[0]),
+ sizeof(scsi_quirk_patterns[0]), &priority);
+ if (priority != 0)
+ sc_link->quirks |= finger->quirks;
+ if ((inqbuf.version & SID_ANSII) == 0 &&
+ (sc_link->quirks & SDEV_FORCELUNS) == 0)
+ sc_link->quirks |= SDEV_NOLUNS;
+
+ if ((sc_link->quirks & SDEV_NOLUNS) == 0)
+ scsi->moreluns |= (1 << target);
+
+ /*
+ * note what BASIC type of device it is
+ */
+ type = inqbuf.device & SID_TYPE;
+ removable = inqbuf.dev_qual2 & SID_REMOVABLE ? 1 : 0;
+
+ if (removable)
+ sc_link->flags |= SDEV_REMOVABLE;
+
+ /*
+ * Any device qualifier that has the top bit set (qualifier&4 != 0)
+ * is vendor specific and won't match in this switch.
+ */
+ dtype = 0;
+ switch (inqbuf.device & SID_QUAL) {
+ case SID_QUAL_LU_OK:
+ qtype = "";
+ break;
+
+ case SID_QUAL_LU_OFFLINE:
+ qtype = " offline";
+ break;
+
+ case SID_QUAL_RSVD:
+ case SID_QUAL_BAD_LU:
+ goto bad;
+
+ default:
+ qtype = "";
+ dtype = "vendor-unique";
+ break;
+ }
+ if (dtype == 0) {
+ switch (type) {
+ case T_DIRECT:
+ dtype = "direct";
+ break;
+ case T_SEQUENTIAL:
+ dtype = "sequential";
+ break;
+ case T_PRINTER:
+ dtype = "printer";
+ break;
+ case T_PROCESSOR:
+ dtype = "processor";
+ break;
+ case T_CDROM:
+ dtype = "cdrom";
+ break;
+ case T_WORM:
+ dtype = "worm";
+ break;
+ case T_SCANNER:
+ dtype = "scanner";
+ break;
+ case T_OPTICAL:
+ dtype = "optical";
+ break;
+ case T_CHANGER:
+ dtype = "changer";
+ break;
+ case T_COMM:
+ dtype = "communication";
+ break;
+ case T_NODEVICE:
+ goto bad;
+ default:
+ dtype = "unknown";
+ break;
+ }
+ }
+
+ scsi_strvis(vendor, inqbuf.vendor, 8);
+ scsi_strvis(product, inqbuf.product, 16);
+ scsi_strvis(revision, inqbuf.revision, 4);
+
+ printf("%s targ %d lun %d: <%s, %s, %s> SCSI%d %d/%s %s%s\n",
+ ((struct device *)sc_link->adapter_softc)->dv_xname,
+ target, lun, vendor, product, revision,
+ inqbuf.version & SID_ANSII, type, dtype,
+ removable ? "removable" : "fixed", qtype);
+
+ sa.sa_sc_link = sc_link;
+ sa.sa_inqbuf = &inqbuf;
+
+ if ((cf = config_search(scsibussubmatch, (struct device *)scsi, &sa)) != 0) {
+ scsi->sc_link[target][lun] = sc_link;
+ config_attach((struct device *)scsi, cf, &sa, NULL);
+ } else
+ goto bad;
+
+ return;
+
+bad:
+ free(sc_link, M_DEVBUF);
+ return;
+}
+
+/*
+ * Return a priority based on how much of the inquiry data matches
+ * the patterns for the particular driver.
+ */
+caddr_t
+scsi_inqmatch(inqbuf, base, nmatches, matchsize, bestpriority)
+ struct scsi_inquiry_data *inqbuf;
+ caddr_t base;
+ int nmatches, matchsize;
+ int *bestpriority;
+{
+ u_int8_t type;
+ boolean removable;
+ caddr_t bestmatch;
+
+ /* Include the qualifier to catch vendor-unique types. */
+ type = inqbuf->device;
+ removable = inqbuf->dev_qual2 & SID_REMOVABLE ? T_REMOV : T_FIXED;
+
+ for (*bestpriority = 0, bestmatch = 0; nmatches--; base += matchsize) {
+ struct scsi_inquiry_pattern *match = (void *)base;
+ int priority, len;
+
+ if (type != match->type)
+ continue;
+ if (removable != match->removable)
+ continue;
+ priority = 2;
+ len = strlen(match->vendor);
+ if (bcmp(inqbuf->vendor, match->vendor, len))
+ continue;
+ priority += len;
+ len = strlen(match->product);
+ if (bcmp(inqbuf->product, match->product, len))
+ continue;
+ priority += len;
+ len = strlen(match->revision);
+ if (bcmp(inqbuf->revision, match->revision, len))
+ continue;
+ priority += len;
+
+#if 0
+ printf("scsi_inqmatch: %d/%d/%d <%s, %s, %s>\n",
+ priority, match->type, match->removable,
+ match->vendor, match->product, match->revision);
+#endif
+ if (priority > *bestpriority) {
+ *bestpriority = priority;
+ bestmatch = base;
+ }
+ }
+
+ return (bestmatch);
+}
diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h
new file mode 100644
index 00000000000..b7258e3027c
--- /dev/null
+++ b/sys/scsi/scsiconf.h
@@ -0,0 +1,281 @@
+/* $NetBSD: scsiconf.h,v 1.25 1995/08/12 20:31:44 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1993, 1994, 1995 Charles Hannum. All rights reserved.
+ *
+ * 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 acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 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.
+ */
+
+/*
+ * Originally written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+#ifndef SCSI_SCSICONF_H
+#define SCSI_SCSICONF_H 1
+
+typedef int boolean;
+
+#include <sys/queue.h>
+#include <machine/cpu.h>
+#include <scsi/scsi_debug.h>
+
+/*
+ * The following documentation tries to describe the relationship between the
+ * various structures defined in this file:
+ *
+ * each adapter type has a scsi_adapter struct. This describes the adapter and
+ * identifies routines that can be called to use the adapter.
+ * each device type has a scsi_device struct. This describes the device and
+ * identifies routines that can be called to use the device.
+ * each existing device position (scsibus + target + lun)
+ * can be described by a scsi_link struct.
+ * Only scsi positions that actually have devices, have a scsi_link
+ * structure assigned. so in effect each device has scsi_link struct.
+ * The scsi_link structure contains information identifying both the
+ * device driver and the adapter driver for that position on that scsi bus,
+ * and can be said to 'link' the two.
+ * each individual scsi bus has an array that points to all the scsi_link
+ * structs associated with that scsi bus. Slots with no device have
+ * a NULL pointer.
+ * each individual device also knows the address of it's own scsi_link
+ * structure.
+ *
+ * -------------
+ *
+ * The key to all this is the scsi_link structure which associates all the
+ * other structures with each other in the correct configuration. The
+ * scsi_link is the connecting information that allows each part of the
+ * scsi system to find the associated other parts.
+ */
+
+
+/*
+ * These entrypoints are called by the high-end drivers to get services from
+ * whatever low-end drivers they are attached to each adapter type has one of
+ * these statically allocated.
+ */
+struct scsi_adapter {
+ int (*scsi_cmd)();
+ void (*scsi_minphys) __P((struct buf *));
+ int (*open_target_lu)();
+ int (*close_target_lu)();
+};
+
+/*
+ * return values for scsi_cmd()
+ */
+#define SUCCESSFULLY_QUEUED 0
+#define TRY_AGAIN_LATER 1
+#define COMPLETE 2
+#define ESCAPE_NOT_SUPPORTED 3
+
+/*
+ * These entry points are called by the low-end drivers to get services from
+ * whatever high-end drivers they are attached to. Each device type has one
+ * of these statically allocated.
+ */
+struct scsi_device {
+ int (*err_handler)(); /* returns -1 to say err processing done */
+ void (*start)();
+ int (*async)();
+ int (*done)(); /* returns -1 to say done processing done */
+};
+
+/*
+ * This structure describes the connection between an adapter driver and
+ * a device driver, and is used by each to call services provided by
+ * the other, and to allow generic scsi glue code to call these services
+ * as well.
+ */
+struct scsi_link {
+ u_int8_t scsibus; /* the Nth scsibus */
+ u_int8_t target; /* targ of this dev */
+ u_int8_t lun; /* lun of this dev */
+ u_int8_t adapter_target; /* what are we on the scsi bus */
+ u_int8_t openings; /* available operations */
+ u_int8_t active; /* operations in progress */
+ u_int8_t flags; /* flags that all devices have */
+#define SDEV_REMOVABLE 0x01 /* media is removable */
+#define SDEV_MEDIA_LOADED 0x02 /* device figures are still valid */
+#define SDEV_WAITING 0x04 /* a process is waiting for this */
+#define SDEV_OPEN 0x08 /* at least 1 open session */
+#define SDEV_DBX 0xf0 /* debuging flags (scsi_debug.h) */
+ u_int8_t quirks; /* per-device oddities */
+#define SDEV_AUTOSAVE 0x01 /* do implicit SAVEDATAPOINTER on disconnect */
+#define SDEV_NOSYNCWIDE 0x02 /* does not grok SDTR or WDTR */
+#define SDEV_NOLUNS 0x04 /* does not grok LUNs */
+#define SDEV_FORCELUNS 0x08 /* prehistoric drive/ctlr groks LUNs */
+ struct scsi_device *device; /* device entry points etc. */
+ void *device_softc; /* needed for call to foo_start */
+ struct scsi_adapter *adapter; /* adapter entry points etc. */
+ void *adapter_softc; /* needed for call to foo_scsi_cmd */
+};
+
+/*
+ * This describes matching information for scsi_inqmatch(). The more things
+ * match, the higher the configuration priority.
+ */
+struct scsi_inquiry_pattern {
+ u_int8_t type;
+ boolean removable;
+ char *vendor;
+ char *product;
+ char *revision;
+};
+
+/*
+ * One of these is allocated and filled in for each scsi bus.
+ * it holds pointers to allow the scsi bus to get to the driver
+ * That is running each LUN on the bus
+ * it also has a template entry which is the prototype struct
+ * supplied by the adapter driver, this is used to initialise
+ * the others, before they have the rest of the fields filled in
+ */
+struct scsibus_softc {
+ struct device sc_dev;
+ struct scsi_link *adapter_link; /* prototype supplied by adapter */
+ struct scsi_link *sc_link[8][8];
+ u_int8_t moreluns;
+};
+
+/*
+ * This is used to pass information from the high-level configuration code
+ * to the device-specific drivers.
+ */
+struct scsibus_attach_args {
+ struct scsi_link *sa_sc_link;
+ struct scsi_inquiry_data *sa_inqbuf;
+};
+
+/*
+ * Each scsi transaction is fully described by one of these structures
+ * It includes information about the source of the command and also the
+ * device and adapter for which the command is destined.
+ * (via the scsi_link structure)
+ */
+struct scsi_xfer {
+ LIST_ENTRY(scsi_xfer) free_list;
+ int flags;
+ struct scsi_link *sc_link; /* all about our device and adapter */
+ int retries; /* the number of times to retry */
+ int timeout; /* in milliseconds */
+ struct scsi_generic *cmd; /* The scsi command to execute */
+ int cmdlen; /* how long it is */
+ u_char *data; /* dma address OR a uio address */
+ int datalen; /* data len (blank if uio) */
+ int resid; /* how much buffer was not touched */
+ int error; /* an error value */
+ struct buf *bp; /* If we need to associate with a buf */
+ struct scsi_sense_data sense; /* 32 bytes*/
+ /*
+ * Believe it or not, Some targets fall on the ground with
+ * anything but a certain sense length.
+ */
+ int req_sense_length; /* Explicit request sense length */
+ u_int8_t status; /* SCSI status */
+ struct scsi_generic cmdstore; /* stash the command in here */
+};
+
+/*
+ * Per-request Flag values
+ */
+#define SCSI_NOSLEEP 0x0001 /* don't sleep */
+#define SCSI_POLL 0x0002 /* poll for completion */
+#define SCSI_AUTOCONF 0x0003 /* shorthand for SCSI_POLL | SCSI_NOSLEEP */
+#define SCSI_USER 0x0004 /* Is a user cmd, call scsi_user_done */
+#define ITSDONE 0x0008 /* the transfer is as done as it gets */
+#define INUSE 0x0010 /* The scsi_xfer block is in use */
+#define SCSI_SILENT 0x0020 /* don't announce NOT READY or MEDIA CHANGE */
+#define SCSI_IGNORE_NOT_READY 0x0040 /* ignore NOT READY */
+#define SCSI_IGNORE_MEDIA_CHANGE 0x0080 /* ignore MEDIA CHANGE */
+#define SCSI_IGNORE_ILLEGAL_REQUEST 0x0100 /* ignore ILLEGAL REQUEST */
+#define SCSI_RESET 0x0200 /* Reset the device in question */
+#define SCSI_DATA_UIO 0x0400 /* The data address refers to a UIO */
+#define SCSI_DATA_IN 0x0800 /* expect data to come INTO memory */
+#define SCSI_DATA_OUT 0x1000 /* expect data to flow OUT of memory */
+#define SCSI_TARGET 0x2000 /* This defines a TARGET mode op. */
+#define SCSI_ESCAPE 0x4000 /* Escape operation */
+
+/*
+ * Escape op codes. This provides an extensible setup for operations
+ * that are not scsi commands. They are intended for modal operations.
+ */
+
+#define SCSI_OP_TARGET 0x0001
+#define SCSI_OP_RESET 0x0002
+#define SCSI_OP_BDINFO 0x0003
+
+/*
+ * Error values an adapter driver may return
+ */
+#define XS_NOERROR 0 /* there is no error, (sense is invalid) */
+#define XS_SENSE 1 /* Check the returned sense for the error */
+#define XS_DRIVER_STUFFUP 2 /* Driver failed to perform operation */
+#define XS_SELTIMEOUT 3 /* The device timed out.. turned off? */
+#define XS_TIMEOUT 4 /* The Timeout reported was caught by SW */
+#define XS_BUSY 5 /* The device busy, try again later? */
+
+caddr_t scsi_inqmatch __P((struct scsi_inquiry_data *, caddr_t, int, int, int *));
+
+struct scsi_xfer *scsi_get_xs __P((struct scsi_link *, int));
+void scsi_free_xs __P((struct scsi_xfer *, int));
+int scsi_execute_xs __P((struct scsi_xfer *));
+u_long scsi_size __P((struct scsi_link *, int));
+int scsi_test_unit_ready __P((struct scsi_link *, int));
+int scsi_change_def __P((struct scsi_link *, int));
+int scsi_inquire __P((struct scsi_link *, struct scsi_inquiry_data *, int));
+int scsi_prevent __P((struct scsi_link *, int, int));
+int scsi_start __P((struct scsi_link *, int, int));
+void scsi_done __P((struct scsi_xfer *));
+int scsi_scsi_cmd __P((struct scsi_link *, struct scsi_generic *,
+ int cmdlen, u_char *data_addr,
+ int datalen, int retries,
+ int timeout, struct buf *bp,
+ int flags));
+int scsi_do_ioctl __P((struct scsi_link *, dev_t, u_long, caddr_t, int, struct proc *));
+void sc_print_addr __P((struct scsi_link *));
+
+void show_scsi_xs __P((struct scsi_xfer *));
+void show_scsi_cmd __P((struct scsi_xfer *));
+void show_mem __P((u_char *, int));
+
+void lto3b __P((u_int32_t val, u_int8_t *bytes));
+u_int32_t _3btol __P((u_int8_t *bytes));
+
+#endif /* SCSI_SCSICONF_H */
diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c
new file mode 100644
index 00000000000..2d8ef52df38
--- /dev/null
+++ b/sys/scsi/sd.c
@@ -0,0 +1,996 @@
+/* $NetBSD: sd.c,v 1.80 1995/10/10 02:53:01 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
+ *
+ * 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 acknowledgement:
+ * This product includes software developed by Charles M. Hannum.
+ * 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.
+ */
+
+/*
+ * Originally written by Julian Elischer (julian@dialix.oz.au)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@dialix.oz.au) Sept 1992
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/disk.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+
+#define SDOUTSTANDING 2
+#define SDRETRIES 4
+
+#define SDUNIT(dev) DISKUNIT(dev)
+#define SDPART(dev) DISKPART(dev)
+#define MAKESDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part)
+
+#define SDLABELDEV(dev) (MAKESDDEV(major(dev), SDUNIT(dev), RAW_PART))
+
+struct sd_softc {
+ struct device sc_dev;
+ struct dkdevice sc_dk;
+
+ int flags;
+#define SDF_LOCKED 0x01
+#define SDF_WANTED 0x02
+#define SDF_WLABEL 0x04 /* label is writable */
+#define SDF_LABELLING 0x08 /* writing label */
+ struct scsi_link *sc_link; /* contains our targ, lun, etc. */
+ struct disk_parms {
+ u_char heads; /* number of heads */
+ u_short cyls; /* number of cylinders */
+ u_char sectors; /* number of sectors/track */
+ int blksize; /* number of bytes/sector */
+ u_long disksize; /* total number sectors */
+ } params;
+ struct buf buf_queue;
+};
+
+int sdmatch __P((struct device *, void *, void *));
+void sdattach __P((struct device *, struct device *, void *));
+
+struct cfdriver sdcd = {
+ NULL, "sd", sdmatch, sdattach, DV_DISK, sizeof(struct sd_softc)
+};
+
+void sdgetdisklabel __P((struct sd_softc *));
+int sd_get_parms __P((struct sd_softc *, int));
+void sdstrategy __P((struct buf *));
+void sdstart __P((struct sd_softc *));
+
+struct dkdriver sddkdriver = { sdstrategy };
+
+struct scsi_device sd_switch = {
+ NULL, /* Use default error handler */
+ sdstart, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+};
+
+struct scsi_inquiry_pattern sd_patterns[] = {
+ {T_DIRECT, T_FIXED,
+ "", "", ""},
+ {T_DIRECT, T_REMOV,
+ "", "", ""},
+ {T_OPTICAL, T_FIXED,
+ "", "", ""},
+ {T_OPTICAL, T_REMOV,
+ "", "", ""},
+};
+
+int
+sdmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct scsibus_attach_args *sa = aux;
+ int priority;
+
+ (void)scsi_inqmatch(sa->sa_inqbuf,
+ (caddr_t)sd_patterns, sizeof(sd_patterns)/sizeof(sd_patterns[0]),
+ sizeof(sd_patterns[0]), &priority);
+ return (priority);
+}
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * a device suitable for this driver.
+ */
+void
+sdattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct sd_softc *sd = (void *)self;
+ struct disk_parms *dp = &sd->params;
+ struct scsibus_attach_args *sa = aux;
+ struct scsi_link *sc_link = sa->sa_sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("sdattach: "));
+
+ /*
+ * Store information needed to contact our base driver
+ */
+ sd->sc_link = sc_link;
+ sc_link->device = &sd_switch;
+ sc_link->device_softc = sd;
+ if (sc_link->openings > SDOUTSTANDING)
+ sc_link->openings = SDOUTSTANDING;
+
+ sd->sc_dk.dk_driver = &sddkdriver;
+#if !defined(i386) || defined(NEWCONFIG)
+ dk_establish(&sd->sc_dk, &sd->sc_dev);
+#endif
+
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ if (scsi_start(sd->sc_link, SSS_START,
+ SCSI_AUTOCONF | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT) ||
+ sd_get_parms(sd, SCSI_AUTOCONF) != 0)
+ printf(": drive offline\n");
+ else
+ printf(": %dMB, %d cyl, %d head, %d sec, %d bytes/sec\n",
+ dp->disksize / (1048576 / dp->blksize), dp->cyls,
+ dp->heads, dp->sectors, dp->blksize);
+}
+
+/*
+ * Wait interruptibly for an exclusive lock.
+ *
+ * XXX
+ * Several drivers do this; it should be abstracted and made MP-safe.
+ */
+int
+sdlock(sd)
+ struct sd_softc *sd;
+{
+ int error;
+
+ while ((sd->flags & SDF_LOCKED) != 0) {
+ sd->flags |= SDF_WANTED;
+ if ((error = tsleep(sd, PRIBIO | PCATCH, "sdlck", 0)) != 0)
+ return error;
+ }
+ sd->flags |= SDF_LOCKED;
+ return 0;
+}
+
+/*
+ * Unlock and wake up any waiters.
+ */
+void
+sdunlock(sd)
+ struct sd_softc *sd;
+{
+
+ sd->flags &= ~SDF_LOCKED;
+ if ((sd->flags & SDF_WANTED) != 0) {
+ sd->flags &= ~SDF_WANTED;
+ wakeup(sd);
+ }
+}
+
+/*
+ * open the device. Make sure the partition info is a up-to-date as can be.
+ */
+int
+sdopen(dev, flag, fmt)
+ dev_t dev;
+ int flag, fmt;
+{
+ struct sd_softc *sd;
+ struct scsi_link *sc_link;
+ int unit, part;
+ int error;
+
+ unit = SDUNIT(dev);
+ if (unit >= sdcd.cd_ndevs)
+ return ENXIO;
+ sd = sdcd.cd_devs[unit];
+ if (!sd)
+ return ENXIO;
+
+ sc_link = sd->sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1,
+ ("sdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit,
+ sdcd.cd_ndevs, part));
+
+ if (error = sdlock(sd))
+ return error;
+
+ if (sd->sc_dk.dk_openmask != 0) {
+ /*
+ * If any partition is open, but the disk has been invalidated,
+ * disallow further opens.
+ */
+ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ error = EIO;
+ goto bad3;
+ }
+ } else {
+ /* Check that it is still responding and ok. */
+ if (error = scsi_test_unit_ready(sc_link,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_IGNORE_NOT_READY))
+ goto bad3;
+
+ /* Start the pack spinning if necessary. */
+ if (error = scsi_start(sc_link, SSS_START,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT))
+ goto bad3;
+
+ sc_link->flags |= SDEV_OPEN;
+
+ /* Lock the pack in. */
+ if (error = scsi_prevent(sc_link, PR_PREVENT,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE))
+ goto bad;
+
+ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ sc_link->flags |= SDEV_MEDIA_LOADED;
+
+ /* Load the physical device parameters. */
+ if (sd_get_parms(sd, 0) != 0) {
+ error = ENXIO;
+ goto bad2;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded "));
+
+ /* Load the partition info if not already loaded. */
+ sdgetdisklabel(sd);
+ SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel loaded "));
+ }
+ }
+
+ part = SDPART(dev);
+
+ /* Check that the partition exists. */
+ if (part != RAW_PART &&
+ (part >= sd->sc_dk.dk_label.d_npartitions ||
+ sd->sc_dk.dk_label.d_partitions[part].p_fstype == FS_UNUSED)) {
+ error = ENXIO;
+ goto bad;
+ }
+
+ /* Insure only one open at a time. */
+ switch (fmt) {
+ case S_IFCHR:
+ sd->sc_dk.dk_copenmask |= (1 << part);
+ break;
+ case S_IFBLK:
+ sd->sc_dk.dk_bopenmask |= (1 << part);
+ break;
+ }
+ sd->sc_dk.dk_openmask = sd->sc_dk.dk_copenmask | sd->sc_dk.dk_bopenmask;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n"));
+ sdunlock(sd);
+ return 0;
+
+bad2:
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+
+bad:
+ if (sd->sc_dk.dk_openmask == 0) {
+ scsi_prevent(sc_link, PR_ALLOW,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE);
+ sc_link->flags &= ~SDEV_OPEN;
+ }
+
+bad3:
+ sdunlock(sd);
+ return error;
+}
+
+/*
+ * close the device.. only called if we are the LAST occurence of an open
+ * device. Convenient now but usually a pain.
+ */
+int
+sdclose(dev, flag, fmt)
+ dev_t dev;
+ int flag, fmt;
+{
+ struct sd_softc *sd = sdcd.cd_devs[SDUNIT(dev)];
+ int part = SDPART(dev);
+ int error;
+
+ if (error = sdlock(sd))
+ return error;
+
+ switch (fmt) {
+ case S_IFCHR:
+ sd->sc_dk.dk_copenmask &= ~(1 << part);
+ break;
+ case S_IFBLK:
+ sd->sc_dk.dk_bopenmask &= ~(1 << part);
+ break;
+ }
+ sd->sc_dk.dk_openmask = sd->sc_dk.dk_copenmask | sd->sc_dk.dk_bopenmask;
+
+ if (sd->sc_dk.dk_openmask == 0) {
+ /* XXXX Must wait for I/O to complete! */
+
+ scsi_prevent(sd->sc_link, PR_ALLOW,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY);
+ sd->sc_link->flags &= ~SDEV_OPEN;
+ }
+
+ sdunlock(sd);
+ return 0;
+}
+
+/*
+ * Actually translate the requested transfer into one the physical driver
+ * can understand. The transfer is described by a buf and will include
+ * only one physical transfer.
+ */
+void
+sdstrategy(bp)
+ struct buf *bp;
+{
+ struct sd_softc *sd = sdcd.cd_devs[SDUNIT(bp->b_dev)];
+ int s;
+
+ SC_DEBUG(sd->sc_link, SDEV_DB2, ("sdstrategy "));
+ SC_DEBUG(sd->sc_link, SDEV_DB1,
+ ("%d bytes @ blk %d\n", bp->b_bcount, bp->b_blkno));
+ /*
+ * The transfer must be a whole number of blocks.
+ */
+ if ((bp->b_bcount % sd->sc_dk.dk_label.d_secsize) != 0) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+ /*
+ * If the device has been made invalid, error out
+ */
+ if ((sd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /*
+ * If it's a null transfer, return immediatly
+ */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ /*
+ * Do bounds checking, adjust transfer. if error, process.
+ * If end of partition, just return.
+ */
+ if (SDPART(bp->b_dev) != RAW_PART &&
+ bounds_check_with_label(bp, &sd->sc_dk.dk_label,
+ (sd->flags & (SDF_WLABEL|SDF_LABELLING)) != 0) <= 0)
+ goto done;
+
+ s = splbio();
+
+ /*
+ * Place it in the queue of disk activities for this disk
+ */
+ disksort(&sd->buf_queue, bp);
+
+ /*
+ * Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion
+ */
+ sdstart(sd);
+
+ splx(s);
+ return;
+
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ /*
+ * Correctly set the buf to indicate a completed xfer
+ */
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+}
+
+/*
+ * sdstart looks to see if there is a buf waiting for the device
+ * and that the device is not already busy. If both are true,
+ * It dequeues the buf and creates a scsi command to perform the
+ * transfer in the buf. The transfer request will call scsi_done
+ * on completion, which will in turn call this routine again
+ * so that the next queued transfer is performed.
+ * The bufs are queued by the strategy routine (sdstrategy)
+ *
+ * This routine is also called after other non-queued requests
+ * have been made of the scsi driver, to ensure that the queue
+ * continues to be drained.
+ *
+ * must be called at the correct (highish) spl level
+ * sdstart() is called at splbio from sdstrategy and scsi_done
+ */
+void
+sdstart(sd)
+ register struct sd_softc *sd;
+{
+ register struct scsi_link *sc_link = sd->sc_link;
+ struct buf *bp = 0;
+ struct buf *dp;
+ struct scsi_rw_big cmd;
+ int blkno, nblks;
+ struct partition *p;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("sdstart "));
+ /*
+ * Check if the device has room for another command
+ */
+ while (sc_link->openings > 0) {
+ /*
+ * there is excess capacity, but a special waits
+ * It'll need the adapter as soon as we clear out of the
+ * way and let it run (user level wait).
+ */
+ if (sc_link->flags & SDEV_WAITING) {
+ sc_link->flags &= ~SDEV_WAITING;
+ wakeup((caddr_t)sc_link);
+ return;
+ }
+
+ /*
+ * See if there is a buf with work for us to do..
+ */
+ dp = &sd->buf_queue;
+ if ((bp = dp->b_actf) == NULL) /* yes, an assign */
+ return;
+ dp->b_actf = bp->b_actf;
+
+ /*
+ * If the device has become invalid, abort all the
+ * reads and writes until all files have been closed and
+ * re-opened
+ */
+ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ continue;
+ }
+
+ /*
+ * We have a buf, now we should make a command
+ *
+ * First, translate the block to absolute and put it in terms
+ * of the logical blocksize of the device.
+ */
+ blkno =
+ bp->b_blkno / (sd->sc_dk.dk_label.d_secsize / DEV_BSIZE);
+ if (SDPART(bp->b_dev) != RAW_PART) {
+ p = &sd->sc_dk.dk_label.d_partitions[SDPART(bp->b_dev)];
+ blkno += p->p_offset;
+ }
+ nblks = howmany(bp->b_bcount, sd->sc_dk.dk_label.d_secsize);
+
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = (bp->b_flags & B_READ) ? READ_BIG : WRITE_BIG;
+ cmd.addr_3 = (blkno >> 24) & 0xff;
+ cmd.addr_2 = (blkno >> 16) & 0xff;
+ cmd.addr_1 = (blkno >> 8) & 0xff;
+ cmd.addr_0 = blkno & 0xff;
+ cmd.length2 = (nblks >> 8) & 0xff;
+ cmd.length1 = nblks & 0xff;
+
+ /*
+ * Call the routine that chats with the adapter.
+ * Note: we cannot sleep as we may be an interrupt
+ */
+ if (scsi_scsi_cmd(sc_link, (struct scsi_generic *)&cmd,
+ sizeof(cmd), (u_char *)bp->b_data, bp->b_bcount,
+ SDRETRIES, 10000, bp, SCSI_NOSLEEP |
+ ((bp->b_flags & B_READ) ? SCSI_DATA_IN : SCSI_DATA_OUT)))
+ printf("%s: not queued", sd->sc_dev.dv_xname);
+ }
+}
+
+int
+sdread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ struct sd_softc *sd = sdcd.cd_devs[SDUNIT(dev)];
+
+ return (physio(sdstrategy, NULL, dev, B_READ,
+ sd->sc_link->adapter->scsi_minphys, uio));
+}
+
+int
+sdwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ struct sd_softc *sd = sdcd.cd_devs[SDUNIT(dev)];
+
+ return (physio(sdstrategy, NULL, dev, B_WRITE,
+ sd->sc_link->adapter->scsi_minphys, uio));
+}
+
+/*
+ * Perform special action on behalf of the user
+ * Knows about the internals of this device
+ */
+int
+sdioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ struct sd_softc *sd = sdcd.cd_devs[SDUNIT(dev)];
+ int error;
+
+ SC_DEBUG(sd->sc_link, SDEV_DB2, ("sdioctl 0x%lx ", cmd));
+
+ /*
+ * If the device is not valid.. abandon ship
+ */
+ if ((sd->sc_link->flags & SDEV_MEDIA_LOADED) == 0)
+ return EIO;
+
+ switch (cmd) {
+ case DIOCGDINFO:
+ *(struct disklabel *)addr = sd->sc_dk.dk_label;
+ return 0;
+
+ case DIOCGPART:
+ ((struct partinfo *)addr)->disklab = &sd->sc_dk.dk_label;
+ ((struct partinfo *)addr)->part =
+ &sd->sc_dk.dk_label.d_partitions[SDPART(dev)];
+ return 0;
+
+ case DIOCWDINFO:
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+
+ if (error = sdlock(sd))
+ return error;
+ sd->flags |= SDF_LABELLING;
+
+ error = setdisklabel(&sd->sc_dk.dk_label,
+ (struct disklabel *)addr, /*sd->sc_dk.dk_openmask : */0,
+ &sd->sc_dk.dk_cpulabel);
+ if (error == 0) {
+ if (cmd == DIOCWDINFO)
+ error = writedisklabel(SDLABELDEV(dev),
+ sdstrategy, &sd->sc_dk.dk_label,
+ &sd->sc_dk.dk_cpulabel);
+ }
+
+ sd->flags &= ~SDF_LABELLING;
+ sdunlock(sd);
+ return error;
+
+ case DIOCWLABEL:
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+ if (*(int *)addr)
+ sd->flags |= SDF_WLABEL;
+ else
+ sd->flags &= ~SDF_WLABEL;
+ return 0;
+
+ default:
+ if (SDPART(dev) != RAW_PART)
+ return ENOTTY;
+ return scsi_do_ioctl(sd->sc_link, dev, cmd, addr, flag, p);
+ }
+
+#ifdef DIAGNOSTIC
+ panic("sdioctl: impossible");
+#endif
+}
+
+/*
+ * Load the label information on the named device
+ */
+void
+sdgetdisklabel(sd)
+ struct sd_softc *sd;
+{
+ struct disklabel *lp = &sd->sc_dk.dk_label;
+ char *errstring;
+
+ bzero(lp, sizeof(struct disklabel));
+ bzero(&sd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
+
+ lp->d_secsize = sd->params.blksize;
+ lp->d_ntracks = sd->params.heads;
+ lp->d_nsectors = sd->params.sectors;
+ lp->d_ncylinders = sd->params.cyls;
+ lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
+ if (lp->d_secpercyl == 0) {
+ lp->d_secpercyl = 100;
+ /* as long as it's not 0 - readdisklabel divides by it (?) */
+ }
+
+ strncpy(lp->d_typename, "SCSI disk", 16);
+ lp->d_type = DTYPE_SCSI;
+ strncpy(lp->d_packname, "fictitious", 16);
+ lp->d_secperunit = sd->params.disksize;
+ lp->d_rpm = 3600;
+ lp->d_interleave = 1;
+ lp->d_flags = 0;
+
+ lp->d_partitions[RAW_PART].p_offset = 0;
+ lp->d_partitions[RAW_PART].p_size =
+ lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
+ lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
+ lp->d_npartitions = RAW_PART + 1;
+
+ lp->d_magic = DISKMAGIC;
+ lp->d_magic2 = DISKMAGIC;
+ lp->d_checksum = dkcksum(lp);
+
+ /*
+ * Call the generic disklabel extraction routine
+ */
+ if (errstring = readdisklabel(MAKESDDEV(0, sd->sc_dev.dv_unit,
+ RAW_PART), sdstrategy, lp, &sd->sc_dk.dk_cpulabel)) {
+ printf("%s: %s\n", sd->sc_dev.dv_xname, errstring);
+ return;
+ }
+}
+
+/*
+ * Find out from the device what it's capacity is
+ */
+u_long
+sd_size(sd, flags)
+ struct sd_softc *sd;
+ int flags;
+{
+ struct scsi_read_cap_data rdcap;
+ struct scsi_read_capacity scsi_cmd;
+ u_long size;
+
+ /*
+ * make up a scsi command and ask the scsi driver to do
+ * it for you.
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = READ_CAPACITY;
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks
+ */
+ if (scsi_scsi_cmd(sd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)&rdcap, sizeof(rdcap), SDRETRIES,
+ 2000, NULL, flags | SCSI_DATA_IN) != 0)
+ return 0;
+
+ size = (rdcap.addr_3 << 24) + (rdcap.addr_2 << 16) +
+ (rdcap.addr_1 << 8) + rdcap.addr_0 + 1;
+
+ return size;
+}
+
+/*
+ * Tell the device to map out a defective block
+ */
+int
+sd_reassign_blocks(sd, block)
+ struct sd_softc *sd;
+ u_long block;
+{
+ struct scsi_reassign_blocks scsi_cmd;
+ struct scsi_reassign_blocks_data rbdata;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ bzero(&rbdata, sizeof(rbdata));
+ scsi_cmd.opcode = REASSIGN_BLOCKS;
+
+ rbdata.length_msb = 0;
+ rbdata.length_lsb = sizeof(rbdata.defect_descriptor[0]);
+ rbdata.defect_descriptor[0].dlbaddr_3 = (block >> 24) & 0xff;
+ rbdata.defect_descriptor[0].dlbaddr_2 = (block >> 16) & 0xff;
+ rbdata.defect_descriptor[0].dlbaddr_1 = (block >> 8) & 0xff;
+ rbdata.defect_descriptor[0].dlbaddr_0 = block & 0xff;
+
+ return scsi_scsi_cmd(sd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)&rbdata, sizeof(rbdata), SDRETRIES,
+ 5000, NULL, SCSI_DATA_OUT);
+}
+
+#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
+
+/*
+ * Get the scsi driver to send a full inquiry to the * device and use the
+ * results to fill out the disk parameter structure.
+ */
+int
+sd_get_parms(sd, flags)
+ struct sd_softc *sd;
+ int flags;
+{
+ struct disk_parms *dp = &sd->params;
+ struct scsi_mode_sense scsi_cmd;
+ struct scsi_mode_sense_data {
+ struct scsi_mode_header header;
+ struct scsi_blk_desc blk_desc;
+ union disk_pages pages;
+ } scsi_sense;
+ u_long sectors;
+
+ /*
+ * do a "mode sense page 4"
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = MODE_SENSE;
+ scsi_cmd.page = 4;
+ scsi_cmd.length = 0x20;
+ /*
+ * If the command worked, use the results to fill out
+ * the parameter structure
+ */
+ if (scsi_scsi_cmd(sd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)&scsi_sense, sizeof(scsi_sense),
+ SDRETRIES, 6000, NULL, flags | SCSI_DATA_IN) != 0) {
+ printf("%s: could not mode sense (4)", sd->sc_dev.dv_xname);
+ fake_it:
+ printf("; using fictitious geometry\n");
+ /*
+ * use adaptec standard fictitious geometry
+ * this depends on which controller (e.g. 1542C is
+ * different. but we have to put SOMETHING here..)
+ */
+ sectors = sd_size(sd, flags);
+ dp->heads = 64;
+ dp->sectors = 32;
+ dp->cyls = sectors / (64 * 32);
+ dp->blksize = 512;
+ dp->disksize = sectors;
+ } else {
+ SC_DEBUG(sd->sc_link, SDEV_DB3,
+ ("%d cyls, %d heads, %d precomp, %d red_write, %d land_zone\n",
+ _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2),
+ scsi_sense.pages.rigid_geometry.nheads,
+ b2tol(scsi_sense.pages.rigid_geometry.st_cyl_wp),
+ b2tol(scsi_sense.pages.rigid_geometry.st_cyl_rwc),
+ b2tol(scsi_sense.pages.rigid_geometry.land_zone)));
+
+ /*
+ * KLUDGE!! (for zone recorded disks)
+ * give a number of sectors so that sec * trks * cyls
+ * is <= disk_size
+ * can lead to wasted space! THINK ABOUT THIS !
+ */
+ dp->heads = scsi_sense.pages.rigid_geometry.nheads;
+ dp->cyls =
+ _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2);
+ dp->blksize = _3btol(scsi_sense.blk_desc.blklen);
+
+ if (dp->heads == 0 || dp->cyls == 0) {
+ printf("%s: mode sense (4) returned nonsense",
+ sd->sc_dev.dv_xname);
+ goto fake_it;
+ }
+
+ if (dp->blksize == 0)
+ dp->blksize = 512;
+
+ sectors = sd_size(sd, flags);
+ dp->disksize = sectors;
+ sectors /= (dp->heads * dp->cyls);
+ dp->sectors = sectors; /* XXX dubious on SCSI */
+ }
+
+ return 0;
+}
+
+int
+sdsize(dev)
+ dev_t dev;
+{
+ struct sd_softc *sd;
+ int part;
+ int size;
+
+ if (sdopen(dev, 0, S_IFBLK) != 0)
+ return -1;
+ sd = sdcd.cd_devs[SDUNIT(dev)];
+ part = SDPART(dev);
+ if (sd->sc_dk.dk_label.d_partitions[part].p_fstype != FS_SWAP)
+ size = -1;
+ else
+ size = sd->sc_dk.dk_label.d_partitions[part].p_size;
+ if (sdclose(dev, 0, S_IFBLK) != 0)
+ return -1;
+ return size;
+}
+
+#ifndef __BDEVSW_DUMP_OLD_TYPE
+/* #define SD_DUMP_NOT_TRUSTED if you just want to watch */
+static struct scsi_xfer sx;
+static int sddoingadump;
+
+/*
+ * dump all of physical memory into the partition specified, starting
+ * at offset 'dumplo' into the partition.
+ */
+int
+sddump(dev, blkno, va, size)
+ dev_t dev;
+ daddr_t blkno;
+ caddr_t va;
+ size_t size;
+{
+ struct sd_softc *sd; /* disk unit to do the I/O */
+ struct disklabel *lp; /* disk's disklabel */
+ int unit, part;
+ int sectorsize; /* size of a disk sector */
+ int nsects; /* number of sectors in partition */
+ int sectoff; /* sector offset of partition */
+ int totwrt; /* total number of sectors left to write */
+ int nwrt; /* current number of sectors to write */
+ struct scsi_rw_big cmd; /* write command */
+ struct scsi_xfer *xs; /* ... convenience */
+ int retval;
+
+ /* Check if recursive dump; if so, punt. */
+ if (sddoingadump)
+ return EFAULT;
+
+ /* Mark as active early. */
+ sddoingadump = 1;
+
+ unit = SDUNIT(dev); /* Decompose unit & partition. */
+ part = SDPART(dev);
+
+ /* Check for acceptable drive number. */
+ if (unit >= sdcd.cd_ndevs || (sd = sdcd.cd_devs[unit]) == NULL)
+ return ENXIO;
+
+ /* Make sure it was initialized. */
+ if (sd->sc_link->flags & SDEV_MEDIA_LOADED != SDEV_MEDIA_LOADED)
+ return ENXIO;
+
+ /* Convert to disk sectors. Request must be a multiple of size. */
+ lp = &sd->sc_dk.dk_label;
+ sectorsize = lp->d_secsize;
+ if ((size % sectorsize) != 0)
+ return EFAULT;
+ totwrt = size / sectorsize;
+ blkno = dbtob(blkno) / sectorsize; /* blkno in DEV_BSIZE units */
+
+ nsects = lp->d_partitions[part].p_size;
+ sectoff = lp->d_partitions[part].p_offset;
+
+ /* Check transfer bounds against partition size. */
+ if ((blkno < 0) || ((blkno + totwrt) > nsects))
+ return EINVAL;
+
+ /* Offset block number to start of partition. */
+ blkno += sectoff;
+
+ xs = &sx;
+
+ while (totwrt > 0) {
+ nwrt = totwrt; /* XXX */
+#ifndef SD_DUMP_NOT_TRUSTED
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = WRITE_BIG;
+ cmd.addr_3 = (blkno >> 24) & 0xff;
+ cmd.addr_2 = (blkno >> 16) & 0xff;
+ cmd.addr_1 = (blkno >> 8) & 0xff;
+ cmd.addr_0 = blkno & 0xff;
+ cmd.length2 = (nwrt >> 8) & 0xff;
+ cmd.length1 = nwrt & 0xff;
+ /*
+ * Fill out the scsi_xfer structure
+ * Note: we cannot sleep as we may be an interrupt
+ * don't use scsi_scsi_cmd() as it may want
+ * to wait for an xs.
+ */
+ bzero(xs, sizeof(sx));
+ xs->flags |= SCSI_AUTOCONF | INUSE | SCSI_DATA_OUT;
+ xs->sc_link = sd->sc_link;
+ xs->retries = SDRETRIES;
+ xs->timeout = 10000; /* 10000 millisecs for a disk ! */
+ xs->cmd = (struct scsi_generic *)&cmd;
+ xs->cmdlen = sizeof(cmd);
+ xs->resid = nwrt * sectorsize;
+ xs->error = XS_NOERROR;
+ xs->bp = 0;
+ xs->data = va;
+ xs->datalen = nwrt * sectorsize;
+
+ /*
+ * Pass all this info to the scsi driver.
+ */
+ retval = (*(sd->sc_link->adapter->scsi_cmd)) (xs);
+ if (retval != COMPLETE)
+ return ENXIO;
+#else /* SD_DUMP_NOT_TRUSTED */
+ /* Let's just talk about this first... */
+ printf("sd%d: dump addr 0x%x, blk %d\n", unit, va, blkno);
+ delay(500 * 1000); /* half a second */
+#endif /* SD_DUMP_NOT_TRUSTED */
+
+ /* update block count */
+ totwrt -= nwrt;
+ blkno += nwrt;
+ va += sectorsize * nwrt;
+ }
+ sddoingadump = 0;
+ return 0;
+}
+#else /* __BDEVSW_DUMP_NEW_TYPE */
+int
+sddump(dev, blkno, va, size)
+ dev_t dev;
+ daddr_t blkno;
+ caddr_t va;
+ size_t size;
+{
+
+ /* Not implemented. */
+ return ENXIO;
+}
+#endif /* __BDEVSW_DUMP_NEW_TYPE */
diff --git a/sys/scsi/st.c b/sys/scsi/st.c
new file mode 100644
index 00000000000..3bc47126faa
--- /dev/null
+++ b/sys/scsi/st.c
@@ -0,0 +1,1786 @@
+/* $NetBSD: st.c,v 1.54 1995/10/13 20:01:08 gwr Exp $ */
+
+/*
+ * Copyright (c) 1994 Charles Hannum. All rights reserved.
+ *
+ * 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 acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 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.
+ */
+
+/*
+ * Originally written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ * major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993
+ */
+
+/*
+ * To do:
+ * work out some better way of guessing what a good timeout is going
+ * to be depending on whether we expect to retension or not.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/mtio.h>
+#include <sys/device.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_tape.h>
+#include <scsi/scsiconf.h>
+
+/* Defines for device specific stuff */
+#define DEF_FIXED_BSIZE 512
+#define ST_RETRIES 4 /* only on non IO commands */
+
+#define STMODE(z) ( minor(z) & 0x03)
+#define STDSTY(z) ((minor(z) >> 2) & 0x03)
+#define STUNIT(z) ((minor(z) >> 4) )
+#define CTLMODE 3
+
+#define SCSI_2_MAX_DENSITY_CODE 0x17 /* maximum density code specified
+ * in SCSI II spec. */
+/*
+ * Define various devices that we know mis-behave in some way,
+ * and note how they are bad, so we can correct for them
+ */
+struct modes {
+ u_int quirks; /* same definitions as in quirkdata */
+ int blksize;
+ u_int8_t density;
+};
+
+struct quirkdata {
+ u_int quirks;
+#define ST_Q_FORCE_BLKSIZE 0x0001
+#define ST_Q_SENSE_HELP 0x0002 /* must do READ for good MODE SENSE */
+#define ST_Q_IGNORE_LOADS 0x0004
+#define ST_Q_BLKSIZE 0x0008 /* variable-block media_blksize > 0 */
+ u_int page_0_size;
+#define MAX_PAGE_0_SIZE 64
+ struct modes modes[4];
+};
+
+struct st_quirk_inquiry_pattern {
+ struct scsi_inquiry_pattern pattern;
+ struct quirkdata quirkdata;
+};
+
+struct st_quirk_inquiry_pattern st_quirk_patterns[] = {
+ {T_SEQUENTIAL, T_REMOV,
+ " ", " ", " ", 0, 0, {
+ ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */
+ ST_Q_FORCE_BLKSIZE, 512, QIC_24, /* minor 4-7 */
+ ST_Q_FORCE_BLKSIZE, 0, HALFINCH_1600, /* minor 8-11 */
+ ST_Q_FORCE_BLKSIZE, 0, HALFINCH_6250 /* minor 12-15 */
+ }},
+ {T_SEQUENTIAL, T_REMOV,
+ "TANDBERG", " TDC 3600 ", "", 0, 12, {
+ 0, 0, 0, /* minor 0-3 */
+ ST_Q_FORCE_BLKSIZE, 0, QIC_525, /* minor 4-7 */
+ 0, 0, QIC_150, /* minor 8-11 */
+ 0, 0, QIC_120 /* minor 12-15 */
+ }},
+ {T_SEQUENTIAL, T_REMOV,
+ "TANDBERG", " TDC 3800 ", "", 0, 0, {
+ ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */
+ 0, 0, QIC_525, /* minor 4-7 */
+ 0, 0, QIC_150, /* minor 8-11 */
+ 0, 0, QIC_120 /* minor 12-15 */
+ }},
+ /*
+ * At least -005 and -007 need this. I'll assume they all do unless I
+ * hear otherwise. - mycroft, 31MAR1994
+ */
+ {T_SEQUENTIAL, T_REMOV,
+ "ARCHIVE ", "VIPER 2525 25462", "", 0, 0, {
+ ST_Q_SENSE_HELP, 0, 0, /* minor 0-3 */
+ ST_Q_SENSE_HELP, 0, QIC_525, /* minor 4-7 */
+ 0, 0, QIC_150, /* minor 8-11 */
+ 0, 0, QIC_120 /* minor 12-15 */
+ }},
+ /*
+ * One user reports that this works for his tape drive. It probably
+ * needs more work. - mycroft, 09APR1994
+ */
+ {T_SEQUENTIAL, T_REMOV,
+ "SANKYO ", "CP525 ", "", 0, 0, {
+ ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */
+ ST_Q_FORCE_BLKSIZE, 512, QIC_525, /* minor 4-7 */
+ 0, 0, QIC_150, /* minor 8-11 */
+ 0, 0, QIC_120 /* minor 12-15 */
+ }},
+ {T_SEQUENTIAL, T_REMOV,
+ "ANRITSU ", "DMT780 ", "", 0, 0, {
+ ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */
+ ST_Q_FORCE_BLKSIZE, 512, QIC_525, /* minor 4-7 */
+ 0, 0, QIC_150, /* minor 8-11 */
+ 0, 0, QIC_120 /* minor 12-15 */
+ }},
+ {T_SEQUENTIAL, T_REMOV,
+ "ARCHIVE ", "VIPER 150 21247", "", 0, 12, {
+ 0, 0, 0, /* minor 0-3 */
+ 0, 0, QIC_150, /* minor 4-7 */
+ 0, 0, QIC_120, /* minor 8-11 */
+ 0, 0, QIC_24 /* minor 12-15 */
+ }},
+ {T_SEQUENTIAL, T_REMOV,
+ "WANGTEK ", "5099ES SCSI", "", 0, 0, {
+ ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */
+ 0, 0, QIC_11, /* minor 4-7 */
+ 0, 0, QIC_24, /* minor 8-11 */
+ 0, 0, QIC_24, /* minor 12-15 */
+ }},
+ {T_SEQUENTIAL, T_REMOV,
+ "WANGTEK ", "5150ES SCSI", "", 0, 0, {
+ ST_Q_FORCE_BLKSIZE, 512, 0, /* minor 0-3 */
+ 0, 0, QIC_24, /* minor 4-7 */
+ 0, 0, QIC_120, /* minor 8-11 */
+ 0, 0, QIC_150 /* minor 12-15 */
+ }},
+ {T_SEQUENTIAL, T_REMOV,
+ "WANGTEK ", "5525ES SCSI REV7", "", 0, 0, {
+ 0, 0, 0, /* minor 0-3 */
+ ST_Q_BLKSIZE, 0, QIC_525, /* minor 4-7 */
+ 0, 0, QIC_150, /* minor 8-11 */
+ 0, 0, QIC_120 /* minor 12-15 */
+ }},
+ {T_SEQUENTIAL, T_REMOV,
+ "WangDAT ", "Model 1300 ", "", 0, 0, {
+ 0, 0, 0, /* minor 0-3 */
+ ST_Q_FORCE_BLKSIZE, 512, DDS, /* minor 4-7 */
+ ST_Q_FORCE_BLKSIZE, 1024, DDS, /* minor 8-11 */
+ ST_Q_FORCE_BLKSIZE, 0, DDS /* minor 12-15 */
+ }},
+#if 0
+ {T_SEQUENTIAL, T_REMOV,
+ "EXABYTE ", "EXB-8200 ", "", 0, 12, {
+ 0, 0, 0, /* minor 0-3 */
+ 0, 0, 0, /* minor 4-7 */
+ 0, 0, 0, /* minor 8-11 */
+ 0, 0, 0 /* minor 12-15 */
+ }},
+#endif
+};
+
+#define NOEJECT 0
+#define EJECT 1
+
+struct st_softc {
+ struct device sc_dev;
+/*--------------------present operating parameters, flags etc.----------------*/
+ int flags; /* see below */
+ u_int quirks; /* quirks for the open mode */
+ int blksize; /* blksize we are using */
+ u_int8_t density; /* present density */
+ u_int page_0_size; /* size of page 0 data */
+ u_int last_dsty; /* last density opened */
+/*--------------------device/scsi parameters----------------------------------*/
+ struct scsi_link *sc_link; /* our link to the adpter etc. */
+/*--------------------parameters reported by the device ----------------------*/
+ int blkmin; /* min blk size */
+ int blkmax; /* max blk size */
+ struct quirkdata *quirkdata; /* if we have a rogue entry */
+/*--------------------parameters reported by the device for this media--------*/
+ u_long numblks; /* nominal blocks capacity */
+ int media_blksize; /* 0 if not ST_FIXEDBLOCKS */
+ u_int8_t media_density; /* this is what it said when asked */
+/*--------------------quirks for the whole drive------------------------------*/
+ u_int drive_quirks; /* quirks of this drive */
+/*--------------------How we should set up when opening each minor device----*/
+ struct modes modes[4]; /* plus more for each mode */
+ u_int8_t modeflags[4]; /* flags for the modes */
+#define DENSITY_SET_BY_USER 0x01
+#define DENSITY_SET_BY_QUIRK 0x02
+#define BLKSIZE_SET_BY_USER 0x04
+#define BLKSIZE_SET_BY_QUIRK 0x08
+/*--------------------storage for sense data returned by the drive------------*/
+ u_char sense_data[MAX_PAGE_0_SIZE]; /*
+ * additional sense data needed
+ * for mode sense/select.
+ */
+ struct buf buf_queue; /* the queue of pending IO operations */
+};
+
+int stmatch __P((struct device *, void *, void *));
+void stattach __P((struct device *, struct device *, void *));
+
+struct cfdriver stcd = {
+ NULL, "st", stmatch, stattach, DV_TAPE, sizeof(struct st_softc)
+};
+
+int st_space __P((struct st_softc *, int number, u_int what, int flags));
+int st_rewind __P((struct st_softc *, u_int immediate, int flags));
+int st_mode_sense __P((struct st_softc *, int flags));
+int st_decide_mode __P((struct st_softc *, boolean first_read));
+int st_read_block_limits __P((struct st_softc *, int flags));
+int st_touch_tape __P((struct st_softc *));
+int st_write_filemarks __P((struct st_softc *, int number, int flags));
+int st_load __P((struct st_softc *, u_int type, int flags));
+int st_mode_select __P((struct st_softc *, int flags));
+void ststrategy();
+int st_check_eod();
+void ststart();
+void st_unmount();
+int st_mount_tape();
+void st_loadquirks();
+void st_identify_drive();
+int st_interpret_sense();
+
+struct scsi_device st_switch = {
+ st_interpret_sense,
+ ststart,
+ NULL,
+ NULL,
+};
+
+#define ST_INFO_VALID 0x0001
+#define ST_BLOCK_SET 0x0002 /* block size, mode set by ioctl */
+#define ST_WRITTEN 0x0004 /* data have been written, EOD needed */
+#define ST_FIXEDBLOCKS 0x0008
+#define ST_AT_FILEMARK 0x0010
+#define ST_EIO_PENDING 0x0020 /* we couldn't report it then (had data) */
+#define ST_NEW_MOUNT 0x0040 /* still need to decide mode */
+#define ST_READONLY 0x0080 /* st_mode_sense says write protected */
+#define ST_FM_WRITTEN 0x0100 /*
+ * EOF file mark written -- used with
+ * ~ST_WRITTEN to indicate that multiple file
+ * marks have been written
+ */
+#define ST_BLANK_READ 0x0200 /* BLANK CHECK encountered already */
+#define ST_2FM_AT_EOD 0x0400 /* write 2 file marks at EOD */
+#define ST_MOUNTED 0x0800 /* Device is presently mounted */
+
+#define ST_PER_ACTION (ST_AT_FILEMARK | ST_EIO_PENDING | ST_BLANK_READ)
+#define ST_PER_MOUNT (ST_INFO_VALID | ST_BLOCK_SET | ST_WRITTEN | \
+ ST_FIXEDBLOCKS | ST_READONLY | ST_FM_WRITTEN | \
+ ST_2FM_AT_EOD | ST_PER_ACTION)
+
+struct scsi_inquiry_pattern st_patterns[] = {
+ {T_SEQUENTIAL, T_REMOV,
+ "", "", ""},
+};
+
+int
+stmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct cfdata *cf = match;
+ struct scsibus_attach_args *sa = aux;
+ int priority;
+
+ (void)scsi_inqmatch(sa->sa_inqbuf,
+ (caddr_t)st_patterns, sizeof(st_patterns)/sizeof(st_patterns[0]),
+ sizeof(st_patterns[0]), &priority);
+ return (priority);
+}
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * A device suitable for this driver
+ */
+void
+stattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct st_softc *st = (void *)self;
+ struct scsibus_attach_args *sa = aux;
+ struct scsi_link *sc_link = sa->sa_sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("stattach: "));
+
+ /*
+ * Store information needed to contact our base driver
+ */
+ st->sc_link = sc_link;
+ sc_link->device = &st_switch;
+ sc_link->device_softc = st;
+ sc_link->openings = 1;
+
+ /*
+ * Check if the drive is a known criminal and take
+ * Any steps needed to bring it into line
+ */
+ st_identify_drive(st, sa->sa_inqbuf);
+
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ printf(": %s", st->quirkdata ? "rogue, " : "");
+ if (scsi_test_unit_ready(sc_link,
+ SCSI_AUTOCONF | SCSI_SILENT | SCSI_IGNORE_MEDIA_CHANGE) ||
+ st_mode_sense(st,
+ SCSI_AUTOCONF | SCSI_SILENT | SCSI_IGNORE_MEDIA_CHANGE))
+ printf("drive empty\n");
+ else {
+ printf("density code 0x%x, ", st->media_density);
+ if (st->media_blksize > 0)
+ printf("%d-byte", st->media_blksize);
+ else
+ printf("variable");
+ printf(" blocks, write-%s\n",
+ (st->flags & ST_READONLY) ? "protected" : "enabled");
+ }
+
+ /*
+ * Set up the buf queue for this device
+ */
+ st->buf_queue.b_active = 0;
+ st->buf_queue.b_actf = 0;
+ st->buf_queue.b_actb = &st->buf_queue.b_actf;
+}
+
+/*
+ * Use the inquiry routine in 'scsi_base' to get drive info so we can
+ * Further tailor our behaviour.
+ */
+void
+st_identify_drive(st, inqbuf)
+ struct st_softc *st;
+ struct scsi_inquiry_data *inqbuf;
+{
+ struct st_quirk_inquiry_pattern *finger;
+ int priority;
+
+ finger = (struct st_quirk_inquiry_pattern *)scsi_inqmatch(inqbuf,
+ (caddr_t)st_quirk_patterns,
+ sizeof(st_quirk_patterns)/sizeof(st_quirk_patterns[0]),
+ sizeof(st_quirk_patterns[0]), &priority);
+ if (priority != 0) {
+ st->quirkdata = &finger->quirkdata;
+ st->drive_quirks = finger->quirkdata.quirks;
+ st->quirks = finger->quirkdata.quirks; /* start value */
+ st->page_0_size = finger->quirkdata.page_0_size;
+ st_loadquirks(st);
+ }
+}
+
+/*
+ * initialise the subdevices to the default (QUIRK) state.
+ * this will remove any setting made by the system operator or previous
+ * operations.
+ */
+void
+st_loadquirks(st)
+ struct st_softc *st;
+{
+ int i;
+ struct modes *mode;
+ struct modes *mode2;
+
+ mode = st->quirkdata->modes;
+ mode2 = st->modes;
+ for (i = 0; i < 4; i++) {
+ bzero(mode2, sizeof(struct modes));
+ st->modeflags[i] &= ~(BLKSIZE_SET_BY_QUIRK |
+ DENSITY_SET_BY_QUIRK | BLKSIZE_SET_BY_USER |
+ DENSITY_SET_BY_USER);
+ if ((mode->quirks | st->drive_quirks) & ST_Q_FORCE_BLKSIZE) {
+ mode2->blksize = mode->blksize;
+ st->modeflags[i] |= BLKSIZE_SET_BY_QUIRK;
+ }
+ if (mode->density) {
+ mode2->density = mode->density;
+ st->modeflags[i] |= DENSITY_SET_BY_QUIRK;
+ }
+ mode++;
+ mode2++;
+ }
+}
+
+/*
+ * open the device.
+ */
+int
+stopen(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ int unit;
+ u_int mode, dsty;
+ int error = 0;
+ struct st_softc *st;
+ struct scsi_link *sc_link;
+
+ unit = STUNIT(dev);
+ if (unit >= stcd.cd_ndevs)
+ return ENXIO;
+ st = stcd.cd_devs[unit];
+ if (!st)
+ return ENXIO;
+
+ mode = STMODE(dev);
+ dsty = STDSTY(dev);
+ sc_link = st->sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("open: dev=0x%x (unit %d (of %d))\n", dev,
+ unit, stcd.cd_ndevs));
+
+ /*
+ * Only allow one at a time
+ */
+ if (sc_link->flags & SDEV_OPEN) {
+ printf("%s: already open\n", st->sc_dev.dv_xname);
+ return EBUSY;
+ }
+
+ /*
+ * Catch any unit attention errors.
+ */
+ if (error = scsi_test_unit_ready(sc_link,
+ SCSI_IGNORE_MEDIA_CHANGE | (mode == CTLMODE ? SCSI_IGNORE_NOT_READY : 0)))
+ goto bad;
+
+ sc_link->flags |= SDEV_OPEN; /* unit attn are now errors */
+
+ /*
+ * If the mode is 3 (e.g. minor = 3,7,11,15)
+ * then the device has been opened to set defaults
+ * This mode does NOT ALLOW I/O, only ioctls
+ */
+ if (mode == CTLMODE)
+ return 0;
+
+ /*
+ * if it's a different mode, or if the media has been
+ * invalidated, unmount the tape from the previous
+ * session but continue with open processing
+ */
+ if (st->last_dsty != dsty || !(sc_link->flags & SDEV_MEDIA_LOADED))
+ st_unmount(st, NOEJECT);
+
+ /*
+ * If we are not mounted, then we should start a new
+ * mount session.
+ */
+ if (!(st->flags & ST_MOUNTED)) {
+ st_mount_tape(dev, flags);
+ st->last_dsty = dsty;
+ }
+
+ /*
+ * Make sure that a tape opened in write-only mode will have
+ * file marks written on it when closed, even if not written to.
+ * This is for SUN compatibility
+ */
+ if ((flags & O_ACCMODE) == FWRITE)
+ st->flags |= ST_WRITTEN;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("open complete\n"));
+ return 0;
+
+bad:
+ st_unmount(st, NOEJECT);
+ sc_link->flags &= ~SDEV_OPEN;
+ return error;
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+int
+stclose(dev)
+ dev_t dev;
+{
+ struct st_softc *st = stcd.cd_devs[STUNIT(dev)];
+
+ SC_DEBUG(st->sc_link, SDEV_DB1, ("closing\n"));
+ if ((st->flags & (ST_WRITTEN | ST_FM_WRITTEN)) == ST_WRITTEN)
+ st_write_filemarks(st, 1, 0);
+ switch (STMODE(dev)) {
+ case 0:
+ case 3: /* for now */
+ st_unmount(st, NOEJECT);
+ break;
+ case 1:
+ /* leave mounted unless media seems to have been removed */
+ if (!(st->sc_link->flags & SDEV_MEDIA_LOADED))
+ st_unmount(st, NOEJECT);
+ break;
+ case 2:
+ st_unmount(st, EJECT);
+ break;
+ }
+ st->sc_link->flags &= ~SDEV_OPEN;
+
+ return 0;
+}
+
+/*
+ * Start a new mount session.
+ * Copy in all the default parameters from the selected device mode.
+ * and try guess any that seem to be defaulted.
+ */
+int
+st_mount_tape(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ int unit;
+ u_int mode, dsty;
+ struct st_softc *st;
+ struct scsi_link *sc_link;
+ int error = 0;
+
+ unit = STUNIT(dev);
+ mode = STMODE(dev);
+ dsty = STDSTY(dev);
+ st = stcd.cd_devs[unit];
+ sc_link = st->sc_link;
+
+ if (st->flags & ST_MOUNTED)
+ return 0;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("mounting\n "));
+ st->flags |= ST_NEW_MOUNT;
+ st->quirks = st->drive_quirks | st->modes[dsty].quirks;
+ /*
+ * If the media is new, then make sure we give it a chance to
+ * to do a 'load' instruction. (We assume it is new.)
+ */
+ if (error = st_load(st, LD_LOAD, 0))
+ return error;
+ /*
+ * Throw another dummy instruction to catch
+ * 'Unit attention' errors. Some drives appear to give
+ * these after doing a Load instruction.
+ * (noteably some DAT drives)
+ */
+ scsi_test_unit_ready(sc_link, SCSI_SILENT); /* XXX */
+
+ /*
+ * Some devices can't tell you much until they have been
+ * asked to look at the media. This quirk does this.
+ */
+ if (st->quirks & ST_Q_SENSE_HELP)
+ if (error = st_touch_tape(st))
+ return error;
+ /*
+ * Load the physical device parameters
+ * loads: blkmin, blkmax
+ */
+ if (error = st_read_block_limits(st, 0))
+ return error;
+ /*
+ * Load the media dependent parameters
+ * includes: media_blksize,media_density,numblks
+ * As we have a tape in, it should be reflected here.
+ * If not you may need the "quirk" above.
+ */
+ if (error = st_mode_sense(st, 0))
+ return error;
+ /*
+ * If we have gained a permanent density from somewhere,
+ * then use it in preference to the one supplied by
+ * default by the driver.
+ */
+ if (st->modeflags[dsty] & (DENSITY_SET_BY_QUIRK | DENSITY_SET_BY_USER))
+ st->density = st->modes[dsty].density;
+ else
+ st->density = st->media_density;
+ /*
+ * If we have gained a permanent blocksize
+ * then use it in preference to the one supplied by
+ * default by the driver.
+ */
+ st->flags &= ~ST_FIXEDBLOCKS;
+ if (st->modeflags[dsty] & (BLKSIZE_SET_BY_QUIRK | BLKSIZE_SET_BY_USER)) {
+ st->blksize = st->modes[dsty].blksize;
+ if (st->blksize)
+ st->flags |= ST_FIXEDBLOCKS;
+ } else {
+ if (error = st_decide_mode(st, FALSE))
+ return error;
+ }
+ if (error = st_mode_select(st, 0)) {
+ printf("%s: cannot set selected mode\n", st->sc_dev.dv_xname);
+ return error;
+ }
+ scsi_prevent(sc_link, PR_PREVENT,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY);
+ st->flags &= ~ST_NEW_MOUNT;
+ st->flags |= ST_MOUNTED;
+ sc_link->flags |= SDEV_MEDIA_LOADED; /* move earlier? */
+
+ return 0;
+}
+
+/*
+ * End the present mount session.
+ * Rewind, and optionally eject the tape.
+ * Reset various flags to indicate that all new
+ * operations require another mount operation
+ */
+void
+st_unmount(st, eject)
+ struct st_softc *st;
+ boolean eject;
+{
+ struct scsi_link *sc_link = st->sc_link;
+ int nmarks;
+
+ if (!(st->flags & ST_MOUNTED))
+ return;
+ SC_DEBUG(sc_link, SDEV_DB1, ("unmounting\n"));
+ st_check_eod(st, FALSE, &nmarks, SCSI_IGNORE_NOT_READY);
+ st_rewind(st, 0, SCSI_IGNORE_NOT_READY);
+ scsi_prevent(sc_link, PR_ALLOW,
+ SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY);
+ if (eject)
+ st_load(st, LD_UNLOAD, SCSI_IGNORE_NOT_READY);
+ st->flags &= ~(ST_MOUNTED | ST_NEW_MOUNT);
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+}
+
+/*
+ * Given all we know about the device, media, mode, 'quirks' and
+ * initial operation, make a decision as to how we should be set
+ * to run (regarding blocking and EOD marks)
+ */
+int
+st_decide_mode(st, first_read)
+ struct st_softc *st;
+ boolean first_read;
+{
+#ifdef SCSIDEBUG
+ struct scsi_link *sc_link = st->sc_link;
+#endif
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("starting block mode decision\n"));
+
+ /*
+ * If the drive can only handle fixed-length blocks and only at
+ * one size, perhaps we should just do that.
+ */
+ if (st->blkmin && (st->blkmin == st->blkmax)) {
+ st->flags |= ST_FIXEDBLOCKS;
+ st->blksize = st->blkmin;
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("blkmin == blkmax of %d\n", st->blkmin));
+ goto done;
+ }
+ /*
+ * If the tape density mandates (or even suggests) use of fixed
+ * or variable-length blocks, comply.
+ */
+ switch (st->density) {
+ case HALFINCH_800:
+ case HALFINCH_1600:
+ case HALFINCH_6250:
+ case DDS:
+ st->flags &= ~ST_FIXEDBLOCKS;
+ st->blksize = 0;
+ SC_DEBUG(sc_link, SDEV_DB3, ("density specified variable\n"));
+ goto done;
+ case QIC_11:
+ case QIC_24:
+ case QIC_120:
+ case QIC_150:
+ case QIC_525:
+ case QIC_1320:
+ st->flags |= ST_FIXEDBLOCKS;
+ if (st->media_blksize > 0)
+ st->blksize = st->media_blksize;
+ else
+ st->blksize = DEF_FIXED_BSIZE;
+ SC_DEBUG(sc_link, SDEV_DB3, ("density specified fixed\n"));
+ goto done;
+ }
+ /*
+ * If we're about to read the tape, perhaps we should choose
+ * fixed or variable-length blocks and block size according to
+ * what the drive found on the tape.
+ */
+ if (first_read &&
+ (!(st->quirks & ST_Q_BLKSIZE) || (st->media_blksize == 0) ||
+ (st->media_blksize == DEF_FIXED_BSIZE) ||
+ (st->media_blksize == 1024))) {
+ if (st->media_blksize > 0)
+ st->flags |= ST_FIXEDBLOCKS;
+ else
+ st->flags &= ~ST_FIXEDBLOCKS;
+ st->blksize = st->media_blksize;
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("Used media_blksize of %d\n", st->media_blksize));
+ goto done;
+ }
+ /*
+ * We're getting no hints from any direction. Choose variable-
+ * length blocks arbitrarily.
+ */
+ st->flags &= ~ST_FIXEDBLOCKS;
+ st->blksize = 0;
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("Give up and default to variable mode\n"));
+
+done:
+ /*
+ * Decide whether or not to write two file marks to signify end-
+ * of-data. Make the decision as a function of density. If
+ * the decision is not to use a second file mark, the SCSI BLANK
+ * CHECK condition code will be recognized as end-of-data when
+ * first read.
+ * (I think this should be a by-product of fixed/variable..julian)
+ */
+ switch (st->density) {
+/* case 8 mm: What is the SCSI density code for 8 mm, anyway? */
+ case QIC_11:
+ case QIC_24:
+ case QIC_120:
+ case QIC_150:
+ case QIC_525:
+ case QIC_1320:
+ st->flags &= ~ST_2FM_AT_EOD;
+ break;
+ default:
+ st->flags |= ST_2FM_AT_EOD;
+ }
+ return 0;
+}
+
+/*
+ * Actually translate the requested transfer into
+ * one the physical driver can understand
+ * The transfer is described by a buf and will include
+ * only one physical transfer.
+ */
+void
+ststrategy(bp)
+ struct buf *bp;
+{
+ struct st_softc *st = stcd.cd_devs[STUNIT(bp->b_dev)];
+ struct buf *dp;
+ int s;
+
+ SC_DEBUG(st->sc_link, SDEV_DB1,
+ ("ststrategy %d bytes @ blk %d\n", bp->b_bcount, bp->b_blkno));
+ /*
+ * If it's a null transfer, return immediatly
+ */
+ if (bp->b_bcount == 0)
+ goto done;
+ /*
+ * Odd sized request on fixed drives are verboten
+ */
+ if (st->flags & ST_FIXEDBLOCKS) {
+ if (bp->b_bcount % st->blksize) {
+ printf("%s: bad request, must be multiple of %d\n",
+ st->sc_dev.dv_xname, st->blksize);
+ bp->b_error = EIO;
+ goto bad;
+ }
+ }
+ /*
+ * as are out-of-range requests on variable drives.
+ */
+ else if (bp->b_bcount < st->blkmin ||
+ (st->blkmax && bp->b_bcount > st->blkmax)) {
+ printf("%s: bad request, must be between %d and %d\n",
+ st->sc_dev.dv_xname, st->blkmin, st->blkmax);
+ bp->b_error = EIO;
+ goto bad;
+ }
+ s = splbio();
+
+ /*
+ * Place it in the queue of activities for this tape
+ * at the end (a bit silly because we only have on user..
+ * (but it could fork()))
+ */
+ dp = &st->buf_queue;
+ bp->b_actf = NULL;
+ bp->b_actb = dp->b_actb;
+ *dp->b_actb = bp;
+ dp->b_actb = &bp->b_actf;
+
+ /*
+ * Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion
+ * (All a bit silly if we're only allowing 1 open but..)
+ */
+ ststart(st);
+
+ splx(s);
+ return;
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ /*
+ * Correctly set the buf to indicate a completed xfer
+ */
+ iodone(bp);
+ return;
+}
+
+/*
+ * ststart looks to see if there is a buf waiting for the device
+ * and that the device is not already busy. If both are true,
+ * It dequeues the buf and creates a scsi command to perform the
+ * transfer required. The transfer request will call scsi_done
+ * on completion, which will in turn call this routine again
+ * so that the next queued transfer is performed.
+ * The bufs are queued by the strategy routine (ststrategy)
+ *
+ * This routine is also called after other non-queued requests
+ * have been made of the scsi driver, to ensure that the queue
+ * continues to be drained.
+ * ststart() is called at splbio
+ */
+void
+ststart(st)
+ struct st_softc *st;
+{
+ struct scsi_link *sc_link = st->sc_link;
+ register struct buf *bp, *dp;
+ struct scsi_rw_tape cmd;
+ int flags;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("ststart "));
+ /*
+ * See if there is a buf to do and we are not already
+ * doing one
+ */
+ while (sc_link->openings > 0) {
+ /* if a special awaits, let it proceed first */
+ if (sc_link->flags & SDEV_WAITING) {
+ sc_link->flags &= ~SDEV_WAITING;
+ wakeup((caddr_t)sc_link);
+ return;
+ }
+
+ bp = st->buf_queue.b_actf;
+ if (!bp)
+ return; /* no work to bother with */
+ if (dp = bp->b_actf)
+ dp->b_actb = bp->b_actb;
+ else
+ st->buf_queue.b_actb = bp->b_actb;
+ *bp->b_actb = dp;
+
+ /*
+ * if the device has been unmounted byt the user
+ * then throw away all requests until done
+ */
+ if (!(st->flags & ST_MOUNTED) ||
+ !(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ /* make sure that one implies the other.. */
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ biodone(bp);
+ continue;
+ }
+ /*
+ * only FIXEDBLOCK devices have pending operations
+ */
+ if (st->flags & ST_FIXEDBLOCKS) {
+ /*
+ * If we are at a filemark but have not reported it yet
+ * then we should report it now
+ */
+ if (st->flags & ST_AT_FILEMARK) {
+ if ((bp->b_flags & B_READ) == B_WRITE) {
+ /*
+ * Handling of ST_AT_FILEMARK in
+ * st_space will fill in the right file
+ * mark count.
+ * Back up over filemark
+ */
+ if (st_space(st, 0, SP_FILEMARKS, 0)) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ biodone(bp);
+ continue;
+ }
+ } else {
+ bp->b_resid = bp->b_bcount;
+ bp->b_error = 0;
+ bp->b_flags &= ~B_ERROR;
+ st->flags &= ~ST_AT_FILEMARK;
+ biodone(bp);
+ continue; /* seek more work */
+ }
+ }
+ /*
+ * If we are at EIO (e.g. EOM) but have not reported it
+ * yet then we should report it now
+ */
+ if (st->flags & ST_EIO_PENDING) {
+ bp->b_resid = bp->b_bcount;
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ st->flags &= ~ST_EIO_PENDING;
+ biodone(bp);
+ continue; /* seek more work */
+ }
+ }
+
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ if ((bp->b_flags & B_READ) == B_WRITE) {
+ cmd.opcode = WRITE;
+ st->flags &= ~ST_FM_WRITTEN;
+ st->flags |= ST_WRITTEN;
+ flags = SCSI_DATA_OUT;
+ } else {
+ cmd.opcode = READ;
+ flags = SCSI_DATA_IN;
+ }
+
+ /*
+ * Handle "fixed-block-mode" tape drives by using the
+ * block count instead of the length.
+ */
+ if (st->flags & ST_FIXEDBLOCKS) {
+ cmd.byte2 |= SRW_FIXED;
+ lto3b(bp->b_bcount / st->blksize, cmd.len);
+ } else
+ lto3b(bp->b_bcount, cmd.len);
+
+ /*
+ * go ask the adapter to do all this for us
+ */
+ if (scsi_scsi_cmd(sc_link, (struct scsi_generic *) &cmd,
+ sizeof(cmd), (u_char *) bp->b_data, bp->b_bcount, 0,
+ 100000, bp, flags | SCSI_NOSLEEP))
+ printf("%s: not queued\n", st->sc_dev.dv_xname);
+ } /* go back and see if we can cram more work in.. */
+}
+
+int
+stread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ struct st_softc *st = stcd.cd_devs[STUNIT(dev)];
+
+ return (physio(ststrategy, NULL, dev, B_READ,
+ st->sc_link->adapter->scsi_minphys, uio));
+}
+
+int
+stwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ struct st_softc *st = stcd.cd_devs[STUNIT(dev)];
+
+ return (physio(ststrategy, NULL, dev, B_WRITE,
+ st->sc_link->adapter->scsi_minphys, uio));
+}
+
+/*
+ * Perform special action on behalf of the user;
+ * knows about the internals of this device
+ */
+int
+stioctl(dev, cmd, arg, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t arg;
+ int flag;
+ struct proc *p;
+{
+ int error = 0;
+ int unit;
+ int number, nmarks, dsty;
+ int flags;
+ struct st_softc *st;
+ int hold_blksize;
+ u_int8_t hold_density;
+ struct mtop *mt = (struct mtop *) arg;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ flags = 0; /* give error messages, act on errors etc. */
+ unit = STUNIT(dev);
+ dsty = STDSTY(dev);
+ st = stcd.cd_devs[unit];
+ hold_blksize = st->blksize;
+ hold_density = st->density;
+
+ switch (cmd) {
+
+ case MTIOCGET: {
+ struct mtget *g = (struct mtget *) arg;
+
+ SC_DEBUG(st->sc_link, SDEV_DB1, ("[ioctl: get status]\n"));
+ bzero(g, sizeof(struct mtget));
+ g->mt_type = 0x7; /* Ultrix compat *//*? */
+ g->mt_blksiz = st->blksize;
+ g->mt_density = st->density;
+ g->mt_mblksiz[0] = st->modes[0].blksize;
+ g->mt_mblksiz[1] = st->modes[1].blksize;
+ g->mt_mblksiz[2] = st->modes[2].blksize;
+ g->mt_mblksiz[3] = st->modes[3].blksize;
+ g->mt_mdensity[0] = st->modes[0].density;
+ g->mt_mdensity[1] = st->modes[1].density;
+ g->mt_mdensity[2] = st->modes[2].density;
+ g->mt_mdensity[3] = st->modes[3].density;
+ break;
+ }
+ case MTIOCTOP: {
+
+ SC_DEBUG(st->sc_link, SDEV_DB1,
+ ("[ioctl: op=0x%x count=0x%x]\n", mt->mt_op, mt->mt_count));
+
+ /* compat: in U*x it is a short */
+ number = mt->mt_count;
+ switch ((short) (mt->mt_op)) {
+ case MTWEOF: /* write an end-of-file record */
+ error = st_write_filemarks(st, number, flags);
+ break;
+ case MTBSF: /* backward space file */
+ number = -number;
+ case MTFSF: /* forward space file */
+ error = st_check_eod(st, FALSE, &nmarks, flags);
+ if (!error)
+ error = st_space(st, number - nmarks,
+ SP_FILEMARKS, flags);
+ break;
+ case MTBSR: /* backward space record */
+ number = -number;
+ case MTFSR: /* forward space record */
+ error = st_check_eod(st, TRUE, &nmarks, flags);
+ if (!error)
+ error = st_space(st, number, SP_BLKS, flags);
+ break;
+ case MTREW: /* rewind */
+ error = st_rewind(st, 0, flags);
+ break;
+ case MTOFFL: /* rewind and put the drive offline */
+ st_unmount(st, EJECT);
+ break;
+ case MTNOP: /* no operation, sets status only */
+ break;
+ case MTRETEN: /* retension the tape */
+ error = st_load(st, LD_RETENSION, flags);
+ if (!error)
+ error = st_load(st, LD_LOAD, flags);
+ break;
+ case MTEOM: /* forward space to end of media */
+ error = st_check_eod(st, FALSE, &nmarks, flags);
+ if (!error)
+ error = st_space(st, 1, SP_EOM, flags);
+ break;
+ case MTCACHE: /* enable controller cache */
+ case MTNOCACHE: /* disable controller cache */
+ break;
+ case MTSETBSIZ: /* Set block size for device */
+#ifdef NOTYET
+ if (!(st->flags & ST_NEW_MOUNT)) {
+ uprintf("re-mount tape before changing blocksize");
+ error = EINVAL;
+ break;
+ }
+#endif
+ if (number == 0) {
+ st->flags &= ~ST_FIXEDBLOCKS;
+ } else {
+ if ((st->blkmin || st->blkmax) &&
+ (number < st->blkmin ||
+ number > st->blkmax)) {
+ error = EINVAL;
+ break;
+ }
+ st->flags |= ST_FIXEDBLOCKS;
+ }
+ st->blksize = number;
+ st->flags |= ST_BLOCK_SET; /*XXX */
+ goto try_new_value;
+
+ case MTSETDNSTY: /* Set density for device and mode */
+ if (number > SCSI_2_MAX_DENSITY_CODE)
+ error = EINVAL;
+ else
+ st->density = number;
+ goto try_new_value;
+
+ default:
+ error = EINVAL;
+ }
+ break;
+ }
+ case MTIOCIEOT:
+ case MTIOCEEOT:
+ break;
+ default:
+ if (STMODE(dev) == CTLMODE)
+ error = scsi_do_ioctl(st->sc_link, dev, cmd, arg, flag, p);
+ else
+ error = ENOTTY;
+ break;
+ }
+ return error;
+/*-----------------------------*/
+try_new_value:
+ /*
+ * Check that the mode being asked for is aggreeable to the
+ * drive. If not, put it back the way it was.
+ */
+ if (error = st_mode_select(st, 0)) { /* put it back as it was */
+ printf("%s: cannot set selected mode\n", st->sc_dev.dv_xname);
+ st->density = hold_density;
+ st->blksize = hold_blksize;
+ if (st->blksize)
+ st->flags |= ST_FIXEDBLOCKS;
+ else
+ st->flags &= ~ST_FIXEDBLOCKS;
+ return error;
+ }
+ /*
+ * As the drive liked it, if we are setting a new default,
+ * set it into the structures as such.
+ *
+ * The means for deciding this are not finalised yet
+ */
+ if (STMODE(dev) == 0x03) {
+ /* special mode */
+ /* XXX */
+ switch ((short) (mt->mt_op)) {
+ case MTSETBSIZ:
+ st->modes[dsty].blksize = st->blksize;
+ st->modeflags[dsty] |= BLKSIZE_SET_BY_USER;
+ break;
+ case MTSETDNSTY:
+ st->modes[dsty].density = st->density;
+ st->modeflags[dsty] |= DENSITY_SET_BY_USER;
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Do a synchronous read.
+ */
+int
+st_read(st, buf, size, flags)
+ struct st_softc *st;
+ int size;
+ int flags;
+ char *buf;
+{
+ struct scsi_rw_tape cmd;
+
+ /*
+ * If it's a null transfer, return immediatly
+ */
+ if (size == 0)
+ return 0;
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = READ;
+ if (st->flags & ST_FIXEDBLOCKS) {
+ cmd.byte2 |= SRW_FIXED;
+ lto3b(size / (st->blksize ? st->blksize : DEF_FIXED_BSIZE),
+ cmd.len);
+ } else
+ lto3b(size, cmd.len);
+ return scsi_scsi_cmd(st->sc_link, (struct scsi_generic *) &cmd,
+ sizeof(cmd), (u_char *) buf, size, 0, 100000, NULL,
+ flags | SCSI_DATA_IN);
+}
+
+#ifdef __STDC__
+#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0)
+#else
+#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0)
+#endif
+
+/*
+ * Ask the drive what it's min and max blk sizes are.
+ */
+int
+st_read_block_limits(st, flags)
+ struct st_softc *st;
+ int flags;
+{
+ struct scsi_block_limits cmd;
+ struct scsi_block_limits_data block_limits;
+ struct scsi_link *sc_link = st->sc_link;
+ int error;
+
+ /*
+ * First check if we have it all loaded
+ */
+ if ((sc_link->flags & SDEV_MEDIA_LOADED))
+ return 0;
+
+ /*
+ * do a 'Read Block Limits'
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = READ_BLOCK_LIMITS;
+
+ /*
+ * do the command, update the global values
+ */
+ if (error = scsi_scsi_cmd(sc_link, (struct scsi_generic *) &cmd,
+ sizeof(cmd), (u_char *) &block_limits, sizeof(block_limits),
+ ST_RETRIES, 5000, NULL, flags | SCSI_DATA_IN))
+ return error;
+
+ st->blkmin = b2tol(block_limits.min_length);
+ st->blkmax = _3btol(&block_limits.max_length_2);
+
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("(%d <= blksize <= %d)\n", st->blkmin, st->blkmax));
+ return 0;
+}
+
+/*
+ * Get the scsi driver to send a full inquiry to the
+ * device and use the results to fill out the global
+ * parameter structure.
+ *
+ * called from:
+ * attach
+ * open
+ * ioctl (to reset original blksize)
+ */
+int
+st_mode_sense(st, flags)
+ struct st_softc *st;
+ int flags;
+{
+ u_int scsi_sense_len;
+ int error;
+ struct scsi_mode_sense cmd;
+ struct scsi_sense {
+ struct scsi_mode_header header;
+ struct scsi_blk_desc blk_desc;
+ u_char sense_data[MAX_PAGE_0_SIZE];
+ } scsi_sense;
+ struct scsi_link *sc_link = st->sc_link;
+
+ scsi_sense_len = 12 + st->page_0_size;
+
+ /*
+ * Set up a mode sense
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = MODE_SENSE;
+ cmd.length = scsi_sense_len;
+
+ /*
+ * do the command, but we don't need the results
+ * just print them for our interest's sake, if asked,
+ * or if we need it as a template for the mode select
+ * store it away.
+ */
+ if (error = scsi_scsi_cmd(sc_link, (struct scsi_generic *) &cmd,
+ sizeof(cmd), (u_char *) &scsi_sense, scsi_sense_len,
+ ST_RETRIES, 5000, NULL, flags | SCSI_DATA_IN))
+ return error;
+
+ st->numblks = _3btol(scsi_sense.blk_desc.nblocks);
+ st->media_blksize = _3btol(scsi_sense.blk_desc.blklen);
+ st->media_density = scsi_sense.blk_desc.density;
+ if (scsi_sense.header.dev_spec & SMH_DSP_WRITE_PROT)
+ st->flags |= ST_READONLY;
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("density code 0x%x, %d-byte blocks, write-%s, ",
+ st->media_density, st->media_blksize,
+ st->flags & ST_READONLY ? "protected" : "enabled"));
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("%sbuffered\n",
+ scsi_sense.header.dev_spec & SMH_DSP_BUFF_MODE ? "" : "un"));
+ if (st->page_0_size)
+ bcopy(scsi_sense.sense_data, st->sense_data, st->page_0_size);
+ sc_link->flags |= SDEV_MEDIA_LOADED;
+ return 0;
+}
+
+/*
+ * Send a filled out parameter structure to the drive to
+ * set it into the desire modes etc.
+ */
+int
+st_mode_select(st, flags)
+ struct st_softc *st;
+ int flags;
+{
+ u_int scsi_select_len;
+ int error;
+ struct scsi_mode_select cmd;
+ struct scsi_select {
+ struct scsi_mode_header header;
+ struct scsi_blk_desc blk_desc;
+ u_char sense_data[MAX_PAGE_0_SIZE];
+ } scsi_select;
+ struct scsi_link *sc_link = st->sc_link;
+
+ scsi_select_len = 12 + st->page_0_size;
+
+ /*
+ * Set up for a mode select
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = MODE_SELECT;
+ cmd.length = scsi_select_len;
+
+ bzero(&scsi_select, scsi_select_len);
+ scsi_select.header.blk_desc_len = sizeof(struct scsi_blk_desc);
+ scsi_select.header.dev_spec |= SMH_DSP_BUFF_MODE_ON;
+ scsi_select.blk_desc.density = st->density;
+ if (st->flags & ST_FIXEDBLOCKS)
+ lto3b(st->blksize, scsi_select.blk_desc.blklen);
+ if (st->page_0_size)
+ bcopy(st->sense_data, scsi_select.sense_data, st->page_0_size);
+
+ /*
+ * do the command
+ */
+ return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &cmd,
+ sizeof(cmd), (u_char *) &scsi_select, scsi_select_len,
+ ST_RETRIES, 5000, NULL, flags | SCSI_DATA_OUT);
+}
+
+/*
+ * skip N blocks/filemarks/seq filemarks/eom
+ */
+int
+st_space(st, number, what, flags)
+ struct st_softc *st;
+ u_int what;
+ int flags;
+ int number;
+{
+ struct scsi_space cmd;
+ int error;
+
+ switch (what) {
+ case SP_BLKS:
+ if (st->flags & ST_PER_ACTION) {
+ if (number > 0) {
+ st->flags &= ~ST_PER_ACTION;
+ return EIO;
+ } else if (number < 0) {
+ if (st->flags & ST_AT_FILEMARK) {
+ /*
+ * Handling of ST_AT_FILEMARK
+ * in st_space will fill in the
+ * right file mark count.
+ */
+ error = st_space(st, 0, SP_FILEMARKS,
+ flags);
+ if (error)
+ return error;
+ }
+ if (st->flags & ST_BLANK_READ) {
+ st->flags &= ~ST_BLANK_READ;
+ return EIO;
+ }
+ st->flags &= ~ST_EIO_PENDING;
+ }
+ }
+ break;
+ case SP_FILEMARKS:
+ if (st->flags & ST_EIO_PENDING) {
+ if (number > 0) {
+ /* pretend we just discovered the error */
+ st->flags &= ~ST_EIO_PENDING;
+ return EIO;
+ } else if (number < 0) {
+ /* back away from the error */
+ st->flags &= ~ST_EIO_PENDING;
+ }
+ }
+ if (st->flags & ST_AT_FILEMARK) {
+ st->flags &= ~ST_AT_FILEMARK;
+ number--;
+ }
+ if ((st->flags & ST_BLANK_READ) && (number < 0)) {
+ /* back away from unwritten tape */
+ st->flags &= ~ST_BLANK_READ;
+ number++; /* XXX dubious */
+ }
+ break;
+ case SP_EOM:
+ if (st->flags & ST_EIO_PENDING) {
+ /* pretend we just discovered the error */
+ st->flags &= ~ST_EIO_PENDING;
+ return EIO;
+ }
+ if (st->flags & ST_AT_FILEMARK)
+ st->flags &= ~ST_AT_FILEMARK;
+ break;
+ }
+ if (number == 0)
+ return 0;
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = SPACE;
+ cmd.byte2 = what;
+ lto3b(number, cmd.number);
+
+ return scsi_scsi_cmd(st->sc_link, (struct scsi_generic *) &cmd,
+ sizeof(cmd), 0, 0, 0, 900000, NULL, flags);
+}
+
+/*
+ * write N filemarks
+ */
+int
+st_write_filemarks(st, number, flags)
+ struct st_softc *st;
+ int flags;
+ int number;
+{
+ struct scsi_write_filemarks cmd;
+
+ /*
+ * It's hard to write a negative number of file marks.
+ * Don't try.
+ */
+ if (number < 0)
+ return EINVAL;
+ switch (number) {
+ case 0: /* really a command to sync the drive's buffers */
+ break;
+ case 1:
+ if (st->flags & ST_FM_WRITTEN) /* already have one down */
+ st->flags &= ~ST_WRITTEN;
+ else
+ st->flags |= ST_FM_WRITTEN;
+ st->flags &= ~ST_PER_ACTION;
+ break;
+ default:
+ st->flags &= ~(ST_PER_ACTION | ST_WRITTEN);
+ }
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = WRITE_FILEMARKS;
+ lto3b(number, cmd.number);
+
+ return scsi_scsi_cmd(st->sc_link, (struct scsi_generic *) &cmd,
+ sizeof(cmd), 0, 0, 0, 100000, NULL, flags);
+}
+
+/*
+ * Make sure the right number of file marks is on tape if the
+ * tape has been written. If the position argument is true,
+ * leave the tape positioned where it was originally.
+ *
+ * nmarks returns the number of marks to skip (or, if position
+ * true, which were skipped) to get back original position.
+ */
+int
+st_check_eod(st, position, nmarks, flags)
+ struct st_softc *st;
+ boolean position;
+ int *nmarks;
+ int flags;
+{
+ int error;
+
+ switch (st->flags & (ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD)) {
+ default:
+ *nmarks = 0;
+ return 0;
+ case ST_WRITTEN:
+ case ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD:
+ *nmarks = 1;
+ break;
+ case ST_WRITTEN | ST_2FM_AT_EOD:
+ *nmarks = 2;
+ }
+ error = st_write_filemarks(st, *nmarks, flags);
+ if (position && !error)
+ error = st_space(st, -*nmarks, SP_FILEMARKS, flags);
+ return error;
+}
+
+/*
+ * load/unload/retension
+ */
+int
+st_load(st, type, flags)
+ struct st_softc *st;
+ u_int type;
+ int flags;
+{
+ struct scsi_load cmd;
+
+ if (type != LD_LOAD) {
+ int error;
+ int nmarks;
+
+ error = st_check_eod(st, FALSE, &nmarks, flags);
+ if (error)
+ return error;
+ }
+ if (st->quirks & ST_Q_IGNORE_LOADS)
+ return 0;
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = LOAD;
+ cmd.how = type;
+
+ return scsi_scsi_cmd(st->sc_link, (struct scsi_generic *) &cmd,
+ sizeof(cmd), 0, 0, ST_RETRIES, 300000, NULL, flags);
+}
+
+/*
+ * Rewind the device
+ */
+int
+st_rewind(st, immediate, flags)
+ struct st_softc *st;
+ u_int immediate;
+ int flags;
+{
+ struct scsi_rewind cmd;
+ int error;
+ int nmarks;
+
+ error = st_check_eod(st, FALSE, &nmarks, flags);
+ if (error)
+ return error;
+ st->flags &= ~ST_PER_ACTION;
+
+ bzero(&cmd, sizeof(cmd));
+ cmd.opcode = REWIND;
+ cmd.byte2 = immediate;
+
+ return scsi_scsi_cmd(st->sc_link, (struct scsi_generic *) &cmd,
+ sizeof(cmd), 0, 0, ST_RETRIES, immediate ? 5000 : 300000, NULL,
+ flags);
+}
+
+/*
+ * Look at the returned sense and act on the error and detirmine
+ * The unix error number to pass back... (0 = report no error)
+ * (-1 = continue processing)
+ */
+int
+st_interpret_sense(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *sc_link = xs->sc_link;
+ struct scsi_sense_data *sense = &xs->sense;
+ struct buf *bp = xs->bp;
+ struct st_softc *st = sc_link->device_softc;
+ u_int8_t key;
+ u_int32_t info;
+
+ /*
+ * Get the sense fields and work out what code
+ */
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ bcopy(sense->extended_info, &info, sizeof info);
+ info = ntohl(info);
+ } else
+ info = xs->datalen; /* bad choice if fixed blocks */
+ if ((sense->error_code & SSD_ERRCODE) != 0x70)
+ return -1; /* let the generic code handle it */
+ if (st->flags & ST_FIXEDBLOCKS) {
+ xs->resid = info * st->blksize;
+ if (sense->extended_flags & SSD_EOM) {
+ st->flags |= ST_EIO_PENDING;
+ if (bp)
+ bp->b_resid = xs->resid;
+ }
+ if (sense->extended_flags & SSD_FILEMARK) {
+ st->flags |= ST_AT_FILEMARK;
+ if (bp)
+ bp->b_resid = xs->resid;
+ }
+ if (sense->extended_flags & SSD_ILI) {
+ st->flags |= ST_EIO_PENDING;
+ if (bp)
+ bp->b_resid = xs->resid;
+ if (sense->error_code & SSD_ERRCODE_VALID &&
+ (xs->flags & SCSI_SILENT) == 0)
+ printf("%s: block wrong size, %d blocks residual\n",
+ st->sc_dev.dv_xname, info);
+
+ /*
+ * This quirk code helps the drive read
+ * the first tape block, regardless of
+ * format. That is required for these
+ * drives to return proper MODE SENSE
+ * information.
+ */
+ if ((st->quirks & ST_Q_SENSE_HELP) &&
+ !(sc_link->flags & SDEV_MEDIA_LOADED))
+ st->blksize -= 512;
+ }
+ /*
+ * If no data was tranfered, do it immediatly
+ */
+ if (xs->resid >= xs->datalen) {
+ if (st->flags & ST_EIO_PENDING)
+ return EIO;
+ if (st->flags & ST_AT_FILEMARK) {
+ if (bp)
+ bp->b_resid = xs->resid;
+ return 0;
+ }
+ }
+ } else { /* must be variable mode */
+ xs->resid = xs->datalen; /* to be sure */
+ if (sense->extended_flags & SSD_EOM)
+ return EIO;
+ if (sense->extended_flags & SSD_FILEMARK) {
+ if (bp)
+ bp->b_resid = bp->b_bcount;
+ return 0;
+ }
+ if (sense->extended_flags & SSD_ILI) {
+ if (info < 0) {
+ /*
+ * the record was bigger than the read
+ */
+ if ((xs->flags & SCSI_SILENT) == 0)
+ printf("%s: %d-byte record too big\n",
+ st->sc_dev.dv_xname,
+ xs->datalen - info);
+ return EIO;
+ }
+ xs->resid = info;
+ if (bp)
+ bp->b_resid = info;
+ }
+ }
+ key = sense->extended_flags & SSD_KEY;
+
+ if (key == 0x8) {
+ /*
+ * This quirk code helps the drive read the
+ * first tape block, regardless of format. That
+ * is required for these drives to return proper
+ * MODE SENSE information.
+ */
+ if ((st->quirks & ST_Q_SENSE_HELP) &&
+ !(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ /* still starting */
+ st->blksize -= 512;
+ } else if (!(st->flags & (ST_2FM_AT_EOD | ST_BLANK_READ))) {
+ st->flags |= ST_BLANK_READ;
+ xs->resid = xs->datalen;
+ if (bp) {
+ bp->b_resid = xs->resid;
+ /* return an EOF */
+ }
+ return 0;
+ }
+ }
+ return -1; /* let the default/generic handler handle it */
+}
+
+/*
+ * The quirk here is that the drive returns some value to st_mode_sense
+ * incorrectly until the tape has actually passed by the head.
+ *
+ * The method is to set the drive to large fixed-block state (user-specified
+ * density and 1024-byte blocks), then read and rewind to get it to sense the
+ * tape. If that doesn't work, try 512-byte fixed blocks. If that doesn't
+ * work, as a last resort, try variable- length blocks. The result will be
+ * the ability to do an accurate st_mode_sense.
+ *
+ * We know we can do a rewind because we just did a load, which implies rewind.
+ * Rewind seems preferable to space backward if we have a virgin tape.
+ *
+ * The rest of the code for this quirk is in ILI processing and BLANK CHECK
+ * error processing, both part of st_interpret_sense.
+ */
+int
+st_touch_tape(st)
+ struct st_softc *st;
+{
+ char *buf;
+ int readsize;
+ int error;
+
+ buf = malloc(1024, M_TEMP, M_NOWAIT);
+ if (!buf)
+ return ENOMEM;
+
+ if (error = st_mode_sense(st, 0))
+ goto bad;
+ st->blksize = 1024;
+ do {
+ switch (st->blksize) {
+ case 512:
+ case 1024:
+ readsize = st->blksize;
+ st->flags |= ST_FIXEDBLOCKS;
+ break;
+ default:
+ readsize = 1;
+ st->flags &= ~ST_FIXEDBLOCKS;
+ }
+ if (error = st_mode_select(st, 0))
+ goto bad;
+ st_read(st, buf, readsize, SCSI_SILENT); /* XXX */
+ if (error = st_rewind(st, 0, 0)) {
+bad: free(buf, M_TEMP);
+ return error;
+ }
+ } while (readsize != 1 && readsize > st->blksize);
+
+ free(buf, M_TEMP);
+ return 0;
+}
+
+int
+stdump(dev, blkno, va, size)
+ dev_t dev;
+ daddr_t blkno;
+ caddr_t va;
+ size_t size;
+{
+
+ /* Not implemented. */
+ return ENXIO;
+}
diff --git a/sys/scsi/su.c b/sys/scsi/su.c
new file mode 100644
index 00000000000..dc2ec883202
--- /dev/null
+++ b/sys/scsi/su.c
@@ -0,0 +1,5 @@
+/* $NetBSD: su.c,v 1.2 1994/06/29 06:43:24 cgd Exp $ */
+
+/* this will be a special user scsi device */
+/* not written yet */
+
diff --git a/sys/scsi/uk.c b/sys/scsi/uk.c
new file mode 100644
index 00000000000..faa473189f7
--- /dev/null
+++ b/sys/scsi/uk.c
@@ -0,0 +1,174 @@
+/* $NetBSD: uk.c,v 1.13 1995/03/24 20:17:15 glass Exp $ */
+
+/*
+ * Copyright (c) 1994 Charles Hannum. All rights reserved.
+ *
+ * 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 acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 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.
+ */
+
+/*
+ * Dummy driver for a device we can't identify.
+ * Originally by Julian Elischer (julian@tfs.com)
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/device.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#define UKUNIT(z) (minor(z))
+
+struct uk_softc {
+ struct device sc_dev;
+
+ struct scsi_link *sc_link; /* all the inter level info */
+};
+
+int ukmatch __P((struct device *, void *, void *));
+void ukattach __P((struct device *, struct device *, void *));
+
+struct cfdriver ukcd = {
+ NULL, "uk", ukmatch, ukattach, DV_DULL, sizeof(struct uk_softc)
+};
+
+/*
+ * This driver is so simple it uses all the default services
+ */
+struct scsi_device uk_switch = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+int
+ukmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+
+ return 1;
+}
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * a device suitable for this driver.
+ */
+void
+ukattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct uk_softc *uk = (void *)self;
+ struct scsibus_attach_args *sa = aux;
+ struct scsi_link *sc_link = sa->sa_sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("ukattach: "));
+
+ /*
+ * Store information needed to contact our base driver
+ */
+ uk->sc_link = sc_link;
+ sc_link->device = &uk_switch;
+ sc_link->device_softc = uk;
+ sc_link->openings = 1;
+
+ printf(": unknown device\n");
+}
+
+/*
+ * open the device.
+ */
+int
+ukopen(dev)
+ dev_t dev;
+{
+ int unit;
+ struct uk_softc *uk;
+ struct scsi_link *sc_link;
+
+ unit = UKUNIT(dev);
+ if (unit >= ukcd.cd_ndevs)
+ return ENXIO;
+ uk = ukcd.cd_devs[unit];
+ if (!uk)
+ return ENXIO;
+
+ sc_link = uk->sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1,
+ ("ukopen: dev=0x%x (unit %d (of %d))\n", dev, unit, ukcd.cd_ndevs));
+
+ /*
+ * Only allow one at a time
+ */
+ if (sc_link->flags & SDEV_OPEN) {
+ printf("%s: already open\n", uk->sc_dev.dv_xname);
+ return EBUSY;
+ }
+
+ sc_link->flags |= SDEV_OPEN;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n"));
+ return 0;
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+int
+ukclose(dev)
+ dev_t dev;
+{
+ struct uk_softc *uk = ukcd.cd_devs[UKUNIT(dev)];
+
+ SC_DEBUG(uk->sc_link, SDEV_DB1, ("closing\n"));
+ uk->sc_link->flags &= ~SDEV_OPEN;
+
+ return 0;
+}
+
+/*
+ * Perform special action on behalf of the user
+ * Only does generic scsi ioctls.
+ */
+int
+ukioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ register struct uk_softc *uk = ukcd.cd_devs[UKUNIT(dev)];
+
+ return scsi_do_ioctl(uk->sc_link, dev, cmd, addr, flag, p);
+}