summaryrefslogtreecommitdiffstats
path: root/sys/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'sys/scsi')
-rw-r--r--sys/scsi/README196
-rw-r--r--sys/scsi/cd.c1317
-rw-r--r--sys/scsi/ch.c487
-rw-r--r--sys/scsi/scsi_all.h340
-rw-r--r--sys/scsi/scsi_base.c896
-rw-r--r--sys/scsi/scsi_cd.h229
-rw-r--r--sys/scsi/scsi_changer.h98
-rw-r--r--sys/scsi/scsi_debug.h53
-rw-r--r--sys/scsi/scsi_disk.h216
-rw-r--r--sys/scsi/scsi_generic.h63
-rw-r--r--sys/scsi/scsi_ioctl.c332
-rw-r--r--sys/scsi/scsi_tape.h204
-rw-r--r--sys/scsi/scsiconf.c699
-rw-r--r--sys/scsi/scsiconf.h249
-rw-r--r--sys/scsi/sd.c1072
-rw-r--r--sys/scsi/st.c1936
-rw-r--r--sys/scsi/su.c4
-rw-r--r--sys/scsi/uk.c158
18 files changed, 8549 insertions, 0 deletions
diff --git a/sys/scsi/README b/sys/scsi/README
new file mode 100644
index 0000000..b110930
--- /dev/null
+++ b/sys/scsi/README
@@ -0,0 +1,196 @@
+This release consists of the following files
+(relative to the base of the source tree )
+
+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
+sys/sys/chio.h
+sys/sys/cdio.h
+sys/sys/mtio.h
+sys/sys/scsiio.h
+sys/i386/conf/EXAMPLE
+sys/i386/isa/ultra14f.c <-runs 14f and 34f
+sys/i386/isa/ultra_all.c.beta <-beta version, runs 14f,24f and 34f
+sys/i386/isa/bt742a.c
+sys/i386/isa/aha1742.c
+sys/i386/isa/aha1542.c
+sys/scsi/syspatches
+sys/scsi/syspatches/conf.c
+sys/scsi/syspatches/user_scsi.diffs
+sys/scsi/syspatches/MAKEDEV.diff
+sys/scsi/syspatches/isa.c.patch
+sys/scsi/syspatches/README
+sys/scsi/uk.c
+sys/scsi/su.c
+sys/scsi/st.c
+sys/scsi/sd.c
+sys/scsi/ch.c
+sys/scsi/cd.c
+sys/scsi/scsi_ioctl.c
+sys/scsi/scsi_base.c
+sys/scsi/scsiconf.c
+sys/scsi/scsi_tape.h
+sys/scsi/scsi_disk.h
+sys/scsi/scsi_changer.h
+sys/scsi/scsi_cd.h
+sys/scsi/scsi_all.h
+sys/scsi/scsi_debug.h
+sys/scsi/scsiconf.h
+sys/scsi/README <--this file
+
+notice sys/scsi/sg.c and sys/sys/sgio.h have been removed
+
+
+----------------------------------------------------------------
+This scsi system is designed to allow the re-use of top end drivers
+such as disk and tape drivers, with different scsi adapters.
+
+As of writing this document, There are top end drivers working for:
+----------------------------------------------------------------
+generic scsi disk
+generic scsi tape
+cd-rom (plays music under the xcplayer (?) program)
+AEG Character recognition devices *
+Calera Character recognition devices *
+Generic scsi-II scanners *
+Exabyte tape changer device.
+GENERIC SCSI DEVICES (user generated scsi commands)
+----------------------------------------------------------------
+
+
+There are also working bottom end drivers for:
+----------------------------------------------------------------
+adaptec 1542 (and 1742 in 1542 mode)
+bustec 742a (apparently works for VESA version (445S?))(and 747?)
+adaptec 174x (note NOT 27xx)
+Ultrastore 14f (works for 34f (VESA version))
+Ultrastore 24f RSN (Beta version included here)
+----------------------------------------------------------------
+
+
+################## Using the scsi system ##################
+------------minor numbers---------------
+This scsi system does not allocate minor numbers to devices depending
+on their SCSI IDs is any way. A devices minor number is dependant
+on the order in which it was found.
+e.g. the first tape found will become st0 (minor number 0)
+ the second found will become st1 (minor number 16)
+ the third will become st2 (minor 32)
+ etc.
+
+These devices could be on the same scsi bus or different scsi busses.
+That would not change their minor numbers.
+
+THE EXCEPTION TO THIS IS IN THE GENERIC SCSI DRIVER. in which case
+the following mapping applies:
+
+BB TTT LLL B= scsi bus number, T = target number, L = LUN.
+
+It is possible to run two different TYPES of scsi adapters at the
+same time and have st0 on one and st1 on another. (for example)
+
+There is a scheme supported in which scsi devices can be 'wired in' even
+if they are not present or powered on at probe time. (see scsiconf.c)
+In addition, the scsi(1) command allows the operator ask for a
+reprobe at any time. Newly found devices will be configured in. Any
+device that does not map to a known device type is attached to the
+'unknown' (uk) driver.
+
+
+--------------making devices------------
+A changed version of /dev/MAKEDEV is supplied that
+can be used to make devices sd[01234] and st[01234]
+
+e.g.
+cd /dev
+sh MAKEDEV sd0 sd1 sd2 st0 st1 cd0
+
+see st(1) and st(4) for info on tape devices.
+
+--------------file layout-------------------
+Originally I had all scsi definitions in one file: scsi.h
+I have since moved definitions of commands so that all
+definitions needed for a particular type of device are
+found together in the include file of that name.
+This approximatly follows the layout of their definition
+in the SCSI-2 spec.
+As such they are:
+
+scsi_all.h general commands for all devices --- CHAPTER 7
+scsi-disk.h commands relevant to disk --- CHAPTER 8
+scsi-tape.h commands for scsi tapes --- CHAPTER 9
+scsi-cd.h commands for cd-roms (and audio) --- CHAPTER 13
+scsi-changer.h commands medium changer devices --- CHAPTER 16
+
+---------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-----------------
+The cd rom driver ha been tested by a number of people and
+grefen@convex.com has completed the audio play
+functions.
+(xcdplayer was available from the 'from_ref' directory on agate)
+
+At this time it is possible audio play is broken on cdroms and I will
+be unable to fix it until I get one to test.
+***IMPORTANT***
+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!
+
+
+---------recent changes-----------
+Removed all bitfields from machine independent sections to make
+it possible for them to be used on big-endian architectures.
+
+Removed scsi specific timeouts in favour of system timeout handling.
+
+Many structures (getting more all the time) now dynamically allocated.
+
+Addition of code in the tape driver to recognise models of drive that
+have particular problems so they can be handled specially.
+
+many bug-fixes and cleanups.
+
+---------even more recent changes:--------
+
+rewrote almost the entire thing..
+
+
+
+------Mon Oct 11 22:20:25 WST 1993------
+
+Code is now all KNF (or close to it).
+
+A new structure has been introduced..
+Called scsi_link, one of these exists for every bus/target/lun
+that has a driver attached to it.
+It has links to the adapter and to the driver, as well as status
+information of global interest. (e.g. if the device is in use).
+The use of this new structure has allowed the compaction of a
+lot of duplicated code into a single copy (now in scsi_base.c)
+and makes more simple the USER level scsi implimentation.
+
+
diff --git a/sys/scsi/cd.c b/sys/scsi/cd.c
new file mode 100644
index 0000000..899db93
--- /dev/null
+++ b/sys/scsi/cd.c
@@ -0,0 +1,1317 @@
+/*
+ * 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
+ *
+ * $Id: cd.c,v 1.18 1994/04/20 07:06:51 davidg Exp $
+ */
+
+#define SPLCD splbio
+#define ESUCCESS 0
+#include <cd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/dkbad.h>
+#include <sys/systm.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/cdio.h>
+
+#include <sys/errno.h>
+#include <sys/disklabel.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>
+
+/* static function prototypes */
+static errval cd_get_parms(int, int);
+static errval cd_get_mode(u_int32, struct cd_mode_data *, u_int32);
+static errval cd_set_mode(u_int32 unit, struct cd_mode_data *);
+static errval cd_read_toc(u_int32, u_int32, u_int32, struct cd_toc_entry *,
+ u_int32);
+
+
+int32 cdstrats, cdqueues;
+
+#include <ddb.h>
+#if NDDB > 0
+#else /* NDDB > 0 */
+#define Debugger()
+#endif /* NDDB > 0 */
+
+#define PAGESIZ 4096
+#define SECSIZE 2048 /* XXX */ /* default only */
+#define CDOUTSTANDING 2
+#define CDRETRIES 1
+
+#define UNITSHIFT 3
+#define PARTITION(z) (minor(z) & 0x07)
+#define RAW_PART 3
+#define UNIT(z) ( (minor(z) >> UNITSHIFT) )
+
+errval cdstrategy();
+
+void cdstart();
+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 */
+ "cd", /* we are to be refered to by this name */
+ 0, /* no device specific flags */
+ 0, 0 /* spares not used */
+};
+
+struct cd_data {
+ u_int32 flags;
+#define CDINIT 0x04 /* device has been init'd */
+ struct scsi_link *sc_link; /* address of scsi low level switch */
+ u_int32 cmdscount; /* cmds allowed outstanding by board */
+ struct cd_parms {
+ u_int32 blksize;
+ u_long disksize; /* total number sectors */
+ } params;
+ struct disklabel disklabel;
+ u_int32 partflags[MAXPARTITIONS]; /* per partition flags */
+#define CDOPEN 0x01
+ u_int32 openparts; /* one bit for each open partition */
+ u_int32 xfer_block_wait;
+ struct buf buf_queue;
+};
+
+#define CD_STOP 0
+#define CD_START 1
+#define CD_EJECT -2
+
+struct cd_driver {
+ u_int32 size;
+ struct cd_data **cd_data;
+} cd_driver;
+
+static u_int32 next_cd_unit = 0;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * A device suitable for this driver
+ */
+int
+cdattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit, i;
+ unsigned char *tbl;
+ struct cd_data *cd, **cdrealloc;
+ struct cd_parms *dp;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("cdattach "));
+
+ /*
+ * Fill out any more info in the
+ * Link structure that we can
+ */
+ unit = next_cd_unit++;
+ sc_link->device = &cd_switch;
+ sc_link->dev_unit = unit;
+ /*
+ * allocate the resources for another drive
+ * if we have already allocate a cd_data pointer we must
+ * copy the old pointers into a new region that is
+ * larger and release the old region, aka realloc
+ */
+ /* XXX
+ * This if will always be true for now, but future code may
+ * preallocate more units to reduce overhead. This would be
+ * done by changing the malloc to be (next_cd_unit * x) and
+ * the cd_driver.size++ to be +x
+ */
+ if (unit >= cd_driver.size) {
+ cdrealloc =
+ malloc(sizeof(cd_driver.cd_data) * next_cd_unit,
+ M_DEVBUF, M_NOWAIT);
+ if (!cdrealloc) {
+ printf("cd%d: malloc failed for cdrealloc\n", unit);
+ return (0);
+ }
+ /* Make sure we have something to copy before we copy it */
+ bzero(cdrealloc, sizeof(cd_driver.cd_data) * next_cd_unit);
+ if (cd_driver.size) {
+ bcopy(cd_driver.cd_data, cdrealloc,
+ sizeof(cd_driver.cd_data) * cd_driver.size);
+ free(cd_driver.cd_data, M_DEVBUF);
+ }
+ cd_driver.cd_data = cdrealloc;
+ cd_driver.cd_data[unit] = NULL;
+ cd_driver.size++;
+ }
+ if (cd_driver.cd_data[unit]) {
+ printf("cd%d: Already has storage!\n", unit);
+ return (0);
+ }
+ /*
+ * allocate the per drive data area
+ */
+ cd = cd_driver.cd_data[unit] =
+ malloc(sizeof(struct cd_data), M_DEVBUF, M_NOWAIT);
+ if (!cd) {
+ printf("cd%d: malloc failed for cd_data\n", unit);
+ return (0);
+ }
+ bzero(cd, sizeof(struct cd_data));
+ dp = &(cd->params);
+ /*
+ * Store information needed to contact our base driver
+ */
+ cd->sc_link = sc_link;
+ /* only allow 1 outstanding command on tapes */
+ sc_link->opennings = cd->cmdscount = CDOUTSTANDING;
+
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ cd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK);
+ if (dp->disksize) {
+ printf("cd%d: cd present.[%d x %d byte records]\n",
+ unit,
+ cd->params.disksize,
+ cd->params.blksize);
+ } else {
+ printf("cd%d: drive empty\n", unit);
+ }
+ cd->flags |= CDINIT;
+ return (1);
+}
+
+/*
+ * open the device. Make sure the partition info is a up-to-date as can be.
+ */
+errval
+cdopen(dev)
+ dev_t dev;
+{
+ errval errcode = 0;
+ u_int32 unit, part;
+ struct cd_parms cd_parms;
+ struct cd_data *cd;
+ struct scsi_link *sc_link;
+ u_int32 heldflags;
+
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= cd_driver.size) {
+ return (ENXIO);
+ }
+ cd = cd_driver.cd_data[unit];
+ /*
+ * Make sure the device has been initialised
+ */
+ if ((cd == NULL) || (!(cd->flags & CDINIT)))
+ 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, cd_driver.size, part));
+ /*
+ * If it's been invalidated, and not everybody has closed it then
+ * forbid re-entry. (may have changed media)
+ */
+ if ((!(sc_link->flags & SDEV_MEDIA_LOADED))
+ && (cd->openparts))
+ return (ENXIO);
+
+ /*
+ * Check that it is still responding and ok.
+ * if the media has been changed this will result in a
+ * "unit attention" error which the error code will
+ * disregard because the SDEV_MEDIA_LOADED flag is not yet set
+ */
+ scsi_test_unit_ready(sc_link, SCSI_SILENT);
+
+ /*
+ * Next time actually take notice of error returns
+ */
+ sc_link->flags |= SDEV_OPEN; /* unit attn errors are now errors */
+ if (scsi_test_unit_ready(sc_link, SCSI_SILENT) != 0) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("not ready\n"));
+ errcode = ENXIO;
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("Device present\n"));
+ /*
+ * In case it is a funny one, tell it to start
+ * not needed for some drives
+ */
+ scsi_start_unit(sc_link, CD_START);
+ scsi_prevent(sc_link, PR_PREVENT, SCSI_SILENT);
+ SC_DEBUG(sc_link, SDEV_DB3, ("started "));
+ /*
+ * Load the physical device parameters
+ */
+ if (cd_get_parms(unit, 0)) {
+ errcode = ENXIO;
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded "));
+ /*
+ * Make up some partition information
+ */
+ cdgetdisklabel(unit);
+ SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel fabricated "));
+ /*
+ * Check the partition is legal
+ */
+ if ((part >= cd->disklabel.d_npartitions)
+ && (part != RAW_PART)) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("partition %d > %d\n", part
+ ,cd->disklabel.d_npartitions));
+ errcode = ENXIO;
+ goto bad;
+ }
+ /*
+ * Check that the partition exists
+ */
+ if ((cd->disklabel.d_partitions[part].p_fstype == FS_UNUSED)
+ && (part != RAW_PART)) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("part %d type UNUSED\n", part));
+ errcode = ENXIO;
+ goto bad;
+ }
+ cd->partflags[part] |= CDOPEN;
+ cd->openparts |= (1 << part);
+ SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n"));
+ sc_link->flags |= SDEV_MEDIA_LOADED;
+ return (0);
+ bad:
+
+ /*
+ * if we would have been the only open
+ * then leave things back as they were
+ */
+ if (!(cd->openparts)) {
+ sc_link->flags &= ~SDEV_OPEN;
+ scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
+ }
+ return (errcode);
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+errval
+cdclose(dev)
+ dev_t dev;
+{
+ u_int8 unit, part;
+ u_int32 old_priority;
+ struct cd_data *cd;
+ struct scsi_link *sc_link;
+
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+ cd = cd_driver.cd_data[unit];
+ sc_link = cd->sc_link;
+ SC_DEBUG(sc_link, SDEV_DB2, ("cd%d: closing part %d\n", unit, part));
+ cd->partflags[part] &= ~CDOPEN;
+ cd->openparts &= ~(1 << part);
+
+ /*
+ * If we were the last open of the entire device, release it.
+ */
+ if (!(cd->openparts)) {
+ scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
+ cd->sc_link->flags &= ~SDEV_OPEN;
+ }
+ return (0);
+}
+
+/*
+ * trim the size of the transfer if needed,
+ * called by physio
+ * basically the smaller of our max and the scsi driver's
+ * minphys (note we have no max ourselves)
+ *
+ * Trim buffer length if buffer-size is bigger than page size
+ */
+void
+cdminphys(bp)
+ struct buf *bp;
+{
+ (*(cd_driver.cd_data[UNIT(bp->b_dev)]->sc_link->adapter->scsi_minphys)) (bp);
+}
+
+/*
+ * 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.
+ */
+errval
+cdstrategy(bp)
+ struct buf *bp;
+{
+ struct buf *dp;
+ u_int32 opri;
+ u_int32 unit = UNIT((bp->b_dev));
+ struct cd_data *cd = cd_driver.cd_data[unit];
+
+ cdstrats++;
+ SC_DEBUG(cd->sc_link, SDEV_DB2, ("\ncdstrategy "));
+ SC_DEBUG(cd->sc_link, SDEV_DB1, ("cd%d: %d bytes @ blk%d\n",
+ unit, bp->b_bcount, bp->b_blkno));
+ cdminphys(bp);
+ /*
+ * If the device has been made invalid, error out
+ * maybe the media changed
+ */
+ if (!(cd->sc_link->flags & SDEV_MEDIA_LOADED)) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /*
+ * can't ever write to a CD
+ */
+ if ((bp->b_flags & B_READ) == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+ /*
+ * If it's a null transfer, return immediatly
+ */
+ if (bp->b_bcount == 0) {
+ goto done;
+ }
+ /*
+ * Decide which unit and partition we are talking about
+ */
+ if (PARTITION(bp->b_dev) != RAW_PART) {
+ /*
+ * do bounds checking, adjust transfer. if error, process.
+ * if end of partition, just return
+ */
+ if (bounds_check_with_label(bp, &cd->disklabel, 1) <= 0)
+ goto done;
+ /* otherwise, process transfer request */
+ } else {
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = 0;
+ }
+ opri = SPLCD();
+ dp = &cd->buf_queue;
+
+ /*
+ * Use a bounce buffer if necessary
+ */
+#ifndef NOBOUNCE
+ if (cd->sc_link->flags & SDEV_BOUNCE)
+ vm_bounce_alloc(bp);
+#endif
+
+ /*
+ * Place it in the queue of disk activities for this disk
+ */
+ disksort(dp, bp);
+
+ /*
+ * Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion
+ */
+ cdstart(unit);
+
+ splx(opri);
+ return 0; /* XXX ??? is this the right 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);
+ return (0);
+}
+
+/*
+ * 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 SPLCD from cdstrategy and scsi_done
+ */
+void
+cdstart(unit)
+ u_int32 unit;
+{
+ register struct buf *bp = 0;
+ register struct buf *dp;
+ struct scsi_rw_big cmd;
+ u_int32 blkno, nblk;
+ struct partition *p;
+ struct cd_data *cd = cd_driver.cd_data[unit];
+ struct scsi_link *sc_link = cd->sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("cdstart%d ", unit));
+ /*
+ * See if there is a buf to do and we are not already
+ * doing one
+ */
+ if (!sc_link->opennings) {
+ return; /* no room for us, unit already underway */
+ }
+ if (sc_link->flags & SDEV_WAITING) { /* is room, but a special waits */
+ return; /* give the special that's waiting a chance to run */
+ }
+ dp = &cd->buf_queue;
+ if ((bp = dp->b_actf) != NULL) { /* yes, an assign */
+ dp->b_actf = bp->b_actf;
+ } else {
+ return;
+ }
+ /*
+ * Should reject all queued entries if SDEV_MEDIA_LOADED is not true.
+ */
+ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ goto bad; /* no I/O.. media changed or something */
+ }
+ /*
+ * 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. Really a bit silly until we have
+ * real partitions, but.
+ */
+ blkno = bp->b_blkno / (cd->params.blksize / 512);
+ if (PARTITION(bp->b_dev) != RAW_PART) {
+ p = cd->disklabel.d_partitions + PARTITION(bp->b_dev);
+ blkno += p->p_offset;
+ }
+ nblk = (bp->b_bcount + (cd->params.blksize - 1)) / (cd->params.blksize);
+ /* what if something asks for 512 bytes not on a 2k boundary? *//*XXX */
+
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.op_code = READ_BIG;
+ cmd.addr_3 = (blkno & 0xff000000UL) >> 24;
+ cmd.addr_2 = (blkno & 0xff0000) >> 16;
+ cmd.addr_1 = (blkno & 0xff00) >> 8;
+ cmd.addr_0 = blkno & 0xff;
+ cmd.length2 = (nblk & 0xff00) >> 8;
+ cmd.length1 = (nblk & 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_un.b_addr,
+ bp->b_bcount,
+ CDRETRIES,
+ 30000,
+ bp,
+ SCSI_NOSLEEP | ((bp->b_flags & B_READ) ?
+ SCSI_DATA_IN : SCSI_DATA_OUT))
+ != SUCCESSFULLY_QUEUED) {
+ bad:
+ printf("cd%d: oops not queued", unit);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+ cdqueues++;
+}
+
+/*
+ * Perform special action on behalf of the user.
+ * Knows about the internals of this device
+ */
+errval
+cdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
+{
+ errval error = 0;
+ u_int32 opri;
+ u_int8 unit, part;
+ register struct cd_data *cd;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+ cd = cd_driver.cd_data[unit];
+ SC_DEBUG(cd->sc_link, SDEV_DB2, ("cdioctl 0x%x ", cmd));
+
+ /*
+ * If the device is not valid.. abandon ship
+ */
+ if (!(cd->sc_link->flags & SDEV_MEDIA_LOADED))
+ return (EIO);
+ switch (cmd) {
+
+ case DIOCSBAD:
+ error = EINVAL;
+ break;
+
+ case DIOCGDINFO:
+ *(struct disklabel *) addr = cd->disklabel;
+ break;
+
+ case DIOCGPART:
+ ((struct partinfo *) addr)->disklab = &cd->disklabel;
+ ((struct partinfo *) addr)->part =
+ &cd->disklabel.d_partitions[PARTITION(dev)];
+ break;
+
+ /*
+ * a bit silly, but someone might want to test something on a
+ * section of cdrom.
+ */
+ case DIOCWDINFO:
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ error = setdisklabel(&cd->disklabel,
+ (struct disklabel *) addr,
+ 0,
+ 0);
+ if (error == 0)
+ break;
+
+ case DIOCWLABEL:
+ error = EBADF;
+ break;
+
+ case CDIOCPLAYTRACKS:
+ {
+ struct ioc_play_track *args
+ = (struct ioc_play_track *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ return (cd_play_tracks(unit
+ ,args->start_track
+ ,args->start_index
+ ,args->end_track
+ ,args->end_index
+ ));
+ }
+ break;
+ case CDIOCPLAYMSF:
+ {
+ struct ioc_play_msf *args
+ = (struct ioc_play_msf *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ return (cd_play_msf(unit
+ ,args->start_m
+ ,args->start_s
+ ,args->start_f
+ ,args->end_m
+ ,args->end_s
+ ,args->end_f
+ ));
+ }
+ break;
+ case CDIOCPLAYBLOCKS:
+ {
+ struct ioc_play_blocks *args
+ = (struct ioc_play_blocks *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ data.page.audio.flags &= ~CD_PA_SOTC;
+ data.page.audio.flags |= CD_PA_IMMED;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ return (cd_play(unit, args->blk, args->len));
+
+ }
+ break;
+ case CDIOCREADSUBCHANNEL:
+ {
+ struct ioc_read_subchannel *args
+ = (struct ioc_read_subchannel *) addr;
+ struct cd_sub_channel_info data;
+ u_int32 len = args->data_len;
+ if (len > sizeof(data) ||
+ len < sizeof(struct cd_sub_channel_header)) {
+ error = EINVAL;
+ break;
+ }
+ if (error = cd_read_subchannel(unit, args->address_format,
+ args->data_format, args->track, &data, len)) {
+ break;
+ }
+ len = min(len, ((data.header.data_len[0] << 8) + data.header.data_len[1] +
+ sizeof(struct cd_sub_channel_header)));
+ if (copyout(&data, args->data, len) != 0) {
+ error = EFAULT;
+ }
+ }
+ break;
+ case CDIOREADTOCHEADER:
+ { /* ??? useless bcopy? XXX */
+ struct ioc_toc_header th;
+ if (error = cd_read_toc(unit, 0, 0,
+ (struct cd_toc_entry *)&th,
+ sizeof th))
+ break;
+ th.len = (th.len & 0xff) << 8 + ((th.len >> 8) & 0xff);
+ bcopy(&th, addr, sizeof th);
+ }
+ break;
+ 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;
+ u_int32 len = te->data_len;
+ th = &data.header;
+
+ if (len > sizeof(data.entries) || len < sizeof(struct cd_toc_entry)) {
+ error = EINVAL;
+ break;
+ }
+ if (error = cd_read_toc(unit, te->address_format,
+ te->starting_track,
+ (struct cd_toc_entry *)&data,
+ len + sizeof(struct ioc_toc_header)))
+ break;
+ len = min(len, ((((th->len & 0xff) << 8) + ((th->len >> 8))) - (sizeof(th->starting_track) + sizeof(th->ending_track))));
+ if (copyout(data.entries, te->data, len) != 0) {
+ error = EFAULT;
+ }
+ }
+ break;
+ case CDIOCSETPATCH:
+ {
+ struct ioc_patch *arg = (struct ioc_patch *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ 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];
+ if (error = cd_set_mode(unit, &data))
+ break; /* eh? */
+ }
+ break;
+ case CDIOCGETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ 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;
+ }
+ break;
+ case CDIOCSETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ 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];
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCSETMONO:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ 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;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCSETSTERIO:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ 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;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCSETMUTE:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ 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;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCSETLEFT:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ 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;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCSETRIGHT:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *) addr;
+ struct cd_mode_data data;
+ if (error = cd_get_mode(unit, &data, AUDIO_PAGE))
+ break;
+ 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;
+ if (error = cd_set_mode(unit, &data))
+ break;
+ }
+ break;
+ case CDIOCRESUME:
+ error = cd_pause(unit, 1);
+ break;
+ case CDIOCPAUSE:
+ error = cd_pause(unit, 0);
+ break;
+ case CDIOCSTART:
+ error = scsi_start_unit(cd->sc_link, 0);
+ break;
+ case CDIOCSTOP:
+ error = scsi_stop_unit(cd->sc_link, 0, 0);
+ break;
+ case CDIOCEJECT:
+ error = scsi_stop_unit(cd->sc_link, 1, 0);
+ break;
+ case CDIOCALLOW:
+ error = scsi_prevent(cd->sc_link, PR_ALLOW, 0);
+ break;
+ case CDIOCPREVENT:
+ error = scsi_prevent(cd->sc_link, PR_PREVENT, 0);
+ break;
+ case CDIOCSETDEBUG:
+ cd->sc_link->flags |= (SDEV_DB1 | SDEV_DB2);
+ break;
+ case CDIOCCLRDEBUG:
+ cd->sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2);
+ break;
+ case CDIOCRESET:
+ return (cd_reset(unit));
+ break;
+ default:
+ if(part == RAW_PART)
+ error = scsi_do_ioctl(cd->sc_link,cmd,addr,flag);
+ else
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+/*
+ * 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
+ */
+errval
+cdgetdisklabel(unit)
+ u_int8 unit;
+{
+ /*unsigned int n, m; */
+ char *errstring;
+ struct cd_data *cd;
+
+ cd = cd_driver.cd_data[unit];
+
+ bzero(&cd->disklabel, sizeof(struct disklabel));
+ /*
+ * make partition 0 the whole disk
+ */
+ strncpy(cd->disklabel.d_typename, "scsi cd_rom", 16);
+ strncpy(cd->disklabel.d_packname, "ficticious", 16);
+ cd->disklabel.d_secsize = cd->params.blksize; /* as long as it's not 0 */
+ cd->disklabel.d_nsectors = 100;
+ cd->disklabel.d_ntracks = 1;
+ cd->disklabel.d_ncylinders = (cd->params.disksize / 100) + 1;
+ cd->disklabel.d_secpercyl = 100;
+ cd->disklabel.d_secperunit = cd->params.disksize;
+ cd->disklabel.d_rpm = 300;
+ cd->disklabel.d_interleave = 1;
+ cd->disklabel.d_flags = D_REMOVABLE;
+
+ /*
+ * remember that comparisons with the partition are done
+ * assuming the blocks are 512 bytes so fudge it.
+ */
+ cd->disklabel.d_npartitions = 1;
+ cd->disklabel.d_partitions[0].p_offset = 0;
+ cd->disklabel.d_partitions[0].p_size
+ = cd->params.disksize * (cd->params.blksize / 512);
+ cd->disklabel.d_partitions[0].p_fstype = 9;
+
+ cd->disklabel.d_magic = DISKMAGIC;
+ cd->disklabel.d_magic2 = DISKMAGIC;
+ cd->disklabel.d_checksum = dkcksum(&(cd->disklabel));
+
+ /*
+ * Signal to other users and routines that we now have a
+ * disklabel that represents the media (maybe)
+ */
+ return (ESUCCESS);
+}
+
+/*
+ * Find out from the device what it's capacity is
+ */
+u_int32
+cd_size(unit, flags)
+ int unit;
+ int flags;
+{
+ struct scsi_read_cd_cap_data rdcap;
+ struct scsi_read_cd_capacity scsi_cmd;
+ u_int32 size;
+ u_int32 blksize;
+ struct cd_data *cd = cd_driver.cd_data[unit];
+
+ /*
+ * make up a scsi command and ask the scsi driver to do
+ * it for you.
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = 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,
+ 20000, /* might be a disk-changer */
+ NULL,
+ SCSI_DATA_IN | flags) != 0) {
+ printf("cd%d: could not get size\n", unit);
+ return (0);
+ } else {
+ size = rdcap.addr_0 + 1;
+ size += rdcap.addr_1 << 8;
+ size += rdcap.addr_2 << 16;
+ size += rdcap.addr_3 << 24;
+ blksize = rdcap.length_0;
+ blksize += rdcap.length_1 << 8;
+ blksize += rdcap.length_2 << 16;
+ blksize += rdcap.length_3 << 24;
+ }
+ if (blksize < 512)
+ blksize = 2048; /* some drives lie ! */
+ if (size < 100)
+ size = 400000; /* ditto */
+ SC_DEBUG(cd->sc_link, SDEV_DB3, ("cd%d: %d %d byte blocks\n"
+ ,unit, size, blksize));
+ cd->params.disksize = size;
+ cd->params.blksize = blksize;
+ return (size);
+}
+
+/*
+ * Get the requested page into the buffer given
+ */
+static errval
+cd_get_mode(unit, data, page)
+ u_int32 unit;
+ struct cd_mode_data *data;
+ u_int32 page;
+{
+ struct scsi_mode_sense scsi_cmd;
+ errval retval;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ bzero(data, sizeof(*data));
+ scsi_cmd.op_code = MODE_SENSE;
+ scsi_cmd.page = page;
+ scsi_cmd.length = sizeof(*data) & 0xff;
+ retval = scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) data,
+ sizeof(*data),
+ CDRETRIES,
+ 20000, /* should be immed */
+ NULL,
+ SCSI_DATA_IN);
+ return (retval);
+}
+
+/*
+ * Get the requested page into the buffer given
+ */
+errval
+cd_set_mode(unit, data)
+ u_int32 unit;
+ struct cd_mode_data *data;
+{
+ struct scsi_mode_select scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MODE_SELECT;
+ scsi_cmd.byte2 |= SMS_PF;
+ scsi_cmd.length = sizeof(*data) & 0xff;
+ data->header.data_length = 0;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) data,
+ sizeof(*data),
+ CDRETRIES,
+ 20000, /* should be immed */
+ NULL,
+ SCSI_DATA_OUT));
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+errval
+cd_play(unit, blk, len)
+ u_int32 unit, blk, len;
+{
+ struct scsi_play scsi_cmd;
+ errval retval;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = PLAY;
+ scsi_cmd.blk_addr[0] = (blk >> 24) & 0xff;
+ scsi_cmd.blk_addr[1] = (blk >> 16) & 0xff;
+ scsi_cmd.blk_addr[2] = (blk >> 8) & 0xff;
+ scsi_cmd.blk_addr[3] = blk & 0xff;
+ scsi_cmd.xfer_len[0] = (len >> 8) & 0xff;
+ scsi_cmd.xfer_len[1] = len & 0xff;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ CDRETRIES,
+ 200000, /* should be immed */
+ NULL,
+ 0));
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+errval
+cd_play_big(unit, blk, len)
+ u_int32 unit, blk, len;
+{
+ struct scsi_play_big scsi_cmd;
+ errval retval;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = PLAY_BIG;
+ scsi_cmd.blk_addr[0] = (blk >> 24) & 0xff;
+ scsi_cmd.blk_addr[1] = (blk >> 16) & 0xff;
+ scsi_cmd.blk_addr[2] = (blk >> 8) & 0xff;
+ scsi_cmd.blk_addr[3] = blk & 0xff;
+ scsi_cmd.xfer_len[0] = (len >> 24) & 0xff;
+ scsi_cmd.xfer_len[1] = (len >> 16) & 0xff;
+ scsi_cmd.xfer_len[2] = (len >> 8) & 0xff;
+ scsi_cmd.xfer_len[3] = len & 0xff;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ CDRETRIES,
+ 20000, /* should be immed */
+ NULL,
+ 0));
+}
+
+/*
+ * Get scsi driver to send a "start playing" command
+ */
+errval
+cd_play_tracks(unit, strack, sindex, etrack, eindex)
+ u_int32 unit, strack, sindex, etrack, eindex;
+{
+ struct scsi_play_track scsi_cmd;
+ errval retval;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = 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_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ CDRETRIES,
+ 20000, /* should be immed */
+ NULL,
+ 0));
+}
+
+/*
+ * Get scsi driver to send a "play msf" command
+ */
+errval
+cd_play_msf(unit, startm, starts, startf, endm, ends, endf)
+ u_int32 unit, startm, starts, startf, endm, ends, endf;
+{
+ struct scsi_play_msf scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = 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_driver.cd_data[unit]->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
+ */
+errval
+cd_pause(unit, go)
+ u_int32 unit, go;
+{
+ struct scsi_pause scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = PAUSE;
+ scsi_cmd.resume = go;
+
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ CDRETRIES,
+ 2000,
+ NULL,
+ 0));
+}
+
+/*
+ * Get scsi driver to send a "RESET" command
+ */
+errval
+cd_reset(unit)
+ u_int32 unit;
+{
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ 0,
+ 0,
+ 0,
+ 0,
+ CDRETRIES,
+ 2000,
+ NULL,
+ SCSI_RESET));
+}
+
+/*
+ * Read subchannel
+ */
+errval
+cd_read_subchannel(unit, mode, format, track, data, len)
+ u_int32 unit, mode, format;
+ int track;
+ struct cd_sub_channel_info *data;
+ u_int32 len;
+{
+ struct scsi_read_subchannel scsi_cmd;
+ errval error;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+
+ scsi_cmd.op_code = 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;
+ scsi_cmd.data_len[1] = (len) & 0xff;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->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
+ */
+static errval
+cd_read_toc(unit, mode, start, data, len)
+ u_int32 unit, mode, start;
+ struct cd_toc_entry *data;
+ u_int32 len;
+{
+ struct scsi_read_toc scsi_cmd;
+ errval error;
+ u_int32 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.op_code = READ_TOC;
+ if (mode == CD_MSF_FORMAT)
+ scsi_cmd.byte2 |= CD_MSF;
+ scsi_cmd.from_track = start;
+ scsi_cmd.data_len[0] = (ntoc) >> 8;
+ scsi_cmd.data_len[1] = (ntoc) & 0xff;
+ return (scsi_scsi_cmd(cd_driver.cd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(struct scsi_read_toc),
+ (u_char *) data,
+ len,
+ CDRETRIES,
+ 5000,
+ NULL,
+ SCSI_DATA_IN));
+}
+
+#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.
+ */
+static errval
+cd_get_parms(unit, flags)
+ int unit;
+ int flags;
+{
+ struct cd_data *cd = cd_driver.cd_data[unit];
+
+ /*
+ * First check if we have it all loaded
+ */
+ if (cd->sc_link->flags & SDEV_MEDIA_LOADED)
+ return (0);
+ /*
+ * give a number of sectors so that sec * trks * cyls
+ * is <= disk_size
+ */
+ if (cd_size(unit, flags)) {
+ cd->sc_link->flags |= SDEV_MEDIA_LOADED;
+ return (0);
+ } else {
+ return (ENXIO);
+ }
+}
+
+int
+cdsize(dev_t dev)
+{
+ return (-1);
+}
diff --git a/sys/scsi/ch.c b/sys/scsi/ch.c
new file mode 100644
index 0000000..315dab9
--- /dev/null
+++ b/sys/scsi/ch.c
@@ -0,0 +1,487 @@
+/*
+ * Written by grefen@?????
+ * Based on scsi drivers by Julian Elischer (julian@tfs.com)
+ *
+ * $Id: ch.c,v 1.7 1993/12/19 00:54:49 wollman Exp $
+ */
+
+#include <sys/types.h>
+#include <ch.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 <scsi/scsi_all.h>
+#include <scsi/scsi_changer.h>
+#include <scsi/scsiconf.h>
+
+static errval ch_mode_sense(u_int32, u_int32);
+
+struct scsi_xfer ch_scsi_xfer[NCH];
+u_int32 ch_xfer_block_wait[NCH];
+
+#define PAGESIZ 4096
+#define STQSIZE 4
+#define CHRETRIES 2
+
+#define MODE(z) ( (minor(z) & 0x0F) )
+#define UNIT(z) ( (minor(z) >> 4) )
+
+#define ESUCCESS 0
+
+errval chattach();
+
+/*
+ * This driver is so simple it uses all the default services
+ */
+struct scsi_device ch_switch =
+{
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "ch",
+ 0,
+ 0, 0
+};
+
+struct ch_data {
+ u_int32 flags;
+ struct scsi_link *sc_link; /* all the inter level info */
+ u_int16 chmo; /* Offset of first CHM */
+ u_int16 chms; /* No. of CHM */
+ u_int16 slots; /* No. of Storage Elements */
+ u_int16 sloto; /* Offset of first SE */
+ u_int16 imexs; /* No. of Import/Export Slots */
+ u_int16 imexo; /* Offset of first IM/EX */
+ u_int16 drives; /* No. of CTS */
+ u_int16 driveo; /* Offset of first CTS */
+ u_int16 rot; /* CHM can rotate */
+ u_long op_matrix; /* possible opertaions */
+ u_int16 lsterr; /* details of lasterror */
+ u_char stor; /* posible Storage locations */
+ u_int32 initialized;
+} ch_data[NCH];
+
+#define CH_OPEN 0x01
+#define CH_KNOWN 0x02
+
+static u_int32 next_ch_unit = 0;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * a device suitable for this driver.
+ */
+errval
+chattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit, i, stat;
+ unsigned char *tbl;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("chattach: "));
+ /*
+ * Check we have the resources for another drive
+ */
+ unit = next_ch_unit++;
+ if (unit >= NCH) {
+ printf("Too many scsi changers..(%d > %d) reconfigure kernel\n", (unit + 1), NCH);
+ return (0);
+ }
+ /*
+ * Store information needed to contact our base driver
+ */
+ ch_data[unit].sc_link = sc_link;
+ sc_link->device = &ch_switch;
+ sc_link->dev_unit = unit;
+
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ if ((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT */ ))) {
+ printf("ch%d: scsi changer :- offline\n", unit);
+ stat = CH_OPEN;
+ } else {
+ printf("ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s)\n",
+ unit, ch_data[unit].slots, ch_data[unit].drives, ch_data[unit].chms, ch_data[unit].imexs);
+ stat = CH_KNOWN;
+ }
+ ch_data[unit].initialized = 1;
+
+ return 1;
+ /* XXX ??? is this the right return val? */
+}
+
+/*
+ * open the device.
+ */
+errval
+chopen(dev)
+ dev_t dev;
+{
+ errval errcode = 0;
+ u_int32 unit, mode;
+ struct scsi_link *sc_link;
+
+ unit = UNIT(dev);
+ mode = MODE(dev);
+
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= NCH) {
+ printf("ch%d: ch %d > %d\n", unit, unit, NCH);
+ errcode = ENXIO;
+ return (errcode);
+ }
+ /*
+ * Only allow one at a time
+ */
+ if (ch_data[unit].flags & CH_OPEN) {
+ printf("ch%d: already open\n", unit);
+ return EBUSY;
+ }
+ /*
+ * Make sure the device has been initialised
+ */
+ if (!ch_data[unit].initialized)
+ return (ENXIO);
+
+ sc_link = ch_data[unit].sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("chopen: dev=0x%x (unit %d (of %d))\n"
+ ,dev, unit, NCH));
+ /*
+ * Catch any unit attention errors.
+ */
+ scsi_test_unit_ready(sc_link, SCSI_SILENT);
+
+ sc_link->flags |= SDEV_OPEN;
+ /*
+ * Check that it is still responding and ok.
+ */
+ if (errcode = (scsi_test_unit_ready(sc_link, 0))) {
+ printf("ch%d: not ready\n", unit);
+ sc_link->flags &= ~SDEV_OPEN;
+ return errcode;
+ }
+ /*
+ * Make sure data is loaded
+ */
+ if (errcode = (ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK))) {
+ printf("ch%d: scsi changer :- offline\n", unit);
+ sc_link->flags &= ~SDEV_OPEN;
+ return (errcode);
+ }
+ ch_data[unit].flags = CH_OPEN;
+ return 0;
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+errval
+chclose(dev)
+ dev_t dev;
+{
+ unsigned char unit, mode;
+ struct scsi_link *sc_link;
+
+ unit = UNIT(dev);
+ mode = MODE(dev);
+ sc_link = ch_data[unit].sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("Closing device"));
+ ch_data[unit].flags = 0;
+ sc_link->flags &= ~SDEV_OPEN;
+ return (0);
+}
+
+/*
+ * Perform special action on behalf of the user
+ * Knows about the internals of this device
+ */
+errval
+chioctl(dev, cmd, arg, mode)
+ dev_t dev;
+ u_int32 cmd;
+ caddr_t arg;
+ int mode;
+{
+ /* struct ch_cmd_buf *args; */
+ union scsi_cmd *scsi_cmd;
+ register i, j;
+ u_int32 opri;
+ errval errcode = 0;
+ unsigned char unit;
+ u_int32 number, flags;
+ errval ret;
+ struct scsi_link *sc_link;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ flags = 0; /* give error messages, act on errors etc. */
+ unit = UNIT(dev);
+ sc_link = ch_data[unit].sc_link;
+
+ switch ((int)cmd) {
+ case CHIOOP:{
+ struct chop *ch = (struct chop *) arg;
+ SC_DEBUG(sc_link, SDEV_DB2,
+ ("[chtape_chop: %x]\n", ch->ch_op));
+
+ switch ((short) (ch->ch_op)) {
+ case CHGETPARAM:
+ ch->u.getparam.chmo = ch_data[unit].chmo;
+ ch->u.getparam.chms = ch_data[unit].chms;
+ ch->u.getparam.sloto = ch_data[unit].sloto;
+ ch->u.getparam.slots = ch_data[unit].slots;
+ ch->u.getparam.imexo = ch_data[unit].imexo;
+ ch->u.getparam.imexs = ch_data[unit].imexs;
+ ch->u.getparam.driveo = ch_data[unit].driveo;
+ ch->u.getparam.drives = ch_data[unit].drives;
+ ch->u.getparam.rot = ch_data[unit].rot;
+ ch->result = 0;
+ return 0;
+ break;
+ case CHPOSITION:
+ return ch_position(unit, &ch->result, ch->u.position.chm,
+ ch->u.position.to,
+ flags);
+ case CHMOVE:
+ return ch_move(unit, &ch->result, ch->u.position.chm,
+ ch->u.move.from, ch->u.move.to,
+ flags);
+ case CHGETELEM:
+ return ch_getelem(unit, &ch->result, ch->u.get_elem_stat.type,
+ ch->u.get_elem_stat.from, &ch->u.get_elem_stat.elem_data,
+ flags);
+ default:
+ return EINVAL;
+ }
+ }
+ default:
+ return scsi_do_ioctl(sc_link, cmd, arg, mode);
+ }
+ return (ret ? ESUCCESS : EIO);
+}
+
+errval
+ch_getelem(unit, stat, type, from, data, flags)
+ u_int32 unit, from, flags;
+ int type;
+ short *stat;
+ char *data;
+{
+ struct scsi_read_element_status scsi_cmd;
+ char elbuf[32];
+ errval ret;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_ELEMENT_STATUS;
+ scsi_cmd.byte2 = type;
+ scsi_cmd.starting_element_addr[0] = (from >> 8) & 0xff;
+ scsi_cmd.starting_element_addr[1] = from & 0xff;
+ scsi_cmd.number_of_elements[1] = 1;
+ scsi_cmd.allocation_length[2] = 32;
+
+ if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) elbuf,
+ 32,
+ CHRETRIES,
+ 100000,
+ NULL,
+ SCSI_DATA_IN | flags) != ESUCCESS)) {
+ *stat = ch_data[unit].lsterr;
+ bcopy(elbuf + 16, data, 16);
+ return ret;
+ }
+ bcopy(elbuf + 16, data, 16); /*Just a hack sh */
+ return ret;
+}
+
+errval
+ch_move(unit, stat, chm, from, to, flags)
+ u_int32 unit, chm, from, to, flags;
+ short *stat;
+{
+ struct scsi_move_medium scsi_cmd;
+ errval ret;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MOVE_MEDIUM;
+ scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff;
+ scsi_cmd.transport_element_address[1] = chm & 0xff;
+ scsi_cmd.source_address[0] = (from >> 8) & 0xff;
+ scsi_cmd.source_address[1] = from & 0xff;
+ scsi_cmd.destination_address[0] = (to >> 8) & 0xff;
+ scsi_cmd.destination_address[1] = to & 0xff;
+ scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0;
+ if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ NULL,
+ 0,
+ CHRETRIES,
+ 100000,
+ NULL,
+ flags) != ESUCCESS)) {
+ *stat = ch_data[unit].lsterr;
+ return ret;
+ }
+ return ret;
+}
+
+errval
+ch_position(unit, stat, chm, to, flags)
+ u_int32 unit, chm, to, flags;
+ short *stat;
+{
+ struct scsi_position_to_element scsi_cmd;
+ errval ret;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = POSITION_TO_ELEMENT;
+ scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff;
+ scsi_cmd.transport_element_address[1] = chm & 0xff;
+ scsi_cmd.source_address[0] = (to >> 8) & 0xff;
+ scsi_cmd.source_address[1] = to & 0xff;
+ scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0;
+ if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ NULL,
+ 0,
+ CHRETRIES,
+ 100000,
+ NULL,
+ flags) != ESUCCESS)) {
+ *stat = ch_data[unit].lsterr;
+ return ret;
+ }
+ return ret;
+}
+
+#ifdef __STDC__
+#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
+#else
+#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 )
+#endif
+
+/*
+ * Get the scsi driver to send a full inquiry to the
+ * device and use the results to fill out the global
+ * parameter structure.
+ */
+static errval
+ch_mode_sense(unit, flags)
+ u_int32 unit, 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;
+ int32 i, l;
+ errval errcode;
+ struct scsi_link *sc_link = ch_data[unit].sc_link;
+
+ /*
+ * First check if we have it all loaded
+ */
+ if (sc_link->flags & SDEV_MEDIA_LOADED)
+ return 0;
+
+ /*
+ * First do a mode sense
+ */
+ /* sc_link->flags &= ~SDEV_MEDIA_LOADED; *//*XXX */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MODE_SENSE;
+ scsi_cmd.byte2 = SMS_DBD;
+ scsi_cmd.page = 0x3f; /* All Pages */
+ scsi_cmd.length = sizeof(scsi_sense);
+
+ /*
+ * Read in the pages
+ */
+ if (errcode = scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(struct scsi_mode_sense),
+ (u_char *) & scsi_sense,
+ sizeof (scsi_sense),
+ CHRETRIES,
+ 5000,
+ NULL,
+ flags | SCSI_DATA_IN) != 0) {
+ if (!(flags & SCSI_SILENT))
+ printf("ch%d: could not mode sense\n", unit);
+ return (errcode);
+ }
+ sc_link->flags |= SDEV_MEDIA_LOADED;
+ l = scsi_sense[0] - 3;
+ b = &scsi_sense[4];
+
+ /*
+ * To avoid alignment problems
+ */
+/* XXX - FIX THIS FOR MSB */
+#define p2copy(valp) (valp[1]+ (valp[0]<<8));valp+=2
+#define p4copy(valp) (valp[3]+ (valp[2]<<8) + (valp[1]<<16) + (valp[0]<<24));valp+=4
+#if 0
+ printf("\nmode_sense %d\n", l);
+ for (i = 0; i < l + 4; i++) {
+ printf("%x%c", scsi_sense[i], i % 8 == 7 ? '\n' : ':');
+ } printf("\n");
+#endif
+ for (i = 0; i < l;) {
+ u_int32 pc = (*b++) & 0x3f;
+ u_int32 pl = *b++;
+ u_char *bb = b;
+ switch ((int)pc) {
+ case 0x1d:
+ ch_data[unit].chmo = p2copy(bb);
+ ch_data[unit].chms = p2copy(bb);
+ ch_data[unit].sloto = p2copy(bb);
+ ch_data[unit].slots = p2copy(bb);
+ ch_data[unit].imexo = p2copy(bb);
+ ch_data[unit].imexs = p2copy(bb);
+ ch_data[unit].driveo = p2copy(bb);
+ ch_data[unit].drives = p2copy(bb);
+ break;
+ case 0x1e:
+ ch_data[unit].rot = (*b) & 1;
+ break;
+ case 0x1f:
+ ch_data[unit].stor = *b & 0xf;
+ bb += 2;
+ ch_data[unit].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_data[unit].chmo, ch_data[unit].chms,
+ ch_data[unit].sloto, ch_data[unit].slots,
+ ch_data[unit].imexo, ch_data[unit].imexs,
+ ch_data[unit].driveo, ch_data[unit].drives,
+ ch_data[unit].rot ? "can" : "can't"));
+ return (0);
+}
diff --git a/sys/scsi/scsi_all.h b/sys/scsi/scsi_all.h
new file mode 100644
index 0000000..2e7bdcf
--- /dev/null
+++ b/sys/scsi/scsi_all.h
@@ -0,0 +1,340 @@
+/*
+ * 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
+ *
+ * $Id: scsi_all.h,v 2.0 93/10/06 21:10:28 julian Exp Locker: julian $
+ */
+
+#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_char opcode;
+ u_char bytes[11];
+};
+
+struct scsi_test_unit_ready
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[3];
+ u_char control;
+};
+
+struct scsi_send_diag
+{
+ u_char op_code;
+ u_char byte2;
+#define SSD_UOL 0x01
+#define SSD_DOL 0x02
+#define SSD_SELFTEST 0x04
+#define SSD_PF 0x10
+ u_char unused[1];
+ u_char paramlen[2];
+ u_char control;
+};
+
+struct scsi_sense
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char length;
+ u_char control;
+};
+
+struct scsi_inquiry
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char length;
+ u_char control;
+};
+
+struct scsi_mode_sense
+{
+ u_char op_code;
+ u_char byte2;
+#define SMS_DBD 0x08
+ u_char 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_char unused;
+ u_char length;
+ u_char control;
+};
+
+struct scsi_mode_sense_big
+{
+ u_char op_code;
+ u_char byte2; /* same bits as small version */
+ u_char page; /* same bits as small version */
+ u_char unused[4];
+ u_char length[2];
+ u_char control;
+};
+
+struct scsi_mode_select
+{
+ u_char op_code;
+ u_char byte2;
+#define SMS_SP 0x01
+#define SMS_PF 0x10
+ u_char unused[2];
+ u_char length;
+ u_char control;
+};
+
+struct scsi_mode_select_big
+{
+ u_char op_code;
+ u_char byte2; /* same bits as small version */
+ u_char unused[5];
+ u_char length[2];
+ u_char control;
+};
+
+struct scsi_reserve
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char length;
+ u_char control;
+};
+
+struct scsi_release
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char length;
+ u_char control;
+};
+
+struct scsi_prevent
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char how;
+ u_char control;
+};
+#define PR_PREVENT 0x01
+#define PR_ALLOW 0x00
+
+struct scsi_changedef
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused1;
+ u_char how;
+ u_char unused[4];
+ u_char datalen;
+ u_char 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_READONLY 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_char 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_char dev_qual2;
+#define SID_QUAL2 0x7F
+#define SID_REMOVABLE 0x80
+ u_char version;
+#define SID_ANSII 0x07
+#define SID_ECMA 0x38
+#define SID_ISO 0xC0
+ u_char response_format;
+ u_char additional_length;
+ u_char unused[2];
+ u_char 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_char extra[8];
+};
+
+
+struct scsi_sense_data
+{
+/* 1*/ u_char error_code; /* same bits as new version */
+ union
+ {
+ struct
+ {
+/* 2*/ u_char blockhi;
+/* 3*/ u_char blockmed;
+/* 4*/ u_char blocklow;
+ } unextended;
+ struct
+ {
+/* 2*/ u_char segment;
+/* 3*/ u_char flags; /* same bits as new version */
+/* 7*/ u_char info[4];
+/* 8*/ u_char extra_len;
+ /* allocate enough room to hold new stuff
+ ( by increasing 16 to 24 below) */
+/*32*/ u_char extra_bytes[24];
+ } extended;
+ }ext;
+}; /* total of 32 bytes */
+struct scsi_sense_data_new
+{
+/* 1*/ u_char error_code;
+#define SSD_ERRCODE 0x7F
+#define SSD_ERRCODE_VALID 0x80
+ union
+ {
+ struct /* this is deprecated, the standard says "DON'T"*/
+ {
+/* 2*/ u_char blockhi;
+/* 3*/ u_char blockmed;
+/* 4*/ u_char blocklow;
+ } unextended;
+ struct
+ {
+/* 2*/ u_char segment;
+/* 3*/ u_char flags;
+#define SSD_KEY 0x0F
+#define SSD_ILI 0x20
+#define SSD_EOM 0x40
+#define SSD_FILEMARK 0x80
+/* 7*/ u_char info[4];
+/* 8*/ u_char extra_len;
+/*12*/ u_char cmd_spec_info[4];
+/*13*/ u_char add_sense_code;
+/*14*/ u_char add_sense_code_qual;
+/*15*/ u_char fru;
+/*16*/ u_char sense_key_spec_1;
+#define SSD_SCS_VALID 0x80
+/*17*/ u_char sense_key_spec_2;
+/*18*/ u_char sense_key_spec_3;
+/*32*/ u_char extra_bytes[14];
+ } extended;
+ }ext;
+}; /* total of 32 bytes */
+
+struct blk_desc
+{
+ u_char density;
+ u_char nblocks[3];
+ u_char reserved;
+ u_char blklen[3];
+};
+
+struct scsi_mode_header
+{
+ u_char data_length; /* Sense data length */
+ u_char medium_type;
+ u_char dev_spec;
+ u_char blk_desc_len;
+};
+
+struct scsi_mode_header_big
+{
+ u_char data_length[2]; /* Sense data length */
+ u_char medium_type;
+ u_char dev_spec;
+ u_char unused[2];
+ u_char 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 0000000..cb7f009
--- /dev/null
+++ b/sys/scsi/scsi_base.c
@@ -0,0 +1,896 @@
+/*
+ * Written By Julian ELischer
+ * Copyright julian Elischer 1993.
+ * Permission is granted to use or redistribute this file in any way as long
+ * as this notice remains. Julian Elischer does not guarantee that this file
+ * is totally correct for any given task and users of this file must
+ * accept responsibility for any damage that occurs from the application of this
+ * file.
+ *
+ * Written by Julian Elischer (julian@dialix.oz.au)
+ * $Id: scsi_base.c,v 1.8 1994/05/19 22:21:05 jkh Exp $
+ */
+
+#define SPLSD splbio
+#define ESUCCESS 0
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <vm/vm.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+
+static errval sc_err1(struct scsi_xfer *);
+static errval scsi_interpret_sense(struct scsi_xfer *);
+
+#ifdef NetBSD
+#ifdef DDB
+int Debugger();
+#else /* DDB */
+#define Debugger()
+#endif /* DDB */
+#else /* NetBSD */
+#include "ddb.h"
+#if NDDB > 0
+#else /* NDDB > 0 */
+#define Debugger()
+#endif /* NDDB > 0 */
+#endif
+
+void sc_print_addr __P((struct scsi_link *sc_link));
+
+struct scsi_xfer *next_free_xs;
+
+/*
+ * 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 *
+get_xs(sc_link, flags)
+ struct scsi_link *sc_link; /* who to charge the xs to */
+ u_int32 flags; /* if this call can sleep */
+{
+ struct scsi_xfer *xs;
+ u_int32 s;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("get_xs\n"));
+ s = splbio();
+ while (!sc_link->opennings) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("sleeping\n"));
+ if (flags & SCSI_NOSLEEP) {
+ splx(s);
+ return 0;
+ }
+ sc_link->flags |= SDEV_WAITING;
+ tsleep((caddr_t)sc_link, PRIBIO, "scsiget", 0);
+ }
+ sc_link->opennings--;
+ if (xs = next_free_xs) {
+ next_free_xs = xs->next;
+ splx(s);
+ } else {
+ splx(s);
+ SC_DEBUG(sc_link, SDEV_DB3, ("making\n"));
+ xs = malloc(sizeof(*xs), M_TEMP,
+ ((flags & SCSI_NOSLEEP) ? M_NOWAIT : M_WAITOK));
+ if (xs == NULL) {
+ sc_print_addr(sc_link);
+ printf("cannot allocate scsi xs\n");
+ return (NULL);
+ }
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("returning\n"));
+ xs->sc_link = sc_link;
+ 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
+free_xs(xs, sc_link, flags)
+ struct scsi_xfer *xs;
+ struct scsi_link *sc_link; /* who to credit for returning it */
+ u_int32 flags;
+{
+ xs->next = next_free_xs;
+ next_free_xs = xs;
+
+ SC_DEBUG(sc_link, SDEV_DB3, ("free_xs\n"));
+ /* if was 0 and someone waits, wake them up */
+ if ((!sc_link->opennings++) && (sc_link->flags & SDEV_WAITING)) {
+ sc_link->flags &= ~SDEV_WAITING;
+ wakeup((caddr_t)sc_link); /* remember, it wakes them ALL up */
+ } else {
+ if (sc_link->device->start) {
+ SC_DEBUG(sc_link, SDEV_DB2, ("calling private start()\n"));
+ (*(sc_link->device->start)) (sc_link->dev_unit);
+ }
+ }
+}
+
+/*
+ * Find out from the device what its capacity is.
+ */
+u_int32
+scsi_size(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_read_cap_data rdcap;
+ struct scsi_read_capacity scsi_cmd;
+ u_int32 size;
+
+ /*
+ * make up a scsi command and ask the scsi driver to do
+ * it for you.
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = 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
+ */
+errval
+scsi_test_unit_ready(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_test_unit_ready scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = TEST_UNIT_READY;
+
+ 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 to run as SCSI-II if it can.
+ */
+errval
+scsi_change_def(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_changedef scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = 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.
+ */
+errval
+scsi_inquire(sc_link, inqbuf, flags)
+ struct scsi_link *sc_link;
+ struct scsi_inquiry_data *inqbuf;
+ u_int32 flags;
+{
+ struct scsi_inquiry scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = 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,
+ 100000,
+ NULL,
+ SCSI_DATA_IN | flags));
+}
+
+/*
+ * Prevent or allow the user to remove the media
+ */
+errval
+scsi_prevent(sc_link, type, flags)
+ struct scsi_link *sc_link;
+ u_int32 type, flags;
+{
+ struct scsi_prevent scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = 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
+ */
+errval
+scsi_start_unit(sc_link, flags)
+ struct scsi_link *sc_link;
+ u_int32 flags;
+{
+ struct scsi_start_stop scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = START_STOP;
+ scsi_cmd.how = SSS_START;
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 10000,
+ NULL,
+ flags));
+}
+
+/*
+ * Get scsi driver to send a "stop" command
+ */
+errval
+scsi_stop_unit(sc_link, eject, flags)
+ struct scsi_link *sc_link;
+ u_int32 eject;
+ u_int32 flags;
+{
+ struct scsi_start_stop scsi_cmd;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = START_STOP;
+ if (eject) {
+ scsi_cmd.how = SSS_LOEJ;
+ }
+
+ return (scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 2,
+ 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;
+ struct buf *bp = xs->bp;
+ errval retval;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("scsi_done\n"));
+#ifdef SCSIDEBUG
+ if (sc_link->flags & SDEV_DB1)
+ {
+ 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) {
+ biodone(xs->bp);
+#ifdef NOTNOW
+ 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 "));
+#endif
+ free_xs(xs, sc_link, 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"));
+ retval = (*sc_link->device->done) (xs);
+ if (retval == -1) {
+ free_xs(xs, sc_link, SCSI_NOSLEEP); /*XXX */
+ return; /* it did it all, finish up */
+ }
+ if (retval == -2) {
+ return; /* it did it all, finish up */
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("continuing with generic done()\n"));
+ }
+ if ((bp = 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((caddr_t)xs);
+ return;
+ }
+ /*
+ * Go and handle errors now.
+ * If it returns -1 then we should RETRY
+ */
+ if ((retval = sc_err1(xs)) == -1) {
+ if ((*(sc_link->adapter->scsi_cmd)) (xs)
+ == SUCCESSFULLY_QUEUED) { /* don't wake the job, ok? */
+ return;
+ }
+ xs->flags |= ITSDONE;
+ }
+ free_xs(xs, sc_link, SCSI_NOSLEEP); /* does a start if needed */
+ biodone(bp);
+}
+
+/*
+ * 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.
+ */
+errval
+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;
+ u_int32 cmdlen;
+ u_char *data_addr;
+ u_int32 datalen;
+ u_int32 retries;
+ u_int32 timeout;
+ struct buf *bp;
+ u_int32 flags;
+{
+ struct scsi_xfer *xs;
+ errval retval;
+ u_int32 s;
+
+ if (bp) flags |= SCSI_NOSLEEP;
+ SC_DEBUG(sc_link, SDEV_DB2, ("scsi_cmd\n"));
+
+ xs = get_xs(sc_link, flags); /* should wait unless booting */
+ if (!xs) return (ENOMEM);
+ /*
+ * Fill out the scsi_xfer structure. We don't know whose context
+ * the cmd is in, so copy it.
+ */
+ bcopy(scsi_cmd, &(xs->cmdstore), cmdlen);
+ xs->flags = INUSE | flags;
+ xs->sc_link = sc_link;
+ xs->retries = retries;
+ xs->timeout = timeout;
+ xs->cmd = &xs->cmdstore;
+ xs->cmdlen = cmdlen;
+ xs->data = data_addr;
+ xs->datalen = datalen;
+ xs->resid = datalen;
+ xs->bp = bp;
+/*XXX*/ /*use constant not magic number */
+ if (datalen && ((caddr_t) data_addr < (caddr_t) KERNBASE)) {
+ if (bp) {
+ printf("Data buffered space not in kernel context\n");
+#ifdef SCSIDEBUG
+ show_scsi_cmd(xs);
+#endif /* SCSIDEBUG */
+ retval = EFAULT;
+ goto bad;
+ }
+#ifdef NOBOUNCE
+ xs->data = malloc(datalen, M_TEMP, M_WAITOK);
+#else
+ xs->data = (caddr_t) vm_bounce_kva_alloc( (datalen + PAGE_SIZE - 1)/PAGE_SIZE);
+#endif
+ /* I think waiting is ok *//*XXX */
+ switch ((int)(flags & (SCSI_DATA_IN | SCSI_DATA_OUT))) {
+ case 0:
+ printf("No direction flags, assuming both\n");
+#ifdef SCSIDEBUG
+ show_scsi_cmd(xs);
+#endif /* SCSIDEBUG */
+ case SCSI_DATA_IN | SCSI_DATA_OUT: /* weird */
+ case SCSI_DATA_OUT:
+ bcopy(data_addr, xs->data, datalen);
+ break;
+ case SCSI_DATA_IN:
+ bzero(xs->data, datalen);
+ }
+ }
+retry:
+ xs->error = XS_NOERROR;
+#ifdef PARANOID
+ if (datalen && ((caddr_t) xs->data < (caddr_t) KERNBASE)) {
+ printf("It's still wrong!\n");
+ }
+#endif /*PARANOID*/
+#ifdef SCSIDEBUG
+ if (sc_link->flags & SDEV_DB3) show_scsi_xs(xs);
+#endif /* SCSIDEBUG */
+ /*
+ * 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
+ */
+
+ retval = (*(sc_link->adapter->scsi_cmd)) (xs);
+
+ switch (retval) {
+ case SUCCESSFULLY_QUEUED:
+ if (bp)
+ return retval; /* will sleep (or not) elsewhere */
+ s = splbio();
+ while (!(xs->flags & ITSDONE)) {
+ tsleep((caddr_t)xs, PRIBIO + 1, "scsicmd", 0);
+ }
+ splx(s);
+ /* fall through to check success of completed command */
+ case COMPLETE: /* Polling command completed ok */
+/*XXX*/ case HAD_ERROR: /* Polling command completed with error */
+ SC_DEBUG(sc_link, SDEV_DB3, ("back in cmd()\n"));
+ if ((retval = sc_err1(xs)) == -1)
+ goto retry;
+ break;
+
+ case TRY_AGAIN_LATER: /* adapter resource shortage */
+ SC_DEBUG(sc_link, SDEV_DB3, ("will try again \n"));
+ /* should sleep 1 sec here */
+ if (xs->retries--) {
+ xs->flags &= ~ITSDONE;
+ goto retry;
+ }
+ default:
+ retval = EIO;
+ }
+ /*
+ * If we had to copy the data out of the user's context,
+ * then do the other half (copy it back or whatever)
+ * and free the memory buffer
+ */
+ if (datalen && (xs->data != data_addr)) {
+ switch ((int)(flags & (SCSI_DATA_IN | SCSI_DATA_OUT))) {
+ case 0:
+ case SCSI_DATA_IN | SCSI_DATA_OUT: /* weird */
+ case SCSI_DATA_IN:
+ bcopy(xs->data, data_addr, datalen);
+ break;
+ }
+#ifdef NOBOUNCE
+ free(xs->data, M_TEMP);
+#else
+ vm_bounce_kva_alloc_free(xs->data, (datalen + PAGE_SIZE - 1)/PAGE_SIZE, 0);
+#endif
+ }
+ /*
+ * we have finished with the xfer stuct, free it and
+ * check if anyone else needs to be started up.
+ */
+bad:
+ free_xs(xs, sc_link, flags); /* includes the 'start' op */
+ if (bp && retval) {
+ bp->b_error = retval;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ }
+ return (retval);
+}
+
+static errval
+sc_err1(xs)
+ struct scsi_xfer *xs;
+{
+ struct buf *bp = xs->bp;
+ errval retval;
+
+ 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 ((int)xs->error) {
+ case XS_NOERROR: /* nearly always hit this one */
+ retval = ESUCCESS;
+ if (bp) {
+ bp->b_error = 0;
+ bp->b_resid = 0;
+ }
+ break;
+
+ case XS_SENSE:
+ if (bp) {
+ bp->b_error = 0;
+ bp->b_resid = 0;
+ if (retval = (scsi_interpret_sense(xs))) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = retval;
+ bp->b_resid = bp->b_bcount;
+ }
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("scsi_interpret_sense (bp) returned %d\n", retval));
+ } else {
+ retval = (scsi_interpret_sense(xs));
+ SC_DEBUG(xs->sc_link, SDEV_DB3,
+ ("scsi_interpret_sense (no bp) returned %d\n", retval));
+ }
+ break;
+
+ case XS_BUSY:
+ /*should somehow arange for a 1 sec delay here (how?) */
+ /* XXX tsleep(&localvar, priority, "foo", hz);
+ that's how! */
+ case XS_TIMEOUT:
+ /*
+ * If we can, resubmit it to the adapter.
+ */
+ if (xs->retries--) {
+ xs->error = XS_NOERROR;
+ xs->flags &= ~ITSDONE;
+ goto retry;
+ }
+ /* fall through */
+ case XS_DRIVER_STUFFUP:
+ if (bp) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ }
+ retval = EIO;
+ break;
+ default:
+ retval = EIO;
+ sc_print_addr(xs->sc_link);
+ printf("unknown error category from scsi driver\n");
+ }
+ return retval;
+retry:
+ return (-1);
+}
+
+/*
+ * 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
+ */
+static errval
+scsi_interpret_sense(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *sense;
+ struct scsi_link *sc_link = xs->sc_link;
+ u_int32 key;
+ u_int32 silent;
+ u_int32 info;
+ errval errcode;
+
+ 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"
+ };
+
+ /*
+ * If the flags say errs are ok, then always return ok.
+ */
+ if (xs->flags & SCSI_ERR_OK)
+ return (ESUCCESS);
+
+ sense = &(xs->sense);
+#ifdef SCSIDEBUG
+ if (sc_link->flags & SDEV_DB1) {
+ u_int32 count = 0;
+ 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->ext.extended.segment,
+ sense->ext.extended.flags & SSD_KEY,
+ sense->ext.extended.flags & SSD_ILI ? 1 : 0,
+ sense->ext.extended.flags & SSD_EOM ? 1 : 0,
+ sense->ext.extended.flags & SSD_FILEMARK ? 1 : 0);
+ printf("info: %x %x %x %x followed by %d extra bytes\n",
+ sense->ext.extended.info[0],
+ sense->ext.extended.info[1],
+ sense->ext.extended.info[2],
+ sense->ext.extended.info[3],
+ sense->ext.extended.extra_len);
+ printf("extra: ");
+ while (count < sense->ext.extended.extra_len) {
+ printf("%x ", sense->ext.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"));
+ errcode = (*sc_link->device->err_handler) (xs);
+ if (errcode != -1)
+ return errcode; /* errcode >= 0 better ? */
+ }
+ /* otherwise use the default */
+ silent = (xs->flags & SCSI_SILENT);
+ 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->ext.extended.flags & SSD_KEY;
+ printf(" DELAYED ERROR, key = 0x%x\n", key);
+ case 0x70:
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ info = ntohl(*((long *) sense->ext.extended.info));
+ } else {
+ info = 0;
+ }
+ key = sense->ext.extended.flags & SSD_KEY;
+
+ if (key && !silent) {
+ sc_print_addr(sc_link);
+ printf("%s", error_mes[key - 1]);
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ switch ((int)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);
+ }
+ }
+ printf("\n");
+ }
+ switch ((int)key) {
+ case 0x0: /* NO SENSE */
+ case 0x1: /* RECOVERED ERROR */
+ if (xs->resid == xs->datalen)
+ xs->resid = 0; /* not short read */
+ case 0xc: /* EQUAL */
+ return (ESUCCESS);
+ case 0x2: /* NOT READY */
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ return (EBUSY);
+ case 0x5: /* ILLEGAL REQUEST */
+ return (EINVAL);
+ case 0x6: /* UNIT ATTENTION */
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ if (sc_link->flags & SDEV_OPEN) {
+ return (EIO);
+ } else {
+ return 0;
+ }
+ case 0x7: /* DATA PROTECT */
+ return (EACCES);
+ case 0xd: /* VOLUME OVERFLOW */
+ return (ENOSPC);
+ case 0x8: /* BLANK CHECK */
+ return (ESUCCESS);
+ default:
+ return (EIO);
+ }
+ /*
+ * Not code 70, just report it
+ */
+ default:
+ if (!silent) {
+ sc_print_addr(sc_link);
+ printf("error code %d",
+ sense->error_code & SSD_ERRCODE);
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ printf(" at block no. %d (decimal)",
+ (sense->ext.unextended.blockhi << 16) +
+ (sense->ext.unextended.blockmed << 8) +
+ (sense->ext.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)
+ int val;
+ u_char *bytes;
+{
+ *bytes++ = (val & 0xff0000) >> 16;
+ *bytes++ = (val & 0xff00) >> 8;
+ *bytes = val & 0xff;
+}
+
+/*
+ * The reverse of lto3b
+ */
+int
+_3btol(bytes)
+ u_char *bytes;
+{
+ u_int32 rc;
+ rc = (*bytes++ << 16);
+ rc += (*bytes++ << 8);
+ rc += *bytes;
+ return ((int) rc);
+}
+
+/*
+ * Print out the scsi_link structure's address info.
+ */
+
+void
+sc_print_addr(sc_link)
+ struct scsi_link *sc_link;
+{
+
+ printf("%s%d(%s%d:%d:%d): ", sc_link->device->name, sc_link->dev_unit,
+ sc_link->adapter->name, sc_link->adapter_unit,
+ 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(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)) {
+ 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)
+ unsigned char *address;
+ u_int32 num;
+{
+ u_int32 x, y;
+ printf("------------------------------");
+ for (y = 0; y < num; y += 1) {
+ if (!(y % 16))
+ printf("\n%03d: ", y);
+ 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 0000000..0a4759b
--- /dev/null
+++ b/sys/scsi/scsi_cd.h
@@ -0,0 +1,229 @@
+/*
+ * 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
+ *
+ * $Id: scsi_cd.h,v 1.6 93/08/26 21:09:19 julian Exp Locker: julian $
+ */
+#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_char op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 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 0000000..85819c8
--- /dev/null
+++ b/sys/scsi/scsi_changer.h
@@ -0,0 +1,98 @@
+/*
+ * 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
+ *
+ * $Id: scsi_changer.h,v 1.5 93/08/26 21:09:22 julian Exp Locker: julian $
+ */
+#ifndef _SCSI_SCSI_CHANGER_H
+#define _SCSI_SCSI_CHANGER_H 1
+
+/*
+ * SCSI command format
+ */
+struct scsi_read_element_status
+{
+ u_char op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 0000000..480ff14
--- /dev/null
+++ b/sys/scsi/scsi_debug.h
@@ -0,0 +1,53 @@
+/*#define SCSIDEBUG 1*/
+/*
+ * Written by Julian Elischer (julian@tfs.com)
+ *
+ * $Id: scsi_debug.h,v 1.3 93/10/10 09:26:05 julian Exp Locker: julian $
+ */
+#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 DEBUGTARG 9 /*9 = dissable*/
+#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)) \
+ { \
+ printf("%s%d(%s%d:%d:%d): ", \
+ sc_link->device->name, \
+ sc_link->dev_unit, \
+ sc_link->adapter->name, \
+ sc_link->adapter_unit, \
+ sc_link->target, \
+ sc_link->lun); \
+ printf Printstuff; \
+ }
+#define SC_DEBUGN(sc_link,Level,Printstuff) \
+ if((sc_link)->flags & (Level)) \
+ { \
+ printf Printstuff; \
+ }
+#else
+#define SC_DEBUG(A,B,C) /* not included */
+#define SC_DEBUGN(A,B,C) /* not included */
+#endif
+
+#endif /*_SCSI_SCSI_DEBUG_H*/
+/* END OF FILE */
+
diff --git a/sys/scsi/scsi_disk.h b/sys/scsi/scsi_disk.h
new file mode 100644
index 0000000..60d0bcc
--- /dev/null
+++ b/sys/scsi/scsi_disk.h
@@ -0,0 +1,216 @@
+/*
+ * 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
+ *
+ * $Id: scsi_disk.h,v 1.4 93/08/26 21:09:23 julian Exp Locker: julian $
+ */
+
+/*
+ * SCSI command format
+ */
+
+#ifndef _SCSI_SCSI_DISK_H
+#define _SCSI_SCSI_DISK_H 1
+
+struct scsi_reassign_blocks
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[3];
+ u_char control;
+};
+
+struct scsi_rw
+{
+ u_char op_code;
+ 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 op_code;
+ 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 op_code;
+ 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 op_code;
+ u_char byte2;
+ u_char unused[2];
+ u_char how;
+#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_generic.h b/sys/scsi/scsi_generic.h
new file mode 100644
index 0000000..44f2bd1
--- /dev/null
+++ b/sys/scsi/scsi_generic.h
@@ -0,0 +1,63 @@
+/*
+ * Contributed by HD Associates (hd@world.std.com).
+ * Copyright (c) 1992, 1993 HD Associates
+ *
+ * Berkeley style copyright. I've just snarfed it out of stdio.h:
+ *
+ * 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 the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ *
+ * from: @(#)stdio.h 5.17 (Berkeley) 6/3/91
+ * $Id$
+ */
+
+/* generic SCSI header file. We use the same minor number format
+ * as on SGI except that the flag bits aren't available because they
+ * are used as the board index.
+ *
+ * The minor number format is:
+ * FF UUU III (FFUU UIII)
+ *
+ * Where:
+ * FF is the board index
+ * UUU are the LUN
+ * III is the SCSI ID (controller)
+ */
+
+#ifndef _SCSI_GENERIC_H_
+#define _SCSI_GENERIC_H_
+
+#define G_SCSI_FLAG(DEV) (((DEV) & 0xC0) >> 6)
+#define G_SCSI_UNIT(DEV) G_SCSI_FLAG(DEV)
+#define G_SCSI_LUN(DEV) (((DEV) & 0x38) >> 3)
+#define G_SCSI_ID(DEV) ((DEV) & 0x7)
+
+#define G_SCSI_MINOR(FLAG, LUN, ID) \
+ (((FLAG) << 6) | ((LUN) << 3) | (ID))
+
+#endif /* _SCSI_GENERIC_H_ */
diff --git a/sys/scsi/scsi_ioctl.c b/sys/scsi/scsi_ioctl.c
new file mode 100644
index 0000000..d35ad2d
--- /dev/null
+++ b/sys/scsi/scsi_ioctl.c
@@ -0,0 +1,332 @@
+/*
+ * Contributed by HD Associates (hd@world.std.com).
+ * Copyright (c) 1992, 1993 HD Associates
+ *
+ * Berkeley style copyright.
+ *
+ *
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#define b_screq b_driver1 /* a patch in buf.h */
+#define b_sc_link b_driver2 /* a patch in buf.h */
+#include <sys/proc.h>
+#include <vm/vm.h>
+
+#include "scbus.h"
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <sys/scsiio.h>
+
+void scsierr(struct buf *, int); /* XXX ??? */
+
+/*
+ * 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.
+ */
+#ifndef min
+#define min(A,B) ((A<B) ? A : B )
+#endif
+
+void scsi_user_done(xs)
+struct scsi_xfer *xs;
+{
+
+ struct buf *bp;
+ scsireq_t *screq;
+
+ 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 ;
+ }
+ screq = bp->b_screq;
+ if (!screq) { /* Is it one of ours? (the SCSI_USER bit says it is) */
+ sc_print_addr(xs->sc_link);
+ printf("User command with no request\n");
+ return ;
+ }
+
+ SC_DEBUG(xs->sc_link,SDEV_DB2,("user-done\n"));
+ screq->retsts = 0;
+ screq->status = xs->status;
+ switch((int)xs->error) {
+ case XS_NOERROR:
+ SC_DEBUG(xs->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(xs->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(xs->sc_link);
+ printf("host adapter code inconsistency\n");
+ screq->retsts = SCCMD_UNKNOWN;
+ break;
+
+ case XS_TIMEOUT:
+ SC_DEBUG(xs->sc_link,SDEV_DB3,("timeout\n"));
+ screq->retsts = SCCMD_TIMEOUT;
+ break;
+
+ case XS_BUSY:
+ SC_DEBUG(xs->sc_link,SDEV_DB3,("busy\n"));
+ screq->retsts = SCCMD_BUSY;
+ break;
+
+ default:
+ sc_print_addr(xs->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() */
+ return; /* it'll free the xs and restart any queue */
+}
+
+
+/* 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(struct buf *bp)
+{
+ errval err;
+ struct scsi_link *sc_link = bp->b_sc_link;
+ scsireq_t *screq;
+ u_int32 flags = 0;
+ int s;
+
+
+ if(!sc_link) {
+ printf("user_strat: No link pointer\n");
+ scsierr(bp,EINVAL);
+ return;
+ }
+ SC_DEBUG(sc_link,SDEV_DB2,("user_strategy\n"));
+ screq = bp->b_screq;
+ if(!screq) {
+ sc_print_addr(sc_link);
+ printf("No request block\n");
+ scsierr(bp,EINVAL);
+ return;
+ }
+
+ /* 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");
+ scsierr(bp, EIO);
+ return;
+ }
+
+ if (screq->timeout == 0) {
+ scsierr(bp, EINVAL);
+ return;
+ }
+
+ if (screq->cmdlen > sizeof(struct scsi_generic)) {
+ sc_print_addr(sc_link);
+ printf("cmdlen too big ");
+ scsierr(bp, EFAULT);
+ return;
+ }
+
+
+ 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;
+ err = scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *)screq->cmd,
+ screq->cmdlen,
+ (u_char *)bp->b_un.b_addr,
+ screq->datalen,
+ 0, /* user must do the retries *//* ignored */
+ screq->timeout,
+ bp,
+ flags | SCSI_USER);
+
+
+
+ /*because there is a bp, scsi_scsi_cmd will return immediatly*/
+ if (err)
+ {
+ scsierr(bp, err);
+ return;
+ }
+ SC_DEBUG(sc_link,SDEV_DB3,("about to sleep\n"));
+ s = splbio();
+ while(!(bp->b_flags & B_DONE))
+ {
+ tsleep((caddr_t)bp, PRIBIO, "scsistrat", 0);
+ }
+ splx(s);
+ SC_DEBUG(sc_link,SDEV_DB3,("back from sleep\n"));
+ return;
+}
+
+void scsiminphys(struct buf *bp)
+{
+ /*XXX*//* call the adapter's minphys */
+}
+
+
+/*
+ * 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
+ */
+errval scsi_do_ioctl(struct scsi_link *sc_link, int cmd, caddr_t addr, int f)
+{
+ errval ret = 0;
+ int phys;
+
+ SC_DEBUG(sc_link,SDEV_DB2,("scsi_do_ioctl(0x%x)\n",cmd));
+ switch(cmd)
+ {
+#if 0
+ case SCIOCCOMMAND:
+ {
+ /*
+ * You won't believe this, but the arg copied in
+ * from the user space, is on the kernel stack
+ * for this process, so we can't write
+ * to it at interrupt time..
+ * we need to copy it in and out!
+ * Make a static copy using malloc!
+ */
+ scsireq_t *screq2 = (scsireq_t *)addr;
+ scsireq_t *screq = (scsireq_t *)addr;
+ int rwflag = (screq->flags & SCCMD_READ) ? B_READ : B_WRITE;
+ struct buf *bp;
+ caddr_t d_addr;
+ int len;
+
+ if((unsigned int)screq < (unsigned int)KERNBASE)
+ {
+ screq = malloc(sizeof(scsireq_t),M_TEMP,M_WAITOK);
+ bcopy(screq2,screq,sizeof(scsireq_t));
+ }
+ bp = malloc(sizeof (struct buf),M_TEMP,M_WAITOK);
+ bzero(bp,sizeof(struct buf));
+ d_addr = screq->databuf;
+ bp->b_bcount = len = screq->datalen;
+ bp->b_screq = screq;
+ bp->b_sc_link = sc_link;
+ if (len) {
+ /* have data, translate it. (physio)*/
+#ifdef __NetBSD__
+#error "dev, mincntfn & uio need defining"
+ ret = physio(scsistrategy, bp, dev, rwflag,
+ mincntfn, uio);
+#else
+ ret = physio(scsistrategy,0,bp,0,rwflag,
+ d_addr,&len,curproc);
+#endif
+ } else {
+ /* if no data, no need to translate it.. */
+ bp->b_un.b_addr = 0;
+ bp->b_dev = -1; /* irrelevant info */
+ bp->b_flags = 0;
+
+ scsistrategy(bp);
+ ret = bp->b_error;
+ }
+ free(bp,M_TEMP);
+ if((unsigned int)screq2 < (unsigned int)KERNBASE)
+ {
+ bcopy(screq,screq2,sizeof(scsireq_t));
+ free(screq,M_TEMP);
+ }
+ break;
+ }
+#endif /* !NetBSD */
+ 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;
+ ret = 0;
+ break;
+ }
+ case SCIOCREPROBE:
+ {
+ extern int scsibus;
+ struct scsi_addr *sca = (struct scsi_addr *) addr;
+
+ ret = scsi_probe_busses(sca->scbus,sca->target,sca->lun);
+ break;
+ }
+ case SCIOCRECONFIG:
+ case SCIOCDECONFIG:
+ ret = EINVAL;
+ break;
+ 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;
+ break;
+ }
+
+ default:
+ ret = ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+void
+scsierr(bp,err)
+ struct buf *bp;
+ int err;
+{
+ bp->b_flags |= B_ERROR;
+ bp->b_error = err;
+ biodone(bp);
+ return;
+}
+
diff --git a/sys/scsi/scsi_tape.h b/sys/scsi/scsi_tape.h
new file mode 100644
index 0000000..ac417c2
--- /dev/null
+++ b/sys/scsi/scsi_tape.h
@@ -0,0 +1,204 @@
+/*
+ * SCSI tape interface description
+ */
+
+/*
+ * 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
+ *
+ * $Id: scsi_tape.h,v 1.8 93/08/31 21:40:16 julian Exp Locker: julian $
+ */
+#ifndef SCSI_SCSI_TAPE_H
+#define SCSI_SCSI_TAPE_H 1
+
+
+
+/*
+ * SCSI command formats
+ */
+
+
+struct scsi_rw_tape
+{
+ u_char op_code;
+ u_char byte2;
+#define SRWT_FIXED 0x01
+ u_char len[3];
+ u_char control;
+} rw_tape;
+
+struct scsi_space
+{
+ u_char op_code;
+ u_char byte2;
+#define SS_CODE 0x03
+ u_char number[3];
+ u_char control;
+} space;
+#define SP_BLKS 0
+#define SP_FILEMARKS 1
+#define SP_SEQ_FILEMARKS 2
+#define SP_EOM 3
+
+struct scsi_write_filemarks
+{
+ u_char op_code;
+ u_char byte2;
+ u_char number[3];
+ u_char control;
+} write_filemarks;
+
+struct scsi_rewind
+{
+ u_char op_code;
+ u_char byte2;
+#define SR_IMMED 0x01
+ u_char unused[3];
+ u_char control;
+} rewind;
+
+struct scsi_load
+{
+ u_char op_code;
+ u_char byte2;
+#define SL_IMMED 0x01
+ u_char unused[2];
+ u_char how;
+ u_char control;
+} load;
+#define LD_UNLOAD 0
+#define LD_LOAD 1
+#define LD_RETEN 2
+
+
+struct scsi_blk_limits
+{
+ u_char op_code;
+ u_char byte2;
+ u_char unused[3];
+ u_char control;
+} blk_limits;
+
+/*
+ * Opcodes
+ */
+
+#define REWIND 0x01
+#define READ_BLK_LIMITS 0x05
+#define READ_COMMAND_TAPE 0x08
+#define WRITE_COMMAND_TAPE 0x0a
+#define WRITE_FILEMARKS 0x10
+#define SPACE 0x11
+#define LOAD_UNLOAD 0x1b /* same as above */
+
+
+
+struct scsi_blk_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 blk_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 Real-to-Real
+ 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_SCSI_TAPE_H*/
diff --git a/sys/scsi/scsiconf.c b/sys/scsi/scsiconf.c
new file mode 100644
index 0000000..9e2dfef
--- /dev/null
+++ b/sys/scsi/scsiconf.c
@@ -0,0 +1,699 @@
+/*
+ * 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
+ *
+ * $Id: scsiconf.c,v 1.7 1993/11/18 05:02:58 rgrimes Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/malloc.h>
+#include "st.h"
+#include "sd.h"
+#include "ch.h"
+#include "cd.h"
+#include "uk.h"
+#include "su.h"
+#ifndef NSCBUS
+#define NSCBUS 8
+#endif /* NSCBUS */
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#ifdef TFS
+#include "bll.h"
+#include "cals.h"
+#include "kil.h"
+#include "scan.h"
+#else /* TFS */
+#define NBLL 0
+#define NCALS 0
+#define NKIL 0
+#define NSCAN 0
+#endif /* TFS */
+
+#if NSD > 0
+extern sdattach();
+#endif /* NSD */
+#if NST > 0
+extern stattach();
+#endif /* NST */
+#if NCH > 0
+extern chattach();
+#endif /* NCH */
+#if NCD > 0
+extern cdattach();
+#endif /* NCD */
+#if NBLL > 0
+extern bllattach();
+#endif /* NBLL */
+#if NCALS > 0
+extern calsattach();
+#endif /* NCALS */
+#if NKIL > 0
+extern kil_attach();
+#endif /* NKIL */
+#if NUK > 0
+extern ukattach();
+#endif /* NUK */
+
+/*
+ * 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_data *scbus_data[NSCBUS];
+
+/*
+ * The structure of pre-configured devices that might be turned
+ * off and therefore may not show up
+ */
+struct predefined {
+ u_char scsibus;
+ u_char dev;
+ u_char lu;
+ errval(*attach_rtn) ();
+ char *devname;
+ char flags;
+} pd[] =
+
+{
+#ifdef EXAMPLE_PREDEFINE
+#if NSD > 0
+ {
+ 0, 0, 0, sdattach, "sd", 0
+ }, /* define a disk at scsibus=0 dev=0 lu=0 */
+#endif /* NSD */
+#endif /* EXAMPLE_PREDEFINE */
+ {
+ 0, 9, 9
+ } /*illegal dummy end entry */
+};
+
+/*
+ * The structure of known drivers for autoconfiguration
+ */
+struct scsidevs {
+ u_int32 type;
+ boolean removable;
+ char *manufacturer;
+ char *model;
+ char *version;
+ errval(*attach_rtn) ();
+ char *devname;
+ char flags; /* 1 show my comparisons during boot(debug) */
+};
+
+#define SC_SHOWME 0x01
+#define SC_ONE_LU 0x00
+#define SC_MORE_LUS 0x02
+#if NUK > 0
+
+static struct scsidevs unknowndev = {
+ -1, 0, "standard", "any"
+ ,"any", ukattach, "uk", SC_MORE_LUS
+};
+#endif /*NUK*/
+static struct scsidevs knowndevs[] =
+{
+#if NSD > 0
+ {
+ T_DIRECT, T_FIXED, "standard", "any"
+ ,"any", sdattach, "sd", SC_ONE_LU
+ },
+ {
+ T_DIRECT, T_FIXED, "MAXTOR ", "XT-4170S "
+ ,"B5A ", sdattach, "mx1", SC_ONE_LU
+ },
+#endif /* NSD */
+#if NST > 0
+ {
+ T_SEQUENTIAL, T_REMOV, "standard", "any"
+ ,"any", stattach, "st", SC_ONE_LU
+ },
+#endif /* NST */
+#if NCALS > 0
+ {
+ T_PROCESSOR, T_FIXED, "standard", "any"
+ ,"any", calsattach, "cals", SC_MORE_LUS
+ },
+#endif /* NCALS */
+#if NCH > 0
+ {
+ T_CHANGER, T_REMOV, "standard", "any"
+ ,"any", chattach, "ch", SC_ONE_LU
+ },
+#endif /* NCH */
+#if NCD > 0
+#ifndef UKTEST /* make cdroms unrecognised to test the uk driver */
+ {
+ T_READONLY, T_REMOV, "SONY ", "CD-ROM CDU-8012 "
+ ,"3.1a", cdattach, "cd", SC_ONE_LU
+ },
+ {
+ T_READONLY, T_REMOV, "PIONEER ", "CD-ROM DRM-600 "
+ ,"any", cdattach, "cd", SC_MORE_LUS
+ },
+#endif
+#endif /* NCD */
+#if NBLL > 0
+ {
+ T_PROCESSOR, T_FIXED, "AEG ", "READER "
+ ,"V1.0", bllattach, "bll", SC_MORE_LUS
+ },
+#endif /* NBLL */
+#if NKIL > 0
+ {
+ T_SCANNER, T_FIXED, "KODAK ", "IL Scanner 900 "
+ ,"any", kil_attach, "kil", SC_ONE_LU
+ },
+#endif /* NKIL */
+
+ {
+ 0
+ }
+};
+
+/*
+ * Declarations
+ */
+struct predefined *scsi_get_predef();
+struct scsidevs *scsi_probedev();
+struct scsidevs *selectdev();
+errval scsi_probe_bus __P((int bus, int targ, int lun));
+
+struct scsi_device probe_switch =
+{
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "probe",
+ 0,
+ { 0, 0 }
+};
+
+/*
+ * controls debug level within the scsi subsystem -
+ * see scsiconf.h for values
+ */
+int32 scsibus = 0x0; /* This is the Nth scsibus we've seen */
+
+/*
+ * The routine called by the adapter boards to get all their
+ * devices configured in.
+ */
+void
+scsi_attachdevs(sc_link_proto)
+ struct scsi_link *sc_link_proto;
+{
+
+ if(scsibus >= NSCBUS) {
+ printf("too many scsi busses, reconfigure the kernel\n");
+ return;
+ }
+ sc_link_proto->scsibus = scsibus;
+ scbus_data[scsibus] = malloc(sizeof(struct scsibus_data), M_TEMP, M_NOWAIT);
+ if(!scbus_data[scsibus]) {
+ panic("scsi_attachdevs: malloc\n");
+ }
+ bzero(scbus_data[scsibus], sizeof(struct scsibus_data));
+ scbus_data[scsibus]->adapter_link = sc_link_proto;
+#if defined(SCSI_DELAY) && SCSI_DELAY > 2
+ printf("%s%d waiting for scsi devices to settle\n",
+ sc_link_proto->adapter->name, sc_link_proto->adapter_unit);
+#else /* SCSI_DELAY > 2 */
+#undef SCSI_DELAY
+#define SCSI_DELAY 2
+#endif /* SCSI_DELAY */
+ DELAY(1000000 * SCSI_DELAY);
+ scsibus++;
+ scsi_probe_bus(scsibus - 1,-1,-1);
+}
+
+/*
+ * Probe the requested scsi bus. It must be already set up.
+ * -1 requests all set up scsi busses.
+ * targ and lun optionally narrow the search if not -1
+ */
+errval
+scsi_probe_busses(int bus, int targ, int lun)
+{
+ if (bus == -1) {
+ for(bus = 0; bus < scsibus; bus++) {
+ scsi_probe_bus(bus, targ, lun);
+ }
+ return 0;
+ } else {
+ return scsi_probe_bus(bus, targ, lun);
+ }
+}
+
+/*
+ * Probe the requested scsi bus. It must be already set up.
+ * targ and lun optionally narrow the search if not -1
+ */
+errval
+scsi_probe_bus(int bus, int targ, int lun)
+{
+ struct scsibus_data *scsi ;
+ int maxtarg,mintarg,maxlun,minlun;
+ struct scsi_link *sc_link_proto;
+ u_int8 scsi_addr ;
+ struct scsidevs *bestmatch = NULL;
+ struct predefined *predef = NULL;
+ struct scsi_link *sc_link = NULL;
+ boolean maybe_more;
+
+ if ((bus < 0 ) || ( bus >= scsibus)) {
+ return ENXIO;
+ }
+ scsi = scbus_data[bus];
+ if(!scsi) return ENXIO;
+ sc_link_proto = scsi->adapter_link;
+ scsi_addr = sc_link_proto->adapter_targ;
+ if(targ == -1){
+ maxtarg = 7;
+ mintarg = 0;
+ } else {
+ if((targ < 0 ) || (targ > 7)) return EINVAL;
+ maxtarg = mintarg = targ;
+ }
+
+ if(lun == -1){
+ maxlun = 7;
+ minlun = 0;
+ } else {
+ if((lun < 0 ) || (lun > 7)) return EINVAL;
+ maxlun = minlun = lun;
+ }
+
+
+ for ( targ = mintarg;targ <= maxtarg; targ++) {
+ maybe_more = 0; /* by default only check 1 lun */
+ if (targ == scsi_addr) {
+ continue;
+ }
+ for ( lun = minlun; lun <= maxlun ;lun++) {
+ /*
+ * The spot appears to already have something
+ * linked in, skip past it. Must be doing a 'reprobe'
+ */
+ if(scsi->sc_link[targ][lun])
+ {/* don't do this one, but check other luns */
+ maybe_more = 1;
+ continue;
+ }
+ /*
+ * If we presently don't have a link block
+ * then allocate one to use while probing
+ */
+ if (!sc_link) {
+ sc_link = malloc(sizeof(*sc_link), M_TEMP, M_NOWAIT);
+ *sc_link = *sc_link_proto; /* struct copy */
+ sc_link->opennings = 1;
+ sc_link->device = &probe_switch;
+ }
+ sc_link->target = targ;
+ sc_link->lun = lun;
+ predef = scsi_get_predef(sc_link, &maybe_more);
+ bestmatch = scsi_probedev(sc_link, &maybe_more);
+ if ((bestmatch) && (predef)) { /* both exist */
+ if (bestmatch->attach_rtn
+ != predef->attach_rtn) {
+ printf("Clash in found/expected devices\n");
+#if NUK > 0
+ if(bestmatch == &unknowndev) {
+ printf("will link in PREDEFINED\n");
+ (*(predef->attach_rtn)) (sc_link);
+ } else
+#endif /*NUK*/
+ {
+ printf("will link in FOUND\n");
+ (*(bestmatch->attach_rtn)) (sc_link);
+ }
+ } else {
+ (*(bestmatch->attach_rtn)) (sc_link);
+ }
+ }
+ if ((bestmatch) && (!predef)) { /* just FOUND */
+ (*(bestmatch->attach_rtn)) (sc_link);
+ }
+ if ((!bestmatch) && (predef)) { /* just predef */
+ (*(predef->attach_rtn)) (sc_link);
+ }
+ if ((bestmatch) || (predef)) { /* one exists */
+ scsi->sc_link[targ][lun] = sc_link;
+ sc_link = NULL; /* it's been used */
+ }
+ if (!(maybe_more)) { /* nothing suggests we'll find more */
+ break; /* nothing here, skip to next targ */
+ }
+ /* otherwise something says we should look further */
+ }
+ }
+ if (sc_link) {
+ free(sc_link, M_TEMP);
+ }
+ return 0;
+}
+
+/*
+ * given a target and lu, check if there is a predefined device for
+ * that address
+ */
+struct predefined *
+scsi_get_predef(sc_link, maybe_more)
+ struct scsi_link *sc_link;
+ boolean *maybe_more;
+{
+ u_int8 unit = sc_link->scsibus;
+ u_int8 target = sc_link->target;
+ u_int8 lu = sc_link->lun;
+ struct scsi_adapter *scsi_adapter = sc_link->adapter;
+ u_int32 upto, numents;
+
+ numents = (sizeof(pd) / sizeof(struct predefined)) - 1;
+
+ for (upto = 0; upto < numents; upto++) {
+ if (pd[upto].scsibus != unit)
+ continue;
+ if (pd[upto].dev != target)
+ continue;
+ if (pd[upto].lu != lu)
+ continue;
+
+ printf("%s%d targ %d lun %d: <%s> - PRECONFIGURED -\n"
+ ,scsi_adapter->name
+ ,unit
+ ,target
+ ,lu
+ ,pd[upto].devname);
+ *maybe_more = pd[upto].flags & SC_MORE_LUS;
+ return (&(pd[upto]));
+ }
+ return ((struct predefined *) 0);
+}
+
+/*
+ * given a target and lu, ask the device what
+ * it is, and find the correct driver table
+ * entry.
+ */
+struct scsidevs *
+scsi_probedev(sc_link, maybe_more)
+ boolean *maybe_more;
+ struct scsi_link *sc_link;
+{
+ u_int8 unit = sc_link->adapter_unit;
+ u_int8 target = sc_link->target;
+ u_int8 lu = sc_link->lun;
+ struct scsi_adapter *scsi_adapter = sc_link->adapter;
+ struct scsidevs *bestmatch = (struct scsidevs *) 0;
+ char *dtype = (char *) 0, *desc;
+ char *qtype;
+ static struct scsi_inquiry_data inqbuf;
+ u_int32 len, qualifier, type;
+ boolean remov;
+ char manu[32];
+ char model[32];
+ char version[32];
+
+ bzero(&inqbuf, sizeof(inqbuf));
+ /*
+ * Ask the device what it is
+ */
+#ifdef SCSIDEBUG
+ if ((target == DEBUGTARG) && (lu == DEBUGLUN))
+ sc_link->flags |= (DEBUGLEVEL);
+ else
+ sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2 | SDEV_DB3 | SDEV_DB4);
+#endif /* SCSIDEBUG */
+ /* catch unit attn */
+ scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT);
+#ifdef DOUBTFULL
+ switch (scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) {
+ case 0: /* said it WAS ready */
+ case EBUSY: /* replied 'NOT READY' but WAS present, continue */
+ case ENXIO:
+ break;
+ case EIO: /* device timed out */
+ case EINVAL: /* Lun not supported */
+ default:
+ return (struct scsidevs *) 0;
+
+ }
+#endif /*DOUBTFULL*/
+#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_NOSLEEP | SCSI_NOMASK | SCSI_SILENT);
+#endif /*SCSI_2_DEF */
+
+ /* Now go ask the device all about itself */
+ if (scsi_inquire(sc_link, &inqbuf, SCSI_NOSLEEP | SCSI_NOMASK) != 0) {
+ return (struct scsidevs *) 0;
+ }
+
+ /*
+ * note what BASIC type of device it is
+ */
+ type = inqbuf.device & SID_TYPE;
+ qualifier = inqbuf.device & SID_QUAL;
+ remov = inqbuf.dev_qual2 & SID_REMOVABLE;
+
+ /*
+ * Any device qualifier that has the top bit set (qualifier&4 != 0)
+ * is vendor specific and won't match in this switch.
+ */
+
+ switch ((int)qualifier) {
+ case SID_QUAL_LU_OK:
+ qtype = "";
+ break;
+
+ case SID_QUAL_LU_OFFLINE:
+ qtype = ", Unit not Connected!";
+ break;
+
+ case SID_QUAL_RSVD:
+ qtype = ", Reserved Peripheral Qualifier!";
+ *maybe_more = 1;
+ return (struct scsidevs *) 0;
+ break;
+
+ case SID_QUAL_BAD_LU:
+ /*
+ * Check for a non-existent unit. If the device is returning
+ * this much, then we must set the flag that has
+ * the searchers keep looking on other luns.
+ */
+ qtype = ", The Target can't support this Unit!";
+ *maybe_more = 1;
+ return (struct scsidevs *) 0;
+
+ default:
+ dtype = "vendor specific";
+ qtype = "";
+ *maybe_more = 1;
+ break;
+ }
+ if (dtype == 0) {
+ switch ((int)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_READONLY:
+ dtype = "readonly";
+ 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:
+ *maybe_more = 1;
+ return (struct scsidevs *) 0;
+ default:
+ dtype = "unknown";
+ break;
+ }
+ }
+ /*
+ * Then if it's advanced enough, more detailed
+ * information
+ */
+ if ((inqbuf.version & SID_ANSII) > 0) {
+ if ((len = inqbuf.additional_length
+ + ((char *) inqbuf.unused
+ - (char *) &inqbuf))
+ > (sizeof(struct scsi_inquiry_data) - 1))
+ len = sizeof(struct scsi_inquiry_data) - 1;
+ desc = inqbuf.vendor;
+ desc[len - (desc - (char *) &inqbuf)] = 0;
+ strncpy(manu, inqbuf.vendor, 8);
+ manu[8] = 0;
+ strncpy(model, inqbuf.product, 16);
+ model[16] = 0;
+ strncpy(version, inqbuf.revision, 4);
+ version[4] = 0;
+ } else
+ /*
+ * If not advanced enough, use default values
+ */
+ {
+ desc = "early protocol device";
+ strncpy(manu, "unknown", 8);
+ strncpy(model, "unknown", 16);
+ strncpy(version, "????", 4);
+ }
+ printf("%s%d targ %d lun %d: type %d(%s) %s SCSI%d\n"
+ ,scsi_adapter->name
+ ,unit
+ ,target
+ ,lu
+ ,type
+ ,dtype
+ ,remov ? "removable" : "fixed"
+ ,inqbuf.version & SID_ANSII
+ );
+ printf("%s%d targ %d lun %d: <%s%s%s>\n"
+ ,scsi_adapter->name
+ ,unit
+ ,target
+ ,lu
+ ,manu
+ ,model
+ ,version
+ );
+ if (qtype[0]) {
+ printf("%s%d targ %d lun %d: qualifier %d(%s)\n"
+ ,scsi_adapter->name
+ ,unit
+ ,target
+ ,lu
+ ,qualifier
+ ,qtype
+ );
+ }
+ /*
+ * Try make as good a match as possible with
+ * available sub drivers
+ */
+ bestmatch = (selectdev(
+ qualifier, type, remov ? T_REMOV : T_FIXED, manu, model, version));
+ if ((bestmatch) && (bestmatch->flags & SC_MORE_LUS)) {
+ *maybe_more = 1;
+ }
+ return (bestmatch);
+}
+/*
+ * Try make as good a match as possible with
+ * available sub drivers
+ */
+struct scsidevs *
+selectdev(qualifier, type, remov, manu, model, rev)
+ u_int32 qualifier, type;
+ boolean remov;
+ char *manu, *model, *rev;
+{
+ u_int32 numents = (sizeof(knowndevs) / sizeof(struct scsidevs)) - 1;
+ u_int32 count = 0;
+ u_int32 bestmatches = 0;
+ struct scsidevs *bestmatch = (struct scsidevs *) 0;
+ struct scsidevs *thisentry = knowndevs;
+
+ type |= qualifier; /* why? */
+
+ thisentry--;
+ while (count++ < numents) {
+ thisentry++;
+ if (type != thisentry->type) {
+ continue;
+ }
+ if (bestmatches < 1) {
+ bestmatches = 1;
+ bestmatch = thisentry;
+ }
+ if (remov != thisentry->removable) {
+ continue;
+ }
+ if (bestmatches < 2) {
+ bestmatches = 2;
+ bestmatch = thisentry;
+ }
+ if (thisentry->flags & SC_SHOWME)
+ printf("\n%s-\n%s-", thisentry->manufacturer, manu);
+ if (strcmp(thisentry->manufacturer, manu)) {
+ continue;
+ }
+ if (bestmatches < 3) {
+ bestmatches = 3;
+ bestmatch = thisentry;
+ }
+ if (thisentry->flags & SC_SHOWME)
+ printf("\n%s-\n%s-", thisentry->model, model);
+ if (strcmp(thisentry->model, model)) {
+ continue;
+ }
+ if (bestmatches < 4) {
+ bestmatches = 4;
+ bestmatch = thisentry;
+ }
+ if (thisentry->flags & SC_SHOWME)
+ printf("\n%s-\n%s-", thisentry->version, rev);
+ if (strcmp(thisentry->version, rev)) {
+ continue;
+ }
+ if (bestmatches < 5) {
+ bestmatches = 5;
+ bestmatch = thisentry;
+ break;
+ }
+ }
+ if (bestmatch == (struct scsidevs *) 0) {
+#if NUK > 0
+ bestmatch = &unknowndev;
+#else
+ printf("No explicit device driver match.\n");
+#endif
+ }
+ return (bestmatch);
+}
diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h
new file mode 100644
index 0000000..f3f9586
--- /dev/null
+++ b/sys/scsi/scsiconf.h
@@ -0,0 +1,249 @@
+/*
+ * 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
+ *
+ * $Id: scsiconf.h,v 1.8 1993/12/19 00:54:55 wollman Exp $
+ */
+#ifndef SCSI_SCSICONF_H
+#define SCSI_SCSICONF_H 1
+typedef int boolean;
+typedef int errval;
+typedef long int int32;
+typedef short int int16;
+typedef char int8;
+typedef unsigned long int u_int32;
+typedef unsigned short int u_int16;
+typedef unsigned char u_int8;
+
+#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
+{
+/* 04*/ int32 (*scsi_cmd)();
+/* 08*/ void (*scsi_minphys)();
+/* 12*/ int32 (*open_target_lu)();
+/* 16*/ int32 (*close_target_lu)();
+/* 20*/ u_int32 (*adapter_info)(); /* see definitions below */
+/* 24*/ char *name; /* name of scsi bus controller */
+/* 32*/ u_long spare[2];
+};
+
+/*
+ * return values for scsi_cmd()
+ */
+#define SUCCESSFULLY_QUEUED 0
+#define TRY_AGAIN_LATER 1
+#define COMPLETE 2
+#define HAD_ERROR 3 /* do not use this, use COMPLETE */
+#define ESCAPE_NOT_SUPPORTED 4
+
+/*
+ * Format of adapter_info() response data
+ * e.g. maximum number of entries queuable to a device by the adapter
+ */
+#define AD_INF_MAX_CMDS 0x000000FF
+/* 24 bits of other adapter characteristics go here */
+
+/*
+ * 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
+{
+/* 4*/ errval (*err_handler)(); /* returns -1 to say err processing complete */
+/* 8*/ void (*start)();
+/* 12*/ int32 (*async)();
+/* 16*/ int32 (*done)(); /* returns -1 to say done processing complete */
+/* 20*/ char *name; /* name of device type */
+/* 24*/ u_int32 flags; /* device type dependent flags */
+/* 32*/ int32 spare[2];
+};
+
+/*
+ * 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
+{
+/* 1*/ u_int8 target; /* targ of this dev */
+/* 2*/ u_int8 lun; /* lun of this dev */
+/* 3*/ u_int8 adapter_targ; /* what are we on the scsi bus */
+/* 4*/ u_int8 adapter_unit; /* e.g. the 0 in aha0 */
+/* 5*/ u_int8 scsibus; /* the Nth scsibus */
+/* 6*/ u_int8 dev_unit; /* e.g. the 0 in sd0 */
+/* 7*/ u_int8 opennings; /* available operations */
+/* 8*/ u_int8 active; /* operations in progress */
+/* 10*/ u_int16 flags; /* flags that all devices have */
+/* 12*/ u_int8 spareb[2]; /* unused */
+/* 16*/ struct scsi_adapter *adapter; /* adapter entry points etc. */
+/* 20*/ struct scsi_device *device; /* device entry points etc. */
+/* 24*/ struct scsi_xfer *active_xs; /* operations under way */
+/* 28*/ void * fordriver; /* for private use by the driver */
+/* 32*/ u_int32 spare;
+};
+#define SDEV_MEDIA_LOADED 0x01 /* device figures are still valid */
+#define SDEV_WAITING 0x02 /* a process is waiting for this */
+#define SDEV_OPEN 0x04 /* at least 1 open session */
+#define SDEV_BOUNCE 0x08 /* unit requires DMA bounce buffer */
+#define SDEV_DBX 0xF0 /* debuging flags (scsi_debug.h) */
+
+/*
+ * 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_data {
+ struct scsi_link *adapter_link; /* prototype supplied by adapter */
+ struct scsi_link *sc_link[8][8];
+};
+
+/*
+ * 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
+{
+/*04*/ struct scsi_xfer *next; /* when free */
+/*08*/ u_int32 flags;
+/*12*/ struct scsi_link *sc_link; /* all about our device and adapter */
+/*13*/ u_int8 retries; /* the number of times to retry */
+/*16*/ u_int8 spare[3];
+/*20*/ int32 timeout; /* in milliseconds */
+/*24*/ struct scsi_generic *cmd; /* The scsi command to execute */
+/*28*/ int32 cmdlen; /* how long it is */
+/*32*/ u_char *data; /* dma address OR a uio address */
+/*36*/ int32 datalen; /* data len (blank if uio) */
+/*40*/ int32 resid; /* how much buffer was not touched */
+/*44*/ int32 error; /* an error value */
+/*48*/ struct buf *bp; /* If we need to associate with a buf */
+/*80*/ struct scsi_sense_data sense; /* 32 bytes*/
+ /*
+ * Believe it or not, Some targets fall on the ground with
+ * anything but a certain sense length.
+ */
+/*84*/ int32 req_sense_length; /* Explicit request sense length */
+/*88*/ int32 status; /* SCSI status */
+/*100*/ struct scsi_generic cmdstore; /* stash the command in here */
+};
+
+/*
+ * Per-request Flag values
+ */
+#define SCSI_NOSLEEP 0x01 /* Not a user... don't sleep */
+#define SCSI_NOMASK 0x02 /* dont allow interrupts.. booting */
+#define SCSI_NOSTART 0x04 /* left over from ancient history */
+#define SCSI_USER 0x08 /* Is a user cmd, call scsi_user_done */
+#define ITSDONE 0x10 /* the transfer is as done as it gets */
+#define INUSE 0x20 /* The scsi_xfer block is in use */
+#define SCSI_SILENT 0x40 /* Don't report errors to console */
+#define SCSI_ERR_OK 0x80 /* An error on this operation is OK. */
+#define SCSI_RESET 0x100 /* Reset the device in question */
+#define SCSI_DATA_UIO 0x200 /* The data address refers to a UIO */
+#define SCSI_DATA_IN 0x400 /* expect data to come INTO memory */
+#define SCSI_DATA_OUT 0x800 /* expect data to flow OUT of memory */
+#define SCSI_TARGET 0x1000 /* This defines a TARGET mode op. */
+#define SCSI_ESCAPE 0x2000 /* 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 0x0 /* there is no error, (sense is invalid) */
+#define XS_SENSE 0x1 /* Check the returned sense for the error */
+#define XS_DRIVER_STUFFUP 0x2 /* Driver failed to perform operation */
+#define XS_TIMEOUT 0x03 /* The device timed out.. turned off? */
+#define XS_SWTIMEOUT 0x04 /* The Timeout reported was caught by SW */
+#define XS_BUSY 0x08 /* The device busy, try again later? */
+
+void scsi_attachdevs __P((struct scsi_link *sc_link_proto));
+struct scsi_xfer *get_xs( struct scsi_link *sc_link, u_int32 flags);
+void free_xs(struct scsi_xfer *xs, struct scsi_link *sc_link,u_int32 flags);
+u_int32 scsi_size( struct scsi_link *sc_link,u_int32 flags);
+errval scsi_test_unit_ready( struct scsi_link *sc_link, u_int32 flags);
+errval scsi_change_def( struct scsi_link *sc_link, u_int32 flags);
+errval scsi_inquire( struct scsi_link *sc_link,
+ struct scsi_inquiry_data *inqbuf, u_int32 flags);
+errval scsi_prevent( struct scsi_link *sc_link, u_int32 type,u_int32 flags);
+errval scsi_start_unit( struct scsi_link *sc_link, u_int32 flags);
+void scsi_done(struct scsi_xfer *xs);
+errval scsi_scsi_cmd( struct scsi_link *sc_link, struct scsi_generic *scsi_cmd,
+ u_int32 cmdlen, u_char *data_addr,
+ u_int32 datalen, u_int32 retries,
+ u_int32 timeout, struct buf *bp,
+ u_int32 flags);
+errval scsi_do_ioctl __P((struct scsi_link *sc_link, int cmd, caddr_t addr, int f));
+
+void show_scsi_xs(struct scsi_xfer *xs);
+void show_scsi_cmd(struct scsi_xfer *xs);
+void show_mem(unsigned char * , u_int32);
+
+void lto3b __P((int val, u_char *bytes));
+int _3btol __P((u_char *bytes));
+
+extern void sc_print_addr(struct scsi_link *);
+
+#endif /*SCSI_SCSICONF_H*/
+/* END OF FILE */
diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c
new file mode 100644
index 0000000..691430e
--- /dev/null
+++ b/sys/scsi/sd.c
@@ -0,0 +1,1072 @@
+/*
+ * 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
+ *
+ * $Id: sd.c,v 1.23 1994/04/20 07:06:57 davidg Exp $
+ */
+
+#define SPLSD splbio
+#define ESUCCESS 0
+#include <sd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/dkbad.h>
+#include <sys/systm.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/dkstat.h>
+#include <sys/disklabel.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+#include <vm/vm.h>
+
+u_int32 sdstrats, sdqueues;
+
+#ifdef NetBSD
+#ifdef DDB
+int Debugger();
+#else /* DDB */
+#define Debugger()
+#endif /* DDB */
+#else /* NetBSD */
+#include <ddb.h>
+#if NDDB > 0
+#else /* NDDB > 0 */
+#define Debugger(s)
+#endif /* NDDB > 0 */
+#endif
+
+#define PAGESIZ 4096
+#define SECSIZE 512
+#define PDLOCATION 29
+#define BOOTRECORDSIGNATURE (0x55aa & 0x00ff)
+#define SDOUTSTANDING 2
+#define SDQSIZE 4
+#define SD_RETRIES 4
+#define MAXTRANSFER 8 /* 1 page at a time */
+
+#define MAKESDDEV(maj, unit, part) (makedev(maj,((unit<<3)+part)))
+#define UNITSHIFT 3
+#define PARTITION(z) (minor(z) & 0x07)
+#define RAW_PART 3
+#define UNIT(z) ( (minor(z) >> UNITSHIFT) )
+
+#define WHOLE_DISK(unit) ( (unit << UNITSHIFT) + RAW_PART )
+
+extern char *readdisklabel();
+errval sdgetdisklabel __P((unsigned char unit));
+errval sd_get_parms __P((int unit, int flags));
+void sdstrategy __P((struct buf *));
+void sdstart __P((u_int32));
+
+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 */
+ "sd",
+ 0,
+ { 0, 0 }
+};
+
+struct sd_data {
+ u_int32 flags;
+#define SDINIT 0x04 /* device has been init'd */
+#define SDHAVELABEL 0x10 /* have read the label */
+#define SDDOSPART 0x20 /* Have read the DOS partition table */
+#define SDWRITEPROT 0x40 /* Device in readonly mode (S/W) */
+ struct scsi_link *sc_link; /* contains our targ, lun etc. */
+ u_int32 ad_info; /* info about the adapter */
+ u_int32 cmdscount; /* cmds allowed outstanding by board */
+ boolean wlabel; /* label is writable */
+ struct disk_parms {
+ u_char heads; /* Number of heads */
+ u_int16 cyls; /* Number of cylinders */
+ u_char sectors; /*dubious *//* Number of sectors/track */
+ u_int16 secsiz; /* Number of bytes/sector */
+ u_int32 disksize; /* total number sectors */
+ } params;
+ struct disklabel disklabel;
+#ifdef NetBSD
+ struct cpu_disklabel cpudisklabel;
+#else
+ struct dos_partition dosparts[NDOSPART]; /* DOS view of disk */
+#endif /* NetBSD */
+ u_int32 partflags[MAXPARTITIONS]; /* per partition flags */
+#define SDOPEN 0x01
+ u_int32 openparts; /* one bit for each open partition */
+ u_int32 sd_start_of_unix; /* unix vs dos partitions */
+ struct buf buf_queue;
+ u_int32 xfer_block_wait;
+} *sd_data[NSD];
+
+static u_int32 next_sd_unit = 0;
+
+static struct scsi_xfer sx;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * a device suitable for this driver.
+ */
+errval
+sdattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit;
+ struct sd_data *sd;
+ struct disk_parms *dp;
+
+ unit = next_sd_unit++;
+ SC_DEBUG(sc_link, SDEV_DB2, ("sdattach: "));
+ /*
+ * Check we have the resources for another drive
+ */
+ if (unit >= NSD) {
+ printf("Too many scsi disks..(%d > %d) reconfigure kernel\n",
+ (unit + 1), NSD);
+ return 0;
+ }
+ if (sd_data[unit]) {
+ printf("sd%d: unit already has storage allocated!\n", unit);
+ return 0;
+ }
+ sd = sd_data[unit] = malloc(sizeof(struct sd_data), M_DEVBUF, M_NOWAIT);
+ if (!sd) {
+ printf("malloc failed in sd.c\n");
+ return (0);
+ }
+ bzero(sd, sizeof(struct sd_data));
+
+ dp = &(sd->params);
+ /*
+ * Store information needed to contact our base driver
+ */
+ sd->sc_link = sc_link;
+ sc_link->device = &sd_switch;
+ sc_link->dev_unit = unit;
+
+ if (sd->sc_link->adapter->adapter_info) {
+ sd->ad_info = ((*(sd->sc_link->adapter->adapter_info)) (sc_link->adapter_unit));
+ sd->cmdscount = sd->ad_info & AD_INF_MAX_CMDS;
+ if (sd->cmdscount > SDOUTSTANDING) {
+ sd->cmdscount = SDOUTSTANDING;
+ }
+ } else {
+ sd->ad_info = 1;
+ sd->cmdscount = 1;
+ }
+ sc_link->opennings = sd->cmdscount;
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ sd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK);
+ printf("sd%d: %dMB (%d total sec), %d cyl, %d head, %d sec, bytes/sec %d\n",
+ unit,
+ dp->disksize / ((1024L * 1024L) / dp->secsiz),
+ dp->disksize,
+ dp->cyls,
+ dp->heads,
+ dp->sectors,
+ dp->secsiz);
+ sd->flags |= SDINIT;
+ return 0;
+}
+
+/*
+ * open the device. Make sure the partition info is a up-to-date as can be.
+ */
+errval
+sdopen(dev)
+ int dev; /* XXX should be dev_t, but avoid promotion problems for now */
+{
+ errval errcode = 0;
+ u_int32 unit, part;
+ struct sd_data *sd;
+ struct scsi_link *sc_link;
+
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+ sd = sd_data[unit];
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= NSD) {
+ return (ENXIO);
+ }
+ /*
+ * Make sure the disk has been initialised
+ * At some point in the future, get the scsi driver
+ * to look for a new device if we are not initted
+ */
+ if ((!sd) || (!(sd->flags & SDINIT))) {
+ 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, NSD, part));
+
+ /*
+ * "unit attention" errors should occur here if the
+ * drive has been restarted or the pack changed.
+ * just ingnore the result, it's a decoy instruction
+ * The error code will act on the error though
+ * and invalidate any media information we had.
+ */
+ scsi_test_unit_ready(sc_link, 0);
+
+ /*
+ * If it's been invalidated, then forget the label
+ */
+ sc_link->flags |= SDEV_OPEN; /* unit attn becomes an err now */
+ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ sd->flags &= ~SDHAVELABEL;
+
+ /*
+ * If somebody still has it open, then forbid re-entry.
+ */
+ if (sd->openparts) {
+ errcode = ENXIO;
+ goto bad;
+ }
+ }
+ /*
+ * In case it is a funny one, tell it to start
+ * not needed for most hard drives (ignore failure)
+ */
+ scsi_start_unit(sc_link, SCSI_ERR_OK | SCSI_SILENT);
+
+ /*
+ * Check that it is still responding and ok.
+ */
+ if (scsi_test_unit_ready(sc_link, 0)) {
+ SC_DEBUG(sc_link, SDEV_DB3, ("device not reponding\n"));
+ errcode = ENXIO;
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("device ok\n"));
+
+ /*
+ * Load the physical device parameters
+ */
+ sd_get_parms(unit, 0); /* sets SDEV_MEDIA_LOADED */
+ if (sd->params.secsiz != SECSIZE) { /* XXX One day... */
+ printf("sd%d: Can't deal with %d bytes logical blocks\n",
+ unit, sd->params.secsiz);
+ Debugger("sd");
+ errcode = ENXIO;
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded "));
+
+ /* Lock the pack in. */
+ scsi_prevent(sc_link, PR_PREVENT, SCSI_ERR_OK | SCSI_SILENT);
+
+ /*
+ * Load the partition info if not already loaded.
+ */
+ if ((errcode = sdgetdisklabel(unit)) && (part != RAW_PART)) {
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel loaded "));
+ /*
+ * Check the partition is legal
+ */
+ if (part >= MAXPARTITIONS) {
+ errcode = ENXIO;
+ goto bad;
+ }
+ SC_DEBUG(sc_link, SDEV_DB3, ("partition ok"));
+
+ /*
+ * Check that the partition exists
+ */
+ if ((sd->disklabel.d_partitions[part].p_size == 0)
+ && (part != RAW_PART)) {
+ errcode = ENXIO;
+ goto bad;
+ }
+ sd->partflags[part] |= SDOPEN;
+ sd->openparts |= (1 << part);
+ SC_DEBUG(sc_link, SDEV_DB3, ("open %d %d\n", sdstrats, sdqueues));
+ return 0;
+
+bad:
+ if (!(sd->openparts)) {
+ scsi_prevent(sc_link, PR_ALLOW, SCSI_ERR_OK | SCSI_SILENT);
+ sc_link->flags &= ~SDEV_OPEN;
+ }
+ return errcode;
+}
+
+/*
+ * close the device.. only called if we are the LAST occurence of an open
+ * device. Convenient now but usually a pain.
+ */
+errval
+sdclose(dev)
+ dev_t dev;
+{
+ unsigned char unit, part;
+ struct sd_data *sd;
+
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+ sd = sd_data[unit];
+ sd->partflags[part] &= ~SDOPEN;
+ sd->openparts &= ~(1 << part);
+ scsi_prevent(sd->sc_link, PR_ALLOW, SCSI_SILENT | SCSI_ERR_OK);
+ if (!(sd->openparts))
+ sd->sc_link->flags &= ~SDEV_OPEN;
+ return 0;
+}
+
+/*
+ * trim the size of the transfer if needed, called by physio
+ * basically the smaller of our max and the scsi driver's
+ * minphys (note we have no max)
+ *
+ * Trim buffer length if buffer-size is bigger than page size
+ */
+void
+sdminphys(bp)
+ struct buf *bp;
+{
+ (*(sd_data[UNIT(bp->b_dev)]->sc_link->adapter->scsi_minphys)) (bp);
+}
+
+/*
+ * 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 buf *dp;
+ u_int32 opri;
+ struct sd_data *sd;
+ u_int32 unit;
+
+ sdstrats++;
+ unit = UNIT((bp->b_dev));
+ sd = sd_data[unit];
+ 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));
+ sdminphys(bp);
+ /*
+ * If the device has been made invalid, error out
+ */
+ if (!(sd->sc_link->flags & SDEV_MEDIA_LOADED)) {
+ sd->flags &= ~SDHAVELABEL;
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /*
+ * "soft" write protect check
+ */
+ if ((sd->flags & SDWRITEPROT) && (bp->b_flags & B_READ) == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+ /*
+ * If it's a null transfer, return immediatly
+ */
+ if (bp->b_bcount == 0) {
+ goto done;
+ }
+ /*
+ * Decide which unit and partition we are talking about
+ * only raw is ok if no label
+ */
+ if (PARTITION(bp->b_dev) != RAW_PART) {
+ if (!(sd->flags & SDHAVELABEL)) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /*
+ * do bounds checking, adjust transfer. if error, process.
+ * if end of partition, just return
+ */
+ if (bounds_check_with_label(bp, &sd->disklabel, sd->wlabel) <= 0)
+ goto done;
+ /* otherwise, process transfer request */
+ } else {
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = 0;
+ }
+ opri = SPLSD();
+ dp = &sd->buf_queue;
+
+ /*
+ * Use a bounce buffer if necessary
+ */
+/*
+#ifndef NOBOUNCE
+ if (sd->sc_link->flags & SDEV_BOUNCE)
+ vm_bounce_alloc(bp);
+#endif
+*/
+
+ /*
+ * Place it in the queue of disk activities for this disk
+ */
+/*
+ cldisksort(dp, bp, 64*1024);
+*/
+if ((bp->b_blkno < 0) || (bp->b_bcount > 3000000) /* || (bp->b_flags & B_WRITE) */) {
+ printf("blkno=%d bcount=%d flags=0x%x\n", bp->b_blkno, bp->b_bcount, bp->b_flags);
+ Debugger("");
+}
+ disksort(dp, bp);
+
+ /*
+ * Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion
+ */
+ sdstart(unit);
+
+ splx(opri);
+ return /*0*/;
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+
+ /*
+ * Correctly set the buf to indicate a completed xfer
+ */
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ return /*0*/;
+}
+
+/*
+ * 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 SPLSD from sdstrategy and scsi_done
+ */
+void
+sdstart(unit)
+ u_int32 unit;
+{
+ register struct sd_data *sd = sd_data[unit];
+ register struct scsi_link *sc_link = sd->sc_link;
+ struct buf *bp = 0;
+ struct buf *dp;
+ struct scsi_rw_big cmd;
+ u_int32 blkno, nblk;
+ struct partition *p;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("sdstart "));
+ /*
+ * Check if the device has room for another command
+ */
+ while (sc_link->opennings) {
+
+ /*
+ * 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) {
+ 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-openned
+ */
+ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ sd->flags &= ~SDHAVELABEL;
+ goto bad;
+ }
+ /*
+ * We have a buf, now we know we are going to go through
+ * With this thing..
+ *
+ * First, translate the block to absolute
+ */
+ p = sd->disklabel.d_partitions + PARTITION(bp->b_dev);
+ blkno = bp->b_blkno + p->p_offset;
+ nblk = (bp->b_bcount + 511) >> 9;
+
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.op_code = (bp->b_flags & B_READ)
+ ? READ_BIG : WRITE_BIG;
+ cmd.addr_3 = (blkno & 0xff000000UL) >> 24;
+ cmd.addr_2 = (blkno & 0xff0000) >> 16;
+ cmd.addr_1 = (blkno & 0xff00) >> 8;
+ cmd.addr_0 = blkno & 0xff;
+ cmd.length2 = (nblk & 0xff00) >> 8;
+ cmd.length1 = (nblk & 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_un.b_addr,
+ bp->b_bcount,
+ SD_RETRIES,
+ 10000,
+ bp,
+ SCSI_NOSLEEP | ((bp->b_flags & B_READ) ?
+ SCSI_DATA_IN : SCSI_DATA_OUT))
+ == SUCCESSFULLY_QUEUED) {
+ sdqueues++;
+ } else {
+bad:
+ printf("sd%d: oops not queued", unit);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ }
+ }
+}
+
+/*
+ * Perform special action on behalf of the user
+ * Knows about the internals of this device
+ */
+errval
+sdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
+{
+ /* struct sd_cmd_buf *args; */
+ errval error = 0;
+ unsigned char unit, part;
+ register struct sd_data *sd;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ unit = UNIT(dev);
+ part = PARTITION(dev);
+ sd = sd_data[unit];
+ SC_DEBUG(sd->sc_link, SDEV_DB1, ("sdioctl (0x%x)", cmd));
+
+ /*
+ * If the device is not valid.. abandon ship
+ */
+ if (!(sd->sc_link->flags & SDEV_MEDIA_LOADED))
+ return (EIO);
+ switch (cmd) {
+
+ case DIOCSBAD:
+ error = EINVAL;
+ break;
+
+ case DIOCGDINFO:
+ *(struct disklabel *) addr = sd->disklabel;
+ break;
+
+ case DIOCGPART:
+ ((struct partinfo *) addr)->disklab = &sd->disklabel;
+ ((struct partinfo *) addr)->part =
+ &sd->disklabel.d_partitions[PARTITION(dev)];
+ break;
+
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ error = setdisklabel(&sd->disklabel,
+ (struct disklabel *)addr,
+ /*(sd->flags & DKFL_BSDLABEL) ? sd->openparts : */ 0
+#ifdef NetBSD
+ ,&sd->cpudisklabel
+#else
+#if 0
+ ,sd->dosparts
+#endif
+#endif
+ );
+ if (error == 0) {
+ sd->flags |= SDHAVELABEL;
+ }
+ break;
+
+ case DIOCWLABEL:
+ sd->flags &= ~SDWRITEPROT;
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ sd->wlabel = *(boolean *) addr;
+ break;
+
+ case DIOCWDINFO:
+ sd->flags &= ~SDWRITEPROT;
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else {
+ error = setdisklabel(&sd->disklabel,
+ (struct disklabel *)addr,
+ /*(sd->flags & SDHAVELABEL) ? sd->openparts : */ 0
+#ifdef NetBSD
+ ,&sd->cpudisklabel
+#else
+#if 0
+ ,sd->dosparts
+#endif
+#endif
+ );
+ if (!error) {
+ boolean wlab;
+
+ /* ok - write will succeed */
+ sd->flags |= SDHAVELABEL;
+
+ /* simulate opening partition 0 so write succeeds */
+ sd->openparts |= (1 << 0); /* XXX */
+ wlab = sd->wlabel;
+ sd->wlabel = 1;
+ error = writedisklabel(dev, sdstrategy,
+ &sd->disklabel
+#ifdef NetBSD
+ ,&sd->cpudisklabel
+#else
+#if 0
+ ,sd->dosparts
+#endif
+#endif
+ );
+ sd->wlabel = wlab;
+ }
+ }
+ break;
+
+ default:
+ if (part == RAW_PART)
+ error = scsi_do_ioctl(sd->sc_link, cmd, addr, flag);
+ else
+ error = ENOTTY;
+ break;
+ }
+ return error;
+}
+
+/*
+ * Load the label information on the named device
+ */
+errval
+sdgetdisklabel(unsigned char unit)
+{
+ char *errstring;
+ struct sd_data *sd = sd_data[unit];
+ dev_t dev;
+
+ dev = makedev(0, (unit << UNITSHIFT) + 3);
+ /*
+ * If the inflo is already loaded, use it
+ */
+ if (sd->flags & SDHAVELABEL)
+ return (ESUCCESS);
+
+ bzero(&sd->disklabel, sizeof(struct disklabel));
+ /*
+ * make partition 3 the whole disk in case of failure then get pdinfo
+ * for historical reasons, make part a same as raw part
+ */
+ sd->disklabel.d_partitions[0].p_offset = 0;
+ sd->disklabel.d_partitions[0].p_size = sd->params.disksize;
+ sd->disklabel.d_partitions[RAW_PART].p_offset = 0;
+ sd->disklabel.d_partitions[RAW_PART].p_size = sd->params.disksize;
+ sd->disklabel.d_npartitions = MAXPARTITIONS;
+ sd->disklabel.d_secsize = SECSIZE; /* as long as it's not 0 */
+ sd->disklabel.d_ntracks = sd->params.heads;
+ sd->disklabel.d_nsectors = sd->params.sectors;
+ sd->disklabel.d_ncylinders = sd->params.cyls;
+ sd->disklabel.d_secpercyl = sd->params.heads * sd->params.sectors;
+ if (sd->disklabel.d_secpercyl == 0) {
+ sd->disklabel.d_secpercyl = 100;
+ /* as long as it's not 0 - readdisklabel divides by it (?) */
+ }
+ /*
+ * Call the generic disklabel extraction routine
+ */
+ if (errstring = readdisklabel(makedev(0, (unit << UNITSHIFT) + 3),
+ sdstrategy,
+ &sd->disklabel
+#ifdef NetBSD
+ ,&sd->cpu_disklabel,
+#else
+ ,sd->dosparts, 0
+#endif
+ )) {
+ printf("sd%d: %s\n", unit, errstring);
+ return ENXIO;
+ }
+ sd->flags |= SDHAVELABEL; /* WE HAVE IT ALL NOW */
+ return ESUCCESS;
+}
+
+/*
+ * Find out from the device what it's capacity is
+ */
+u_int32
+sd_size(unit, flags)
+ int unit, flags;
+{
+ struct scsi_read_cap_data rdcap;
+ struct scsi_read_capacity scsi_cmd;
+ u_int32 size;
+
+ /*
+ * make up a scsi command and ask the scsi driver to do
+ * it for you.
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_CAPACITY;
+
+ /*
+ * If the command works, interpret the result as a 4 byte
+ * number of blocks
+ */
+ if (scsi_scsi_cmd(sd_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) & rdcap,
+ sizeof(rdcap),
+ SD_RETRIES,
+ 2000,
+ NULL,
+ flags | SCSI_DATA_IN) != 0) {
+ printf("sd%d: could not get size\n", unit);
+ 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);
+}
+
+/*
+ * Tell the device to map out a defective block
+ */
+errval
+sd_reassign_blocks(unit, block)
+ int unit, 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.op_code = 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_data[unit]->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) & rbdata,
+ sizeof(rbdata),
+ SD_RETRIES,
+ 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.
+ */
+errval
+sd_get_parms(unit, flags)
+ int unit, flags;
+{
+ struct sd_data *sd = sd_data[unit];
+ struct disk_parms *disk_parms = &sd->params;
+ struct scsi_mode_sense scsi_cmd;
+ struct scsi_mode_sense_data {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ union disk_pages pages;
+ } scsi_sense;
+ u_int32 sectors;
+
+ /*
+ * First check if we have it all loaded
+ */
+ if (sd->sc_link->flags & SDEV_MEDIA_LOADED)
+ return 0;
+
+ /*
+ * do a "mode sense page 4"
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = 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),
+ SD_RETRIES,
+ 2000,
+ NULL,
+ flags | SCSI_DATA_IN) != 0) {
+
+ printf("sd%d could not mode sense (4).", unit);
+ printf(" Using ficticious geometry\n");
+ /*
+ * use adaptec standard ficticious geometry
+ * this depends on which controller (e.g. 1542C is
+ * different. but we have to put SOMETHING here..)
+ */
+ sectors = sd_size(unit, flags);
+ disk_parms->heads = 64;
+ disk_parms->sectors = 32;
+ disk_parms->cyls = sectors / (64 * 32);
+ disk_parms->secsiz = SECSIZE;
+ disk_parms->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 !
+ */
+ disk_parms->heads = scsi_sense.pages.rigid_geometry.nheads;
+ disk_parms->cyls = _3btol(&scsi_sense.pages.rigid_geometry.ncyl_2);
+ disk_parms->secsiz = _3btol(scsi_sense.blk_desc.blklen);
+
+ sectors = sd_size(unit, flags);
+ disk_parms->disksize = sectors;
+ /* Check if none of these values are zero */
+ if(disk_parms->heads && disk_parms->cyls) {
+ sectors /= (disk_parms->heads * disk_parms->cyls);
+ }
+ else {
+ /* set it to something reasonable */
+ sectors = 32;
+ disk_parms->heads = 64;
+ disk_parms->cyls = sectors / (64 * 32);
+ }
+ disk_parms->sectors = sectors; /* dubious on SCSI *//*XXX */
+ }
+ sd->sc_link->flags |= SDEV_MEDIA_LOADED;
+ return 0;
+}
+
+int
+sdsize(dev_t dev)
+{
+ u_int32 unit = UNIT(dev), part = PARTITION(dev), val;
+ struct sd_data *sd;
+
+ if (unit >= NSD)
+ return -1;
+
+ sd = sd_data[unit];
+ if (!sd)
+ return -1;
+ if ((sd->flags & SDINIT) == 0)
+ return -1;
+ if (sd == 0 || (sd->flags & SDHAVELABEL) == 0) {
+ val = sdopen(MAKESDDEV(major(dev), unit, RAW_PART), FREAD, S_IFBLK, 0);
+ if (val != 0)
+ return -1;
+ }
+ if (sd->flags & SDWRITEPROT)
+ return -1;
+
+ return (int)sd->disklabel.d_partitions[part].p_size;
+}
+
+/*
+ * dump all of physical memory into the partition specified, starting
+ * at offset 'dumplo' into the partition.
+ */
+errval
+sddump(dev_t dev)
+{ /* dump core after a system crash */
+ register struct sd_data *sd; /* disk unit to do the IO */
+ int32 num; /* number of sectors to write */
+ u_int32 unit, part;
+ int32 blkoff, blknum, blkcnt = MAXTRANSFER;
+ int32 nblocks;
+ char *addr;
+ struct scsi_rw_big cmd;
+ extern int Maxmem;
+ static int sddoingadump = 0;
+ extern caddr_t CADDR1; /* map the page we are about to write, here */
+ extern struct pte *CMAP1;
+ struct scsi_xfer *xs = &sx;
+ errval retval;
+ int c;
+
+ addr = (char *) 0; /* starting address */
+
+ /* toss any characters present prior to dump */
+ while ((c = sgetc(1)) && (c != 0x100)); /*syscons and pccons differ */
+
+ /* size of memory to dump */
+ num = Maxmem;
+ unit = UNIT(dev); /* eventually support floppies? */
+ part = PARTITION(dev); /* file system */
+ /* check for acceptable drive number */
+ if (unit >= NSD)
+ return (ENXIO);
+
+ sd = sd_data[unit];
+ if (!sd)
+ return (ENXIO);
+ /* was it ever initialized etc. ? */
+ if (!(sd->flags & SDINIT))
+ return (ENXIO);
+ if (sd->sc_link->flags & SDEV_MEDIA_LOADED != SDEV_MEDIA_LOADED)
+ return (ENXIO);
+ if (sd->flags & SDWRITEPROT)
+ return (ENXIO);
+
+ /* Convert to disk sectors */
+ num = (u_int32) num * NBPG / sd->disklabel.d_secsize;
+
+ /* check if controller active */
+ if (sddoingadump)
+ return (EFAULT);
+
+ nblocks = sd->disklabel.d_partitions[part].p_size;
+ blkoff = sd->disklabel.d_partitions[part].p_offset;
+
+ /* check transfer bounds against partition size */
+ if ((dumplo < 0) || ((dumplo + num) > nblocks))
+ return (EINVAL);
+
+ sddoingadump = 1;
+
+ blknum = dumplo + blkoff;
+ while (num > 0) {
+ *(int *)CMAP1 =
+ PG_V | PG_KW | trunc_page(addr);
+ tlbflush();
+ /*
+ * Fill out the scsi command
+ */
+ bzero(&cmd, sizeof(cmd));
+ cmd.op_code = WRITE_BIG;
+ cmd.addr_3 = (blknum & 0xff000000) >> 24;
+ cmd.addr_2 = (blknum & 0xff0000) >> 16;
+ cmd.addr_1 = (blknum & 0xff00) >> 8;
+ cmd.addr_0 = blknum & 0xff;
+ cmd.length2 = (blkcnt & 0xff00) >> 8;
+ cmd.length1 = (blkcnt & 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_NOMASK | SCSI_NOSLEEP | INUSE;
+ xs->sc_link = sd->sc_link;
+ xs->retries = SD_RETRIES;
+ xs->timeout = 10000; /* 10000 millisecs for a disk ! */
+ xs->cmd = (struct scsi_generic *) &cmd;
+ xs->cmdlen = sizeof(cmd);
+ xs->resid = blkcnt * 512;
+ xs->error = XS_NOERROR;
+ xs->bp = 0;
+ xs->data = (u_char *) CADDR1;
+ xs->datalen = blkcnt * 512;
+
+ /*
+ * Pass all this info to the scsi driver.
+ */
+ retval = (*(sd->sc_link->adapter->scsi_cmd)) (xs);
+ switch (retval) {
+ case SUCCESSFULLY_QUEUED:
+ case HAD_ERROR:
+ return (ENXIO); /* we said not to sleep! */
+ case COMPLETE:
+ break;
+ default:
+ return (ENXIO); /* we said not to sleep! */
+ }
+
+ if ((unsigned) addr % (1024 * 1024) == 0)
+ printf("%d ", num / 2048);
+ /* update block count */
+ num -= blkcnt;
+ blknum += blkcnt;
+ (int) addr += 512 * blkcnt;
+
+ /* operator aborting dump? */
+ if ((c = sgetc(1)) && (c != 0x100))
+ return (EINTR);
+ }
+ return (0);
+}
diff --git a/sys/scsi/st.c b/sys/scsi/st.c
new file mode 100644
index 0000000..b72cb76
--- /dev/null
+++ b/sys/scsi/st.c
@@ -0,0 +1,1936 @@
+/*
+ * Written by Julian Elischer (julian@tfs.com)(now 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.
+ *
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00098
+ * -------------------- ----- ----------------------
+ *
+ * 16 Feb 93 Julian Elischer ADDED for SCSI system
+ * 1.15 is the last version to support MACH and OSF/1
+ */
+/* $Revision: 1.15 $ */
+
+/*
+ * 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
+ *
+ * $Id: st.c,v 1.15 1994/01/29 10:30:41 rgrimes Exp $
+ */
+
+/*
+ * 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 <st.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <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 <scsi/scsi_all.h>
+#include <scsi/scsi_tape.h>
+#include <scsi/scsiconf.h>
+
+u_int32 ststrats, stqueues;
+
+/* Defines for device specific stuff */
+#define PAGE_0_SENSE_DATA_SIZE 12
+#define PAGESIZ 4096
+#define DEF_FIXED_BSIZE 512
+#define ST_RETRIES 4 /* only on non IO commands */
+
+#define MODE(z) ( (minor(z) & 0x03) )
+#define DSTY(z) ( ((minor(z) >> 2) & 0x03) )
+#define UNIT(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_int32 blksiz;
+ u_int32 quirks; /* same definitions as in rogues */
+ char density;
+ char spare[3];
+};
+
+struct rogues {
+ char *name;
+ char *manu;
+ char *model;
+ char *version;
+ u_int32 quirks; /* valid for all modes */
+ struct modes modes[4];
+};
+
+/* define behaviour codes (quirks) */
+#define ST_Q_NEEDS_PAGE_0 0x00001
+#define ST_Q_FORCE_FIXED_MODE 0x00002
+#define ST_Q_FORCE_VAR_MODE 0x00004
+#define ST_Q_SNS_HLP 0x00008 /* must do READ for good MODE SENSE */
+#define ST_Q_IGNORE_LOADS 0x00010
+#define ST_Q_BLKSIZ 0x00020 /* variable-block media_blksiz > 0 */
+
+static struct rogues gallery[] = /* ends with an all-null entry */
+{
+ {"Such an old device ", "pre-scsi", " unknown model ", "????",
+ 0,
+ {
+ {512, ST_Q_FORCE_FIXED_MODE, 0}, /* minor 0,1,2,3 */
+ {512, ST_Q_FORCE_FIXED_MODE, QIC_24}, /* minor 4,5,6,7 */
+ {0, ST_Q_FORCE_VAR_MODE, HALFINCH_1600}, /* minor 8,9,10,11 */
+ {0, ST_Q_FORCE_VAR_MODE, HALFINCH_6250} /* minor 12,13,14,15 */
+ }
+ },
+ {"Tandberg tdc3600", "TANDBERG", " TDC 3600", "????",
+ ST_Q_NEEDS_PAGE_0,
+ {
+ {0, 0, 0}, /* minor 0,1,2,3 */
+ {0, ST_Q_FORCE_VAR_MODE, QIC_525}, /* minor 4,5,6,7 */
+ {0, 0, QIC_150}, /* minor 8,9,10,11 */
+ {0, 0, QIC_120} /* minor 12,13,14,15 */
+ }
+ },
+ {"Rev 5 of the Archive 2525", "ARCHIVE ", "VIPER 2525 25462", "-005",
+ 0,
+ {
+ {0, ST_Q_SNS_HLP, 0}, /* minor 0,1,2,3 */
+ {0, ST_Q_SNS_HLP, QIC_525}, /* minor 4,5,6,7 */
+ {0, 0, QIC_150}, /* minor 8,9,10,11 */
+ {0, 0, QIC_120} /* minor 12,13,14,15 */
+ }
+ },
+ {"Archive Viper 150", "ARCHIVE ", "VIPER 150", "????",
+ ST_Q_NEEDS_PAGE_0,
+ {
+ {0, 0, 0}, /* minor 0,1,2,3 */
+ {0, 0, QIC_150}, /* minor 4,5,6,7 */
+ {0, 0, QIC_120}, /* minor 8,9,10,11 */
+ {0, 0, QIC_24} /* minor 12,13,14,15 */
+ }
+ },
+ {"Wangtek 5525ES", "WANGTEK ", "5525ES SCSI REV7", "????",
+ 0,
+ {
+ {0, 0, 0}, /* minor 0,1,2,3 */
+ {0, ST_Q_BLKSIZ, QIC_525}, /* minor 4,5,6,7 */
+ {0, 0, QIC_150}, /* minor 8,9,10,11 */
+ {0, 0, QIC_120} /* minor 12,13,14,15 */
+ }
+ },
+ {"WangDAT model 1300", "WangDAT ", "Model 1300", "????",
+ 0,
+ {
+ {0, 0, 0}, /* minor 0,1,2,3 */
+ {512, ST_Q_FORCE_FIXED_MODE, 0x13}, /* minor 4,5,6,7 */
+ {1024, ST_Q_FORCE_FIXED_MODE, 0x13}, /* minor 8,9,10,11 */
+ {0, ST_Q_FORCE_VAR_MODE, 0x13} /* minor 12,13,14,15 */
+ }
+ },
+ {(char *) 0}
+};
+
+errval st_space __P((u_int32 unit, int32 number, u_int32 what, u_int32 flags));
+errval st_rewind __P((u_int32 unit, boolean immed, u_int32 flags));
+errval st_mode_sense __P((u_int32 unit, u_int32 flags));
+errval st_decide_mode __P((u_int32 unit, boolean first_read));
+errval st_rd_blk_lim __P((u_int32 unit, u_int32 flags));
+errval st_touch_tape __P((u_int32 unit));
+errval st_write_filemarks __P((u_int32 unit, int32 number, u_int32 flags));
+errval st_load __P((u_int32 unit, u_int32 type, u_int32 flags));
+errval st_mode_select __P((u_int32 unit, u_int32 flags));
+void ststrategy();
+void stminphys();
+int32 st_chkeod();
+errval stattach();
+void ststart();
+void st_unmount();
+errval st_mount_tape();
+void st_loadquirks();
+void st_identify_drive();
+errval st_interpret_sense();
+
+#define ESUCCESS 0
+#define NOEJECT 0
+#define EJECT 1
+
+struct scsi_device st_switch =
+{
+ st_interpret_sense, /* check errors with us first */
+ ststart, /* we have a queue, and this is how we service it */
+ NULL,
+ NULL, /* use the default 'done' routine */
+ "st",
+ 0,
+ { 0, 0 }
+};
+
+struct st_data {
+/*--------------------present operating parameters, flags etc.----------------*/
+ u_int32 flags; /* see below */
+ u_int32 blksiz; /* blksiz we are using */
+ u_int32 density; /* present density */
+ u_int32 quirks; /* quirks for the open mode */
+ u_int32 last_dsty; /* last density openned */
+/*--------------------device/scsi parameters----------------------------------*/
+ struct scsi_link *sc_link; /* our link to the adpter etc. */
+/*--------------------parameters reported by the device ----------------------*/
+ u_int32 blkmin; /* min blk size */
+ u_int32 blkmax; /* max blk size */
+ struct rogues *rogues; /* if we have a rogue entry */
+/*--------------------parameters reported by the device for this media--------*/
+ u_int32 numblks; /* nominal blocks capacity */
+ u_int32 media_blksiz; /* 0 if not ST_FIXEDBLOCKS */
+ u_int32 media_density; /* this is what it said when asked */
+/*--------------------quirks for the whole drive------------------------------*/
+ u_int32 drive_quirks; /* quirks of this drive */
+/*--------------------How we should set up when openning each minor device----*/
+ struct modes modes[4]; /* plus more for each mode */
+ u_int8 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------------*/
+ unsigned char sense_data[12]; /*
+ * additional sense data needed
+ * for mode sense/select.
+ */
+ struct buf *buf_queue; /* the queue of pending IO operations */
+ struct scsi_xfer scsi_xfer; /* scsi xfer struct for this drive */
+ u_int32 xfer_block_wait; /* is a process waiting? */
+} *st_data[NST];
+
+#define ST_INITIALIZED 0x01
+#define ST_INFO_VALID 0x02
+#define ST_OPEN 0x04
+#define ST_BLOCK_SET 0x08 /* block size, mode set by ioctl */
+#define ST_WRITTEN 0x10 /* data have been written, EOD needed */
+#define ST_FIXEDBLOCKS 0x20
+#define ST_AT_FILEMARK 0x40
+#define ST_EIO_PENDING 0x80 /* we couldn't report it then (had data) */
+#define ST_NEW_MOUNT 0x100 /* still need to decide mode */
+#define ST_READONLY 0x200 /* st_mode_sense says write protected */
+#define ST_FM_WRITTEN 0x400 /*
+ * EOF file mark written -- used with
+ * ~ST_WRITTEN to indicate that multiple file
+ * marks have been written
+ */
+#define ST_BLANK_READ 0x800 /* BLANK CHECK encountered already */
+#define ST_2FM_AT_EOD 0x1000 /* write 2 file marks at EOD */
+#define ST_MOUNTED 0x2000 /* 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)
+
+static u_int32 next_st_unit = 0;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * A device suitable for this driver
+ */
+
+errval
+stattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit;
+ struct st_data *st;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("stattach: "));
+ /*
+ * Check we have the resources for another drive
+ */
+ unit = next_st_unit++;
+
+ if (unit >= NST) {
+ printf("Too many scsi tapes..(%d > %d) reconfigure kernel\n",
+ (unit + 1), NST);
+ return 0;
+ }
+ if (st_data[unit]) {
+ printf("st%d: Already has storage!\n", unit);
+ return 0;
+ }
+ sc_link->device = &st_switch;
+ sc_link->dev_unit = unit;
+ st = st_data[unit] = malloc(sizeof(struct st_data), M_DEVBUF, M_NOWAIT);
+ if (!st) {
+ printf("st%d: malloc failed in st.c\n", unit);
+ return 0;
+ }
+ bzero(st, sizeof(struct st_data));
+
+ /*
+ * Store information needed to contact our base driver
+ */
+ st->sc_link = sc_link;
+
+ /*
+ * Check if the drive is a known criminal and take
+ * Any steps needed to bring it into line
+ */
+ st_identify_drive(unit);
+
+ /*
+ * Use the subdriver to request information regarding
+ * the drive. We cannot use interrupts yet, so the
+ * request must specify this.
+ */
+ if (st_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) {
+ printf("st%d: drive offline\n", unit);
+ } else {
+ printf("st%d: density code 0x%x, ", unit, st->media_density);
+ if (!scsi_test_unit_ready(sc_link, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) {
+ if (st->media_blksiz) {
+ printf("%d-byte", st->media_blksiz);
+ } else {
+ printf("variable");
+ }
+ printf(" blocks, write-%s\n",
+ (st->flags & ST_READONLY) ? "protected" : "enabled");
+ } else {
+ printf(" drive empty\n");
+ }
+ }
+ /*
+ * Set up the buf queue for this device
+ */
+ st->buf_queue = 0;
+ st->flags |= ST_INITIALIZED;
+ return 0;
+}
+
+/*
+ * Use the inquiry routine in 'scsi_base' to get drive info so we can
+ * Further tailor our behaviour.
+ */
+void
+st_identify_drive(unit)
+ u_int32 unit;
+{
+ struct st_data *st = st_data[unit];
+ struct scsi_inquiry_data inqbuf;
+ struct rogues *finger;
+ char manu[32];
+ char model[32];
+ char model2[32];
+ char version[32];
+ u_int32 model_len;
+
+ /*
+ * Get the device type information
+ */
+ if (scsi_inquire(st->sc_link, &inqbuf,
+ SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT) != 0) {
+ printf("st%d: couldn't get device type, using default\n", unit);
+ return;
+ }
+ if ((inqbuf.version & SID_ANSII) == 0) {
+ /*
+ * If not advanced enough, use default values
+ */
+ strncpy(manu, "pre-scsi", 8);
+ manu[8] = 0;
+ strncpy(model, " unknown model ", 16);
+ model[16] = 0;
+ strncpy(version, "????", 4);
+ version[4] = 0;
+ } else {
+ strncpy(manu, inqbuf.vendor, 8);
+ manu[8] = 0;
+ strncpy(model, inqbuf.product, 16);
+ model[16] = 0;
+ strncpy(version, inqbuf.revision, 4);
+ version[4] = 0;
+ }
+
+ /*
+ * Load the parameters for this kind of device, so we
+ * treat it as appropriate for each operating mode.
+ * Only check the number of characters in the array's
+ * model entry, not the entire model string returned.
+ */
+ finger = gallery;
+ while (finger->name) {
+ model_len = 0;
+ while (finger->model[model_len] && (model_len < 32)) {
+ model2[model_len] = model[model_len];
+ model_len++;
+ }
+ model2[model_len] = 0;
+ if ((strcmp(manu, finger->manu) == 0)
+ && (strcmp(model2, finger->model) == 0 ||
+ strcmp("????????????????", finger->model) == 0)
+ && (strcmp(version, finger->version) == 0 ||
+ strcmp("????", finger->version) == 0)) {
+ printf("st%d: %s is a known rogue\n", unit, finger->name);
+ st->rogues = finger;
+ st->drive_quirks = finger->quirks;
+ st->quirks = finger->quirks; /*start value */
+ st_loadquirks(st);
+ break;
+ } else {
+ finger++; /* go to next suspect */
+ }
+ }
+}
+
+/*
+ * 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_data *st;
+{
+ int i;
+ struct modes *mode;
+ struct modes *mode2;
+
+ if (!st->rogues)
+ return;
+ mode = st->rogues->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->blksiz && ((mode->quirks | st->drive_quirks)
+ & (ST_Q_FORCE_FIXED_MODE))) {
+ mode2->blksiz = mode->blksiz;
+ st->modeflags[i] |= BLKSIZE_SET_BY_QUIRK;
+ } else {
+ if ((mode->quirks | st->drive_quirks)
+ & ST_Q_FORCE_VAR_MODE) {
+ mode2->blksiz = 0;
+ 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.
+ */
+errval
+stopen(dev, flags)
+ dev_t dev;
+ u_int32 flags;
+{
+ u_int32 unit, mode, dsty;
+ errval errno = 0;
+ struct st_data *st;
+ struct scsi_link *sc_link;
+ unit = UNIT(dev);
+ mode = MODE(dev);
+ dsty = DSTY(dev);
+
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= NST) {
+ return (ENXIO);
+ }
+ st = st_data[unit];
+ /*
+ * Make sure the device has been initialised
+ */
+ if ((st == NULL) || (!(st->flags & ST_INITIALIZED)))
+ return (ENXIO);
+
+ sc_link = st->sc_link;
+ SC_DEBUG(sc_link, SDEV_DB1, ("open: dev=0x%x (unit %d (of %d))\n"
+ ,dev, unit, NST));
+ /*
+ * Only allow one at a time
+ */
+ if (st->flags & ST_OPEN) {
+ return (EBUSY);
+ }
+ /*
+ * Throw out a dummy instruction to catch 'Unit attention
+ * errors (the error handling will invalidate all our
+ * device info if we get one, but otherwise, ignore it)
+ */
+ scsi_test_unit_ready(sc_link, SCSI_SILENT);
+
+ 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 openned to set defaults
+ * This mode does NOT ALLOW I/O, only ioctls
+ */
+ if (mode == CTLMODE)
+ return 0;
+
+ /*
+ * Check that the device is ready to use (media loaded?)
+ * This time take notice of the return result
+ */
+ if (errno = (scsi_test_unit_ready(sc_link, 0))) {
+ printf("st%d: not ready\n", unit);
+ st_unmount(unit, NOEJECT);
+ return (errno);
+ }
+ /*
+ * 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(unit, 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"));
+
+ st->flags |= ST_OPEN;
+ return (0);
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+errval
+stclose(dev)
+ dev_t dev;
+{
+ unsigned char unit, mode;
+ struct st_data *st;
+ struct scsi_link *sc_link;
+
+ unit = UNIT(dev);
+ mode = MODE(dev);
+ st = st_data[unit];
+ sc_link = st->sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("closing\n"));
+ if ((st->flags & (ST_WRITTEN | ST_FM_WRITTEN)) == ST_WRITTEN)
+ st_write_filemarks(unit, 1, 0);
+ switch (mode & 0x3) {
+ case 0:
+ case 3: /* for now */
+ st_unmount(unit, NOEJECT);
+ break;
+ case 1: /*leave mounted unless media seems to have been removed */
+ if (!(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ st_unmount(unit, NOEJECT);
+ }
+ break;
+ case 2:
+ st_unmount(unit, EJECT);
+ break;
+ }
+ sc_link->flags &= ~SDEV_OPEN;
+ st->flags &= ~ST_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.
+ */
+errval
+st_mount_tape(dev, flags)
+ dev_t dev;
+ u_int32 flags;
+{
+ u_int32 unit, mode, dsty;
+ struct st_data *st;
+ struct scsi_link *sc_link;
+ errval errno = 0;
+
+ unit = UNIT(dev);
+ mode = MODE(dev);
+ dsty = DSTY(dev);
+ st = st_data[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 (errno = st_load(unit, LD_LOAD, 0)) {
+ return (errno);
+ }
+ /*
+ * 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);
+
+ /*
+ * 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_SNS_HLP) {
+ if (errno = st_touch_tape(unit))
+ return errno;
+ }
+ /*
+ * Load the physical device parameters
+ * loads: blkmin, blkmax
+ */
+ if (errno = st_rd_blk_lim(unit, 0)) {
+ return errno;
+ }
+ /*
+ * Load the media dependent parameters
+ * includes: media_blksiz,media_density,numblks
+ * As we have a tape in, it should be reflected here.
+ * If not you may need the "quirk" above.
+ */
+ if (errno = st_mode_sense(unit, 0)) {
+ return errno;
+ }
+ /*
+ * 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->blksiz = st->modes[dsty].blksiz;
+ if (st->blksiz) {
+ st->flags |= ST_FIXEDBLOCKS;
+ }
+ } else {
+ if (errno = st_decide_mode(unit, FALSE)) {
+ return errno;
+ }
+ }
+ if (errno = st_mode_select(unit, 0)) {
+ printf("st%d: Cannot set selected mode", unit);
+ return errno;
+ }
+ scsi_prevent(sc_link, PR_PREVENT, 0); /* who cares if it fails? */
+ 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(int unit, boolean eject)
+{
+ struct st_data *st = st_data[unit];
+ struct scsi_link *sc_link = st->sc_link;
+ int32 nmarks;
+
+ if (!(st->flags & ST_MOUNTED))
+ return;
+ SC_DEBUG(sc_link, SDEV_DB1, ("unmounting\n"));
+ st_chkeod(unit, FALSE, &nmarks, SCSI_SILENT);
+ st_rewind(unit, FALSE, SCSI_SILENT);
+ scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT);
+ if (eject) {
+ st_load(unit, LD_UNLOAD, SCSI_SILENT);
+ }
+ 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)
+ */
+errval
+st_decide_mode(unit, first_read)
+ u_int32 unit;
+ boolean first_read;
+{
+ struct st_data *st = st_data[unit];
+#ifdef SCSIDEBUG
+ struct scsi_link *sc_link = st->sc_link;
+#endif
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("starting block mode decision\n"));
+
+ /*
+ * If the user hasn't already specified fixed or variable-length
+ * blocks and the block size (zero if variable-length), we'll
+ * have to try to figure them out ourselves.
+ *
+ * Our first shot at a method is, "The quirks made me do it!"
+ */
+ switch ((int)(st->quirks & (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE))) {
+ case (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE):
+ printf("st%d: bad quirks\n", unit);
+ return (EINVAL);
+ case ST_Q_FORCE_FIXED_MODE: /*specified fixed, but not what size */
+ st->flags |= ST_FIXEDBLOCKS;
+ if (st->blkmin && (st->blkmin == st->blkmax))
+ st->blksiz = st->blkmin;
+ else if (st->media_blksiz > 0)
+ st->blksiz = st->media_blksiz;
+ else
+ st->blksiz = DEF_FIXED_BSIZE;
+ SC_DEBUG(sc_link, SDEV_DB3, ("Quirks force fixed mode(%d)\n",
+ st->blksiz));
+ goto done;
+ case ST_Q_FORCE_VAR_MODE:
+ st->flags &= ~ST_FIXEDBLOCKS;
+ st->blksiz = 0;
+ SC_DEBUG(sc_link, SDEV_DB3, ("Quirks force variable mode\n"));
+ goto done;
+ }
+ /*
+ * 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->blksiz = 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 ((int)st->density) {
+ case HALFINCH_800:
+ case HALFINCH_1600:
+ case HALFINCH_6250:
+ case DDS:
+ st->flags &= ~ST_FIXEDBLOCKS;
+ st->blksiz = 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_blksiz > 0) {
+ st->blksiz = st->media_blksiz;
+ } else {
+ st->blksiz = 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_BLKSIZ)
+ || (st->media_blksiz == 0)
+ || (st->media_blksiz == DEF_FIXED_BSIZE)
+ || (st->media_blksiz == 1024))) {
+ if (st->media_blksiz == 0) {
+ st->flags &= ~ST_FIXEDBLOCKS;
+ } else {
+ st->flags |= ST_FIXEDBLOCKS;
+ }
+ st->blksiz = st->media_blksiz;
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("Used media_blksiz of %d\n", st->media_blksiz));
+ goto done;
+ }
+ /*
+ * We're getting no hints from any direction. Choose variable-
+ * length blocks arbitrarily.
+ */
+ st->flags &= ~ST_FIXEDBLOCKS;
+ st->blksiz = 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 ((int)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;
+}
+
+/*
+ * trim the size of the transfer if needed,
+ * called by physio
+ * basically the smaller of our min and the scsi driver's
+ * minphys
+ */
+void
+stminphys(bp)
+ struct buf *bp;
+{
+ (*(st_data[UNIT(bp->b_dev)]->sc_link->adapter->scsi_minphys)) (bp);
+}
+
+/*
+ * 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 buf **dp;
+ unsigned char unit;
+ u_int32 opri;
+ struct st_data *st;
+
+ ststrats++;
+ unit = UNIT((bp->b_dev));
+ st = st_data[unit];
+ SC_DEBUG(st->sc_link, SDEV_DB1,
+ (" strategy: %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->blksiz) {
+ printf("st%d: bad request, must be multiple of %d\n",
+ unit, st->blksiz);
+ bp->b_error = EIO;
+ goto bad;
+ }
+ }
+ /*
+ * as are out-of-range requests on variable drives.
+ */
+ else if (bp->b_bcount < st->blkmin || bp->b_bcount > st->blkmax) {
+ printf("st%d: bad request, must be between %d and %d\n",
+ unit, st->blkmin, st->blkmax);
+ bp->b_error = EIO;
+ goto bad;
+ }
+ stminphys(bp);
+ opri = splbio();
+
+ /*
+ * Use a bounce buffer if necessary
+ */
+#ifndef NOBOUNCE
+ if (st->sc_link->flags & SDEV_BOUNCE)
+ vm_bounce_alloc(bp);
+#endif
+
+ /*
+ * 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);
+ while (*dp) {
+ dp = &((*dp)->b_actf);
+ }
+ *dp = bp;
+ bp->b_actf = NULL;
+
+ /*
+ * 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(unit);
+
+ splx(opri);
+ 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(unit)
+ u_int32 unit;
+{
+ struct st_data *st = st_data[unit];
+ struct scsi_link *sc_link = st->sc_link;
+ register struct buf *bp = 0;
+ struct scsi_rw_tape cmd;
+ u_int32 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->opennings != 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;
+ }
+ if ((bp = st->buf_queue) == NULL) {
+ return; /* no work to bother with */
+ }
+ st->buf_queue = bp->b_actf;
+
+ /*
+ * 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;
+ goto badnews;
+ }
+ /*
+ * 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(unit, 0, SP_FILEMARKS, 0) !=
+ ESUCCESS)
+ goto badnews;
+ } 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.op_code = WRITE_COMMAND_TAPE;
+ st->flags &= ~ST_FM_WRITTEN;
+ st->flags |= ST_WRITTEN;
+ flags = SCSI_DATA_OUT;
+ } else {
+ cmd.op_code = READ_COMMAND_TAPE;
+ 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 |= SRWT_FIXED;
+ lto3b(bp->b_bcount / st->blksiz, 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_un.b_addr,
+ bp->b_bcount,
+ 0, /* can't retry a read on a tape really */
+ 100000,
+ bp,
+ flags | SCSI_NOSLEEP) == SUCCESSFULLY_QUEUED) {
+ stqueues++;
+ } else {
+badnews:
+ printf("st%d: oops not queued\n", unit);
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ biodone(bp);
+ }
+ } /* go back and see if we can cram more work in.. */
+}
+
+/*
+ * Perform special action on behalf of the user;
+ * knows about the internals of this device
+ */
+errval
+stioctl(dev, cmd, arg, flag)
+ dev_t dev;
+ int cmd;
+ caddr_t arg;
+ int flag;
+{
+ errval errcode = 0;
+ unsigned char unit;
+ u_int32 number, flags, dsty;
+ struct st_data *st;
+ u_int32 hold_blksiz;
+ u_int32 hold_density;
+ int32 nmarks;
+ 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 = UNIT(dev);
+ dsty = DSTY(dev);
+ st = st_data[unit];
+ hold_blksiz = st->blksiz;
+ 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_density = st->density;
+ g->mt_blksiz = st->blksiz;
+ g->mt_density0 = st->modes[0].density;
+ g->mt_density1 = st->modes[1].density;
+ g->mt_density2 = st->modes[2].density;
+ g->mt_density3 = st->modes[3].density;
+ g->mt_blksiz0 = st->modes[0].blksiz;
+ g->mt_blksiz1 = st->modes[1].blksiz;
+ g->mt_blksiz2 = st->modes[2].blksiz;
+ g->mt_blksiz3 = st->modes[3].blksiz;
+ 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 */
+ errcode = st_write_filemarks(unit, number, flags);
+ break;
+ case MTBSF: /* backward space file */
+ number = -number;
+ case MTFSF: /* forward space file */
+ errcode = st_chkeod(unit, FALSE, &nmarks, flags);
+ if (errcode == ESUCCESS)
+ errcode = st_space(unit, number - nmarks,
+ SP_FILEMARKS, flags);
+ break;
+ case MTBSR: /* backward space record */
+ number = -number;
+ case MTFSR: /* forward space record */
+ errcode = st_chkeod(unit, TRUE, &nmarks, flags);
+ if (errcode == ESUCCESS)
+ errcode = st_space(unit, number, SP_BLKS, flags);
+ break;
+ case MTREW: /* rewind */
+ errcode = st_rewind(unit, FALSE, flags);
+ break;
+ case MTOFFL: /* rewind and put the drive offline */
+ st_unmount(unit, EJECT);
+ break;
+ case MTNOP: /* no operation, sets status only */
+ 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");
+ errcode = EINVAL;
+ break;
+ }
+#endif
+ if (number == 0) {
+ st->flags &= ~ST_FIXEDBLOCKS;
+ } else {
+ if ((st->blkmin || st->blkmax) /* they exist */
+ &&((number < st->blkmin
+ || number > st->blkmax))) {
+ errcode = EINVAL;
+ break;
+ }
+ st->flags |= ST_FIXEDBLOCKS;
+ }
+ st->blksiz = 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) {
+ errcode = EINVAL;
+ } else {
+ st->density = number;
+ }
+ goto try_new_value;
+
+ default:
+ errcode = EINVAL;
+ }
+ break;
+ }
+ case MTIOCIEOT:
+ case MTIOCEEOT:
+ break;
+ default:
+ if(MODE(dev) == CTLMODE)
+ errcode = scsi_do_ioctl(st->sc_link,cmd,arg,flag);
+ else
+ errcode = ENOTTY;
+ break;
+ }
+ return errcode;
+/*-----------------------------*/
+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 (errcode = st_mode_select(unit, 0)) { /* put it back as it was */
+ printf("st%d: Cannot set selected mode", unit);
+ st->density = hold_density;
+ st->blksiz = hold_blksiz;
+ if (st->blksiz) {
+ st->flags |= ST_FIXEDBLOCKS;
+ } else {
+ st->flags &= ~ST_FIXEDBLOCKS;
+ }
+ return (errcode);
+ }
+ /*
+ * 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 (MODE(dev) == 0x03) {
+ /* special mode */
+ /* XXX */
+ switch ((short) (mt->mt_op)) {
+ case MTSETBSIZ:
+ st->modes[dsty].blksiz = st->blksiz;
+ 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.
+ */
+errval
+st_read(unit, buf, size, flags)
+ u_int32 unit, size, flags;
+ char *buf;
+{
+ struct scsi_rw_tape scsi_cmd;
+ struct st_data *st = st_data[unit];
+
+ /*
+ * If it's a null transfer, return immediatly
+ */
+ if (size == 0) {
+ return (ESUCCESS);
+ }
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_COMMAND_TAPE;
+ if (st->flags & ST_FIXEDBLOCKS) {
+ scsi_cmd.byte2 |= SRWT_FIXED;
+ lto3b(size / (st->blksiz ? st->blksiz : DEF_FIXED_BSIZE),
+ scsi_cmd.len);
+ } else {
+ lto3b(size, scsi_cmd.len);
+ }
+ return (scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) buf,
+ size,
+ 0, /* not on io commands */
+ 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.
+ */
+errval
+st_rd_blk_lim(unit, flags)
+ u_int32 unit, flags;
+{
+ struct scsi_blk_limits scsi_cmd;
+ struct scsi_blk_limits_data scsi_blkl;
+ struct st_data *st = st_data[unit];
+ errval errno;
+ struct scsi_link *sc_link = st->sc_link;
+
+ /*
+ * First check if we have it all loaded
+ */
+ if ((sc_link->flags & SDEV_MEDIA_LOADED))
+ return 0;
+
+ /*
+ * do a 'Read Block Limits'
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = READ_BLK_LIMITS;
+
+ /*
+ * do the command, update the global values
+ */
+ if (errno = scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) & scsi_blkl,
+ sizeof(scsi_blkl),
+ ST_RETRIES,
+ 5000,
+ NULL,
+ flags | SCSI_DATA_IN)) {
+ return errno;
+ }
+ st->blkmin = b2tol(scsi_blkl.min_length);
+ st->blkmax = _3btol(&scsi_blkl.max_length_2);
+
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("(%d <= blksiz <= %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)
+ */
+errval
+st_mode_sense(unit, flags)
+ u_int32 unit, flags;
+{
+ u_int32 scsi_sense_len;
+ errval errno;
+ char *scsi_sense_ptr;
+ struct scsi_mode_sense scsi_cmd;
+ struct scsi_sense {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ } scsi_sense;
+
+ struct scsi_sense_page_0 {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ unsigned char sense_data[PAGE_0_SENSE_DATA_SIZE];
+ /* Tandberg tape drives returns page 00
+ * with the sense data, whether or not
+ * you want it( ie the don't like you
+ * saying you want anything less!!!!!
+ * They also expect page 00
+ * back when you issue a mode select
+ */
+ } scsi_sense_page_0;
+ struct st_data *st = st_data[unit];
+ struct scsi_link *sc_link = st->sc_link;
+
+ /*
+ * Define what sort of structure we're working with
+ */
+ if (st->quirks & ST_Q_NEEDS_PAGE_0) {
+ scsi_sense_len = sizeof(scsi_sense_page_0);
+ scsi_sense_ptr = (char *) &scsi_sense_page_0;
+ } else {
+ scsi_sense_len = sizeof(scsi_sense);
+ scsi_sense_ptr = (char *) &scsi_sense;
+ }
+ /*
+ * Set up a mode sense
+ */
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MODE_SENSE;
+ scsi_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 (errno = scsi_scsi_cmd(sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) scsi_sense_ptr,
+ scsi_sense_len,
+ ST_RETRIES,
+ 5000,
+ NULL,
+ flags | SCSI_DATA_IN)) {
+ return errno;
+ }
+ st->numblks = _3btol(((struct scsi_sense *)scsi_sense_ptr)->blk_desc.nblocks);
+ st->media_blksiz = _3btol(((struct scsi_sense *)scsi_sense_ptr)->blk_desc.blklen);
+ st->media_density = ((struct scsi_sense *) scsi_sense_ptr)->blk_desc.density;
+ if (((struct scsi_sense *) scsi_sense_ptr)->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_blksiz,
+ st->flags & ST_READONLY ? "protected" : "enabled"));
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("%sbuffered\n",
+ ((struct scsi_sense *) scsi_sense_ptr)->header.dev_spec
+ & SMH_DSP_BUFF_MODE ? "" : "un"));
+ if (st->quirks & ST_Q_NEEDS_PAGE_0) {
+ bcopy(((struct scsi_sense_page_0 *) scsi_sense_ptr)->sense_data,
+ st->sense_data,
+ sizeof(((struct scsi_sense_page_0 *) scsi_sense_ptr)->sense_data));
+ }
+ 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.
+ */
+errval
+st_mode_select(unit, flags)
+ u_int32 unit, flags;
+{
+ u_int32 dat_len;
+ char *dat_ptr;
+ struct scsi_mode_select scsi_cmd;
+ struct dat {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ } dat;
+ struct dat_page_0 {
+ struct scsi_mode_header header;
+ struct blk_desc blk_desc;
+ unsigned char sense_data[PAGE_0_SENSE_DATA_SIZE];
+ } dat_page_0;
+ struct st_data *st = st_data[unit];
+
+ /*
+ * Define what sort of structure we're working with
+ */
+ if (st->quirks & ST_Q_NEEDS_PAGE_0) {
+ dat_len = sizeof(dat_page_0);
+ dat_ptr = (char *) &dat_page_0;
+ } else {
+ dat_len = sizeof(dat);
+ dat_ptr = (char *) &dat;
+ }
+ /*
+ * Set up for a mode select
+ */
+ bzero(dat_ptr, dat_len);
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = MODE_SELECT;
+ scsi_cmd.length = dat_len;
+ ((struct dat *) dat_ptr)->header.blk_desc_len = sizeof(struct blk_desc);
+ ((struct dat *) dat_ptr)->header.dev_spec |= SMH_DSP_BUFF_MODE_ON;
+ ((struct dat *) dat_ptr)->blk_desc.density = st->density;
+ if (st->flags & ST_FIXEDBLOCKS) {
+ lto3b(st->blksiz, ((struct dat *) dat_ptr)->blk_desc.blklen);
+ }
+ if (st->quirks & ST_Q_NEEDS_PAGE_0) {
+ bcopy(st->sense_data, ((struct dat_page_0 *) dat_ptr)->sense_data,
+ sizeof(((struct dat_page_0 *) dat_ptr)->sense_data));
+ /* the Tandberg tapes need the block size to */
+ /* be set on each mode sense/select. */
+ }
+ /*
+ * do the command
+ */
+ return (scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ (u_char *) dat_ptr,
+ dat_len,
+ ST_RETRIES,
+ 5000,
+ NULL,
+ flags | SCSI_DATA_OUT));
+}
+
+/*
+ * skip N blocks/filemarks/seq filemarks/eom
+ */
+errval
+st_space(unit, number, what, flags)
+ u_int32 unit, what, flags;
+ int32 number;
+{
+ errval error;
+ struct scsi_space scsi_cmd;
+ struct st_data *st = st_data[unit];
+
+ switch ((int)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(unit, 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 discover 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++; /* dubious */
+ }
+ }
+ if (number == 0) {
+ return (ESUCCESS);
+ }
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = SPACE;
+ scsi_cmd.byte2 = what & SS_CODE;
+ lto3b(number, scsi_cmd.number);
+ return (scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 0, /* no retries please , just fail */
+ 600000, /* 10 mins enough? */
+ NULL,
+ flags));
+}
+
+/*
+ * write N filemarks
+ */
+errval
+st_write_filemarks(unit, number, flags)
+ u_int32 unit, flags;
+ int32 number;
+{
+ struct scsi_write_filemarks scsi_cmd;
+ struct st_data *st = st_data[unit];
+
+ /*
+ * It's hard to write a negative number of file marks.
+ * Don't try.
+ */
+ if (number < 0) {
+ return EINVAL;
+ }
+ switch ((int)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(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = WRITE_FILEMARKS;
+ lto3b(number, scsi_cmd.number);
+ return scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ 0, /* no retries, just fail */
+ 100000, /* 10 secs.. (may need to repos head ) */
+ 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.
+ */
+int32
+st_chkeod(unit, position, nmarks, flags)
+ u_int32 unit;
+ boolean position;
+ int32 *nmarks;
+ u_int32 flags;
+{
+ errval error;
+ struct st_data *st = st_data[unit];
+
+ switch ((int)(st->flags & (ST_WRITTEN | ST_FM_WRITTEN | ST_2FM_AT_EOD))) {
+ default:
+ *nmarks = 0;
+ return (ESUCCESS);
+ 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(unit, *nmarks, flags);
+ if (position && (error == ESUCCESS))
+ error = st_space(unit, -*nmarks, SP_FILEMARKS, flags);
+ return (error);
+}
+
+/*
+ * load/unload (with retension if true)
+ */
+errval
+st_load(unit, type, flags)
+ u_int32 unit, type, flags;
+{
+ struct scsi_load scsi_cmd;
+ struct st_data *st = st_data[unit];
+ struct scsi_link *sc_link = st->sc_link;
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ if (type != LD_LOAD) {
+ errval error;
+ int32 nmarks;
+
+ error = st_chkeod(unit, FALSE, &nmarks, flags);
+ if (error != ESUCCESS)
+ return (error);
+ sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ }
+ if (st->quirks & ST_Q_IGNORE_LOADS)
+ return (0);
+ scsi_cmd.op_code = LOAD_UNLOAD;
+ scsi_cmd.how |= type;
+ return (scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ ST_RETRIES,
+ 300000, /* 5 min */
+ NULL,
+ flags));
+}
+
+/*
+ * Rewind the device
+ */
+errval
+st_rewind(unit, immed, flags)
+ u_int32 unit, flags;
+ boolean immed;
+{
+ struct scsi_rewind scsi_cmd;
+ struct st_data *st = st_data[unit];
+ errval error;
+ int32 nmarks;
+
+ error = st_chkeod(unit, FALSE, &nmarks, flags);
+ if (error != ESUCCESS)
+ return (error);
+ st->flags &= ~ST_PER_ACTION;
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.op_code = REWIND;
+ scsi_cmd.byte2 = immed ? SR_IMMED : 0;
+ return (scsi_scsi_cmd(st->sc_link,
+ (struct scsi_generic *) &scsi_cmd,
+ sizeof(scsi_cmd),
+ 0,
+ 0,
+ ST_RETRIES,
+ immed ? 5000 : 300000, /* 5 sec or 5 min */
+ NULL,
+ flags));
+}
+
+#ifdef NETBSD
+#define SIGNAL_SHORT_READ
+#else
+#define SIGNAL_SHORT_READ bp->b_flags |= B_ERROR;
+#endif
+
+/*
+ * 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)
+ */
+errval
+st_interpret_sense(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *sc_link = xs->sc_link;
+ struct scsi_sense_data *sense = &(xs->sense);
+ boolean silent = xs->flags & SCSI_SILENT;
+ struct buf *bp = xs->bp;
+ u_int32 unit = sc_link->dev_unit;
+ struct st_data *st = st_data[unit];
+ u_int32 key;
+ int32 info;
+
+ /*
+ * Get the sense fields and work out what code
+ */
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ info = ntohl(*((int32 *) sense->ext.extended.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->blksiz;
+ if (sense->ext.extended.flags & SSD_EOM) {
+ st->flags |= ST_EIO_PENDING;
+ if (bp) {
+ bp->b_resid = xs->resid;
+ SIGNAL_SHORT_READ
+ }
+ }
+ if (sense->ext.extended.flags & SSD_FILEMARK) {
+ st->flags |= ST_AT_FILEMARK;
+ if (bp) {
+ bp->b_resid = xs->resid;
+ SIGNAL_SHORT_READ
+ }
+ }
+ if (sense->ext.extended.flags & SSD_ILI) {
+ st->flags |= ST_EIO_PENDING;
+ if (bp) {
+ bp->b_resid = xs->resid;
+ SIGNAL_SHORT_READ
+ }
+ if (sense->error_code & SSD_ERRCODE_VALID &&
+ !silent)
+ printf("st%d: block wrong size"
+ ", %d blocks residual\n", unit
+ ,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_SNS_HLP) &&
+ !(sc_link->flags & SDEV_MEDIA_LOADED)) {
+ st->blksiz -= 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;
+ SIGNAL_SHORT_READ
+ }
+ return 0;
+ }
+ }
+ } else { /* must be variable mode */
+ xs->resid = xs->datalen; /* to be sure */
+ if (sense->ext.extended.flags & SSD_EOM) {
+ return (EIO);
+ }
+ if (sense->ext.extended.flags & SSD_FILEMARK) {
+ if (bp)
+ bp->b_resid = bp->b_bcount;
+ return 0;
+ }
+ if (sense->ext.extended.flags & SSD_ILI) {
+ if (info < 0) {
+ /*
+ * the record was bigger than the read
+ */
+ if (!silent)
+ printf("st%d: %d-byte record "
+ "too big\n", unit,
+ xs->datalen - info);
+ return (EIO);
+ }
+ xs->resid = info;
+ if (bp) {
+ bp->b_resid = info;
+ SIGNAL_SHORT_READ
+ }
+ }
+ }
+ key = sense->ext.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_SNS_HLP) &&
+ !(sc_link->flags & SDEV_MEDIA_LOADED)) { /* still starting */
+ st->blksiz -= 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 (ESUCCESS);
+ }
+ }
+ 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.
+ */
+errval
+st_touch_tape(unit)
+ u_int32 unit;
+{
+ struct st_data *st = st_data[unit];
+ char *buf;
+ u_int32 readsiz;
+ errval errno;
+
+ buf = malloc(1024, M_TEMP, M_NOWAIT);
+ if (!buf)
+ return (ENOMEM);
+
+ if (errno = st_mode_sense(unit, 0)) {
+ goto bad;
+ }
+ st->blksiz = 1024;
+ do {
+ switch ((int)st->blksiz) {
+ case 512:
+ case 1024:
+ readsiz = st->blksiz;
+ st->flags |= ST_FIXEDBLOCKS;
+ break;
+ default:
+ readsiz = 1;
+ st->flags &= ~ST_FIXEDBLOCKS;
+ } if (errno = st_mode_select(unit, 0)) {
+ goto bad;
+ }
+ st_read(unit, buf, readsiz, SCSI_SILENT);
+ if (errno = st_rewind(unit, FALSE, 0)) {
+bad: free(buf, M_TEMP);
+ return (errno);
+ }
+ } while (readsiz != 1 && readsiz > st->blksiz);
+ free(buf, M_TEMP);
+ return 0;
+}
diff --git a/sys/scsi/su.c b/sys/scsi/su.c
new file mode 100644
index 0000000..de4f017
--- /dev/null
+++ b/sys/scsi/su.c
@@ -0,0 +1,4 @@
+
+/* 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 0000000..149e2df
--- /dev/null
+++ b/sys/scsi/uk.c
@@ -0,0 +1,158 @@
+/*
+ * Dummy driver for a device we can't identify.
+ * by Julian Elischer (julian@tfs.com)
+ *
+ * $Id: uk.c,v 1.2 1993/11/25 01:37:35 wollman Exp $
+ */
+
+#include <sys/param.h>
+#include "systm.h"
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#define NUK 16
+
+/*
+ * This driver is so simple it uses all the default services
+ */
+struct scsi_device uk_switch =
+{
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "uk",
+ 0,
+ 0, 0
+};
+
+struct uk_data {
+ u_int32 flags;
+ struct scsi_link *sc_link; /* all the inter level info */
+} uk_data[NUK];
+
+#define UK_KNOWN 0x02
+
+static u_int32 next_uk_unit = 0;
+
+/*
+ * The routine called by the low level scsi routine when it discovers
+ * a device suitable for this driver.
+ */
+errval
+ukattach(sc_link)
+ struct scsi_link *sc_link;
+{
+ u_int32 unit, i, stat;
+ unsigned char *tbl;
+
+ SC_DEBUG(sc_link, SDEV_DB2, ("ukattach: "));
+ /*
+ * Check we have the resources for another drive
+ */
+ unit = next_uk_unit++;
+ if (unit >= NUK) {
+ printf("Too many unknown devices..(%d > %d) reconfigure kernel\n",
+ (unit + 1), NUK);
+ return (0);
+ }
+ /*
+ * Store information needed to contact our base driver
+ */
+ uk_data[unit].sc_link = sc_link;
+ sc_link->device = &uk_switch;
+ sc_link->dev_unit = unit;
+
+ printf("uk%d: unknown device\n", unit);
+ uk_data[unit].flags = UK_KNOWN;
+
+ return 1; /* XXX ??? */
+
+}
+
+/*
+ * open the device.
+ */
+errval
+ukopen(dev)
+ dev_t dev;
+{
+ errval errcode = 0;
+ u_int32 unit, mode;
+ struct scsi_link *sc_link;
+ unit = minor(dev);
+
+ /*
+ * Check the unit is legal
+ */
+ if (unit >= NUK) {
+ printf("uk%d: uk %d > %d\n", unit, unit, NUK);
+ return ENXIO;
+ }
+
+ /*
+ * Make sure the device has been initialised
+ */
+ if((uk_data[unit].flags & UK_KNOWN) == 0) {
+ printf("uk%d: not set up\n", unit);
+ return ENXIO;
+ }
+
+ /*
+ * Only allow one at a time
+ */
+ sc_link = uk_data[unit].sc_link;
+ if (sc_link->flags & SDEV_OPEN) {
+ printf("uk%d: already open\n", unit);
+ return ENXIO;
+ }
+ sc_link->flags |= SDEV_OPEN;
+ SC_DEBUG(sc_link, SDEV_DB1, ("ukopen: dev=0x%x (unit %d (of %d))\n"
+ ,dev, unit, NUK));
+ /*
+ * Catch any unit attention errors.
+ */
+ return 0;
+}
+
+/*
+ * close the device.. only called if we are the LAST
+ * occurence of an open device
+ */
+errval
+ukclose(dev)
+ dev_t dev;
+{
+ unsigned char unit = 0, mode; /* XXX !!! XXX FIXME!!! 0??? */
+ struct scsi_link *sc_link;
+
+ sc_link = uk_data[unit].sc_link;
+
+ SC_DEBUG(sc_link, SDEV_DB1, ("Closing device"));
+ sc_link->flags &= ~SDEV_OPEN;
+ return (0);
+}
+
+/*
+ * Perform special action on behalf of the user
+ * Only does generic scsi ioctls.
+ */
+errval
+ukioctl(dev, cmd, arg, mode)
+ dev_t dev;
+ u_int32 cmd;
+ caddr_t arg;
+ int mode;
+{
+ unsigned char unit;
+ struct scsi_link *sc_link;
+
+ /*
+ * Find the device that the user is talking about
+ */
+ unit = minor(dev);
+ sc_link = uk_data[unit].sc_link;
+ return(scsi_do_ioctl(sc_link,cmd,arg,mode));
+}
+
OpenPOWER on IntegriCloud