ADDS NEW SCSI CODE BY JULIAN ELISCHER
authorJulian Elischer <julian@dialix.oz.au>
Mon, 15 Feb 1993 00:00:00 +0000 (00:00 +0000)
committerJulian Elischer <julian@dialix.oz.au>
Mon, 15 Feb 1993 00:00:00 +0000 (00:00 +0000)
[ Editors: ]
  This is the new scsi driver package done by Julian Elischer that supports
many new SCSI devices and controller boards.  See the file /sys/scsi/README
for details.  Please note that this driver is slated to *completely replace*
the current AS driver, so your comments are appreciated, especially those of
you with Adaptec compatable and/or older Adaptec controllers.

AUTHOR: Julian Elischer (julian@ref.tfs.com)
AUTHOR: Rodney W. Grimes [repackaging] (rgrimes@agora.rain.com))
386BSD-Patchkit: patch00098

24 files changed:
usr/src/sys.386bsd/i386/conf/AHATEST [new file with mode: 0644]
usr/src/sys.386bsd/i386/conf/AHBTEST [new file with mode: 0644]
usr/src/sys.386bsd/i386/conf/BTTEST [new file with mode: 0644]
usr/src/sys.386bsd/i386/conf/UHATEST [new file with mode: 0644]
usr/src/sys.386bsd/i386/isa/aha1542.c [new file with mode: 0644]
usr/src/sys.386bsd/i386/isa/aha1742.c [new file with mode: 0644]
usr/src/sys.386bsd/i386/isa/bt742a.c [new file with mode: 0644]
usr/src/sys.386bsd/i386/isa/isa.h
usr/src/sys.386bsd/i386/isa/ultra14f.c [new file with mode: 0644]
usr/src/sys.386bsd/scsi/README [new file with mode: 0644]
usr/src/sys.386bsd/scsi/README.AHA1742 [new file with mode: 0644]
usr/src/sys.386bsd/scsi/cd.c [new file with mode: 0644]
usr/src/sys.386bsd/scsi/ch.c [new file with mode: 0644]
usr/src/sys.386bsd/scsi/scsi_all.h [new file with mode: 0644]
usr/src/sys.386bsd/scsi/scsi_cd.h [new file with mode: 0644]
usr/src/sys.386bsd/scsi/scsi_changer.h [new file with mode: 0644]
usr/src/sys.386bsd/scsi/scsi_disk.h [new file with mode: 0644]
usr/src/sys.386bsd/scsi/scsi_tape.h [new file with mode: 0644]
usr/src/sys.386bsd/scsi/scsiconf.c [new file with mode: 0644]
usr/src/sys.386bsd/scsi/scsiconf.h [new file with mode: 0644]
usr/src/sys.386bsd/scsi/sd.c [new file with mode: 0644]
usr/src/sys.386bsd/scsi/st.c [new file with mode: 0644]
usr/src/sys.386bsd/sys/cdio.h [new file with mode: 0644]
usr/src/sys.386bsd/sys/chio.h [new file with mode: 0644]

diff --git a/usr/src/sys.386bsd/i386/conf/AHATEST b/usr/src/sys.386bsd/i386/conf/AHATEST
new file mode 100644 (file)
index 0000000..1195df4
--- /dev/null
@@ -0,0 +1,59 @@
+#
+# SCSITEST -- Generic ISA machine -- scsi test kernel
+#
+machine                "i386"
+cpu            "i386"
+ident          SCSITEST
+timezone       8 dst
+maxusers       10
+options                INET,ISOFS,NFS
+options                "COMPAT_43"
+options                "TCP_COMPAT_42"
+
+config         "386bsd"        root on wd0 swap on wd0 and sd0
+
+controller     isa0
+controller     wd0     at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk           wd0     at wd0 drive 0
+disk           wd0     at wd0 drive 1
+
+controller     fd0     at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk           fd0     at fd0 drive 0
+disk           fd1     at fd0 drive 1
+
+device         pc0     at isa? port "IO_KBD" tty irq 1 vector pcrint
+device         npx0    at isa? port "IO_NPX" irq 13 vector npxintr
+device         com1    at isa? port "IO_COM1" tty irq 4 vector comintr
+device         com2    at isa? port "IO_COM2" tty irq 3 vector comintr
+
+controller     aha0    at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+controller     aha1    at isa? port "IO_AHA1" bio irq 12 drq 7 vector ahaintr
+#controller    bt0     at isa? port "IO_BT0" bio irq 12  vector btintr
+controller     scbus0
+
+device         sd0
+device         sd1
+device         sd2
+device         sd3
+
+device         st0
+device         st1
+device         st2
+device         st3
+
+device         cd0
+device         cd1
+
+device         we0 at isa? port 0x300 net irq 10 iomem 0xd0000 iosiz 8192 vector weintr
+
+
+pseudo-device  loop
+pseudo-device  ether
+pseudo-device  sl      2
+pseudo-device  log
+pseudo-device  ddb
+pseudo-device  pty     4
+
+pseudo-device  swappager
+pseudo-device  vnodepager
+pseudo-device  devpager
diff --git a/usr/src/sys.386bsd/i386/conf/AHBTEST b/usr/src/sys.386bsd/i386/conf/AHBTEST
new file mode 100644 (file)
index 0000000..06780c7
--- /dev/null
@@ -0,0 +1,60 @@
+#
+# SCSITEST -- Generic ISA machine -- scsi test kernel
+#
+machine                "i386"
+cpu            "i386"
+ident          SCSITEST
+timezone       8 dst
+maxusers       10
+options                INET,ISOFS,NFS
+options                "COMPAT_43"
+options                "TCP_COMPAT_42"
+
+config         "386bsd"        root on sd0 swap on sd0
+
+controller     isa0
+controller     wd0     at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk           wd0     at wd0 drive 0
+disk           wd0     at wd0 drive 1
+
+controller     fd0     at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk           fd0     at fd0 drive 0
+disk           fd1     at fd0 drive 1
+
+device         pc0     at isa? port "IO_KBD" tty irq 1 vector pcrint
+device         npx0    at isa? port "IO_NPX" irq 13 vector npxintr
+device         com1    at isa? port "IO_COM1" tty irq 4 vector comintr
+device         com2    at isa? port "IO_COM2" tty irq 3 vector comintr
+
+#controller    aha0    at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+#controller    aha1    at isa? port "IO_AHA1" bio irq 12 drq 7 vector ahaintr
+#controller    bt0     at isa? port "IO_BT0" bio irq 12  vector btintr
+controller     ahb0    at isa? bio irq 12  vector ahbintr
+controller     scbus0
+
+device         sd0
+device         sd1
+device         sd2
+device         sd3
+
+device         st0
+device         st1
+device         st2
+device         st3
+
+device         cd0
+device         cd1
+
+device         we0 at isa? port 0x280 net irq 2 iomem 0xd0000 iosiz 8192 vector weintr
+
+
+pseudo-device  loop
+pseudo-device  ether
+pseudo-device  sl      2
+pseudo-device  log
+pseudo-device  ddb
+pseudo-device  pty     4
+
+pseudo-device  swappager
+pseudo-device  vnodepager
+pseudo-device  devpager
diff --git a/usr/src/sys.386bsd/i386/conf/BTTEST b/usr/src/sys.386bsd/i386/conf/BTTEST
new file mode 100644 (file)
index 0000000..ebc46c5
--- /dev/null
@@ -0,0 +1,59 @@
+#
+# SCSITEST -- Generic ISA machine -- scsi test kernel
+#
+machine                "i386"
+cpu            "i386"
+ident          SCSITEST
+timezone       8 dst
+maxusers       10
+options                INET,ISOFS,NFS
+options                "COMPAT_43"
+options                "TCP_COMPAT_42"
+
+config         "386bsd"        root on sd0 swap on sd0
+
+controller     isa0
+controller     wd0     at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk           wd0     at wd0 drive 0
+disk           wd0     at wd0 drive 1
+
+controller     fd0     at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk           fd0     at fd0 drive 0
+disk           fd1     at fd0 drive 1
+
+device         pc0     at isa? port "IO_KBD" tty irq 1 vector pcrint
+device         npx0    at isa? port "IO_NPX" irq 13 vector npxintr
+device         com1    at isa? port "IO_COM1" tty irq 4 vector comintr
+device         com2    at isa? port "IO_COM2" tty irq 3 vector comintr
+
+#controller    aha0    at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+#controller    aha1    at isa? port "IO_AHA1" bio irq 12 drq 7 vector ahaintr
+controller     bt0     at isa? port "IO_BT0" bio irq 12  vector btintr
+controller     scbus0
+
+device         sd0
+device         sd1
+device         sd2
+device         sd3
+
+device         st0
+device         st1
+device         st2
+device         st3
+
+device         cd0
+device         cd1
+
+device         we0 at isa? port 0x280 net irq 2 iomem 0xd0000 iosiz 8192 vector weintr
+
+
+pseudo-device  loop
+pseudo-device  ether
+pseudo-device  sl      2
+pseudo-device  log
+pseudo-device  ddb
+pseudo-device  pty     4
+
+pseudo-device  swappager
+pseudo-device  vnodepager
+pseudo-device  devpager
diff --git a/usr/src/sys.386bsd/i386/conf/UHATEST b/usr/src/sys.386bsd/i386/conf/UHATEST
new file mode 100644 (file)
index 0000000..ff9ef37
--- /dev/null
@@ -0,0 +1,59 @@
+#
+# SCSITEST -- Generic ISA machine -- scsi test kernel -ultrastore 14f adapter
+#
+machine                "i386"
+cpu            "i386"
+ident          UHATEST
+timezone       8 dst
+maxusers       10
+options                INET,ISOFS,NFS
+options                "COMPAT_43"
+options                "TCP_COMPAT_42"
+
+config         "386bsd"        root on wd0 swap on wd0 and sd0
+
+controller     isa0
+controller     wd0     at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk           wd0     at wd0 drive 0
+disk           wd0     at wd0 drive 1
+
+controller     fd0     at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk           fd0     at fd0 drive 0
+disk           fd1     at fd0 drive 1
+
+device         pc0     at isa? port "IO_KBD" tty irq 1 vector pcrint
+device         npx0    at isa? port "IO_NPX" irq 13 vector npxintr
+device         com1    at isa? port "IO_COM1" tty irq 4 vector comintr
+device         com2    at isa? port "IO_COM2" tty irq 3 vector comintr
+
+controller     uha0    at isa? port "IO_UHA0" bio irq 11 drq 5 vector uhaintr
+#controller    aha0    at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
+#controller    bt0     at isa? port "IO_BT0" bio irq 12  vector btintr
+controller     scbus0
+
+device         sd0
+device         sd1
+device         sd2
+device         sd3
+
+device         st0
+device         st1
+device         st2
+device         st3
+
+device         cd0
+device         cd1
+
+device         we0 at isa? port 0x300 net irq 10 iomem 0xd0000 iosiz 8192 vector weintr
+
+
+pseudo-device  loop
+pseudo-device  ether
+pseudo-device  sl      2
+pseudo-device  log
+pseudo-device  ddb
+pseudo-device  pty     4
+
+pseudo-device  swappager
+pseudo-device  vnodepager
+pseudo-device  devpager
diff --git a/usr/src/sys.386bsd/i386/isa/aha1542.c b/usr/src/sys.386bsd/i386/isa/aha1542.c
new file mode 100644 (file)
index 0000000..85271fc
--- /dev/null
@@ -0,0 +1,1644 @@
+/*
+ * (Mostly) 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.
+ *
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+/*
+ * HISTORY
+ * $Log: aha1542.c,v $
+ * Revision 1.6  1992/08/24  21:01:58  jason
+ * many changes and bugfixes for osf1
+ *
+ * Revision 1.5  1992/07/31  01:22:03  julian
+ * support improved scsi.h layout
+ *
+ * Revision 1.4  1992/07/25  03:11:26  julian
+ * check each request fro sane flags.
+ *
+ * Revision 1.3  1992/07/24  00:52:45  julian
+ * improved timeout handling.
+ * added support for two arguments to the sd_done (or equiv) call so that
+ * they can pre-queue several arguments.
+ * slightly clean up error handling
+ *
+ * Revision 1.2  1992/07/17  22:03:54  julian
+ * upgraded the timeout code.
+ * added support for UIO-based i/o (as used for pmem operations)
+ *
+ * Revision 1.1  1992/05/27  00:51:12  balsup
+ * machkern/cor merge
+ */
+
+/*
+ * a FEW lines in this driver come from a MACH adaptec-disk driver
+ * so the copyright below is included:
+ *
+ * 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.
+ */
+
+
+#include <sys/types.h>
+#include <aha.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>
+
+#ifdef  MACH    /* EITHER CMU OR OSF */
+#include <i386/ipl.h>
+#include <i386at/scsi.h>
+#include <i386at/scsiconf.h>
+
+#ifdef  OSF     /* OSF ONLY */
+#include <sys/table.h>
+#include <i386/handler.h>
+#include <i386/dispatcher.h>
+#include <i386/AT386/atbus.h>
+
+#else   OSF     /* CMU ONLY */
+#include <i386at/atbus.h>
+#include <i386/pio.h>
+#endif  OSF
+#endif  MACH    /* end of MACH specific */
+
+#ifdef  __386BSD__      /* 386BSD specific */
+#define isa_dev isa_device
+#define dev_unit id_unit
+#define dev_addr id_iobase
+
+#include <i386/isa/isa_device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#endif  __386BSD__
+
+
+#ifdef __386BSD__
+#include "ddb.h"
+#if    NDDB > 0
+int    Debugger();
+#else  NDDB
+#define Debugger() panic("should call debugger here (adaptec.c)")
+#endif NDDB
+#endif __386BSD__
+extern int delaycount;  /* from clock setup code */
+
+/************************** board definitions *******************************/
+/*
+ * I/O Port Interface
+ */
+
+#define        AHA_BASE                aha_base[unit]
+#define        AHA_CTRL_STAT_PORT      (AHA_BASE + 0x0)        /* control & status */
+#define        AHA_CMD_DATA_PORT       (AHA_BASE + 0x1)        /* cmds and datas */
+#define        AHA_INTR_PORT           (AHA_BASE + 0x2)        /* Intr. stat */
+
+/*
+ * AHA_CTRL_STAT bits (write)
+ */
+
+#define AHA_HRST               0x80    /* Hardware reset */
+#define AHA_SRST               0x40    /* Software reset */
+#define AHA_IRST               0x20    /* Interrupt reset */
+#define AHA_SCRST              0x10    /* SCSI bus reset */
+
+/*
+ * AHA_CTRL_STAT bits (read)
+ */
+
+#define AHA_STST               0x80    /* Self test in Progress */
+#define AHA_DIAGF              0x40    /* Diagnostic Failure */
+#define AHA_INIT               0x20    /* Mbx Init required */
+#define AHA_IDLE               0x10    /* Host Adapter Idle */
+#define AHA_CDF                        0x08    /* cmd/data out port full */
+#define AHA_DF                 0x04    /* Data in port full */
+#define AHA_INVDCMD            0x01    /* Invalid command */
+
+/*
+ * AHA_CMD_DATA bits (write)
+ */
+
+#define        AHA_NOP                 0x00    /* No operation */
+#define AHA_MBX_INIT           0x01    /* Mbx initialization */
+#define AHA_START_SCSI         0x02    /* start scsi command */
+#define AHA_START_BIOS         0x03    /* start bios command */
+#define AHA_INQUIRE            0x04    /* Adapter Inquiry */
+#define AHA_MBO_INTR_EN                0x05    /* Enable MBO available interrupt */
+#define AHA_SEL_TIMEOUT_SET    0x06    /* set selection time-out */
+#define AHA_BUS_ON_TIME_SET    0x07    /* set bus-on time */
+#define AHA_BUS_OFF_TIME_SET   0x08    /* set bus-off time */
+#define AHA_SPEED_SET          0x09    /* set transfer speed */
+#define AHA_DEV_GET            0x0a    /* return installed devices */
+#define AHA_CONF_GET           0x0b    /* return configuration data */
+#define AHA_TARGET_EN          0x0c    /* enable target mode */
+#define AHA_SETUP_GET          0x0d    /* return setup data */
+#define AHA_WRITE_CH2          0x1a    /* write channel 2 buffer */
+#define AHA_READ_CH2           0x1b    /* read channel 2 buffer */
+#define AHA_WRITE_FIFO         0x1c    /* write fifo buffer */
+#define AHA_READ_FIFO          0x1d    /* read fifo buffer */
+#define AHA_ECHO               0x1e    /* Echo command data */
+
+struct aha_cmd_buf {
+        u_char byte[16];       
+};
+
+/*
+ * AHA_INTR_PORT bits (read)
+ */
+
+#define AHA_ANY_INTR           0x80    /* Any interrupt */
+#define AHA_SCRD               0x08    /* SCSI reset detected */
+#define AHA_HACC               0x04    /* Command complete */
+#define AHA_MBOA               0x02    /* MBX out empty */
+#define AHA_MBIF               0x01    /* MBX in full */
+
+/*
+ * Mail box defs 
+ */
+
+#define AHA_MBX_SIZE           16      /* mail box size */
+
+struct aha_mbx {
+       struct aha_mbx_out {
+               unsigned char cmd;
+               unsigned char ccb_addr[3];
+       } mbo [AHA_MBX_SIZE];
+       struct aha_mbx_in{
+               unsigned char stat;
+               unsigned char ccb_addr[3];
+       } mbi[AHA_MBX_SIZE];
+};
+
+/*
+ * mbo.cmd values
+ */
+
+#define AHA_MBO_FREE   0x0     /* MBO entry is free */
+#define AHA_MBO_START  0x1     /* MBO activate entry */
+#define AHA_MBO_ABORT  0x2     /* MBO abort entry */
+
+#define AHA_MBI_FREE   0x0     /* MBI entry is free */
+#define AHA_MBI_OK     0x1     /* completed without error */
+#define AHA_MBI_ABORT  0x2     /* aborted ccb */
+#define AHA_MBI_UNKNOWN        0x3     /* Tried to abort invalid CCB */
+#define AHA_MBI_ERROR  0x4     /* Completed with error */
+
+extern struct aha_mbx aha_mbx[];
+
+/* FOR OLD VERSIONS OF THE !%$@ this may have to be 16 (yuk) */
+#define        AHA_NSEG        17      /* Number of scatter gather segments <= 16 */
+                               /* allow 64 K i/o (min) */
+
+struct aha_ccb {
+       unsigned char   opcode;
+       unsigned char   lun:3;
+       unsigned char   data_in:1;              /* must be 0 */
+       unsigned char   data_out:1;             /* must be 0 */
+       unsigned char   target:3;
+       unsigned char   scsi_cmd_length;
+       unsigned char   req_sense_length;
+       unsigned char   data_length[3];
+       unsigned char   data_addr[3];
+       unsigned char   link_addr[3];
+       unsigned char   link_id;
+       unsigned char   host_stat;
+       unsigned char   target_stat;
+       unsigned char   reserved[2];
+       struct  scsi_generic    scsi_cmd;
+       struct  scsi_sense_data scsi_sense;
+       struct  aha_scat_gath {
+               unsigned char seg_len[3];
+               unsigned char seg_addr[3];
+       } scat_gath[AHA_NSEG];
+       struct  aha_ccb *next;
+       struct  scsi_xfer       *xfer;          /* the scsi_xfer for this cmd */
+       struct  aha_mbx_out     *mbx;           /* pointer to mail box */
+       long int        delta;  /* difference from previous*/
+       struct  aha_ccb *later,*sooner;
+       int     flags;
+#define CCB_FREE        0
+#define CCB_ACTIVE      1
+#define CCB_ABORTED     2
+
+};
+
+struct aha_ccb *aha_soonest = (struct  aha_ccb *)0;
+struct aha_ccb *aha_latest = (struct  aha_ccb *)0;
+long int       aha_furtherest = 0;     /* longest time in the timeout queue */
+
+/*
+ * opcode fields
+ */
+
+#define AHA_INITIATOR_CCB      0x00    /* SCSI Initiator CCB */
+#define AHA_TARGET_CCB         0x01    /* SCSI Target CCB */
+#define AHA_INIT_SCAT_GATH_CCB 0x02    /* SCSI Initiator with scattter gather*/
+#define AHA_RESET_CCB          0x81    /* SCSI Bus reset */
+
+
+/*
+ * aha_ccb.host_stat values
+ */
+
+#define AHA_OK         0x00    /* cmd ok */
+#define AHA_LINK_OK    0x0a    /* Link cmd ok */
+#define AHA_LINK_IT    0x0b    /* Link cmd ok + int */
+#define AHA_SEL_TIMEOUT        0x11    /* Selection time out */
+#define AHA_OVER_UNDER 0x12    /* Data over/under run */
+#define AHA_BUS_FREE   0x13    /* Bus dropped at unexpected time */
+#define AHA_INV_BUS    0x14    /* Invalid bus phase/sequence */
+#define AHA_BAD_MBO    0x15    /* Incorrect MBO cmd */
+#define AHA_BAD_CCB    0x16    /* Incorrect ccb opcode */
+#define AHA_BAD_LINK   0x17    /* Not same values of LUN for links */
+#define AHA_INV_TARGET 0x18    /* Invalid target direction */
+#define AHA_CCB_DUP    0x19    /* Duplicate CCB received */
+#define AHA_INV_CCB    0x1a    /* Invalid CCB or segment list */
+#define AHA_ABORTED      42
+
+
+
+
+struct aha_setup
+{
+       u_char  sync_neg:1;
+       u_char  parity:1;
+       u_char  :6;
+       u_char  speed;
+       u_char  bus_on;
+       u_char  bus_off;
+       u_char  num_mbx;
+       u_char  mbx[3];
+       struct
+       {
+               u_char  offset:4;
+               u_char  period:3;
+               u_char  valid:1;
+       }sync[8];
+       u_char  disc_sts;
+};
+
+struct aha_config
+{
+       u_char  chan;
+       u_char  intr;
+       u_char  scsi_dev:3;
+       u_char  :5;
+};
+
+#define INT9   0x01
+#define INT10  0x02
+#define INT11  0x04
+#define INT12  0x08
+#define INT14  0x20
+#define INT15  0x40
+
+#define CHAN0  0x01
+#define CHAN5  0x20
+#define CHAN6  0x40
+#define CHAN7  0x80
+
+
+/*********************************** end of board definitions***************/
+
+
+#ifdef MACH
+#define PHYSTOKV(x)    phystokv(x)
+#define KVTOPHYS(x)    kvtophys(x)
+#else  MACH
+#ifdef __386BSD__
+#define PHYSTOKV(x)    (x | 0xFE000000)
+#define KVTOPHYS(x)    vtophys(x)
+#else  __386BSD__
+#endif __386BSD__
+#endif MACH
+#define        AHA_DMA_PAGES   AHA_NSEG
+
+#define PAGESIZ        4096
+#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); }
+
+
+u_char                 aha_scratch_buf[256];
+#ifdef MACH
+caddr_t                        aha_base[NAHA];         /* base port for each board */
+#else
+short                  aha_base[NAHA];         /* base port for each board */
+#endif
+struct aha_mbx         aha_mbx[NAHA];
+struct aha_ccb         *aha_ccb_free[NAHA];
+struct aha_ccb         aha_ccb[NAHA][AHA_MBX_SIZE];
+struct scsi_xfer       aha_scsi_xfer[NAHA];
+struct isa_dev         *ahainfo[NAHA];
+struct aha_ccb         *aha_get_ccb();
+int                    aha_int[NAHA];
+int                    aha_dma[NAHA];
+int                    aha_scsi_dev[NAHA];
+int                    aha_initialized[NAHA];
+#ifdef OSF
+int                    aha_attached[NAHA];
+#endif OSF
+int                    aha_debug = 0;
+
+int ahaprobe(), ahaattach(), ahaintr();
+#ifdef MACH
+struct isa_driver      ahadriver = { ahaprobe, 0, ahaattach, "aha", 0, 0, 0};
+int                    (*ahaintrs[])() = {ahaintr, 0};
+#endif
+#ifdef __386BSD__
+struct isa_driver      ahadriver = { ahaprobe, ahaattach, "aha",};
+#endif __386BSD__
+static int             ahaunit = 0;
+
+
+#define aha_abortmbx(mbx) \
+       (mbx)->cmd = AHA_MBO_ABORT; \
+       outb(AHA_CMD_DATA_PORT, AHA_START_SCSI);
+#define aha_startmbx(mbx) \
+       (mbx)->cmd = AHA_MBO_START; \
+       outb(AHA_CMD_DATA_PORT, AHA_START_SCSI);
+
+
+
+int    aha_scsi_cmd();
+int    aha_timeout();
+void   ahaminphys();
+long int       aha_adapter_info();
+
+struct scsi_switch     aha_switch =
+{
+       aha_scsi_cmd,
+       ahaminphys,
+       0,
+       0,
+       aha_adapter_info,
+       0,0,0
+};     
+#define AHA_CMD_TIMEOUT_FUDGE  200     /* multiplied to get Secs       */
+#define AHA_RESET_TIMEOUT      1000000 /* time to wait for reset       */
+#define AHA_SCSI_TIMEOUT_FUDGE 20      /* divided by for mSecs         */
+
+
+/***********************************************************************\
+* aha_cmd(unit,icnt, ocnt,wait, retval, opcode, args)                  *
+* Activate Adapter command                                             *
+*      icnt:   number of args (outbound bytes written after opcode)    *
+*      ocnt:   number of expected returned bytes                       *
+*      wait:   number of seconds to wait for response                  *
+*      retval: buffer where to place returned bytes                    *
+*      opcode: opcode AHA_NOP, AHA_MBX_INIT, AHA_START_SCSI ...        *
+*      args:   parameters                                              *
+*                                                                      *
+* Performs an adapter command through the ports. Not to be confused    *
+*      with a scsi command, which is read in via the dma               *
+* One of the adapter commands tells it to read in a scsi command       *
+\***********************************************************************/
+
+
+aha_cmd(unit,icnt, ocnt, wait,retval, opcode, args)
+
+u_char *retval;
+unsigned opcode;
+u_char args;
+{
+       unsigned *ic = &opcode;
+       u_char oc;
+       register i;
+       int     sts;
+
+       /*******************************************************\
+       * multiply the wait argument by a big constant          *
+       * zero defaults to 1                                    *
+       \*******************************************************/
+       if(!wait) 
+               wait = AHA_CMD_TIMEOUT_FUDGE * delaycount; 
+       else
+               wait *= AHA_CMD_TIMEOUT_FUDGE * delaycount; 
+       /*******************************************************\
+       * Wait for the adapter to go idle, unless it's one of   *
+       * the commands which don't need this                    *
+       \*******************************************************/
+       if (opcode != AHA_MBX_INIT && opcode != AHA_START_SCSI)
+       {
+               i = AHA_CMD_TIMEOUT_FUDGE * delaycount; /* 1 sec?*/
+               while (--i)
+               {
+                       sts = inb(AHA_CTRL_STAT_PORT);
+                       if (sts & AHA_IDLE)
+                       {
+                               break;
+                       }
+               }
+               if (!i)
+               {
+                       printf("aha_cmd: aha1542 host not idle(0x%x)\n",sts);
+                       return(ENXIO);
+               }
+       }
+       /*******************************************************\
+       * Now that it is idle, if we expect output, preflush the*
+       * queue feeding to us.                                  *
+       \*******************************************************/
+       if (ocnt)
+       {
+               while((inb(AHA_CTRL_STAT_PORT)) & AHA_DF)
+                       inb(AHA_CMD_DATA_PORT);
+       }
+                       
+       /*******************************************************\
+       * Output the command and the number of arguments given  *
+       * for each byte, first check the port is empty.         *
+       \*******************************************************/
+       icnt++;         /* include the command */
+       while (icnt--)
+       {
+               sts = inb(AHA_CTRL_STAT_PORT);
+               for (i=0; i< wait; i++)
+               {
+                       sts = inb(AHA_CTRL_STAT_PORT);
+                       if (!(sts & AHA_CDF))
+                               break;
+               }
+               if (i >=  wait)
+               {
+                       printf("aha_cmd: aha1542 cmd/data port full\n");
+                       outb(AHA_CTRL_STAT_PORT, AHA_SRST); 
+                       return(ENXIO);
+               }
+               outb(AHA_CMD_DATA_PORT, (u_char)(*ic++));
+       }
+       /*******************************************************\
+       * If we expect input, loop that many times, each time,  *
+       * looking for the data register to have valid data      *
+       \*******************************************************/
+       while (ocnt--)
+       {
+               sts = inb(AHA_CTRL_STAT_PORT);
+               for (i=0; i< wait; i++)
+               {
+                       sts = inb(AHA_CTRL_STAT_PORT);
+                       if (sts  & AHA_DF)
+                               break;
+               }
+               if (i >=  wait)
+               {
+                       printf("aha_cmd: aha1542 cmd/data port empty %d\n",ocnt);
+                       return(ENXIO);
+               }
+               oc = inb(AHA_CMD_DATA_PORT);
+               if (retval)
+                       *retval++ = oc;
+       }
+       /*******************************************************\
+       * Wait for the board to report a finised instruction    *
+       \*******************************************************/
+       i=AHA_CMD_TIMEOUT_FUDGE * delaycount;   /* 1 sec? */
+       while (--i)
+       {
+               sts = inb(AHA_INTR_PORT);
+               if (sts & AHA_HACC)
+               {
+                       break;
+               }
+       }
+       if (!i)
+       {
+               printf("aha_cmd: aha1542 host not finished(0x%x)\n",sts);
+               return(ENXIO);
+       }
+       outb(AHA_CTRL_STAT_PORT, AHA_IRST);
+       return(0);
+}
+
+/*******************************************************\
+* Check if the device can be found at the port given   *
+* and if so, set it up ready for further work          *
+* as an argument, takes the isa_dev structure from     *
+* autoconf.c                                           *
+\*******************************************************/
+ahaprobe(dev)
+struct isa_dev *dev;
+{
+       int     unit = ahaunit;
+#if defined(OSF)
+       static ihandler_t aha_handler[NAHA];
+       static ihandler_id_t *aha_handler_id[NAHA];
+       register ihandler_t *chp = &aha_handler[unit];;
+#endif /* defined(OSF) */
+
+       /***********************************************\
+       /***********************************************\
+       * find unit and check we have that many defined *
+       \***********************************************/
+       dev->dev_unit = unit;
+       aha_base[unit] = dev->dev_addr;
+       if(unit >= NAHA) 
+       {
+               printf("aha: unit number (%d) too high\n",unit);
+               return(0);
+       }
+       /***********************************************\
+       * Try initialise a unit at this location        *
+       * sets up dma and bus speed, loads aha_int[unit]*
+       \***********************************************/
+       if (aha_init(unit) != 0)
+       {
+               return(0);
+       }
+
+       /***********************************************\
+       * If it's there, put in it's interrupt vectors  *
+       \***********************************************/
+#if !defined(OSF)
+#if    defined MACH
+       iunit[aha_int[unit]] =unit;
+       ivect[aha_int[unit]] = ahaintr;
+       intpri[aha_int[unit]] = dev->dev_spl;
+       form_pic_mask();
+       /*take_dev_irq(dev);*/
+#else
+#ifdef __386BSD__
+       dev->id_irq = (1 << aha_int[unit]);
+       dev->id_drq = aha_dma[unit];
+#endif __386BSD__
+#endif
+#else /* !defined(OSF) */
+       chp->ih_level = dev->dev_pic;
+       chp->ih_handler = dev->dev_intr[0];
+       chp->ih_resolver = i386_resolver;
+       chp->ih_rdev = dev;
+       chp->ih_stats.intr_type = INTR_DEVICE;
+       chp->ih_stats.intr_cnt = 0;
+       chp->ih_hparam[0].intparam = unit;
+       if ((aha_handler_id[unit] = handler_add(chp)) != NULL)
+               handler_enable(aha_handler_id[unit]);
+       else
+               panic("Unable to add aha interrupt handler");
+#endif /* !defined(OSF) */
+#ifdef __386BSD__
+       printf("\n  **");
+#else  __386BSD__
+       printf("port=%x spl=%d\n",
+          dev->dev_addr, dev->dev_spl);
+#endif __386BSD__
+       ahaunit ++;
+       return(1);
+}
+
+/***********************************************\
+* Attach all the sub-devices we can find       *
+\***********************************************/
+ahaattach(dev)
+struct isa_dev *dev;
+{
+       int     unit = dev->dev_unit;
+
+#ifdef __386BSD__
+       printf(" probing for scsi devices**\n");
+#endif __386BSD__
+       /***********************************************\
+       * ask the adapter what subunits are present     *
+       \***********************************************/
+       scsi_attachdevs( unit, aha_scsi_dev[unit], &aha_switch);
+#if defined(OSF)
+       aha_attached[unit]=1;
+#endif /* defined(OSF) */
+       if(!unit) /* only one for all boards */
+       {
+               aha_timeout(0);
+       }
+#ifdef __386BSD__
+       printf("aha%d",unit);
+#endif __386BSD__
+       return;
+
+}
+
+
+/***********************************************\
+* Return some information to the caller about  *
+* the adapter and it's capabilities            *
+\***********************************************/
+long int aha_adapter_info(unit)
+int    unit;
+{
+       return(2);      /* 2 outstanding requests at a time per device */
+}
+
+/***********************************************\
+* Catch an interrupt from the adaptor          *
+\***********************************************/
+ahaintr(unit)
+{
+       struct aha_ccb *ccb;
+       unsigned char stat;
+       register i;
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("ahaintr ");
+       /***********************************************\
+       * First acknowlege the interrupt, Then if it's  *
+       * not telling about a completed operation       *
+       * just return.                                  *
+       \***********************************************/
+       stat = inb(AHA_INTR_PORT);
+       outb(AHA_CTRL_STAT_PORT, AHA_IRST);
+       if(scsi_debug & TRACEINTERRUPTS)
+               printf("int ");
+       if (! (stat & AHA_MBIF))
+               return(1);
+       if(scsi_debug & TRACEINTERRUPTS)
+               printf("b ");
+#if defined(OSF)
+       if (!aha_attached[unit])
+       {
+               return(1);
+       }
+#endif /* defined(OSF) */
+       /***********************************************\
+       * If it IS then process the competed operation  *
+       \***********************************************/
+       for (i = 0; i < AHA_MBX_SIZE; i++)
+       {
+               if (aha_mbx[unit].mbi[i].stat != AHA_MBI_FREE) 
+               {
+                       ccb = (struct aha_ccb *)PHYSTOKV(
+                               (_3btol(aha_mbx[unit].mbi[i].ccb_addr)));
+
+                       if((stat =  aha_mbx[unit].mbi[i].stat) != AHA_MBI_OK)
+                       {
+                               switch(stat)
+                               {
+                               case    AHA_MBI_ABORT:
+                                       if(aha_debug)
+                                           printf("abort");
+                                       ccb->host_stat = AHA_ABORTED;
+                                       break;
+
+                               case    AHA_MBI_UNKNOWN:
+                                       ccb = (struct aha_ccb *)0;
+                                       if(aha_debug)
+                                            printf("unknown ccb for abort ");
+                                       /* may have missed it */
+                                       /* no such ccb known for abort */
+
+                               case    AHA_MBI_ERROR:
+                                       break;
+
+                               default:
+                                       panic("Impossible mbxi status");
+
+                               }
+                               if( aha_debug && ccb )
+                               {
+                                       u_char  *cp;
+                                       cp = (u_char *)(&(ccb->scsi_cmd));
+                                       printf("op=%x %x %x %x %x %x\n", 
+                                               cp[0], cp[1], cp[2],
+                                               cp[3], cp[4], cp[5]);
+                                       printf("stat %x for mbi[%d]\n"
+                                               , aha_mbx[unit].mbi[i].stat, i);
+                                       printf("addr = 0x%x\n", ccb);
+                               }
+                       }
+                       if(ccb)
+                       {
+                               aha_remove_timeout(ccb);
+                               aha_done(unit,ccb);
+                       }
+                       aha_mbx[unit].mbi[i].stat = AHA_MBI_FREE;
+               }
+       }
+       return(1);
+}
+
+/***********************************************\
+* A ccb (and hence a mbx-out is put onto the   *
+* free list.                                   *
+\***********************************************/
+aha_free_ccb(unit,ccb, flags)
+struct aha_ccb *ccb;
+{
+       unsigned int opri;
+       
+       if(scsi_debug & PRINTROUTINES)
+               printf("ccb%d(0x%x)> ",unit,flags);
+       if (!(flags & SCSI_NOMASK)) 
+               opri = splbio();
+
+       ccb->next = aha_ccb_free[unit];
+       aha_ccb_free[unit] = ccb;
+       ccb->flags = CCB_FREE;
+       if(ccb->sooner || ccb->later)
+       {
+               printf("yikes, still in timeout queue\n");
+               aha_remove_timeout(ccb);
+       }
+       /***********************************************\
+       * If there were none, wake abybody waiting for  *
+       * one to come free, starting with queued entries*
+       \***********************************************/
+       if (!ccb->next) {
+               wakeup(&aha_ccb_free[unit]);
+       }
+       if (!(flags & SCSI_NOMASK)) 
+               splx(opri);
+}
+
+/***********************************************\
+* Get a free ccb (and hence mbox-out entry)    *
+\***********************************************/
+struct aha_ccb *
+aha_get_ccb(unit,flags)
+{
+       unsigned opri;
+       struct aha_ccb *rc;
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("<ccb%d(0x%x) ",unit,flags);
+       if (!(flags & SCSI_NOMASK)) 
+               opri = splbio();
+       /***********************************************\
+       * If we can and have to, sleep waiting for one  *
+       * to come free                                  *
+       \***********************************************/
+       while ((!(rc = aha_ccb_free[unit])) && (!(flags & SCSI_NOSLEEP)))
+       {
+               sleep(&aha_ccb_free[unit], PRIBIO);
+       }
+       if (rc) 
+       {
+               aha_ccb_free[unit] = aha_ccb_free[unit]->next;
+               rc->flags = CCB_ACTIVE;
+       }
+       if (!(flags & SCSI_NOMASK)) 
+               splx(opri);
+       return(rc);
+}
+               
+
+/***********************************************\
+* We have a ccb which has been processed by the        *
+* adaptor, now we look to see how the operation        *
+* went. Wake up the owner if waiting           *
+\***********************************************/
+aha_done(unit,ccb)
+struct aha_ccb *ccb;
+{
+       struct  scsi_sense_data *s1,*s2;
+       struct  scsi_xfer *xs = ccb->xfer;
+
+       if(scsi_debug & PRINTROUTINES )
+               printf("aha_done ");
+       /***********************************************\
+       * Otherwise, put the results of the operation   *
+       * into the xfer and call whoever started it     *
+       \***********************************************/
+       if(!(xs->flags & INUSE))
+       {
+               printf("exiting but not in use! ");
+               Debugger();
+       }
+       if (    (       ccb->host_stat != AHA_OK 
+                       || ccb->target_stat != SCSI_OK)
+             && (!(xs->flags & SCSI_ERR_OK)))
+       {
+               s1 = (struct scsi_sense_data *)(((char *)(&ccb->scsi_cmd)) 
+                               + ccb->scsi_cmd_length);
+               s2 = &(xs->sense);
+
+               if(ccb->host_stat)
+               {
+                       switch(ccb->host_stat)
+                       {
+                       case    AHA_ABORTED:
+                       case    AHA_SEL_TIMEOUT:        /* No response */
+                               xs->error = XS_TIMEOUT;
+                               break;
+                       default:        /* Other scsi protocol messes */
+                               xs->error = XS_DRIVER_STUFFUP;
+                               if (aha_debug > 1)
+                               {
+                                       printf("host_stat%x\n",
+                                               ccb->host_stat);
+                               }
+                       }
+
+               }
+               else
+               {
+                       switch(ccb->target_stat)
+                       {
+                       case 0x02:
+                               /* structure copy!!!!!*/
+                               *s2=*s1;
+                               xs->error = XS_SENSE;
+                               break;
+                       case 0x08:
+                               xs->error = XS_BUSY;
+                               break;
+                       default:
+                               if (aha_debug > 1)
+                               {
+                                       printf("target_stat%x\n",
+                                               ccb->target_stat);
+                               }
+                               xs->error = XS_DRIVER_STUFFUP;
+                       }
+               }
+       }
+       else            /* All went correctly  OR errors expected */
+       {
+               xs->resid = 0;
+       }
+       xs->flags |= ITSDONE;
+       aha_free_ccb(unit,ccb, xs->flags);
+       if(xs->when_done)
+               (*(xs->when_done))(xs->done_arg,xs->done_arg2);
+}
+
+
+/***********************************************\
+* Start the board, ready for normal operation  *
+\***********************************************/
+aha_init(unit)
+int    unit;
+{
+       unsigned char ad[3];
+       volatile int i,sts;
+       struct  aha_config conf;
+
+       /***********************************************\
+       * reset board, If it doesn't respond, assume    *
+       * that it's not there.. good for the probe      *
+       \***********************************************/
+
+       outb(AHA_CTRL_STAT_PORT, AHA_HRST|AHA_SRST);
+
+       for (i=0; i < AHA_RESET_TIMEOUT; i++)
+       {
+               sts = inb(AHA_CTRL_STAT_PORT) ;
+               if ( sts == (AHA_IDLE | AHA_INIT))
+                       break;
+       }
+       if (i >= AHA_RESET_TIMEOUT)
+       {
+               if (aha_debug)
+                       printf("aha_init: No answer from adaptec board\n");
+               return(ENXIO);
+       }
+
+       /***********************************************\
+       * Assume we have a board at this stage          *
+       * setup dma channel from jumpers and save int   *
+       * level                                         *
+       \***********************************************/
+#ifdef __386BSD__
+       printf("aha%d reading board settings, ",unit);
+#define        PRNT(x)
+#else  __386BSD__
+       printf("aha%d:",unit);
+#define        PRNT(x) printf(x)
+#endif __386BSD__
+       aha_cmd(unit,0, sizeof(conf), 0 ,&conf, AHA_CONF_GET);
+       switch(conf.chan)
+       {
+       case    CHAN0:
+               outb(0x0b, 0x0c);
+               outb(0x0a, 0x00);
+               aha_dma[unit] = 0;
+               PRNT("dma=0 ");
+               break;
+       case    CHAN5:
+               outb(0xd6, 0xc1);
+               outb(0xd4, 0x01);
+               aha_dma[unit] = 5;
+               PRNT("dma=5 ");
+               break;
+       case    CHAN6:
+               outb(0xd6, 0xc2);
+               outb(0xd4, 0x02);
+               aha_dma[unit] = 6;
+               PRNT("dma=6 ");
+               break;
+       case    CHAN7:
+               outb(0xd6, 0xc3);
+               outb(0xd4, 0x03);
+               aha_dma[unit] = 7;
+               PRNT("dma=7 ");
+               break;
+       default:
+               printf("illegal dma jumper setting\n");
+               return(EIO);
+       }
+       switch(conf.intr)
+       {
+       case    INT9:
+               aha_int[unit] = 9;
+               PRNT("int=9 ");
+               break;
+       case    INT10:
+               aha_int[unit] = 10;
+               PRNT("int=10 ");
+               break;
+       case    INT11:
+               aha_int[unit] = 11;
+               PRNT("int=11 ");
+               break;
+       case    INT12:
+               aha_int[unit] = 12;
+               PRNT("int=12 ");
+               break;
+       case    INT14:
+               aha_int[unit] = 14;
+               PRNT("int=14 ");
+               break;
+       case    INT15:
+               aha_int[unit] = 15;
+               PRNT("int=15 ");
+               break;
+       default:
+               printf("illegal int jumper setting\n");
+               return(EIO);
+       }
+       /* who are we on the scsi bus */
+       aha_scsi_dev[unit] = conf.scsi_dev;
+
+
+       /***********************************************\
+       * Initialize memory transfer speed              *
+       \***********************************************/
+       if(!(aha_set_bus_speed(unit)))
+       {
+               return(EIO);
+       }
+       
+
+       /***********************************************\
+       * Initialize mail box                           *
+       \***********************************************/
+
+       lto3b(KVTOPHYS(&aha_mbx[unit]), ad);
+
+       aha_cmd(unit,4, 0, 0, 0, AHA_MBX_INIT,
+                       AHA_MBX_SIZE,
+                       ad[0],
+                       ad[1],
+                       ad[2]);
+
+
+       /***********************************************\
+       * link the ccb's with the mbox-out entries and  *
+       * into a free-list                              *
+       \***********************************************/
+       for (i=0; i < AHA_MBX_SIZE; i++) {
+               aha_ccb[unit][i].next = aha_ccb_free[unit];
+               aha_ccb_free[unit] = &aha_ccb[unit][i];
+               aha_ccb_free[unit]->flags = CCB_FREE;
+               aha_ccb_free[unit]->mbx = &aha_mbx[unit].mbo[i];
+               lto3b(KVTOPHYS(aha_ccb_free[unit]), aha_mbx[unit].mbo[i].ccb_addr);
+       }
+
+       /***********************************************\
+       * Note that we are going and return (to probe)  *
+       \***********************************************/
+       aha_initialized[unit]++;
+       return(0);
+}
+
+
+
+
+
+void ahaminphys(bp)
+struct buf *bp;
+{
+#ifdef MACH
+#if !defined(OSF)
+       bp->b_flags |= B_NPAGES;                /* can support scat/gather */
+#endif /* !defined(OSF) */
+#endif MACH
+/*     aha seems to explode with 17 segs (64k may require 17 segs) */
+/*     on old boards so use a max of 16 segs if you have problems here*/
+       if(bp->b_bcount > ((AHA_NSEG - 1) * PAGESIZ))
+       {
+               bp->b_bcount = ((AHA_NSEG - 1) * PAGESIZ);
+       }
+}
+       
+/***********************************************\
+* start a scsi operation given the command and *
+* the data address. Also needs the unit, target        *
+* and lu                                       *
+\***********************************************/
+int    aha_scsi_cmd(xs)
+struct scsi_xfer *xs;
+{
+       struct  scsi_sense_data *s1,*s2;
+       struct aha_ccb *ccb;
+       struct aha_scat_gath *sg;
+       int     seg;    /* scatter gather seg being worked on */
+       int i   = 0;
+       int rc  =  0;
+       int     thiskv;
+       int     thisphys,nextphys;
+       int     unit =xs->adapter;
+       int     bytes_this_seg,bytes_this_page,datalen,flags;
+       struct  iovec   *iovp;
+       int     s;
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("aha_scsi_cmd ");
+       /***********************************************\
+       * get a ccb (mbox-out) to use. If the transfer  *
+       * is from a buf (possibly from interrupt time)  *
+       * then we can't allow it to sleep               *
+       \***********************************************/
+       flags = xs->flags;
+       if(!(flags & INUSE))
+       {
+               printf("not in use!");
+               Debugger();
+               xs->flags |= INUSE;
+       }
+       if(flags & ITSDONE)
+       {
+               printf("Already done! check device retry code ");
+               Debugger();
+               xs->flags &= ~ITSDONE;
+       }
+       if(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */
+       if (!(ccb = aha_get_ccb(unit,flags)))
+       {
+               xs->error = XS_DRIVER_STUFFUP;
+               return(TRY_AGAIN_LATER);
+       }
+
+       if (ccb->mbx->cmd != AHA_MBO_FREE)
+               printf("MBO not free\n");
+
+       /***********************************************\
+       * Put all the arguments for the xfer in the ccb *
+       \***********************************************/
+       ccb->xfer               =       xs;
+       if(flags & SCSI_RESET)
+       {
+               ccb->opcode     =       AHA_RESET_CCB;
+       }
+       else
+       {
+               /* can't use S/G if zero length */
+               ccb->opcode     =       (xs->datalen?
+                                               AHA_INIT_SCAT_GATH_CCB
+                                               :AHA_INITIATOR_CCB);
+       }
+       ccb->target             =       xs->targ;;
+       ccb->data_out           =       0;
+       ccb->data_in            =       0;
+       ccb->lun                =       xs->lu;
+       ccb->scsi_cmd_length    =       xs->cmdlen;
+       ccb->req_sense_length   =       sizeof(ccb->scsi_sense);
+
+       if((xs->datalen) && (!(flags & SCSI_RESET)))
+       { /* can use S/G only if not zero length */
+               lto3b(KVTOPHYS(ccb->scat_gath), ccb->data_addr );
+               sg              =       ccb->scat_gath ;
+               seg             =       0;
+               if(flags & SCSI_DATA_UIO)
+               {
+                       iovp = ((struct uio *)xs->data)->uio_iov;
+                       datalen = ((struct uio *)xs->data)->uio_iovcnt;
+                       while ((datalen) && (seg < AHA_NSEG))
+                       {
+                               lto3b(iovp->iov_base,&(sg->seg_addr));
+                               lto3b(iovp->iov_len,&(sg->seg_len));
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("(0x%x@0x%x)"
+                                                       ,iovp->iov_len
+                                                       ,iovp->iov_base);
+                               sg++;
+                               iovp++;
+                               seg++;
+                               datalen--;
+                       }
+               }
+               else
+               {
+                       /***********************************************\
+                       * Set up the scatter gather block               *
+                       \***********************************************/
+               
+                       if(scsi_debug & SHOWSCATGATH)
+                               printf("%d @0x%x:- ",xs->datalen,xs->data);
+                       datalen         =       xs->datalen;
+                       thiskv          =       (int)xs->data;
+                       thisphys        =       KVTOPHYS(thiskv);
+               
+                       while ((datalen) && (seg < AHA_NSEG))
+                       {
+                               bytes_this_seg  = 0;
+       
+                               /* put in the base address */
+                               lto3b(thisphys,&(sg->seg_addr));
+               
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("0x%x",thisphys);
+       
+                               /* do it at least once */
+                               nextphys = thisphys;    
+                               while ((datalen) && (thisphys == nextphys))
+                               /***************************************\
+                               * This page is contiguous (physically)  *
+                               * with the the last, just extend the    *
+                               * length                                *
+                               \***************************************/
+                               {
+                                       /** how far to the end of the page ***/
+                                       nextphys = (thisphys & (~(PAGESIZ - 1)))
+                                                               + PAGESIZ;
+                                       bytes_this_page = nextphys - thisphys;
+                                       /**** or the data ****/
+                                       bytes_this_page = min(bytes_this_page
+                                                               ,datalen);
+                                       bytes_this_seg  += bytes_this_page;
+                                       datalen         -= bytes_this_page;
+               
+                                       /**** get more ready for the next page ****/
+                                       thiskv  = (thiskv & (~(PAGESIZ - 1)))
+                                                               + PAGESIZ;
+                                       if(datalen)
+                                               thisphys = KVTOPHYS(thiskv);
+                               }
+                               /***************************************\
+                               * next page isn't contiguous, finish the seg*
+                               \***************************************/
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("(0x%x)",bytes_this_seg);
+                               lto3b(bytes_this_seg,&(sg->seg_len));   
+                               sg++;
+                               seg++;
+                       }
+               }
+               lto3b(seg * sizeof(struct aha_scat_gath),ccb->data_length);
+               if(scsi_debug & SHOWSCATGATH)
+                       printf("\n");
+               if (datalen)
+               { /* there's still data, must have run out of segs! */
+                       printf("aha_scsi_cmd%d: more than %d DMA segs\n",
+                               unit,AHA_NSEG);
+                       xs->error = XS_DRIVER_STUFFUP;
+                       aha_free_ccb(unit,ccb,flags);
+                       return(HAD_ERROR);
+               }
+
+       }
+       else
+       {       /* No data xfer, use non S/G values */
+               lto3b(0, ccb->data_addr );
+               lto3b(0,ccb->data_length);
+       }
+       lto3b(0, ccb->link_addr );
+       /***********************************************\
+       * Put the scsi command in the ccb and start it  *
+       \***********************************************/
+       if(!(flags & SCSI_RESET))
+               bcopy(xs->cmd, &ccb->scsi_cmd, ccb->scsi_cmd_length);
+       if(scsi_debug & SHOWCOMMANDS)
+       {
+               u_char  *b = (u_char *)&ccb->scsi_cmd;
+               if(!(flags & SCSI_RESET))
+               {
+                       int i = 0;
+                       printf("aha%d:%d:%d-"
+                               ,unit
+                               ,ccb->target
+                               ,ccb->lun );
+                               while(i < ccb->scsi_cmd_length )
+                               {
+                                       if(i) printf(",");
+                                        printf("%x",b[i++]);
+                               }
+               }
+               else
+               {
+                       printf("aha%d:%d:%d-RESET- " 
+                               ,unit 
+                               ,ccb->target
+                               ,ccb->lun
+                       );
+               }
+       }
+       if (!(flags & SCSI_NOMASK))
+       {
+               s= splbio(); /* stop instant timeouts */
+               aha_add_timeout(ccb,xs->timeout);
+               aha_startmbx(ccb->mbx);
+               /***********************************************\
+               * Usually return SUCCESSFULLY QUEUED            *
+               \***********************************************/
+               splx(s);
+               if(scsi_debug & TRACEINTERRUPTS)
+                       printf("sent ");
+               return(SUCCESSFULLY_QUEUED);
+       }
+       aha_startmbx(ccb->mbx);
+       if(scsi_debug & TRACEINTERRUPTS)
+               printf("cmd_sent, waiting ");
+       /***********************************************\
+       * If we can't use interrupts, poll on completion*
+       \***********************************************/
+       {
+               int done = 0;
+               int count = delaycount * xs->timeout / AHA_SCSI_TIMEOUT_FUDGE;
+               while((!done) && count)
+               {
+                       i=0;
+                       while ( (!done) && i<AHA_MBX_SIZE)
+                       {
+                               if ((aha_mbx[unit].mbi[i].stat != AHA_MBI_FREE )
+                                  && (PHYSTOKV(_3btol(aha_mbx[unit].mbi[i].ccb_addr)
+                                       == (int)ccb)))
+                               {
+                                       aha_mbx[unit].mbi[i].stat = AHA_MBI_FREE;
+                                       aha_done(unit,ccb);
+                                       done++;
+                               }
+                               i++;
+                       }
+                       count--;
+               }
+               if (!count)
+               {
+                       if (!(xs->flags & SCSI_SILENT))
+                               printf("cmd fail\n");
+                       aha_abortmbx(ccb->mbx);
+                       count = delaycount * 2000 / AHA_SCSI_TIMEOUT_FUDGE;
+                       while((!done) && count)
+                       {
+                               i=0;
+                               while ( (!done) && i<AHA_MBX_SIZE)
+                               {
+                                       if ((aha_mbx[unit].mbi[i].stat != AHA_MBI_FREE )
+                                       && (PHYSTOKV(_3btol(aha_mbx[unit].mbi[i].ccb_addr)
+                                               == (int)ccb)))
+                                       {
+                                               aha_mbx[unit].mbi[i].stat = AHA_MBI_FREE;
+                                               aha_done(unit,ccb);
+                                               done++;
+                                       }
+                                       i++;
+                               }
+                               count--;
+                       }
+                       if(!count)
+                       {
+                               printf("abort failed in wait\n");
+                               ccb->mbx->cmd = AHA_MBO_FREE;
+                       }
+                       aha_free_ccb(unit,ccb,flags);
+                       ahaintr(unit);
+                       xs->error = XS_DRIVER_STUFFUP;
+                       return(HAD_ERROR);
+               }
+               ahaintr(unit);
+               if(xs->error) return(HAD_ERROR);
+               return(COMPLETE);
+
+       } 
+}
+/***************************************************************\
+* try each speed in turn, when we find one that works, use     *
+* the NEXT one for a safety margin, unless that doesn't exist  *
+* or doesn't work. returns the nSEC value of the time used     *
+* or 0 if it could get a working speed ( or the NEXT speed     *
+* failed)                                                      *
+\***************************************************************/
+
+int    aha_set_bus_speed(unit)
+int    unit;
+{
+       int     speed;
+       int     retval,retval2;
+
+#ifdef EISA
+       speed = 0; /* start at the fastest */
+#else  EISA
+       speed = 1; /* 100 ns can crash some ISA busses (!?!) */
+#endif EISA
+       while (1)
+       {
+               retval = aha_bus_speed_check(unit,speed);
+               if(retval == HAD_ERROR) 
+               {
+                       printf("no working bus speed!!!\n");
+                       return(0);
+               }
+               if(retval == 0)
+               {
+                       speed++;
+               }
+               else    /* Go one slower to be safe */
+               {       /* unless eisa at 100 ns.. trust it */
+                       if(speed != 0)
+                       {
+                               speed++;
+                       }
+                       printf("%d nSEC ok, use ",retval);
+                       retval2 = aha_bus_speed_check(unit,speed);
+                       if(retval2 == HAD_ERROR) /* retval is slowest already */
+                       {
+                               printf("marginal ");
+                               retval2 = retval;
+                       }
+                       if(retval2)
+                       {
+                               printf("%d nSEC ",retval2);
+                               return(retval2);
+                       }
+                       else
+                       {
+                               printf(".. slower failed, abort.\n",retval);
+                               return(0);
+                       }
+
+               }
+       }
+}
+
+/***************************************************************\
+* Set the DMA speed to the Nth speed and try an xfer. If it    *
+* fails return 0, if it succeeds return the nSec value selected        *
+* If there is no such speed return HAD_ERROR.                  *
+\***************************************************************/
+static struct bus_speed
+{
+       char    arg;
+       int     nsecs;
+}aha_bus_speeds[] =
+{
+       {0x88,100},
+       {0x99,150},
+       {0xaa,200},
+       {0xbb,250},
+       {0xcc,300},
+       {0xdd,350},
+       {0xee,400},
+       {0xff,450}
+};
+static char aha_test_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz!@";
+
+int aha_bus_speed_check(unit,speed)
+int    unit,speed;
+{
+       int     numspeeds = sizeof(aha_bus_speeds)/sizeof(struct bus_speed);
+       u_char  ad[3];
+
+       /*******************************************************\
+       * Check we have such an entry                           *
+       \*******************************************************/
+       if(speed >= numspeeds) return(HAD_ERROR);       /* illegal speed */
+       
+       /*******************************************************\
+       * Set the dma-speed                                     *
+       \*******************************************************/
+       aha_cmd(unit,1, 0, 0, 0, AHA_SPEED_SET,aha_bus_speeds[speed].arg);
+
+       /*******************************************************\
+       * put the test data into the buffer and calculate       *
+       * it's address. Read it onto the board                  *
+       \*******************************************************/
+       strcpy(aha_scratch_buf,aha_test_string);
+       lto3b(KVTOPHYS(aha_scratch_buf),ad);
+
+       aha_cmd(unit,3, 0, 0, 0, AHA_WRITE_FIFO, ad[0], ad[1], ad[2]);
+
+       /*******************************************************\
+       * clear the buffer then copy the contents back from the *
+       * board.                                                *
+       \*******************************************************/
+       bzero(aha_scratch_buf,54);      /* 54 bytes transfered by test */
+
+       aha_cmd(unit,3, 0, 0, 0, AHA_READ_FIFO, ad[0], ad[1], ad[2]);
+
+       /*******************************************************\
+       * Compare the original data and the final data and      *
+       * return the correct value depending upon the result    *
+       \*******************************************************/
+       if(strcmp(aha_test_string,aha_scratch_buf))
+       {       /* copy failed.. assume too fast */
+               return(0);
+       }
+       else
+       {       /* copy succeded assume speed ok */
+               return(aha_bus_speeds[speed].nsecs);
+       }
+}
+
+
+/*
+ *                +----------+    +----------+    +----------+
+ * aha_soonest--->|    later |--->|     later|--->|     later|-->0
+ *                | [Delta]  |    | [Delta]  |    | [Delta]  |
+ *           0<---|sooner    |<---|sooner    |<---|sooner    |<---aha_latest
+ *                +----------+    +----------+    +----------+
+ *
+ *     aha_furtherest = sum(Delta[1..n])
+ */
+aha_add_timeout(ccb,time)
+struct aha_ccb *ccb;
+int    time;
+{
+       int     timeprev;
+       struct aha_ccb *prev;
+       int     s = splbio();
+
+       if(prev = aha_latest) /* yes, an assign */
+       {
+               timeprev = aha_furtherest;
+       }
+       else
+       {
+               timeprev = 0;
+       }
+       while(prev && (timeprev > time)) 
+       {
+               timeprev -= prev->delta;
+               prev = prev->sooner;
+       }
+       if(prev)
+       {
+               ccb->delta = time - timeprev;
+               if( ccb->later = prev->later) /* yes an assign */
+               {
+                       ccb->later->sooner = ccb;
+                       ccb->later->delta -= ccb->delta;
+               }
+               else
+               {
+                       aha_furtherest = time;
+                       aha_latest = ccb;
+               }
+               ccb->sooner = prev;
+               prev->later = ccb;
+       }
+       else
+       {
+               if( ccb->later = aha_soonest) /* yes, an assign*/
+               {
+                       ccb->later->sooner = ccb;
+                       ccb->later->delta -= time;
+               }
+               else
+               {
+                       aha_furtherest = time;
+                       aha_latest = ccb;
+               }
+               ccb->delta = time;
+               ccb->sooner = (struct aha_ccb *)0;
+               aha_soonest = ccb;
+       }
+       splx(s);
+}
+
+aha_remove_timeout(ccb)
+struct aha_ccb *ccb;
+{
+       int     s = splbio();
+
+       if(ccb->sooner)
+       {
+               ccb->sooner->later = ccb->later;
+       }
+       else
+       {
+               aha_soonest = ccb->later;
+       }
+       if(ccb->later)
+       {
+               ccb->later->sooner = ccb->sooner;
+               ccb->later->delta += ccb->delta;
+       }
+       else
+       {
+               aha_latest = ccb->sooner;
+               aha_furtherest -= ccb->delta;
+       }
+       ccb->sooner = ccb->later = (struct aha_ccb *)0;
+       splx(s);
+}
+
+
+extern int     hz;
+#define ONETICK 500 /* milliseconds */
+#define SLEEPTIME ((hz * 1000) / ONETICK)
+aha_timeout(arg)
+int    arg;
+{
+       struct  aha_ccb  *ccb;
+       int     unit;
+       int     s       = splbio();
+
+       while( ccb = aha_soonest )
+       {
+               if(ccb->delta <= ONETICK)
+               /***********************************************\
+               * It has timed out, we need to do some work     *
+               \***********************************************/
+               {
+                       unit = ccb->xfer->adapter;
+                       printf("aha%d: device %d timed out ",unit
+                               ,ccb->xfer->targ);
+
+                       /***************************************\
+                       * Unlink it from the queue              *
+                       \***************************************/
+                       aha_remove_timeout(ccb);
+
+                       /***************************************\
+                       * If The ccb's mbx is not free, then    *
+                       * the board has gone south              *
+                       \***************************************/
+                       if(ccb->mbx->cmd != AHA_MBO_FREE)
+                       {
+                               printf("aha%d not taking commands!\n"
+                                                       ,unit);
+                               Debugger();
+                       }
+                       /***************************************\
+                       * If it has been through before, then   *
+                       * a previous abort has failed, don't    *
+                       * try abort again                       *
+                       \***************************************/
+                       if(ccb->flags == CCB_ABORTED) /* abort timed out */
+                       {
+                               printf(" AGAIN\n");
+                               ccb->xfer->retries = 0; /* I MEAN IT ! */
+                               ccb->host_stat = AHA_ABORTED;
+                               aha_done(unit,ccb);
+                       }
+                       else    /* abort the operation that has timed out */
+                       {
+                               printf("\n");
+                               aha_abortmbx(ccb->mbx);
+                                               /* 2 secs for the abort */
+                               aha_add_timeout(ccb,2000 + ONETICK);
+                               ccb->flags = CCB_ABORTED;
+                       }
+               }
+               else
+               /***********************************************\
+               * It has not timed out, adjust and leave        *
+               \***********************************************/
+               {
+                       ccb->delta -= ONETICK;
+                       aha_furtherest -= ONETICK;
+                       break;
+               }
+       }
+       splx(s);
+       timeout(aha_timeout,arg,SLEEPTIME);
+}
diff --git a/usr/src/sys.386bsd/i386/isa/aha1742.c b/usr/src/sys.386bsd/i386/isa/aha1742.c
new file mode 100644 (file)
index 0000000..721f6f7
--- /dev/null
@@ -0,0 +1,1420 @@
+/*
+ * 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.
+ *
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ * commenced: Sun Sep 27 18:14:01 PDT 1992
+ */
+
+#include <sys/types.h>
+#include <ahb.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>
+
+#ifdef  MACH    /* EITHER CMU OR OSF */
+#include <i386/ipl.h>
+#include <i386at/scsi.h>
+#include <i386at/scsiconf.h>
+
+#ifdef  OSF     /* OSF ONLY */
+#include <sys/table.h>
+#include <i386/handler.h>
+#include <i386/dispatcher.h>
+#include <i386/AT386/atbus.h>
+
+#else   OSF     /* CMU ONLY */
+#include <i386at/atbus.h>
+#include <i386/pio.h>
+#endif  OSF
+#endif  MACH    /* end of MACH specific */
+
+#ifdef  __386BSD__      /* 386BSD specific */
+#define isa_dev isa_device
+#define dev_unit id_unit
+#define dev_addr id_iobase
+
+#include <i386/include/pio.h>
+#include <i386/isa/isa_device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#endif  __386BSD__
+
+/*\f*/
+
+#ifdef  __386BSD__
+#include "ddb.h"
+#if     NDDB > 0
+int     Debugger();
+#else   NDDB
+#define Debugger() panic("should call debugger here (adaptec.c)")
+#endif  NDDB
+#endif  __386BSD__
+
+#ifdef  MACH
+int     Debugger();
+#endif  MACH
+
+typedef unsigned long int physaddr;
+
+#ifdef        MACH
+extern physaddr kvtophys();
+#define PHYSTOKV(x)   phystokv(x)
+#define KVTOPHYS(x)   kvtophys(x)
+#endif MACH
+
+#ifdef        __386BSD__
+#define PHYSTOKV(x)   (x | 0xFE000000)
+#define KVTOPHYS(x)   vtophys(x)
+#endif        __386BSD__
+
+extern int delaycount;  /* from clock setup code */
+#define        NUM_CONCURRENT  16      /* number of concurrent ops per board */
+#define        AHB_NSEG        33      /* number of dma segments supported     */
+#define FUDGE(X)       (X>>1)  /* our loops are slower than spinwait() */
+/*\f*/
+/***********************************************************************\
+* AHA1740 standard EISA Host ID regs  (Offset from slot base)          *
+\***********************************************************************/
+#define HID0           0xC80 /* 0,1: msb of ID2, 3-7: ID1      */
+#define HID1           0xC81 /* 0-4: ID3, 4-7: LSB ID2         */
+#define HID2           0xC82 /* product, 0=174[20] 1 = 1744    */
+#define HID3           0xC83 /* firmware revision              */
+
+#define CHAR1(B1,B2) (((B1>>2) & 0x1F) | '@')
+#define CHAR2(B1,B2) (((B1<<3) & 0x18) | ((B2>>5) & 0x7)|'@')
+#define CHAR3(B1,B2) ((B2 & 0x1F) | '@')
+
+/* AHA1740 EISA board control registers (Offset from slot base) */
+#define        EBCTRL          0xC84
+#define  CDEN          0x01
+/***********************************************************************\
+* AHA1740 EISA board mode registers (Offset from slot base)            *
+\***********************************************************************/
+#define PORTADDR       0xCC0
+#define         PORTADDR_ENHANCED      0x80
+#define BIOSADDR       0xCC1
+#define        INTDEF          0xCC2
+#define        SCSIDEF         0xCC3
+#define        BUSDEF          0xCC4
+#define        RESV0           0xCC5
+#define        RESV1           0xCC6
+#define        RESV2           0xCC7
+/**** bit definitions for INTDEF ****/
+#define        INT9    0x00
+#define        INT10   0x01
+#define        INT11   0x02
+#define        INT12   0x03
+#define        INT14   0x05
+#define        INT15   0x06
+#define INTHIGH 0x08    /* int high=ACTIVE (else edge) */
+#define        INTEN   0x10
+/**** bit definitions for SCSIDEF ****/
+#define        HSCSIID 0x0F    /* our SCSI ID */
+#define        RSTPWR  0x10    /* reset scsi bus on power up or reset */
+/**** bit definitions for BUSDEF ****/
+#define        B0uS    0x00    /* give up bus immediatly */
+#define        B4uS    0x01    /* delay 4uSec. */
+#define        B8uS    0x02
+/***********************************************************************\
+* AHA1740 ENHANCED mode mailbox control regs (Offset from slot base)   *
+\***********************************************************************/
+#define MBOXOUT0       0xCD0
+#define MBOXOUT1       0xCD1
+#define MBOXOUT2       0xCD2
+#define MBOXOUT3       0xCD3
+
+#define        ATTN            0xCD4
+#define        G2CNTRL         0xCD5
+#define        G2INTST         0xCD6
+#define G2STAT         0xCD7
+
+#define        MBOXIN0         0xCD8
+#define        MBOXIN1         0xCD9
+#define        MBOXIN2         0xCDA
+#define        MBOXIN3         0xCDB
+
+#define G2STAT2                0xCDC
+
+/*******************************************************\
+* Bit definitions for the 5 control/status registers   *
+\*******************************************************/
+#define        ATTN_TARGET             0x0F
+#define        ATTN_OPCODE             0xF0
+#define  OP_IMMED              0x10
+#define          AHB_TARG_RESET        0x80
+#define  OP_START_ECB          0x40
+#define  OP_ABORT_ECB          0x50
+
+#define        G2CNTRL_SET_HOST_READY  0x20
+#define        G2CNTRL_CLEAR_EISA_INT  0x40
+#define        G2CNTRL_HARD_RESET      0x80
+
+#define        G2INTST_TARGET          0x0F
+#define        G2INTST_INT_STAT        0xF0
+#define         AHB_ECB_OK             0x10
+#define         AHB_ECB_RECOVERED      0x50
+#define         AHB_HW_ERR             0x70
+#define         AHB_IMMED_OK           0xA0
+#define         AHB_ECB_ERR            0xC0
+#define         AHB_ASN                0xD0    /* for target mode */
+#define         AHB_IMMED_ERR          0xE0
+
+#define        G2STAT_BUSY             0x01
+#define        G2STAT_INT_PEND         0x02
+#define        G2STAT_MBOX_EMPTY       0x04
+
+#define        G2STAT2_HOST_READY      0x01
+/*\f*/
+
+struct ahb_dma_seg
+{
+       physaddr        addr;
+       long            len;
+};
+
+struct ahb_ecb_status
+{
+       u_short status;
+#       define ST_DON  0x0001
+#       define ST_DU   0x0002
+#       define ST_QF   0x0008
+#       define ST_SC   0x0010
+#       define ST_DO   0x0020
+#       define ST_CH   0x0040
+#       define ST_INT  0x0080
+#       define ST_ASA  0x0100
+#       define ST_SNS  0x0200
+#       define ST_INI  0x0800
+#       define ST_ME   0x1000
+#       define ST_ECA  0x4000
+       u_char  ha_status;
+#       define HS_OK                   0x00
+#       define HS_CMD_ABORTED_HOST     0x04
+#       define HS_CMD_ABORTED_ADAPTER  0x05
+#       define HS_TIMED_OUT            0x11
+#       define HS_HARDWARE_ERR         0x20
+#       define HS_SCSI_RESET_ADAPTER   0x22
+#       define HS_SCSI_RESET_INCOMING  0x23
+       u_char  targ_status;
+#       define TS_OK                   0x00
+#       define TS_CHECK_CONDITION      0x02
+#       define TS_BUSY                 0x08
+       u_long  resid_count;
+       u_long  resid_addr;
+       u_short addit_status;
+       u_char  sense_len;
+       u_char  unused[9];
+       u_char  cdb[6];
+};
+
+/*\f*/
+
+struct ecb
+{
+       u_char  opcode;
+#       define ECB_SCSI_OP     0x01
+       u_char  :4;
+       u_char  options:3;
+       u_char  :1;
+       short opt1;
+#       define ECB_CNE 0x0001
+#       define ECB_DI  0x0080
+#       define ECB_SES 0x0400
+#       define ECB_S_G 0x1000
+#       define ECB_DSB 0x4000
+#       define ECB_ARS 0x8000
+       short opt2;
+#       define ECB_LUN 0x0007
+#       define ECB_TAG 0x0008
+#       define ECB_TT  0x0030
+#       define ECB_ND  0x0040
+#       define ECB_DAT 0x0100
+#       define ECB_DIR 0x0200
+#       define ECB_ST  0x0400
+#       define ECB_CHK 0x0800
+#       define ECB_REC 0x4000
+#       define ECB_NRB 0x8000
+       u_short         unused1;
+       physaddr        data;
+       u_long          datalen;
+       physaddr        status;
+       physaddr        chain;
+       short           unused2;
+       short           unused3;
+       physaddr        sense;
+       u_char          senselen;
+       u_char          cdblen;
+       short           cksum;
+       u_char          cdb[12];
+       /*-----------------end of hardware supported fields----------------*/
+       struct  ecb     *next;  /* in free list */
+       struct  scsi_xfer *xs; /* the scsi_xfer for this cmd */
+       long    int     delta;  /* difference from previous*/
+       struct  ecb     *later,*sooner;
+       int             flags;
+#define ECB_FREE       0
+#define ECB_ACTIVE     1
+#define ECB_ABORTED    2
+#define ECB_IMMED      4
+#define ECB_IMMED_FAIL 8
+       struct  ahb_dma_seg     ahb_dma[AHB_NSEG];
+       struct  ahb_ecb_status  ecb_status;
+       struct  scsi_sense_data ecb_sense;
+};
+
+struct ecb     *ahb_soonest    = (struct ecb *)0;
+struct ecb     *ahb_latest     = (struct ecb *)0;
+long   int     ahb_furtherest  = 0; /* longest time in the timeout queue */
+/*\f*/
+
+struct ahb_data
+{
+       int     flags;
+#define        AHB_INIT        0x01;
+       int     baseport;
+       struct  ecb ecbs[NUM_CONCURRENT];
+       struct  ecb *free_ecb;
+       int     our_id;                 /* our scsi id */
+       int     vect;
+       struct  ecb *immed_ecb;         /* an outstanding immediete command */
+} ahb_data[NAHB];
+
+int    ahbprobe();
+int    ahb_attach();
+int    ahbintr();
+int    ahb_scsi_cmd();
+int    ahb_timeout();
+struct ecb *cheat;
+void   ahbminphys();
+long int ahb_adapter_info();
+
+#ifdef  MACH
+struct  isa_driver      ahbdriver = { ahbprobe, 0, ahb_attach, "ahb", 0, 0, 0};
+int (*ahbintrs[])() = {ahbintr, 0};
+#endif  MACH
+
+#ifdef  __386BSD__
+struct  isa_driver      ahbdriver = { ahbprobe, ahb_attach, "ahb"};
+#endif  __386BSD__
+
+#define        MAX_SLOTS       8
+static ahb_slot = 0;   /* slot last board was found in */
+static ahb_unit = 0;
+int    ahb_debug = 0;
+#define AHB_SHOWECBS 0x01
+#define AHB_SHOWINTS 0x02
+#define AHB_SHOWCMDS 0x04
+#define AHB_SHOWMISC 0x08
+#define FAIL   1
+#define SUCCESS 0
+#define PAGESIZ 4096
+
+struct scsi_switch     ahb_switch = 
+{
+       ahb_scsi_cmd,
+       ahbminphys,
+       0,
+       0,
+       ahb_adapter_info,
+       0,0,0
+};
+
+/*\f*/
+/***********************************************************************\
+* Function to send a command out through a mailbox                     *
+\***********************************************************************/
+ahb_send_mbox( int             unit
+               ,int            opcode
+               ,int            target
+               ,struct ecb     *ecb)
+{
+       int     port = ahb_data[unit].baseport;
+       int     spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */
+       int     s = splbio();
+       int     stport = port + G2STAT;
+
+       while(      ((inb(stport) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY))
+                                       != (G2STAT_MBOX_EMPTY))
+               && (spincount--));
+       if(spincount == -1)
+       {
+               printf("ahb%d: board not responding\n",unit);
+               Debugger();
+       }
+
+       outl(port + MBOXOUT0,KVTOPHYS(ecb));    /* don't know this will work */
+       outb(port + ATTN, opcode|target);
+
+       splx(s);
+}
+
+/***********************************************************************\
+* Function to poll for command completion when in poll mode            *
+\***********************************************************************/
+ahb_poll(int unit ,int wait) /* in msec  */
+{
+       int     port = ahb_data[unit].baseport;
+       int     spincount = FUDGE(delaycount) * wait; /* in msec */
+       int     stport = port + G2STAT;
+int    start = spincount;
+
+retry:
+       while( (spincount--) && (!(inb(stport) &  G2STAT_INT_PEND)));
+       if(spincount == -1)
+       {
+               printf("ahb%d: board not responding\n",unit);
+               return(EIO);
+       }
+if ((int)cheat != PHYSTOKV(inl(port + MBOXIN0)))
+{
+       printf("discarding %x ",inl(port + MBOXIN0));
+       outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
+       spinwait(50);
+       goto retry;
+}/* don't know this will work */
+       ahbintr(unit);
+       return(0);
+}
+/***********************************************************************\
+* Function to  send an immediate type command to the adapter           *
+\***********************************************************************/
+ahb_send_immed(        int             unit
+               ,int            target
+               ,u_long         cmd)
+{
+       int     port = ahb_data[unit].baseport;
+       int     spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */
+       int     s = splbio();
+       int     stport = port + G2STAT;
+
+       while(      ((inb(stport) & (G2STAT_BUSY | G2STAT_MBOX_EMPTY))
+                                       != (G2STAT_MBOX_EMPTY))
+               && (spincount--));
+       if(spincount == -1)
+       {
+               printf("ahb%d: board not responding\n",unit);
+               Debugger();
+       }
+
+       outl(port + MBOXOUT0,cmd);      /* don't know this will work */
+       outb(port + G2CNTRL, G2CNTRL_SET_HOST_READY);
+       outb(port + ATTN, OP_IMMED | target);
+       splx(s);
+}
+
+/*\f*/
+
+/*******************************************************\
+* Check  the slots looking for a board we recognise    *
+* If we find one, note it's address (slot) and call    *
+* the actual probe routine to check it out.            *
+\*******************************************************/
+ahbprobe(dev)
+struct isa_dev *dev;
+{
+       int     port;
+       u_char  byte1,byte2,byte3;
+       ahb_slot++;
+       while (ahb_slot<8)
+       {
+               port = 0x1000 * ahb_slot;
+               byte1 = inb(port + HID0);
+               byte2 = inb(port + HID1);
+               byte3 = inb(port + HID2);
+               if(byte1 == 0xff)
+               {
+                       ahb_slot++;
+                       continue;
+               }
+               if ((CHAR1(byte1,byte2) == 'A')
+                && (CHAR2(byte1,byte2) == 'D')
+                && (CHAR3(byte1,byte2) == 'P')
+                && ((byte3 == 0 ) || (byte3 == 1)))
+               {
+                       dev->dev_addr = port;
+                       return(ahbprobe1(dev));
+               }
+               ahb_slot++;
+       }
+       return(0);
+}
+/*******************************************************\
+* Check if the device can be found at the port given    *
+* and if so, set it up ready for further work           *
+* as an argument, takes the isa_dev structure from      *
+* autoconf.c                                            *
+\*******************************************************/
+ahbprobe1(dev)
+struct isa_dev *dev;
+{
+       /***********************************************\
+       * find unit and check we have that many defined *
+       \***********************************************/
+       int     unit = ahb_unit;
+#if defined(OSF)
+       static ihandler_t ahb_handler[NAHB];
+       static ihandler_id_t *ahb_handler_id[NAHB];
+       register ihandler_t *chp = &ahb_handler[unit];;
+#endif /* defined(OSF) */
+
+       dev->dev_unit = unit;
+       ahb_data[unit].baseport = dev->dev_addr;
+       if(unit >= NAHB) 
+       {
+               printf("ahb: unit number (%d) too high\n",unit);
+               return(0);
+       }
+       /***********************************************\
+       * Try initialise a unit at this location        *
+       * sets up dma and bus speed, loads ahb_data[unit].vect*
+       \***********************************************/
+       if (ahb_init(unit) != 0)
+       {
+               return(0);
+       }
+
+       /***********************************************\
+       * If it's there, put in it's interrupt vectors  *
+       \***********************************************/
+#ifdef MACH
+#if defined(OSF)                               /* OSF */
+       chp->ih_level = dev->dev_pic;
+       chp->ih_handler = dev->dev_intr[0];
+       chp->ih_resolver = i386_resolver;
+       chp->ih_rdev = dev;
+       chp->ih_stats.intr_type = INTR_DEVICE;
+       chp->ih_stats.intr_cnt = 0;
+       chp->ih_hparam[0].intparam = unit;
+       if ((ahb_handler_id[unit] = handler_add(chp)) != NULL)
+               handler_enable(ahb_handler_id[unit]);
+       else
+               panic("Unable to add ahb interrupt handler");
+#else                                          /* CMU */
+       dev->dev_pic = ahb_data[unit].vect;
+       take_dev_irq(dev);
+#endif /* !defined(OSF) */
+       printf("port=%x spl=%d\n", dev->dev_addr, dev->dev_spl);
+#endif MACH
+#ifdef  __386BSD__                             /* 386BSD */
+        dev->id_irq = (1 << ahb_data[unit].vect);
+        dev->id_drq = -1; /* use EISA dma */
+       printf("\n  **");
+#endif  __386BSD__
+
+       ahb_unit++;
+       return(1);
+}
+
+/***********************************************\
+* Attach all the sub-devices we can find       *
+\***********************************************/
+ahb_attach(dev)
+struct isa_dev *dev;
+{
+       int     unit = dev->dev_unit;
+
+
+#ifdef  __386BSD__
+       printf(" probing for scsi devices**\n");
+#endif  __386BSD__
+
+       /***********************************************\
+       * ask the adapter what subunits are present     *
+       \***********************************************/
+       scsi_attachdevs( unit, ahb_data[unit].our_id, &ahb_switch);
+#if defined(OSF)
+       ahb_attached[unit]=1;
+#endif /* defined(OSF) */
+       if(!unit) /* only one for all boards */
+       {
+               ahb_timeout(0);
+       }
+#ifdef  __386BSD__
+       printf("ahb%d",unit);
+#endif  __386BSD__
+       return;
+}
+
+/***********************************************\
+* Return some information to the caller about   *
+* the adapter and it's capabilities             *
+\***********************************************/
+long int ahb_adapter_info(unit)
+int    unit;
+{
+       return(2);      /* 2 outstanding requests at a time per device */
+}
+
+/***********************************************\
+* Catch an interrupt from the adaptor          *
+\***********************************************/
+ahbintr(unit)
+{
+       struct ecb      *ecb;
+       unsigned char   stat;
+       register        i;
+       u_char          ahbstat;
+       int             target;
+       long int        mboxval;
+
+       int     port = ahb_data[unit].baseport;
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("ahbintr ");
+
+#if defined(OSF)
+       if (!ahb_attached[unit])
+       {
+               return(1);
+       }
+#endif /* defined(OSF) */
+       while(inb(port + G2STAT) & G2STAT_INT_PEND)
+       {
+               /***********************************************\
+               * First get all the information and then        *
+               * acknowlege the interrupt                      *
+               \***********************************************/
+               ahbstat = inb(port + G2INTST);
+               target = ahbstat & G2INTST_TARGET;
+               stat = ahbstat & G2INTST_INT_STAT;
+               mboxval = inl(port + MBOXIN0);/* don't know this will work */
+               outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
+               if(scsi_debug & TRACEINTERRUPTS)
+                       printf("status = 0x%x ",stat);
+               /***********************************************\
+               * Process the completed operation               *
+               \***********************************************/
+       
+               if(stat == AHB_ECB_OK) /* common case is fast */
+               {
+                       ecb = (struct ecb *)PHYSTOKV(mboxval); 
+               }
+               else
+               {
+                       switch(stat)
+                       {
+                       case    AHB_IMMED_OK:
+                               ecb = ahb_data[unit].immed_ecb;
+                               ahb_data[unit].immed_ecb = 0;
+                               break;
+                       case    AHB_IMMED_ERR:
+                               ecb = ahb_data[unit].immed_ecb;
+                               ecb->flags |= ECB_IMMED_FAIL;
+                               ahb_data[unit].immed_ecb = 0;
+                               break;
+                       case    AHB_ASN:        /* for target mode */
+                               ecb = 0;
+                               break;
+                       case    AHB_HW_ERR:
+                               ecb = 0;
+                               break;
+                       case    AHB_ECB_RECOVERED:
+                               ecb = (struct ecb *)PHYSTOKV(mboxval); 
+                               break;
+                       case    AHB_ECB_ERR:
+                               ecb = (struct ecb *)PHYSTOKV(mboxval); 
+                               break;
+                       default:
+                               printf(" Unknown return from ahb%d(%x)\n",unit,ahbstat);
+                               ecb=0;
+                       }
+               }
+               if(ecb)
+               {
+                       if(ahb_debug & AHB_SHOWCMDS )
+                       {
+                               ahb_show_scsi_cmd(ecb->xs);
+                       }
+                       if((ahb_debug & AHB_SHOWECBS) && ecb)
+                               printf("<int ecb(%x)>",ecb);
+                       ahb_remove_timeout(ecb);
+                       ahb_done(unit,ecb,((stat == AHB_ECB_OK)?SUCCESS:FAIL));
+               }
+       }
+       return(1);
+}
+
+/***********************************************\
+* We have a ecb which has been processed by the        *
+* adaptor, now we look to see how the operation        *
+* went.                                                *
+\***********************************************/
+ahb_done(unit,ecb,state)
+int    unit,state;
+struct ecb *ecb;
+{
+       struct  ahb_ecb_status  *stat = &ecb->ecb_status;
+       struct  scsi_sense_data *s1,*s2;
+       struct  scsi_xfer *xs = ecb->xs;
+
+       if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS))
+               printf("ahb_done ");
+       /***********************************************\
+       * Otherwise, put the results of the operation   *
+       * into the xfer and call whoever started it     *
+       \***********************************************/
+       if(ecb->flags & ECB_IMMED)
+       {
+               if(ecb->flags & ECB_IMMED_FAIL)
+               {
+                       xs->error = XS_DRIVER_STUFFUP;
+               }
+               goto done;
+       }
+       if ( (state == SUCCESS) || (xs->flags & SCSI_ERR_OK))
+       {               /* All went correctly  OR errors expected */
+               xs->resid = 0;
+               xs->error = 0;
+       }
+       else
+       {
+
+               s1 = &(ecb->ecb_sense);
+               s2 = &(xs->sense);
+
+               if(stat->ha_status)
+               {
+                       switch(stat->ha_status)
+                       {
+                       case    HS_SCSI_RESET_ADAPTER:
+                               break;
+                       case    HS_SCSI_RESET_INCOMING:
+                               break;
+                       case    HS_CMD_ABORTED_HOST:    /* No response */
+                       case    HS_CMD_ABORTED_ADAPTER: /* No response */
+                               break;
+                       case    HS_TIMED_OUT:           /* No response */
+                               if (ahb_debug & AHB_SHOWMISC)
+                               {
+                                       printf("timeout reported back\n");
+                               }
+                               xs->error = XS_TIMEOUT;
+                               break;
+                       default:        /* Other scsi protocol messes */
+                               xs->error = XS_DRIVER_STUFFUP;
+                               if (ahb_debug & AHB_SHOWMISC)
+                               {
+                                       printf("unexpected ha_status: %x\n",
+                                               stat->ha_status);
+                               }
+                       }
+
+               }
+               else
+               {
+                       switch(stat->targ_status)
+                       {
+                       case TS_CHECK_CONDITION:
+                               /* structure copy!!!!!*/
+                               *s2=*s1;
+                               xs->error = XS_SENSE;
+                               break;
+                       case TS_BUSY:
+                               xs->error = XS_BUSY;
+                               break;
+                       default:
+                               if (ahb_debug & AHB_SHOWMISC)
+                               {
+                                       printf("unexpected targ_status: %x\n",
+                                               stat->targ_status);
+                               }
+                               xs->error = XS_DRIVER_STUFFUP;
+                       }
+               }
+       }
+done:  xs->flags |= ITSDONE;
+       ahb_free_ecb(unit,ecb, xs->flags);
+       if(xs->when_done)
+               (*(xs->when_done))(xs->done_arg,xs->done_arg2);
+}
+
+/***********************************************\
+* A ecb (and hence a mbx-out is put onto the   *
+* free list.                                   *
+\***********************************************/
+ahb_free_ecb(unit,ecb, flags)
+struct ecb *ecb;
+{
+       unsigned int opri;
+       
+       if(scsi_debug & PRINTROUTINES)
+               printf("ecb%d(0x%x)> ",unit,flags);
+       if (!(flags & SCSI_NOMASK)) 
+               opri = splbio();
+
+       ecb->next = ahb_data[unit].free_ecb;
+       ahb_data[unit].free_ecb = ecb;
+       ecb->flags = ECB_FREE;
+       /***********************************************\
+       * If there were none, wake abybody waiting for  *
+       * one to come free, starting with queued entries*
+       \***********************************************/
+       if (!ecb->next) {
+               wakeup(&ahb_data[unit].free_ecb);
+       }
+       if (!(flags & SCSI_NOMASK)) 
+               splx(opri);
+}
+
+/***********************************************\
+* Get a free ecb (and hence mbox-out entry)    *
+\***********************************************/
+struct ecb *
+ahb_get_ecb(unit,flags)
+{
+       unsigned opri;
+       struct ecb *rc;
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("<ecb%d(0x%x) ",unit,flags);
+       if (!(flags & SCSI_NOMASK)) 
+               opri = splbio();
+       /***********************************************\
+       * If we can and have to, sleep waiting for one  *
+       * to come free                                  *
+       \***********************************************/
+       while ((!(rc = ahb_data[unit].free_ecb)) && (!(flags & SCSI_NOSLEEP)))
+       {
+               sleep(&ahb_data[unit].free_ecb, PRIBIO);
+       }
+       if (rc) 
+       {
+               ahb_data[unit].free_ecb = rc->next;
+               rc->flags = ECB_ACTIVE;
+       }
+       if (!(flags & SCSI_NOMASK)) 
+               splx(opri);
+       return(rc);
+}
+               
+
+
+/***********************************************\
+* Start the board, ready for normal operation  *
+\***********************************************/
+ahb_init(unit)
+int    unit;
+{
+       int     port = ahb_data[unit].baseport;
+       int     intdef;
+       int     spincount = FUDGE(delaycount) * 1000; /* 1 sec enough? */
+       int     i;
+       int     stport = port + G2STAT;
+#define        NO_NO 1
+#ifdef NO_NO
+       /***********************************************\
+       * reset board, If it doesn't respond, assume    *
+       * that it's not there.. good for the probe      *
+       \***********************************************/
+       outb(port + EBCTRL,CDEN);       /* enable full card */
+       outb(port + PORTADDR,PORTADDR_ENHANCED);
+
+       outb(port + G2CNTRL,G2CNTRL_HARD_RESET);
+       spinwait(1);
+       outb(port + G2CNTRL,0);
+       spinwait(10);
+       while(      ((inb(stport) & G2STAT_BUSY ))
+               && (spincount--));
+       if(spincount == -1)
+       {
+               if (ahb_debug & AHB_SHOWMISC)
+                       printf("ahb_init: No answer from bt742a board\n");
+               return(ENXIO);
+       }
+       i = inb(port + MBOXIN0) & 0xff;
+       if(i)
+       {
+               printf("self test failed, val = 0x%x\n",i);
+               return(EIO);
+       }
+#endif
+       while( inb(stport) &  G2STAT_INT_PEND)
+       {
+               printf(".");
+               outb(port + G2CNTRL, G2CNTRL_CLEAR_EISA_INT);
+               spinwait(10);
+       }
+       outb(port + EBCTRL,CDEN);       /* enable full card */
+       outb(port + PORTADDR,PORTADDR_ENHANCED);
+       /***********************************************\
+       * Assume we have a board at this stage          *
+       * setup dma channel from jumpers and save int   *
+       * level                                         *
+       \***********************************************/
+#ifdef __386BSD__
+       printf("ahb%d reading board settings, ",unit);
+#define        PRNT(x)
+#else  __386BSD__
+       printf("ahb%d:",unit);
+#define        PRNT(x) printf(x)
+#endif __386BSD__
+
+       intdef = inb(port + INTDEF);
+       switch(intdef & 0x07)
+       {
+       case    INT9:
+               ahb_data[unit].vect = 9;
+               PRNT("int=9 ");
+               break;
+       case    INT10:
+               ahb_data[unit].vect = 10;
+               PRNT("int=10 ");
+               break;
+       case    INT11:
+               ahb_data[unit].vect = 11;
+               PRNT("int=11 ");
+               break;
+       case    INT12:
+               ahb_data[unit].vect = 12;
+               PRNT("int=12 ");
+               break;
+       case    INT14:
+               ahb_data[unit].vect = 14;
+               PRNT("int=14 ");
+               break;
+       case    INT15:
+               ahb_data[unit].vect = 15;
+               PRNT("int=15 ");
+               break;
+       default:
+               printf("illegal int setting\n");
+               return(EIO);
+       }
+       outb(port + INTDEF ,(intdef | INTEN)); /* make sure we can interrupt */
+       /* who are we on the scsi bus */
+       ahb_data[unit].our_id = (inb(port + SCSIDEF) & HSCSIID);
+
+       /***********************************************\
+       * link up all our ECBs into a free list         *
+       \***********************************************/
+       for (i=0; i < NUM_CONCURRENT; i++)
+       {
+               ahb_data[unit].ecbs[i].next = ahb_data[unit].free_ecb;
+               ahb_data[unit].free_ecb = &ahb_data[unit].ecbs[i];
+               ahb_data[unit].free_ecb->flags = ECB_FREE;
+       }
+
+       /***********************************************\
+       * Note that we are going and return (to probe)  *
+       \***********************************************/
+       ahb_data[unit].flags |= AHB_INIT;
+       return( 0 );
+}
+
+
+#ifndef        min
+#define min(x,y) (x < y ? x : y)
+#endif min
+
+
+void ahbminphys(bp)
+struct buf *bp;
+{
+#ifdef MACH
+#if    !defined(OSF)
+       bp->b_flags |= B_NPAGES;                /* can support scat/gather */
+#endif /* defined(OSF) */
+#endif MACH
+       if(bp->b_bcount > ((AHB_NSEG-1) * PAGESIZ))
+       {
+               bp->b_bcount = ((AHB_NSEG-1) * PAGESIZ);
+       }
+}
+       
+/***********************************************\
+* start a scsi operation given the command and *
+* the data address. Also needs the unit, target        *
+* and lu                                       *
+\***********************************************/
+int    ahb_scsi_cmd(xs)
+struct scsi_xfer *xs;
+{
+       struct  scsi_sense_data *s1,*s2;
+       struct ecb *ecb;
+       struct ahb_dma_seg *sg;
+       int     seg;    /* scatter gather seg being worked on */
+       int i   = 0;
+       int rc  =  0;
+       int     thiskv;
+       physaddr        thisphys,nextphys;
+       int     unit =xs->adapter;
+       int     bytes_this_seg,bytes_this_page,datalen,flags;
+       struct  iovec   *iovp;
+       int     s;
+       if(scsi_debug & PRINTROUTINES)
+               printf("ahb_scsi_cmd ");
+       /***********************************************\
+       * get a ecb (mbox-out) to use. If the transfer  *
+       * is from a buf (possibly from interrupt time)  *
+       * then we can't allow it to sleep               *
+       \***********************************************/
+       flags = xs->flags;
+       if(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */
+       if(flags & ITSDONE)
+       {
+               printf("Already done?");
+               xs->flags &= ~ITSDONE;
+       }
+       if(!(flags & INUSE))
+       {
+               printf("Not in use?");
+               xs->flags |= INUSE;
+       }
+       if (!(ecb = ahb_get_ecb(unit,flags)))
+       {
+               xs->error = XS_DRIVER_STUFFUP;
+               return(TRY_AGAIN_LATER);
+       }
+
+cheat = ecb;
+       if(ahb_debug & AHB_SHOWECBS)
+                               printf("<start ecb(%x)>",ecb);
+       if(scsi_debug & SHOWCOMMANDS)
+       {
+               ahb_show_scsi_cmd(xs);
+       }
+       ecb->xs = xs;
+       /***********************************************\
+       * If it's a reset, we need to do an 'immediate' *
+       * command, and store it's ccb for later         *
+       * if there is already an immediate waiting,     *
+       * then WE must wait                             *
+       \***********************************************/
+       if(flags & SCSI_RESET)
+       {
+               ecb->flags |= ECB_IMMED;
+               if(ahb_data[unit].immed_ecb)
+               {
+                       return(TRY_AGAIN_LATER);
+               }
+               ahb_data[unit].immed_ecb = ecb;
+               if (!(flags & SCSI_NOMASK))
+               {
+                       s = splbio();
+                       ahb_send_immed(unit,xs->targ,AHB_TARG_RESET);
+                       ahb_add_timeout(ecb,xs->timeout);
+                       splx(s);
+                       return(SUCCESSFULLY_QUEUED);
+               }
+               else
+               {
+                       ahb_send_immed(unit,xs->targ,AHB_TARG_RESET);
+                       /***********************************************\
+                       * If we can't use interrupts, poll on completion*
+                       \***********************************************/
+                       if(scsi_debug & TRACEINTERRUPTS)
+                               printf("wait ");
+                       if( ahb_poll(unit,xs->timeout))
+                       {
+                               ahb_free_ecb(unit,ecb,flags);
+                               xs->error = XS_TIMEOUT;
+                               return(HAD_ERROR);
+                       }
+                       return(COMPLETE);
+               }
+       }       
+       /***********************************************\
+       * Put all the arguments for the xfer in the ecb *
+       \***********************************************/
+       ecb->opcode = ECB_SCSI_OP;
+       ecb->opt1 = ECB_SES|ECB_DSB|ECB_ARS;
+       if(xs->datalen)
+       {
+               ecb->opt1 |= ECB_S_G;
+       }
+       ecb->opt2               =       xs->lu | ECB_NRB;
+       ecb->cdblen             =       xs->cmdlen;
+       ecb->sense              =       KVTOPHYS(&(ecb->ecb_sense));
+       ecb->senselen           =       sizeof(ecb->ecb_sense);
+       ecb->status             =       KVTOPHYS(&(ecb->ecb_status));
+
+       if(xs->datalen)
+       { /* should use S/G only if not zero length */
+               ecb->data       =       KVTOPHYS(ecb->ahb_dma);
+               sg              =       ecb->ahb_dma ;
+               seg             =       0;
+               if(flags & SCSI_DATA_UIO)
+               {
+                       iovp = ((struct uio *)xs->data)->uio_iov;
+                       datalen = ((struct uio *)xs->data)->uio_iovcnt;
+                       xs->datalen = 0;
+                       while ((datalen) && (seg < AHB_NSEG))
+                       {
+                               sg->addr = (physaddr)iovp->iov_base;
+                               xs->datalen += sg->len = iovp->iov_len; 
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("(0x%x@0x%x)"
+                                                       ,iovp->iov_len
+                                                       ,iovp->iov_base);
+                               sg++;
+                               iovp++;
+                               seg++;
+                               datalen--;
+                       }
+               }
+               else
+               {
+                       /***********************************************\
+                       * Set up the scatter gather block               *
+                       \***********************************************/
+               
+                       if(scsi_debug & SHOWSCATGATH)
+                               printf("%d @0x%x:- ",xs->datalen,xs->data);
+                       datalen         =       xs->datalen;
+                       thiskv          =       (int)xs->data;
+                       thisphys        =       KVTOPHYS(thiskv);
+               
+                       while ((datalen) && (seg < AHB_NSEG))
+                       {
+                               bytes_this_seg  = 0;
+       
+                               /* put in the base address */
+                               sg->addr = thisphys;
+               
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("0x%x",thisphys);
+       
+                               /* do it at least once */
+                               nextphys = thisphys;    
+                               while ((datalen) && (thisphys == nextphys))
+                               /*********************************************\
+                               * This page is contiguous (physically) with   *
+                               * the the last, just extend the length        *
+                               \*********************************************/
+                               {
+                                       /* how far to the end of the page */
+                                       nextphys= (thisphys & (~(PAGESIZ - 1)))
+                                                               + PAGESIZ;
+                                       bytes_this_page = nextphys - thisphys;
+                                       /**** or the data ****/
+                                       bytes_this_page = min(bytes_this_page
+                                                               ,datalen);
+                                       bytes_this_seg  += bytes_this_page;
+                                       datalen         -= bytes_this_page;
+               
+                                       /* get more ready for the next page */
+                                       thiskv  = (thiskv & (~(PAGESIZ - 1)))
+                                                               + PAGESIZ;
+                                       if(datalen)
+                                               thisphys = KVTOPHYS(thiskv);
+                               }
+                               /********************************************\
+                               * next page isn't contiguous, finish the seg *
+                               \********************************************/
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("(0x%x)",bytes_this_seg);
+                               sg->len = bytes_this_seg;       
+                               sg++;
+                               seg++;
+                       }
+               } /*end of iov/kv decision */
+               ecb->datalen = seg * sizeof(struct ahb_dma_seg);
+               if(scsi_debug & SHOWSCATGATH)
+                       printf("\n");
+               if (datalen)
+               { /* there's still data, must have run out of segs! */
+                       printf("ahb_scsi_cmd%d: more than %d DMA segs\n",
+                               unit,AHB_NSEG);
+                       xs->error = XS_DRIVER_STUFFUP;
+                       ahb_free_ecb(unit,ecb,flags);
+                       return(HAD_ERROR);
+               }
+
+       }
+       else
+       {       /* No data xfer, use non S/G values */
+               ecb->data = (physaddr)0;
+               ecb->datalen = 0;
+       }
+       ecb->chain = (physaddr)0;
+       /***********************************************\
+       * Put the scsi command in the ecb and start it  *
+       \***********************************************/
+       bcopy(xs->cmd, ecb->cdb, xs->cmdlen);
+       /***********************************************\
+       * Usually return SUCCESSFULLY QUEUED            *
+       \***********************************************/
+       if (!(flags & SCSI_NOMASK))
+       {
+               s = splbio();
+               ahb_send_mbox(unit,OP_START_ECB,xs->targ,ecb);
+               ahb_add_timeout(ecb,xs->timeout);
+               splx(s);
+               if(scsi_debug & TRACEINTERRUPTS)
+                       printf("cmd_sent ");
+               return(SUCCESSFULLY_QUEUED);
+       }
+       /***********************************************\
+       * If we can't use interrupts, poll on completion*
+       \***********************************************/
+       ahb_send_mbox(unit,OP_START_ECB,xs->targ,ecb);
+       if(scsi_debug & TRACEINTERRUPTS)
+               printf("cmd_wait ");
+       do
+       {
+               if(ahb_poll(unit,xs->timeout))
+               {
+                       if (!(xs->flags & SCSI_SILENT)) printf("cmd fail\n");
+                       ahb_send_mbox(unit,OP_ABORT_ECB,xs->targ,ecb);
+                       if(ahb_poll(unit,2000))
+                       {
+                               printf("abort failed in wait\n");
+                               ahb_free_ecb(unit,ecb,flags);
+                       }
+                       xs->error = XS_DRIVER_STUFFUP;
+                       splx(s);
+                       return(HAD_ERROR);
+               }
+       } while (!(xs->flags & ITSDONE));/* something (?) else finished */
+       splx(s);
+scsi_debug = 0;ahb_debug = 0;
+       if(xs->error)
+       {
+               return(HAD_ERROR);
+       }
+       return(COMPLETE);
+}
+
+/*
+ *                +----------+    +----------+    +----------+
+ * ahb_soonest--->|    later |--->|     later|--->|     later|--->0
+ *                | [Delta]  |    | [Delta]  |    | [Delta]  |
+ *           0<---|sooner    |<---|sooner    |<---|sooner    |<---ahb_latest
+ *                +----------+    +----------+    +----------+
+ *
+ *     ahb_furtherest = sum(Delta[1..n])
+ */
+ahb_add_timeout(ecb,time)
+struct ecb     *ecb;
+int    time;
+{
+       int     timeprev;
+       struct ecb *prev;
+       int     s = splbio();
+
+       if(prev = ahb_latest) /* yes, an assign */
+       {
+               timeprev = ahb_furtherest;
+       }
+       else
+       {
+               timeprev = 0;
+       }
+       while(prev && (timeprev > time)) 
+       {
+               timeprev -= prev->delta;
+               prev = prev->sooner;
+       }
+       if(prev)
+       {
+               ecb->delta = time - timeprev;
+               if( ecb->later = prev->later) /* yes an assign */
+               {
+                       ecb->later->sooner = ecb;
+                       ecb->later->delta -= ecb->delta;
+               }
+               else
+               {
+                       ahb_furtherest = time;
+                       ahb_latest = ecb;
+               }
+               ecb->sooner = prev;
+               prev->later = ecb;
+       }
+       else
+       {
+               if( ecb->later = ahb_soonest) /* yes, an assign*/
+               {
+                       ecb->later->sooner = ecb;
+                       ecb->later->delta -= time;
+               }
+               else
+               {
+                       ahb_furtherest = time;
+                       ahb_latest = ecb;
+               }
+               ecb->delta = time;
+               ecb->sooner = (struct ecb *)0;
+               ahb_soonest = ecb;
+       }
+       splx(s);
+}
+
+ahb_remove_timeout(ecb)
+struct ecb     *ecb;
+{
+       int     s = splbio();
+
+       if(ecb->sooner)
+       {
+               ecb->sooner->later = ecb->later;
+       }
+       else
+       {
+               ahb_soonest = ecb->later;
+       }
+       if(ecb->later)
+       {
+               ecb->later->sooner = ecb->sooner;
+               ecb->later->delta += ecb->delta;
+       }
+       else
+       {
+               ahb_latest = ecb->sooner;
+               ahb_furtherest -= ecb->delta;
+       }
+       ecb->sooner = ecb->later = (struct ecb *)0;
+       splx(s);
+}
+
+
+extern int     hz;
+#define ONETICK 500 /* milliseconds */
+#define SLEEPTIME ((hz * 1000) / ONETICK)
+ahb_timeout(arg)
+int    arg;
+{
+       struct  ecb  *ecb;
+       int     unit;
+       int     s       = splbio();
+
+       while( ecb = ahb_soonest )
+       {
+               if(ecb->delta <= ONETICK)
+               /***********************************************\
+               * It has timed out, we need to do some work     *
+               \***********************************************/
+               {
+                       unit = ecb->xs->adapter;
+                       printf("ahb%d:%d device timed out\n",unit
+                                       ,ecb->xs->targ);
+                       if(ahb_debug & AHB_SHOWECBS)
+                               ahb_print_active_ecb();
+
+                       /***************************************\
+                       * Unlink it from the queue              *
+                       \***************************************/
+                       ahb_remove_timeout(ecb);
+
+                       /***************************************\
+                       * If it's immediate, don't try abort it *
+                       \***************************************/
+                       if(ecb->flags & ECB_IMMED)
+                       {
+                               ecb->xs->retries = 0; /* I MEAN IT ! */
+                               ecb->flags |= ECB_IMMED_FAIL;
+                                ahb_done(unit,ecb,FAIL);
+                               continue;
+                       }
+                       /***************************************\
+                       * If it has been through before, then   *
+                       * a previous abort has failed, don't    *
+                       * try abort again                       *
+                       \***************************************/
+                       if(ecb->flags == ECB_ABORTED) /* abort timed out */
+                       {
+                               printf("AGAIN");
+                               ecb->xs->retries = 0; /* I MEAN IT ! */
+                               ecb->ecb_status.ha_status = HS_CMD_ABORTED_HOST;
+                               ahb_done(unit,ecb,FAIL);
+                       }
+                       else    /* abort the operation that has timed out */
+                       {
+                               printf("\n");
+                               ahb_send_mbox(unit,OP_ABORT_ECB,ecb->xs->targ,ecb);
+                                       /* 2 secs for the abort */
+                               ahb_add_timeout(ecb,2000 + ONETICK);
+                               ecb->flags = ECB_ABORTED;
+                       }
+               }
+               else
+               /***********************************************\
+               * It has not timed out, adjust and leave        *
+               \***********************************************/
+               {
+                       ecb->delta -= ONETICK;
+                       ahb_furtherest -= ONETICK;
+                       break;
+               }
+       }
+       splx(s);
+       timeout(ahb_timeout,arg,SLEEPTIME);
+}
+
+ahb_show_scsi_cmd(struct scsi_xfer *xs)
+{
+       u_char  *b = (u_char *)xs->cmd;
+       int i = 0;
+       if(!(xs->flags & SCSI_RESET))
+       {
+               printf("ahb%d:%d:%d-"
+                       ,xs->adapter
+                       ,xs->targ
+                       ,xs->lu);
+               while(i < xs->cmdlen )
+               {
+                       if(i) printf(",");
+                       printf("%x",b[i++]);
+               }
+               printf("-\n");
+       }
+       else
+       {
+               printf("ahb%d:%d:%d-RESET-\n" 
+                       ,xs->adapter 
+                       ,xs->targ
+                       ,xs->lu
+               );
+       }
+}
+ahb_print_ecb(ecb)
+struct ecb *ecb;
+{
+       printf("ecb:%x op:%x cmdlen:%d senlen:%d\n"
+               ,ecb
+               ,ecb->opcode
+               ,ecb->cdblen
+               ,ecb->senselen);
+       printf("        datlen:%d hstat:%x tstat:%x delta:%d flags:%x\n"
+               ,ecb->datalen
+               ,ecb->ecb_status.ha_status
+               ,ecb->ecb_status.targ_status
+               ,ecb->delta
+               ,ecb->flags);
+       ahb_show_scsi_cmd(ecb->xs);
+}
+
+ahb_print_active_ecb()
+{
+       struct  ecb *ecb;
+       ecb = ahb_soonest;
+
+       while(ecb)
+       {
+               ahb_print_ecb(ecb);
+               ecb = ecb->later;
+       }
+       printf("Furtherest = %d\n",ahb_furtherest);
+}
diff --git a/usr/src/sys.386bsd/i386/isa/bt742a.c b/usr/src/sys.386bsd/i386/isa/bt742a.c
new file mode 100644 (file)
index 0000000..dcdba5b
--- /dev/null
@@ -0,0 +1,1537 @@
+/*
+ * 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.
+ *
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ */
+
+/*
+ * HISTORY
+ * $Log: bt742a.c,v $
+ * Revision 1.7  1992/08/24  22:40:16  jason
+ * BIG_DMA ifdef for 512 dma segments instead of 128 segments
+ *
+ * Revision 1.6  1992/08/24  21:01:58  jason
+ * many changes and bugfixes for osf1
+ *
+ * Revision 1.5  1992/07/31  01:22:03  julian
+ * support improved scsi.h layout
+ *
+ * Revision 1.4  1992/07/25  03:11:26  julian
+ * check each request fro sane flags.
+ *
+ * Revision 1.3  1992/07/24  00:52:45  julian
+ * improved timeout handling.
+ * added support for two arguments to the sd_done (or equiv) call so that
+ * they can pre-queue several arguments.
+ * slightly clean up error handling
+ *
+ * Revision 1.2  1992/07/17  22:03:54  julian
+ * upgraded the timeout code.
+ * added support for UIO-based i/o (as used for pmem operations)
+ *
+ * Revision 1.1  1992/05/27  00:51:12  balsup
+ * machkern/cor merge
+ * 
+ */
+
+/*
+ * bt742a BT-1542A SCSI driver
+ */
+
+#include <sys/types.h>
+#include <bt.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>
+
+#ifdef MACH    /* EITHER CMU OR OSF */
+#include <i386/ipl.h>
+#include <i386at/scsi.h>
+#include <i386at/scsiconf.h>
+
+#ifdef OSF     /* OSF ONLY */
+#include <sys/table.h>
+#include <i386/handler.h>
+#include <i386/dispatcher.h>
+#include <i386/AT386/atbus.h>
+
+#else  OSF     /* CMU ONLY */
+#include <i386at/atbus.h>
+#include <i386/pio.h>
+#endif OSF
+#endif MACH    /* end of MACH specific */
+
+#ifdef __386BSD__      /* 386BSD specific */
+#define isa_dev isa_device
+#define dev_unit id_unit
+#define dev_addr id_iobase
+
+#include <i386/isa/isa_device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#endif __386BSD__
+
+
+#ifdef __386BSD__
+#include "ddb.h"
+#if    NDDB > 0
+int    Debugger();
+#else  NDDB
+#define        Debugger() panic("should call debugger here (adaptec.c)")
+#endif NDDB
+#endif __386BSD__
+
+#ifdef MACH
+int    Debugger();
+#endif MACH
+
+extern int delaycount; /* from clock setup code */
+typedef unsigned long int physaddr;
+
+/*
+ * I/O Port Interface
+ */
+
+#define        BT_BASE         bt_base[unit]
+#define        BT_CTRL_STAT_PORT       (BT_BASE + 0x0) /* control & status */
+#define        BT_CMD_DATA_PORT        (BT_BASE + 0x1) /* cmds and datas */
+#define        BT_INTR_PORT            (BT_BASE + 0x2) /* Intr. stat */
+
+/*
+ * BT_CTRL_STAT bits (write)
+ */
+
+#define BT_HRST                0x80    /* Hardware reset */
+#define BT_SRST                0x40    /* Software reset */
+#define BT_IRST                0x20    /* Interrupt reset */
+#define BT_SCRST       0x10    /* SCSI bus reset */
+
+/*
+ * BT_CTRL_STAT bits (read)
+ */
+
+#define BT_STST                0x80    /* Self test in Progress */
+#define BT_DIAGF       0x40    /* Diagnostic Failure */
+#define BT_INIT                0x20    /* Mbx Init required */
+#define BT_IDLE                0x10    /* Host Adapter Idle */
+#define BT_CDF         0x08    /* cmd/data out port full */
+#define BT_DF          0x04    /* Data in port full */
+#define BT_INVDCMD     0x01    /* Invalid command */
+
+/*
+ * BT_CMD_DATA bits (write)
+ */
+
+#define        BT_NOP                  0x00    /* No operation */
+#define BT_MBX_INIT            0x01    /* Mbx initialization */
+#define BT_START_SCSI          0x02    /* start scsi command */
+#define BT_START_BIOS          0x03    /* start bios command */
+#define BT_INQUIRE             0x04    /* Adapter Inquiry */
+#define BT_MBO_INTR_EN         0x05    /* Enable MBO available interrupt */
+#define BT_SEL_TIMEOUT_SET     0x06    /* set selection time-out */
+#define BT_BUS_ON_TIME_SET     0x07    /* set bus-on time */
+#define BT_BUS_OFF_TIME_SET    0x08    /* set bus-off time */
+#define BT_SPEED_SET           0x09    /* set transfer speed */
+#define BT_DEV_GET             0x0a    /* return installed devices */
+#define BT_CONF_GET            0x0b    /* return configuration data */
+#define BT_TARGET_EN           0x0c    /* enable target mode */
+#define BT_SETUP_GET           0x0d    /* return setup data */
+#define BT_WRITE_CH2           0x1a    /* write channel 2 buffer */
+#define BT_READ_CH2            0x1b    /* read channel 2 buffer */
+#define BT_WRITE_FIFO          0x1c    /* write fifo buffer */
+#define BT_READ_FIFO           0x1d    /* read fifo buffer */
+#define BT_ECHO                        0x1e    /* Echo command data */
+#define BT_MBX_INIT_EXTENDED   0x81    /* Mbx initialization */
+#define BT_INQUIRE_EXTENDED    0x8D    /* Adapter Setup Inquiry */
+
+struct bt_cmd_buf {
+        u_char byte[16];       
+};
+
+/*
+ * BT_INTR_PORT bits (read)
+ */
+
+#define BT_ANY_INTR            0x80    /* Any interrupt */
+#define BT_SCRD                0x08    /* SCSI reset detected */
+#define BT_HACC                0x04    /* Command complete */
+#define BT_MBOA                0x02    /* MBX out empty */
+#define BT_MBIF                0x01    /* MBX in full */
+
+/*
+ * Mail box defs 
+ */
+
+#define BT_MBX_SIZE            16      /* mail box size */
+
+struct bt_mbx
+{
+       struct bt_mbx_out {
+               physaddr        ccb_addr;
+               unsigned char   dummy[3];
+               unsigned char   cmd;
+       } mbo [BT_MBX_SIZE];
+       struct bt_mbx_in{
+               physaddr        ccb_addr;
+               unsigned char   btstat;
+               unsigned char   sdstat;
+               unsigned char   dummy;
+               unsigned char   stat;
+       } mbi[BT_MBX_SIZE];
+};
+
+/*
+ * mbo.cmd values
+ */
+
+#define BT_MBO_FREE    0x0     /* MBO entry is free */
+#define BT_MBO_START   0x1     /* MBO activate entry */
+#define BT_MBO_ABORT   0x2     /* MBO abort entry */
+
+#define BT_MBI_FREE    0x0     /* MBI entry is free */
+#define BT_MBI_OK      0x1     /* completed without error */
+#define BT_MBI_ABORT   0x2     /* aborted ccb */
+#define BT_MBI_UNKNOWN 0x3     /* Tried to abort invalid CCB */
+#define BT_MBI_ERROR   0x4     /* Completed with error */
+
+extern struct bt_mbx bt_mbx[];
+
+#if    defined(BIG_DMA)
+/* #define     BT_NSEG 8192    /* Number of scatter gather segments - to much vm */
+#define        BT_NSEG 512
+#else
+#define        BT_NSEG 33
+#endif /* BIG_DMA */
+struct bt_scat_gath
+       {
+               unsigned long   seg_len;
+               physaddr        seg_addr;
+       };
+
+struct bt_ccb {
+       unsigned char           opcode;
+       unsigned char           :3,data_in:1,data_out:1,:3;
+       unsigned char           scsi_cmd_length;
+       unsigned char           req_sense_length;
+       /*------------------------------------longword boundary */
+       unsigned long           data_length;
+       /*------------------------------------longword boundary */
+       physaddr                data_addr;
+       /*------------------------------------longword boundary */
+       unsigned char           dummy[2];
+       unsigned char           host_stat;
+       unsigned char           target_stat;
+       /*------------------------------------longword boundary */
+       unsigned char           target;
+       unsigned char           lun;
+       unsigned char           scsi_cmd[12];   /* 12 bytes (bytes only)*/
+       unsigned char           dummy2[1];
+       unsigned char           link_id;
+       /*------------------------------------4 longword boundary */
+       physaddr                link_addr;
+       /*------------------------------------longword boundary */
+       physaddr                sense_ptr;
+       /*------------------------------------longword boundary */
+       struct  scsi_sense_data scsi_sense;
+       /*------------------------------------longword boundary */
+       struct  bt_scat_gath    scat_gath[BT_NSEG];
+       /*------------------------------------longword boundary */
+       struct  bt_ccb          *next;
+       /*------------------------------------longword boundary */
+       struct  scsi_xfer       *xfer;          /* the scsi_xfer for this cmd */
+       /*------------------------------------longword boundary */
+       struct  bt_mbx_out      *mbx;           /* pointer to mail box */
+       /*------------------------------------longword boundary */
+       long    int     delta;  /* difference from previous*/
+       struct bt_ccb   *later,*sooner;
+       int             flags;
+#define        CCB_FREE        0
+#define CCB_ACTIVE     1
+#define        CCB_ABORTED     2
+};
+
+struct bt_ccb *bt_soonest = (struct bt_ccb *)0;
+struct bt_ccb *bt_latest = (struct bt_ccb *)0;
+long int       bt_furtherest = 0;      /* longest time in the timeout queue */
+/*
+ * opcode fields
+ */
+
+#define BT_INITIATOR_CCB       0x00    /* SCSI Initiator CCB */
+#define BT_TARGET_CCB          0x01    /* SCSI Target CCB */
+#define BT_INIT_SCAT_GATH_CCB  0x02    /* SCSI Initiator with scattter gather*/
+#define BT_RESET_CCB           0x81    /* SCSI Bus reset */
+
+
+/*
+ * bt_ccb.host_stat values
+ */
+
+#define BT_OK          0x00    /* cmd ok */
+#define BT_LINK_OK     0x0a    /* Link cmd ok */
+#define BT_LINK_IT     0x0b    /* Link cmd ok + int */
+#define BT_SEL_TIMEOUT 0x11    /* Selection time out */
+#define BT_OVER_UNDER  0x12    /* Data over/under run */
+#define BT_BUS_FREE    0x13    /* Bus dropped at unexpected time */
+#define BT_INV_BUS     0x14    /* Invalid bus phase/sequence */
+#define BT_BAD_MBO     0x15    /* Incorrect MBO cmd */
+#define BT_BAD_CCB     0x16    /* Incorrect ccb opcode */
+#define BT_BAD_LINK    0x17    /* Not same values of LUN for links */
+#define BT_INV_TARGET  0x18    /* Invalid target direction */
+#define BT_CCB_DUP     0x19    /* Duplicate CCB received */
+#define BT_INV_CCB     0x1a    /* Invalid CCB or segment list */
+#define BT_ABORTED     42      /* pseudo value from driver */
+
+
+
+struct bt_setup
+{
+       u_char  sync_neg:1;
+       u_char  parity:1;
+       u_char  :6;
+       u_char  speed;
+       u_char  bus_on;
+       u_char  bus_off;
+       u_char  num_mbx;
+       u_char  mbx[4];
+       struct
+       {
+               u_char  offset:4;
+               u_char  period:3;
+               u_char  valid:1;
+       }sync[8];
+       u_char  disc_sts;
+};
+
+struct bt_config
+{
+       u_char  chan;
+       u_char  intr;
+       u_char  scsi_dev:3;
+       u_char  :5;
+};
+
+#define INT9   0x01
+#define INT10  0x02
+#define INT11  0x04
+#define INT12  0x08
+#define INT14  0x20
+#define INT15  0x40
+
+#define EISADMA        0x00
+#define CHAN0  0x01
+#define CHAN5  0x20
+#define CHAN6  0x40
+#define CHAN7  0x80
+
+
+
+
+#ifdef        MACH
+extern physaddr        kvtophys();
+#define PHYSTOKV(x)   phystokv(x)
+#define KVTOPHYS(x)   kvtophys(x)
+#endif MACH
+
+#ifdef        __386BSD__
+#define PHYSTOKV(x)   (x | 0xFE000000)
+#define KVTOPHYS(x)   vtophys(x)
+#endif        __386BSD__
+
+
+
+#define PAGESIZ        4096
+#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); }
+
+
+u_char                 bt_scratch_buf[256];
+#ifdef MACH
+caddr_t                        bt_base[NBT];           /* base port for each board */
+#else  MACH
+short                  bt_base[NBT];           /* base port for each board */
+#endif MACH
+struct bt_mbx          bt_mbx[NBT];
+struct bt_ccb          *bt_ccb_free[NBT];
+struct bt_ccb          bt_ccb[NBT][BT_MBX_SIZE];
+struct scsi_xfer       bt_scsi_xfer[NBT];
+struct isa_dev         *btinfo[NBT];
+struct bt_ccb          *bt_get_ccb();
+int                    bt_int[NBT];
+int                    bt_dma[NBT];
+int                    bt_scsi_dev[NBT];
+int                    bt_initialized[NBT];
+#if defined(OSF)
+int                    bt_attached[NBT];
+#endif /* defined(OSF) */
+
+/***********debug values *************/
+#define        BT_SHOWCCBS 0x01
+#define        BT_SHOWINTS 0x02
+#define        BT_SHOWCMDS 0x04
+#define        BT_SHOWMISC 0x08
+int    bt_debug = 0;
+
+
+int btprobe(), btattach();
+int btintr();
+
+#ifdef MACH
+struct isa_driver      btdriver = { btprobe, 0, btattach, "bt", 0, 0, 0};
+int (*btintrs[])() = {btintr, 0};
+#endif MACH
+
+#ifdef __386BSD__
+struct isa_driver      btdriver = { btprobe, btattach, "bt"};
+#endif __386BSD__
+
+static int     btunit = 0;
+
+#define bt_abortmbx(mbx) \
+       (mbx)->cmd = BT_MBO_ABORT; \
+       outb(BT_CMD_DATA_PORT, BT_START_SCSI);
+#define bt_startmbx(mbx) \
+       (mbx)->cmd = BT_MBO_START; \
+       outb(BT_CMD_DATA_PORT, BT_START_SCSI);
+
+
+
+int    bt_scsi_cmd();
+int    bt_timeout();
+void   btminphys();
+long int bt_adapter_info();
+
+struct scsi_switch     bt_switch =
+{
+       bt_scsi_cmd,
+       btminphys,
+       0,
+       0,
+       bt_adapter_info,
+       0,0,0
+};     
+#define BT_CMD_TIMEOUT_FUDGE 200 /* multiplied to get Secs */
+#define BT_RESET_TIMEOUT 1000000
+#define BT_SCSI_TIMEOUT_FUDGE 20 /* divided by for mSecs */
+
+
+/***********************************************************************\
+* bt_cmd(unit,icnt, ocnt,wait, retval, opcode, args)                   *
+* Activate Adapter command                                             *
+*      icnt:   number of args (outbound bytes written after opcode)    *
+*      ocnt:   number of expected returned bytes                       *
+*      wait:   number of seconds to wait for response                  *
+*      retval: buffer where to place returned bytes                    *
+*      opcode: opcode BT_NOP, BT_MBX_INIT, BT_START_SCSI ...           *
+*      args:   parameters                                              *
+*                                                                      *
+* Performs an adapter command through the ports. Not to be confused    *
+*      with a scsi command, which is read in via the dma               *
+* One of the adapter commands tells it to read in a scsi command       *
+\***********************************************************************/
+bt_cmd(unit,icnt, ocnt, wait,retval, opcode, args)
+
+u_char *retval;
+unsigned opcode;
+u_char args;
+{
+       unsigned *ic = &opcode;
+       u_char oc;
+       register i;
+       int     sts;
+
+       /*******************************************************\
+       * multiply the wait argument by a big constant          *
+       * zero defaults to 1                                    *
+       \*******************************************************/
+       if(!wait) 
+               wait = BT_CMD_TIMEOUT_FUDGE * delaycount; 
+       else
+               wait *= BT_CMD_TIMEOUT_FUDGE * delaycount; 
+       /*******************************************************\
+       * Wait for the adapter to go idle, unless it's one of   *
+       * the commands which don't need this                    *
+       \*******************************************************/
+       if (opcode != BT_MBX_INIT && opcode != BT_START_SCSI)
+       {
+               i = BT_CMD_TIMEOUT_FUDGE * delaycount; /* 1 sec?*/
+               while (--i)
+               {
+                       sts = inb(BT_CTRL_STAT_PORT);
+                       if (sts & BT_IDLE)
+                       {
+                               break;
+                       }
+               }
+               if (!i)
+               {
+                       printf("bt_cmd: bt742a host not idle(0x%x)\n",sts);
+                       return(ENXIO);
+               }
+       }
+       /*******************************************************\
+       * Now that it is idle, if we expect output, preflush the*
+       * queue feeding to us.                                  *
+       \*******************************************************/
+       if (ocnt)
+       {
+               while((inb(BT_CTRL_STAT_PORT)) & BT_DF)
+                       inb(BT_CMD_DATA_PORT);
+       }
+                       
+       /*******************************************************\
+       * Output the command and the number of arguments given  *
+       * for each byte, first check the port is empty.         *
+       \*******************************************************/
+       icnt++;         /* include the command */
+       while (icnt--)
+       {
+               sts = inb(BT_CTRL_STAT_PORT);
+               for (i=0; i< wait; i++)
+               {
+                       sts = inb(BT_CTRL_STAT_PORT);
+                       if (!(sts & BT_CDF))
+                               break;
+               }
+               if (i >=  wait)
+               {
+                       printf("bt_cmd: bt742a cmd/data port full\n");
+                       outb(BT_CTRL_STAT_PORT, BT_SRST); 
+                       return(ENXIO);
+               }
+               outb(BT_CMD_DATA_PORT, (u_char)(*ic++));
+       }
+       /*******************************************************\
+       * If we expect input, loop that many times, each time,  *
+       * looking for the data register to have valid data      *
+       \*******************************************************/
+       while (ocnt--)
+       {
+               sts = inb(BT_CTRL_STAT_PORT);
+               for (i=0; i< wait; i++)
+               {
+                       sts = inb(BT_CTRL_STAT_PORT);
+                       if (sts  & BT_DF)
+                               break;
+               }
+               if (i >=  wait)
+               {
+                       printf("bt_cmd: bt742a cmd/data port empty %d\n",ocnt);
+                       return(ENXIO);
+               }
+               oc = inb(BT_CMD_DATA_PORT);
+               if (retval)
+                       *retval++ = oc;
+       }
+       /*******************************************************\
+       * Wait for the board to report a finised instruction    *
+       \*******************************************************/
+       i=BT_CMD_TIMEOUT_FUDGE * delaycount;    /* 1 sec? */
+       while (--i)
+       {
+               sts = inb(BT_INTR_PORT);
+               if (sts & BT_HACC)
+               {
+                       break;
+               }
+       }
+       if (!i)
+       {
+               printf("bt_cmd: bt742a host not finished(0x%x)\n",sts);
+               return(ENXIO);
+       }
+       outb(BT_CTRL_STAT_PORT, BT_IRST);
+       return(0);
+}
+
+/*******************************************************\
+* Check if the device can be found at the port given   *
+* and if so, set it up ready for further work          *
+* as an argument, takes the isa_dev structure from     *
+* autoconf.c                                           *
+\*******************************************************/
+
+btprobe(dev)
+struct isa_dev *dev;
+{
+       /***********************************************\
+       * find unit and check we have that many defined *
+       \***********************************************/
+       int     unit = btunit;
+#if defined(OSF)
+       static ihandler_t bt_handler[NBT];
+       static ihandler_id_t *bt_handler_id[NBT];
+       register ihandler_t *chp = &bt_handler[unit];;
+#endif /* defined(OSF) */
+
+       dev->dev_unit = unit;
+       bt_base[unit] = dev->dev_addr;
+       if(unit >= NBT) 
+       {
+               printf("bt: unit number (%d) too high\n",unit);
+               return(0);
+       }
+       /***********************************************\
+       * Try initialise a unit at this location        *
+       * sets up dma and bus speed, loads bt_int[unit]*
+       \***********************************************/
+       if (bt_init(unit) != 0)
+       {
+               return(0);
+       }
+
+       /***********************************************\
+       * If it's there, put in it's interrupt vectors  *
+       \***********************************************/
+#ifdef MACH
+#if defined(OSF)                               /* OSF */
+       chp->ih_level = dev->dev_pic;
+       chp->ih_handler = dev->dev_intr[0];
+       chp->ih_resolver = i386_resolver;
+       chp->ih_rdev = dev;
+       chp->ih_stats.intr_type = INTR_DEVICE;
+       chp->ih_stats.intr_cnt = 0;
+       chp->ih_hparam[0].intparam = unit;
+       if ((bt_handler_id[unit] = handler_add(chp)) != NULL)
+               handler_enable(bt_handler_id[unit]);
+       else
+               panic("Unable to add bt interrupt handler");
+#else                                          /* CMU */
+       dev->dev_pic = bt_int[unit];
+       take_dev_irq(dev);
+#endif /* !defined(OSF) */
+       printf("port=%x spl=%d\n", dev->dev_addr, dev->dev_spl);
+#endif MACH
+#ifdef  __386BSD__                             /* 386BSD */
+        dev->id_irq = (1 << bt_int[unit]);
+        dev->id_drq = bt_dma[unit];
+       printf("\n  **");
+#endif  __386BSD__
+
+       btunit++;
+       return(1);
+}
+
+/***********************************************\
+* Attach all the sub-devices we can find       *
+\***********************************************/
+btattach(dev)
+struct isa_dev *dev;
+{
+       int     unit = dev->dev_unit;
+
+
+#ifdef  __386BSD__
+       printf(" probing for scsi devices**\n");
+#endif  __386BSD__
+
+       /***********************************************\
+       * ask the adapter what subunits are present     *
+       \***********************************************/
+       scsi_attachdevs( unit, bt_scsi_dev[unit], &bt_switch);
+#if defined(OSF)
+       bt_attached[unit]=1;
+#endif /* defined(OSF) */
+       if(!unit) /* only one for all boards */
+       {
+               bt_timeout(0);
+       }
+#ifdef  __386BSD__
+       printf("bt%d",unit);
+#endif  __386BSD__
+       return;
+}
+
+/***********************************************\
+* Return some information to the caller about   *
+* the adapter and it's capabilities             *
+\***********************************************/
+long int bt_adapter_info(unit)
+int    unit;
+{
+       return(2);      /* 2 outstanding requests at a time per device */
+}
+
+/***********************************************\
+* Catch an interrupt from the adaptor          *
+\***********************************************/
+btintr(unit)
+{
+       struct bt_ccb *ccb;
+       unsigned char stat;
+       register i;
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("btintr ");
+       /***********************************************\
+       * First acknowlege the interrupt, Then if it's  *
+       * not telling about a completed operation       *
+       * just return.                                  *
+       \***********************************************/
+       stat = inb(BT_INTR_PORT);
+       outb(BT_CTRL_STAT_PORT, BT_IRST);
+       if(scsi_debug & TRACEINTERRUPTS)
+               printf("int = 0x%x ",stat);
+       if (! (stat & BT_MBIF))
+               return 1;
+       if(scsi_debug & TRACEINTERRUPTS)
+               printf("mbxi ");
+#if defined(OSF)
+       if (!bt_attached[unit])
+       {
+               return(1);
+       }
+#endif /* defined(OSF) */
+       /***********************************************\
+       * If it IS then process the competed operation  *
+       \***********************************************/
+       for (i = 0; i < BT_MBX_SIZE; i++)
+       {
+               if (bt_mbx[unit].mbi[i].stat != BT_MBI_FREE)
+               {
+                       ccb = (struct bt_ccb *)PHYSTOKV(
+                                       (bt_mbx[unit].mbi[i].ccb_addr));
+                       if((bt_debug & BT_SHOWCCBS) && ccb)
+                               printf("<int ccb(%x)>",ccb);
+                       if((stat =  bt_mbx[unit].mbi[i].stat) != BT_MBI_OK)
+                       {
+                               switch(stat)
+                               {
+                               case    BT_MBI_ABORT:
+                                       if(bt_debug & BT_SHOWMISC)
+                                               printf("abort ");
+                                       ccb->host_stat = BT_ABORTED;
+                                       break;
+
+                               case    BT_MBI_UNKNOWN:
+                                       ccb = (struct bt_ccb *)0;
+                                       if(bt_debug & BT_SHOWMISC)
+                                               printf("unknown ccb for abort");
+                                       break;
+
+                               case    BT_MBI_ERROR:
+                                       break;
+
+                               default:
+                                       panic("Impossible mbxi status");
+
+                               }
+                               if((bt_debug & BT_SHOWCMDS ) && ccb)
+                               {
+                                       u_char  *cp;
+                                       cp = ccb->scsi_cmd;
+                                       printf("op=%x %x %x %x %x %x\n", 
+                                               cp[0], cp[1], cp[2],
+                                               cp[3], cp[4], cp[5]);
+                                       printf("stat %x for mbi[%d]\n"
+                                               , bt_mbx[unit].mbi[i].stat, i);
+                                       printf("addr = 0x%x\n", ccb);
+                               }
+                       }
+                       if(ccb)
+                       {
+                               bt_remove_timeout(ccb);
+                               bt_done(unit,ccb);
+                       }
+                       bt_mbx[unit].mbi[i].stat = BT_MBI_FREE;
+               }
+       }
+       return(1);
+}
+
+/***********************************************\
+* A ccb (and hence a mbx-out is put onto the   *
+* free list.                                   *
+\***********************************************/
+bt_free_ccb(unit,ccb, flags)
+struct bt_ccb *ccb;
+{
+       unsigned int opri;
+       
+       if(scsi_debug & PRINTROUTINES)
+               printf("ccb%d(0x%x)> ",unit,flags);
+       if (!(flags & SCSI_NOMASK)) 
+               opri = splbio();
+
+       ccb->next = bt_ccb_free[unit];
+       bt_ccb_free[unit] = ccb;
+       ccb->flags = CCB_FREE;
+       /***********************************************\
+       * If there were none, wake abybody waiting for  *
+       * one to come free, starting with queued entries*
+       \***********************************************/
+       if (!ccb->next) {
+               wakeup(&bt_ccb_free[unit]);
+       }
+       if (!(flags & SCSI_NOMASK)) 
+               splx(opri);
+}
+
+/***********************************************\
+* Get a free ccb (and hence mbox-out entry)    *
+\***********************************************/
+struct bt_ccb *
+bt_get_ccb(unit,flags)
+{
+       unsigned opri;
+       struct bt_ccb *rc;
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("<ccb%d(0x%x) ",unit,flags);
+       if (!(flags & SCSI_NOMASK)) 
+               opri = splbio();
+       /***********************************************\
+       * If we can and have to, sleep waiting for one  *
+       * to come free                                  *
+       \***********************************************/
+       while ((!(rc = bt_ccb_free[unit])) && (!(flags & SCSI_NOSLEEP)))
+       {
+               sleep(&bt_ccb_free[unit], PRIBIO);
+       }
+       if (rc) 
+       {
+               bt_ccb_free[unit] = rc->next;
+               rc->flags = CCB_ACTIVE;
+       }
+       if (!(flags & SCSI_NOMASK)) 
+               splx(opri);
+       return(rc);
+}
+               
+
+/***********************************************\
+* We have a ccb which has been processed by the        *
+* adaptor, now we look to see how the operation        *
+* went. Wake up the owner if waiting           *
+\***********************************************/
+bt_done(unit,ccb)
+struct bt_ccb *ccb;
+{
+       struct  scsi_sense_data *s1,*s2;
+       struct  scsi_xfer *xs = ccb->xfer;
+
+       if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS))
+               printf("bt_done ");
+       /***********************************************\
+       * Otherwise, put the results of the operation   *
+       * into the xfer and call whoever started it     *
+       \***********************************************/
+       if (    (       ccb->host_stat != BT_OK 
+                       || ccb->target_stat != SCSI_OK)
+             && (!(xs->flags & SCSI_ERR_OK)))
+       {
+
+               s1 = &(ccb->scsi_sense);
+               s2 = &(xs->sense);
+
+               if(ccb->host_stat)
+               {
+                       switch(ccb->host_stat)
+                       {
+                       case    BT_ABORTED:     /* No response */
+                       case    BT_SEL_TIMEOUT: /* No response */
+                               if (bt_debug & BT_SHOWMISC)
+                               {
+                                       printf("timeout reported back\n");
+                               }
+                               xs->error = XS_TIMEOUT;
+                               break;
+                       default:        /* Other scsi protocol messes */
+                               xs->error = XS_DRIVER_STUFFUP;
+                               if (bt_debug & BT_SHOWMISC)
+                               {
+                                       printf("unexpected host_stat: %x\n",
+                                               ccb->host_stat);
+                               }
+                       }
+
+               }
+               else
+               {
+                       switch(ccb->target_stat)
+                       {
+                       case 0x02:
+                               /* structure copy!!!!!*/
+                               *s2=*s1;
+                               xs->error = XS_SENSE;
+                               break;
+                       case 0x08:
+                               xs->error = XS_BUSY;
+                               break;
+                       default:
+                               if (bt_debug & BT_SHOWMISC)
+                               {
+                                       printf("unexpected target_stat: %x\n",
+                                               ccb->target_stat);
+                               }
+                               xs->error = XS_DRIVER_STUFFUP;
+                       }
+               }
+       }
+       else            /* All went correctly  OR errors expected */
+       {
+               xs->resid = 0;
+       }
+       xs->flags |= ITSDONE;
+       bt_free_ccb(unit,ccb, xs->flags);
+       if(xs->when_done)
+               (*(xs->when_done))(xs->done_arg,xs->done_arg2);
+}
+
+/***********************************************\
+* Start the board, ready for normal operation  *
+\***********************************************/
+bt_init(unit)
+int    unit;
+{
+       unsigned char ad[4];
+       volatile int i,sts;
+       struct  bt_config conf;
+
+       /***********************************************\
+       * reset board, If it doesn't respond, assume    *
+       * that it's not there.. good for the probe      *
+       \***********************************************/
+
+       outb(BT_CTRL_STAT_PORT, BT_HRST|BT_SRST);
+
+       for (i=0; i < BT_RESET_TIMEOUT; i++)
+       {
+               sts = inb(BT_CTRL_STAT_PORT) ;
+               if ( sts == (BT_IDLE | BT_INIT))
+                       break;
+       }
+       if (i >= BT_RESET_TIMEOUT)
+       {
+               if (bt_debug & BT_SHOWMISC)
+                       printf("bt_init: No answer from bt742a board\n");
+               return(ENXIO);
+       }
+
+       /***********************************************\
+       * Assume we have a board at this stage          *
+       * setup dma channel from jumpers and save int   *
+       * level                                         *
+       \***********************************************/
+#ifdef __386BSD__
+       printf("bt%d reading board settings, ",unit);
+#define        PRNT(x)
+#else  __386BSD__
+       printf("bt%d:",unit);
+#define        PRNT(x) printf(x)
+#endif __386BSD__
+
+       bt_cmd(unit,0, sizeof(conf), 0 ,&conf, BT_CONF_GET);
+       switch(conf.chan)
+       {
+       case    EISADMA:
+               bt_dma[unit] = -1;
+               PRNT("eisa dma ");
+               break;
+       case    CHAN0:
+               outb(0x0b, 0x0c);
+               outb(0x0a, 0x00);
+               bt_dma[unit] = 0;
+               PRNT("dma=0 ");
+               break;
+       case    CHAN5:
+               outb(0xd6, 0xc1);
+               outb(0xd4, 0x01);
+               bt_dma[unit] = 5;
+               PRNT("dma=5 ");
+               break;
+       case    CHAN6:
+               outb(0xd6, 0xc2);
+               outb(0xd4, 0x02);
+               bt_dma[unit] = 6;
+               PRNT("dma=6 ");
+               break;
+       case    CHAN7:
+               outb(0xd6, 0xc3);
+               outb(0xd4, 0x03);
+               bt_dma[unit] = 7;
+               PRNT("dma=7 ");
+               break;
+       default:
+               printf("illegal dma setting %x\n",conf.chan);
+               return(EIO);
+       }
+       switch(conf.intr)
+       {
+       case    INT9:
+               bt_int[unit] = 9;
+               PRNT("int=9 ");
+               break;
+       case    INT10:
+               bt_int[unit] = 10;
+               PRNT("int=10 ");
+               break;
+       case    INT11:
+               bt_int[unit] = 11;
+               PRNT("int=11 ");
+               break;
+       case    INT12:
+               bt_int[unit] = 12;
+               PRNT("int=12 ");
+               break;
+       case    INT14:
+               bt_int[unit] = 14;
+               PRNT("int=14 ");
+               break;
+       case    INT15:
+               bt_int[unit] = 15;
+               PRNT("int=15 ");
+               break;
+       default:
+               printf("illegal int setting\n");
+               return(EIO);
+       }
+       /* who are we on the scsi bus */
+       bt_scsi_dev[unit] = conf.scsi_dev;
+       /***********************************************\
+       * Initialize mail box                           *
+       \***********************************************/
+
+       *((physaddr *)ad) = KVTOPHYS(&bt_mbx[unit]);
+       bt_cmd(unit,5, 0, 0, 0, BT_MBX_INIT_EXTENDED
+               , BT_MBX_SIZE
+               , ad[0]
+               , ad[1]
+               , ad[2] 
+               , ad[3]);
+
+       /***********************************************\
+       * link the ccb's with the mbox-out entries and  *
+       * into a free-list                              *
+       \***********************************************/
+       for (i=0; i < BT_MBX_SIZE; i++) {
+               bt_ccb[unit][i].next = bt_ccb_free[unit];
+               bt_ccb_free[unit] = &bt_ccb[unit][i];
+               bt_ccb_free[unit]->flags = CCB_FREE;
+               bt_ccb_free[unit]->mbx = &bt_mbx[unit].mbo[i];
+               bt_mbx[unit].mbo[i].ccb_addr = KVTOPHYS(bt_ccb_free[unit]) ;
+       }
+
+       /***********************************************\
+       * Note that we are going and return (to probe)  *
+       \***********************************************/
+       bt_initialized[unit]++;
+       return( 0 );
+}
+
+
+#ifndef        min
+#define min(x,y) (x < y ? x : y)
+#endif min
+
+
+void btminphys(bp)
+struct buf *bp;
+{
+#ifdef MACH
+#if    !defined(OSF)
+       bp->b_flags |= B_NPAGES;                /* can support scat/gather */
+#endif /* defined(OSF) */
+#endif MACH
+       if(bp->b_bcount > ((BT_NSEG-1) * PAGESIZ))
+       {
+               bp->b_bcount = ((BT_NSEG-1) * PAGESIZ);
+       }
+}
+       
+/***********************************************\
+* start a scsi operation given the command and *
+* the data address. Also needs the unit, target        *
+* and lu                                       *
+\***********************************************/
+int    bt_scsi_cmd(xs)
+struct scsi_xfer *xs;
+{
+       struct  scsi_sense_data *s1,*s2;
+       struct bt_ccb *ccb;
+       struct bt_scat_gath *sg;
+       int     seg;    /* scatter gather seg being worked on */
+       int i   = 0;
+       int rc  =  0;
+       int     thiskv;
+       physaddr        thisphys,nextphys;
+       int     unit =xs->adapter;
+       int     bytes_this_seg,bytes_this_page,datalen,flags;
+       struct  iovec   *iovp;
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("bt_scsi_cmd ");
+       /***********************************************\
+       * get a ccb (mbox-out) to use. If the transfer  *
+       * is from a buf (possibly from interrupt time)  *
+       * then we can't allow it to sleep               *
+       \***********************************************/
+       flags = xs->flags;
+       if(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */
+       if(flags & ITSDONE)
+       {
+               printf("Already done?");
+               xs->flags &= ~ITSDONE;
+       }
+       if(!(flags & INUSE))
+       {
+               printf("Not in use?");
+               xs->flags |= INUSE;
+       }
+       if (!(ccb = bt_get_ccb(unit,flags)))
+       {
+               xs->error = XS_DRIVER_STUFFUP;
+               return(TRY_AGAIN_LATER);
+       }
+
+       if(bt_debug & BT_SHOWCCBS)
+                               printf("<start ccb(%x)>",ccb);
+       if (ccb->mbx->cmd != BT_MBO_FREE)
+               printf("MBO not free\n");
+
+       /***********************************************\
+       * Put all the arguments for the xfer in the ccb *
+       \***********************************************/
+       ccb->xfer               =       xs;
+       if(flags & SCSI_RESET)
+       {
+               ccb->opcode     =       BT_RESET_CCB;
+       }
+       else
+       {
+               /* can't use S/G if zero length */
+               ccb->opcode     =       (xs->datalen?
+                                               BT_INIT_SCAT_GATH_CCB
+                                               :BT_INITIATOR_CCB);
+       }
+       ccb->target             =       xs->targ;;
+       ccb->data_out           =       0;
+       ccb->data_in            =       0;
+       ccb->lun                =       xs->lu;
+       ccb->scsi_cmd_length    =       xs->cmdlen;
+       ccb->sense_ptr          =       KVTOPHYS(&(ccb->scsi_sense));
+       ccb->req_sense_length   =       sizeof(ccb->scsi_sense);
+
+       if((xs->datalen) && (!(flags & SCSI_RESET)))
+       { /* can use S/G only if not zero length */
+               ccb->data_addr = KVTOPHYS(ccb->scat_gath);
+               sg              =       ccb->scat_gath ;
+               seg             =       0;
+               if(flags & SCSI_DATA_UIO)
+               {
+                       iovp = ((struct uio *)xs->data)->uio_iov;
+                       datalen = ((struct uio *)xs->data)->uio_iovcnt;
+                       xs->datalen = 0;
+                       while ((datalen) && (seg < BT_NSEG))
+                       {
+                               sg->seg_addr = (physaddr)iovp->iov_base;
+                               xs->datalen += sg->seg_len = iovp->iov_len;     
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("(0x%x@0x%x)"
+                                                       ,iovp->iov_len
+                                                       ,iovp->iov_base);
+                               sg++;
+                               iovp++;
+                               seg++;
+                               datalen--;
+                       }
+               }
+               else
+               {
+                       /***********************************************\
+                       * Set up the scatter gather block               *
+                       \***********************************************/
+               
+                       if(scsi_debug & SHOWSCATGATH)
+                               printf("%d @0x%x:- ",xs->datalen,xs->data);
+                       datalen         =       xs->datalen;
+                       thiskv          =       (int)xs->data;
+                       thisphys        =       KVTOPHYS(thiskv);
+               
+                       while ((datalen) && (seg < BT_NSEG))
+                       {
+                               bytes_this_seg  = 0;
+       
+                               /* put in the base address */
+                               sg->seg_addr = thisphys;
+               
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("0x%x",thisphys);
+       
+                               /* do it at least once */
+                               nextphys = thisphys;    
+                               while ((datalen) && (thisphys == nextphys))
+                               /*********************************************\
+                               * This page is contiguous (physically) with   *
+                               * the the last, just extend the length        *
+                               \*********************************************/
+                               {
+                                       /* how far to the end of the page */
+                                       nextphys= (thisphys & (~(PAGESIZ - 1)))
+                                                               + PAGESIZ;
+                                       bytes_this_page = nextphys - thisphys;
+                                       /**** or the data ****/
+                                       bytes_this_page = min(bytes_this_page
+                                                               ,datalen);
+                                       bytes_this_seg  += bytes_this_page;
+                                       datalen         -= bytes_this_page;
+               
+                                       /* get more ready for the next page */
+                                       thiskv  = (thiskv & (~(PAGESIZ - 1)))
+                                                               + PAGESIZ;
+                                       if(datalen)
+                                               thisphys = KVTOPHYS(thiskv);
+                               }
+                               /********************************************\
+                               * next page isn't contiguous, finish the seg *
+                               \********************************************/
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("(0x%x)",bytes_this_seg);
+                               sg->seg_len = bytes_this_seg;   
+                               sg++;
+                               seg++;
+                       }
+               } /*end of iov/kv decision */
+               ccb->data_length = seg * sizeof(struct bt_scat_gath);
+               if(scsi_debug & SHOWSCATGATH)
+                       printf("\n");
+               if (datalen)
+               { /* there's still data, must have run out of segs! */
+                       printf("bt_scsi_cmd%d: more than %d DMA segs\n",
+                               unit,BT_NSEG);
+                       xs->error = XS_DRIVER_STUFFUP;
+                       bt_free_ccb(unit,ccb,flags);
+                       return(HAD_ERROR);
+               }
+
+       }
+       else
+       {       /* No data xfer, use non S/G values */
+               ccb->data_addr = (physaddr)0;
+               ccb->data_length = 0;
+       }
+       ccb->link_id = 0;
+       ccb->link_addr = (physaddr)0;
+       /***********************************************\
+       * Put the scsi command in the ccb and start it  *
+       \***********************************************/
+       if(!(flags & SCSI_RESET))
+       {
+               bcopy(xs->cmd, ccb->scsi_cmd, ccb->scsi_cmd_length);
+       }
+       if(scsi_debug & SHOWCOMMANDS)
+       {
+               u_char  *b = ccb->scsi_cmd;
+               if(!(flags & SCSI_RESET))
+               {
+                       int i = 0;
+                       printf("bt%d:%d:%d-"
+                               ,unit
+                               ,ccb->target
+                               ,ccb->lun);
+                       while(i < ccb->scsi_cmd_length )
+                       {
+                               if(i) printf(",");
+                               printf("%x",b[i++]);
+                       }
+                       printf("-\n");
+               }
+               else
+               {
+                       printf("bt%d:%d:%d-RESET- " 
+                               ,unit 
+                               ,ccb->target
+                               ,ccb->lun
+                       );
+               }
+       }
+       bt_startmbx(ccb->mbx);
+       /***********************************************\
+       * Usually return SUCCESSFULLY QUEUED            *
+       \***********************************************/
+       if(scsi_debug & TRACEINTERRUPTS)
+               printf("cmd_sent ");
+       if (!(flags & SCSI_NOMASK))
+       {
+               bt_add_timeout(ccb,xs->timeout);
+               return(SUCCESSFULLY_QUEUED);
+       }
+       /***********************************************\
+       * If we can't use interrupts, poll on completion*
+       \***********************************************/
+       {
+               int done = 0;
+               int count = delaycount * xs->timeout / BT_SCSI_TIMEOUT_FUDGE;
+               if(scsi_debug & TRACEINTERRUPTS)
+                       printf("wait ");
+               while((!done) && count)
+               {
+                       i=0;
+                       while ( (!done) && i<BT_MBX_SIZE)
+                       {
+                               if ((bt_mbx[unit].mbi[i].stat != BT_MBI_FREE )
+                                  && (PHYSTOKV(bt_mbx[unit].mbi[i].ccb_addr)
+                                       == (int)ccb))
+                               {
+                                       bt_mbx[unit].mbi[i].stat = BT_MBI_FREE;
+                                       bt_done(unit,ccb);
+                                       done++;
+                               }
+                               i++;
+                       }
+                       count--;
+               }
+               if (!count)
+               {
+                       if (!(xs->flags & SCSI_SILENT))
+                               printf("cmd fail\n");
+                       bt_abortmbx(ccb->mbx);
+                       count = delaycount * 2000 / BT_SCSI_TIMEOUT_FUDGE;
+                       while((!done) && count)
+                       {
+                               i=0;
+                               while ( (!done) && i<BT_MBX_SIZE)
+                               {
+                                       if ((bt_mbx[unit].mbi[i].stat != BT_MBI_FREE )
+                                       && (PHYSTOKV((bt_mbx[unit].mbi[i].ccb_addr)
+                                               == (int)ccb)))
+                                       {
+                                               bt_mbx[unit].mbi[i].stat = BT_MBI_FREE;
+                                               bt_done(unit,ccb);
+                                               done++;
+                                       }
+                                       i++;
+                               }
+                               count--;
+                       }
+                       if(!count)
+                       {
+                               printf("abort failed in wait\n");
+                               ccb->mbx->cmd = BT_MBO_FREE;
+                       }
+                       bt_free_ccb(unit,ccb,flags);
+                       btintr(unit);
+                       xs->error = XS_DRIVER_STUFFUP;
+                       return(HAD_ERROR);
+               }
+               btintr(unit);
+               if(xs->error) return(HAD_ERROR);
+               return(COMPLETE);
+       } 
+}
+
+/*
+ *               +----------+     +----------+     +----------+
+ * bt_soonest--->|    later |---->|     later|---->|     later|--->0
+ *               | [Delta]  |     | [Delta]  |     | [Delta]  |
+ *        0<-----|sooner    |<----|sooner    |<----|sooner    |<----bt_latest
+ *               +----------+     +----------+     +----------+
+ *
+ *     bt_furtherest = sum(Delta[1..n])
+ */
+bt_add_timeout(ccb,time)
+struct bt_ccb  *ccb;
+int    time;
+{
+       int     timeprev;
+       struct bt_ccb *prev;
+       int     s = splbio();
+
+       if(prev = bt_latest) /* yes, an assign */
+       {
+               timeprev = bt_furtherest;
+       }
+       else
+       {
+               timeprev = 0;
+       }
+       while(prev && (timeprev > time)) 
+       {
+               timeprev -= prev->delta;
+               prev = prev->sooner;
+       }
+       if(prev)
+       {
+               ccb->delta = time - timeprev;
+               if( ccb->later = prev->later) /* yes an assign */
+               {
+                       ccb->later->sooner = ccb;
+                       ccb->later->delta -= ccb->delta;
+               }
+               else
+               {
+                       bt_furtherest = time;
+                       bt_latest = ccb;
+               }
+               ccb->sooner = prev;
+               prev->later = ccb;
+       }
+       else
+       {
+               if( ccb->later = bt_soonest) /* yes, an assign*/
+               {
+                       ccb->later->sooner = ccb;
+                       ccb->later->delta -= time;
+               }
+               else
+               {
+                       bt_furtherest = time;
+                       bt_latest = ccb;
+               }
+               ccb->delta = time;
+               ccb->sooner = (struct bt_ccb *)0;
+               bt_soonest = ccb;
+       }
+       splx(s);
+}
+
+bt_remove_timeout(ccb)
+struct bt_ccb  *ccb;
+{
+       int     s = splbio();
+
+       if(ccb->sooner)
+       {
+               ccb->sooner->later = ccb->later;
+       }
+       else
+       {
+               bt_soonest = ccb->later;
+       }
+       if(ccb->later)
+       {
+               ccb->later->sooner = ccb->sooner;
+               ccb->later->delta += ccb->delta;
+       }
+       else
+       {
+               bt_latest = ccb->sooner;
+               bt_furtherest -= ccb->delta;
+       }
+       ccb->sooner = ccb->later = (struct bt_ccb *)0;
+       splx(s);
+}
+
+
+extern int     hz;
+#define ONETICK 500 /* milliseconds */
+#define SLEEPTIME ((hz * 1000) / ONETICK)
+bt_timeout(arg)
+int    arg;
+{
+       struct  bt_ccb  *ccb;
+       int     unit;
+       int     s       = splbio();
+
+       while( ccb = bt_soonest )
+       {
+               if(ccb->delta <= ONETICK)
+               /***********************************************\
+               * It has timed out, we need to do some work     *
+               \***********************************************/
+               {
+                       unit = ccb->xfer->adapter;
+                       printf("bt%d:%d device timed out\n",unit
+                                       ,ccb->xfer->targ);
+                       if(bt_debug & BT_SHOWCCBS)
+                               tfs_print_active_ccbs();
+
+                       /***************************************\
+                       * Unlink it from the queue              *
+                       \***************************************/
+                       bt_remove_timeout(ccb);
+
+                       /***************************************\
+                       * If The ccb's mbx is not free, then    *
+                       * the board has gone south              *
+                       \***************************************/
+                       if(ccb->mbx->cmd != BT_MBO_FREE)
+                       {
+                               printf("bt%d not taking commands!\n"
+                                                       ,unit);
+                               Debugger();
+                       }
+                       /***************************************\
+                       * If it has been through before, then   *
+                       * a previous abort has failed, don't    *
+                       * try abort again                       *
+                       \***************************************/
+                       if(ccb->flags == CCB_ABORTED) /* abort timed out */
+                       {
+                               printf("AGAIN");
+                               ccb->xfer->retries = 0; /* I MEAN IT ! */
+                               ccb->host_stat = BT_ABORTED;
+                               bt_done(unit,ccb);
+                       }
+                       else    /* abort the operation that has timed out */
+                       {
+                               printf("\n");
+                               bt_abortmbx(ccb->mbx);
+                                       /* 2 secs for the abort */
+                               bt_add_timeout(ccb,2000 + ONETICK);
+                               ccb->flags = CCB_ABORTED;
+                       }
+               }
+               else
+               /***********************************************\
+               * It has not timed out, adjust and leave        *
+               \***********************************************/
+               {
+                       ccb->delta -= ONETICK;
+                       bt_furtherest -= ONETICK;
+                       break;
+               }
+       }
+       splx(s);
+       timeout(bt_timeout,arg,SLEEPTIME);
+}
+
+tfs_print_ccb(ccb)
+struct bt_ccb *ccb;
+{
+       printf("ccb:%x op:%x cmdlen:%d senlen:%d\n"
+               ,ccb
+               ,ccb->opcode
+               ,ccb->scsi_cmd_length
+               ,ccb->req_sense_length);
+       printf("        datlen:%d hstat:%x tstat:%x delta:%d flags:%x\n"
+               ,ccb->data_length
+               ,ccb->host_stat
+               ,ccb->target_stat
+               ,ccb->delta
+               ,ccb->flags);
+}
+
+tfs_print_active_ccbs()
+{
+       struct  bt_ccb *ccb;
+       ccb = bt_soonest;
+
+       while(ccb)
+       {
+               tfs_print_ccb(ccb);
+               ccb = ccb->later;
+       }
+       printf("Furtherest = %d\n",bt_furtherest);
+}
index c573639..b67fdcc 100644 (file)
  * SUCH DAMAGE.
  *
  *     @(#)isa.h       5.7 (Berkeley) 5/9/91
  * SUCH DAMAGE.
  *
  *     @(#)isa.h       5.7 (Berkeley) 5/9/91
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 15 Feb 93   Julian Elischer         Added entries for some scsi adapters
  */
 
 /*
  */
 
 /*
@@ -86,8 +92,14 @@ unsigned kbd_8042cmd(int);
                                        /* 0x280 - 0x2F7 Open */
 
 #define IO_COM2                0x2f8           /* COM2 i/o address */
                                        /* 0x280 - 0x2F7 Open */
 
 #define IO_COM2                0x2f8           /* COM2 i/o address */
-
-                                       /* 0x300 - 0x36F Open */
+                                       /* 0x300 - 0x32F Open */
+
+#define        IO_BT0          0x330           /* bustek 742a default addr. */
+#define        IO_AHA0         0x330           /* adaptec 1542 default addr. */
+#define        IO_UHA0         0x330           /* ultrastore 14f default addr. */
+#define        IO_BT1          0x334           /* bustek 742a default addr. */
+#define        IO_AHA1         0x334           /* adaptec 1542 default addr. */
+                                       /* 0x338 - 0x36F Open */
 
 #define IO_FD2         0x370           /* secondary base i/o address */
 #define IO_LPT1                0x378           /* Parallel Port #1 */
 
 #define IO_FD2         0x370           /* secondary base i/o address */
 #define IO_LPT1                0x378           /* Parallel Port #1 */
diff --git a/usr/src/sys.386bsd/i386/isa/ultra14f.c b/usr/src/sys.386bsd/i386/isa/ultra14f.c
new file mode 100644 (file)
index 0000000..46626b7
--- /dev/null
@@ -0,0 +1,1308 @@
+/*
+ * Ported for use with the UltraStor 14f by Gary Close (gclose@wvnvms.wvnet.edu)
+ * Thanks to Julian Elischer for advice and help with this port.
+ *
+ * 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.
+ *
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ * commenced: Sun Sep 27 18:14:01 PDT 1992
+ */
+#include <sys/types.h>
+#include <uha.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>
+
+#ifdef  MACH    /* EITHER CMU OR OSF */
+#include <i386/ipl.h>
+#include <i386at/scsi.h>
+#include <i386at/scsiconf.h>
+
+#ifdef  OSF     /* OSF ONLY */
+#include <sys/table.h>
+#include <i386/handler.h>
+#include <i386/dispatcher.h>
+#include <i386/AT386/atbus.h>
+
+#else   OSF     /* CMU ONLY */
+#include <i386at/atbus.h>
+#include <i386/pio.h>
+#endif  OSF
+#endif  MACH    /* end of MACH specific */
+
+#ifdef  __386BSD__      /* 386BSD specific */
+#define isa_dev isa_device
+#define dev_unit id_unit
+#define dev_addr id_iobase
+
+#include <i386/include/pio.h>
+#include <i386/isa/isa_device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#endif  __386BSD__
+
+/*\f*/
+
+#ifdef  __386BSD__
+#include "ddb.h"
+#if     NDDB > 0
+int     Debugger();
+#else   NDDB
+#define Debugger() panic("should call debugger here")
+#endif  NDDB
+#endif  __386BSD__
+
+#ifdef  MACH
+int     Debugger();
+#endif  MACH
+
+typedef struct {unsigned char addr[4]; } physaddr;
+typedef struct {unsigned char len[4]; } physlen;
+
+
+#ifdef        MACH
+extern physaddr kvtophys();
+#define PHYSTOKV(x)   phystokv(x)
+#define KVTOPHYS(x)   kvtophys(x)
+#endif MACH
+
+#ifdef        __386BSD__
+#define PHYSTOKV(x)   (x | 0xFE000000)
+#define KVTOPHYS(x)   vtophys(x)
+#endif        __386BSD__
+
+extern int delaycount;  /* from clock setup code */
+#define NUM_CONCURRENT 16      /* number of concurrent ops per board */
+#define UHA_NSEG        33      /* number of dma segments supported     */
+#define FUDGE(X)        (X>>1)  /* our loops are slower than spinwait() */
+/*\f*/
+/************************** board definitions *******************************/
+/*
+ * I/O Port Interface
+*/
+ #define UHA_LMASK              (0x000)  /* local doorbell mask reg */
+ #define UHA_LINT               (0x001)  /* local doorbell int/stat reg */
+ #define UHA_SMASK              (0x002)  /* system doorbell mask reg */
+ #define UHA_SINT               (0x003)  /* system doorbell int/stat reg */
+ #define UHA_ID0                (0x004)  /* product id reg 0 */
+ #define UHA_ID1                (0x005)  /* product id reg 1 */
+ #define UHA_CONF1              (0x006)  /* config reg 1 */
+ #define UHA_CONF2              (0x007)  /* config reg 2 */
+ #define UHA_OGM0               (0x008)  /* outgoing mail ptr 0 least sig */
+ #define UHA_OGM1               (0x009)  /* outgoing mail ptr 1 least mid */
+ #define UHA_OGM2               (0x00a)  /* outgoing mail ptr 2 most mid  */
+ #define UHA_OGM3               (0x00b)  /* outgoing mail ptr 3 most sig  */
+ #define UHA_ICM0               (0x00c)  /* incoming mail ptr 0 */
+ #define UHA_ICM1               (0x00d)  /* incoming mail ptr 1 */
+ #define UHA_ICM2               (0x00e)  /* incoming mail ptr 2 */
+ #define UHA_ICM3               (0x00f)  /* incoming mail ptr 3 */
+
+ /*
+* UHA_LMASK bits (read only) 
+*/
+
+#define UHA_LDIE                0x80    /* local doorbell int enabled */
+#define UHA_SRSTE               0x40    /* soft reset enabled */
+#define UHA_ABORTEN             0x10    /* abort MSCP enabled */
+#define UHA_OGMINTEN            0x01    /* outgoing mail interrupt enabled */
+
+/*
+* UHA_LINT bits (read)
+*/
+
+#define UHA_LDIP                0x80    /* local doorbell int pending */
+
+/*
+* UHA_LINT bits (write)
+*/
+
+#define UHA_ADRST               0x40    /* adapter soft reset */
+#define UHA_SBRST               0x20    /* scsi bus reset */
+#define UHA_ASRST               0x60    /* adapter and scsi reset */
+#define UHA_ABORT               0x10    /* abort MSCP */
+#define UHA_OGMINT              0x01    /* tell adapter to get mail */
+
+/*
+* UHA_SMASK bits (read)
+*/
+
+#define UHA_SINTEN              0x80    /* system doorbell interupt Enabled */
+#define UHA_ABORT_COMPLETE_EN   0x10    /* abort MSCP command complete int Enabled */
+#define UHA_ICM_ENABLED         0x01    /* ICM interrupt enabled
+
+/*
+* UHA_SMASK bits (write)
+*/
+
+#define UHA_ENSINT              0x80    /* enable system doorbell interrupt */
+#define UHA_EN_ABORT_COMPLETE   0x10    /* enable abort MSCP complete int */
+#define UHA_ENICM               0x01    /* enable ICM interrupt */
+
+/*
+* UHA_SINT bits (read)
+*/
+
+#define UHA_SINTP               0x80    /* system doorbell int pending */
+#define UHA_ABORT_SUCC          0x10    /* abort MSCP successful */
+#define UHA_ABORT_FAIL          0x18    /* abort MSCP failed */
+
+/*
+* UHA_SINT bits (write)
+*/
+
+#define UHA_ABORT_ACK           0x18    /* acknowledge status and clear */
+#define UHA_ICM_ACK             0x01    /* acknowledge ICM and clear */
+
+/* 
+* UHA_CONF1 bits (read only)
+*/
+
+#define UHA_DMA_CH5             0x00    /* DMA channel 5 */
+#define UHA_DMA_CH6             0x40    /* 6 */
+#define UHA_DMA_CH7             0x80    /* 7 */
+#define UHA_IRQ15               0x00    /* IRQ 15 */
+#define UHA_IRQ14               0x10    /* 14 */
+#define UHA_IRQ11               0x20    /* 11 */
+#define UHA_IRQ10               0x30    /* 10 */
+
+/***********************************
+* ha_status error codes
+\***********************************/
+
+#define UHA_NO_ERR             0x00            /* No error supposedly */
+#define UHA_SBUS_ABORT_ERR     0x84            /* scsi bus abort error */
+#define UHA_SBUS_TIMEOUT       0x91            /* scsi bus selection timeout */
+#define UHA_SBUS_OVER_UNDER    0x92            /* scsi bus over/underrun */
+#define UHA_BAD_SCSI_CMD       0x96            /* illegal scsi command */
+#define UHA_AUTO_SENSE_ERR     0x9b            /* auto request sense err */
+#define UHA_SBUS_RES_ERR       0xa3            /* scsi bus reset error */
+#define UHA_BAD_SG_LIST                0xff            /* invalid scatter gath list */
+
+/*\f*/
+
+struct  uha_dma_seg
+{
+       physaddr                        addr;
+       physlen                         len;
+};
+/*\f*/
+
+struct  mscp
+{
+       unsigned char           opcode:3;
+       #define U14_HAC         0x01            /*host adapter command*/
+       #define U14_TSP         0x02            /*target scsi pass through command*/
+       #define U14_SDR         0x04            /*scsi device reset*/
+       unsigned char           xdir:2;         /*xfer direction*/
+       #define U14_SDET        0x00            /*determined by scsi command*/
+       #define U14_SDIN        0x01            /*scsi data in*/
+       #define U14_SDOUT       0x02            /*scsi data out*/
+       #define U14_NODATA      0x03            /*no data xfer*/
+       unsigned char           dcn:1;          /*disable disconnect for this command*/
+       unsigned char           ca:1;           /*Cache control*/
+       unsigned char           sgth:1;         /*scatter gather flag*/
+       unsigned char           target:3;
+       unsigned char           chan:2;         /*scsi channel (always 0 for 14f)*/
+       unsigned char           lun:3;
+       physaddr                data;
+       physlen                 datalen;
+       physaddr                link;
+       unsigned char           link_id;
+       unsigned char           sg_num;         /*number of scat gath segs */
+                                               /*in s-g list if sg flag is*/
+                                               /*set. starts at 1, 8bytes per*/
+       unsigned char           senselen;
+       unsigned char           cdblen;
+       unsigned char           cdb[12];
+       unsigned char           ha_status;
+       unsigned char           targ_status;
+       physaddr                sense;          /* if 0 no auto sense */
+       /*-----------------end of hardware supported fields----------------*/
+       struct  mscp     *next;  /* in free list */
+       struct  scsi_xfer *xs; /* the scsi_xfer for this cmd */
+       long    int     delta;  /* difference from previous*/
+       struct  mscp     *later,*sooner;
+       int             flags;
+#define MSCP_FREE        0
+#define MSCP_ACTIVE      1
+#define MSCP_ABORTED     2
+       struct  uha_dma_seg     uha_dma[UHA_NSEG];
+       struct  scsi_sense_data mscp_sense;
+};
+
+struct  mscp     *uha_soonest    = (struct mscp *)0;
+struct  mscp     *uha_latest     = (struct mscp *)0;
+long    int     uha_furtherest  = 0; /* longest time in the timeout queue */
+/*\f*/
+
+struct  uha_data
+{
+       int     flags;
+#define UHA_INIT        0x01;
+       int     baseport;
+       struct  mscp mscps[NUM_CONCURRENT];
+       struct  mscp *free_mscp;
+       int     our_id;                 /* our scsi id */
+       int     vect;
+       int     dma;
+} uha_data[NUHA];
+
+int     uhaprobe();
+int     uha_attach();
+int     uhaintr();
+int     uha_scsi_cmd();
+int     uha_timeout();
+int    uha_abort();
+struct  mscp *cheat;
+void    uhaminphys();
+long int uha_adapter_info();
+
+unsigned long int scratch;
+
+#ifdef  MACH
+struct  isa_driver      uhadriver = { uhaprobe, 0, uha_attach, "uha", 0, 0, 0};
+int (*uhaintrs[])() = {uhaintr, 0};
+#endif  MACH
+
+#ifdef  __386BSD__
+struct  isa_driver      uhadriver = { uhaprobe, uha_attach, "uha"};
+#endif  __386BSD__
+
+static uha_unit = 0;
+int     uha_debug = 0;
+#define UHA_SHOWMSCPS 0x01
+#define UHA_SHOWINTS 0x02
+#define UHA_SHOWCMDS 0x04
+#define UHA_SHOWMISC 0x08
+#define FAIL    1
+#define SUCCESS 0
+#define PAGESIZ 4096
+
+struct  scsi_switch     uha_switch = 
+{
+       uha_scsi_cmd,
+       uhaminphys,
+       0,
+       0,
+       uha_adapter_info,
+       0,0,0
+};
+
+/*\f*/
+/***********************************************************************\
+* Function to send a command out through a mailbox                      *
+\***********************************************************************/
+uha_send_mbox(  int             unit
+               ,struct mscp     *mscp)
+{
+       int     port = uha_data[unit].baseport;
+       int     spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */
+       int     s = splbio();
+               
+       while(      ((inb(port + UHA_LINT) & (UHA_LDIP))
+                                       != (0))
+               && (spincount--));
+       if(spincount == -1)
+       {
+               printf("uha%d: board not responding\n",unit);
+               Debugger();
+       }
+
+       outl(port + UHA_OGM0,KVTOPHYS(mscp)); 
+       outb(port + UHA_LINT, (UHA_OGMINT));
+       splx(s);
+}
+
+/***********************************************************************\
+* Function to send abort to 14f                                         *
+\***********************************************************************/
+
+uha_abort(     int             unit
+               ,struct mscp    *mscp)
+{
+       int     port = uha_data[unit].baseport;
+       int     spincount = FUDGE(delaycount) * 1;
+       int     abortcount = FUDGE(delaycount) * 2000;
+       int     s = splbio();
+       
+       while(((inb(port + UHA_LINT) & (UHA_LDIP))
+                               != (0))
+               && (spincount--));
+       if(spincount == -1);
+       {
+               printf("uha%d: board not responding\n",unit);
+               Debugger();
+       }
+
+       outl(port + UHA_OGM0,KVTOPHYS(mscp));
+       outb(port + UHA_LINT,UHA_ABORT);
+
+       while((abortcount--) && (!(inb(port + UHA_SINT) & UHA_ABORT_FAIL)));
+       if(abortcount == -1)
+       {
+               printf("uha%d: board not responding\n",unit);
+               Debugger();
+       }
+       if((inb(port + UHA_SINT) & 0x10) != 0)
+       {
+               outb(port + UHA_SINT,UHA_ABORT_ACK);
+               return(1);
+       }
+       else
+       {
+               outb(port + UHA_SINT,UHA_ABORT_ACK);
+               return(0);
+       }
+}
+
+/***********************************************************************\
+* Function to poll for command completion when in poll mode             *
+\***********************************************************************/
+uha_poll(int unit ,int wait) /* in msec  */
+{
+       int     port = uha_data[unit].baseport;
+       int     spincount = FUDGE(delaycount) * wait; /* in msec */
+       int     stport = port + UHA_SINT;
+       int     start = spincount;
+
+retry:
+       while( (spincount--) && (!(inb(stport) &  UHA_SINTP)));
+       if(spincount == -1)
+       {
+               printf("uha%d: board not responding\n",unit);
+               return(EIO);
+       }
+if ((int)cheat != PHYSTOKV(inl(port + UHA_ICM0)))
+{
+       printf("discarding %x ",inl(port + UHA_ICM0));
+       outb(port + UHA_SINT, UHA_ICM_ACK);
+       spinwait(50);
+       goto retry;
+}/* don't know this will work */
+       uhaintr(unit);
+       return(0);
+}
+
+/*******************************************************\
+* Check if the device can be found at the port given    *
+* and if so, set it up ready for further work           *
+* as an argument, takes the isa_dev structure from      *
+* autoconf.c                                            *
+\*******************************************************/
+uhaprobe(dev)
+struct isa_dev *dev;
+{
+       int     unit = uha_unit;
+       dev->dev_unit = unit;
+       uha_data[unit].baseport = dev->dev_addr;
+       if(unit >= NUHA)
+       {
+               printf("uha: unit number (%d) too high\n",unit);
+               return(0);
+       }
+       
+       /*try and initialize unit at this location*/
+       if (uha_init(unit) != 0)
+       {
+               return(0);
+       }
+
+       /* if its there put in it's interrupt and DRQ vectors */
+
+       dev->id_irq = (1 << uha_data[unit].vect);
+       dev->id_drq = uha_data[unit].dma;
+
+       
+       uha_unit ++;
+return(1);
+}
+
+/***********************************************\
+* Attach all the sub-devices we can find        *
+\***********************************************/
+uha_attach(dev)
+struct  isa_dev *dev;
+{
+       int     unit = dev->dev_unit;
+
+
+#ifdef  __386BSD__
+       printf(" probing for scsi devices**\n");
+#endif  __386BSD__
+
+       /***********************************************\
+       * ask the adapter what subunits are present     *
+       \***********************************************/
+       scsi_attachdevs( unit, uha_data[unit].our_id, &uha_switch);
+
+#if defined(OSF)
+       uha_attached[unit]=1;
+#endif /* defined(OSF) */
+       if(!unit)  /* only one for all boards */
+       {
+               uha_timeout(0);
+       }
+
+
+#ifdef  __386BSD__
+       printf("uha%d",unit);
+#endif  __386BSD__
+       return;
+}
+
+/***********************************************\
+* Return some information to the caller about   *
+* the adapter and it's capabilities             *
+\***********************************************/
+long int uha_adapter_info(unit)
+int     unit;
+{
+       return(2);      /* 2 outstanding requests at a time per device */
+}
+
+/***********************************************\
+* Catch an interrupt from the adaptor           *
+\***********************************************/
+uhaintr(unit)
+{
+       struct mscp      *mscp;
+       u_char          uhastat;
+       unsigned long int        mboxval;
+
+       int     port = uha_data[unit].baseport;
+
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("uhaintr ");
+
+#if defined(OSF)
+       if (!uha_attached[unit])
+       {
+               return(1);
+       }
+#endif /* defined(OSF) */
+       while(inb(port + UHA_SINT) & UHA_SINTP)
+       {
+               /***********************************************\
+               * First get all the information and then        *
+               * acknowlege the interrupt                      *
+               \***********************************************/
+               uhastat = inb(port + UHA_SINT);
+               mboxval = inl(port + UHA_ICM0);
+               outb(port + UHA_SINT,UHA_ICM_ACK);
+
+               if(scsi_debug & TRACEINTERRUPTS)
+                       printf("status = 0x%x ",uhastat);
+               /***********************************************\
+               * Process the completed operation               *
+               \***********************************************/
+       
+                       mscp = (struct mscp *)(PHYSTOKV(mboxval)); 
+
+                       if(uha_debug & UHA_SHOWCMDS )
+                       {
+                               uha_show_scsi_cmd(mscp->xs);
+                       }
+                       if((uha_debug & UHA_SHOWMSCPS) && mscp)
+                               printf("<int mscp(%x)>",mscp);
+                       uha_remove_timeout(mscp);
+
+                       uha_done(unit,mscp);
+       }
+       return(1);
+}
+
+/***********************************************\
+* We have a mscp which has been processed by the *
+* adaptor, now we look to see how the operation *
+* went.                                         *
+\***********************************************/
+
+uha_done(unit,mscp)
+int     unit;
+struct mscp *mscp;
+{
+       struct  scsi_sense_data *s1,*s2;
+       struct  scsi_xfer *xs = mscp->xs;
+
+       if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS))
+               printf("uha_done ");
+       /***********************************************\
+       * Otherwise, put the results of the operation   *
+       * into the xfer and call whoever started it     *
+       \***********************************************/
+       if ( (mscp->ha_status == UHA_NO_ERR) || (xs->flags & SCSI_ERR_OK))
+       {               /* All went correctly  OR errors expected */
+               xs->resid = 0;
+               xs->error = 0;
+       }
+       else
+       {
+
+               s1 = &(mscp->mscp_sense);
+               s2 = &(xs->sense);
+
+               if(mscp->ha_status != UHA_NO_ERR)
+               {
+                       switch(mscp->ha_status)
+                       {
+                       case    UHA_SBUS_TIMEOUT:           /* No response */
+                               if (uha_debug & UHA_SHOWMISC)
+                               {
+                                       printf("timeout reported back\n");
+                               }
+                               xs->error = XS_TIMEOUT;
+                               break;
+                       case    UHA_SBUS_OVER_UNDER:
+                               if (uha_debug & UHA_SHOWMISC)
+                               {
+                                       printf("scsi bus xfer over/underrun\n");
+                               }
+                               xs->error = XS_DRIVER_STUFFUP;
+                               break;
+                       case    UHA_BAD_SG_LIST:
+                               if (uha_debug & UHA_SHOWMISC)
+                               {
+                                       printf("bad sg list reported back\n");
+                               }
+                               xs->error = XS_DRIVER_STUFFUP;
+                               break;
+                       default:        /* Other scsi protocol messes */
+                               xs->error = XS_DRIVER_STUFFUP;
+                               if (uha_debug & UHA_SHOWMISC)
+                               {
+                                       printf("unexpected ha_status: %x\n",
+                                               mscp->ha_status);
+                               }
+                       }
+
+               }
+               else
+               {
+
+                       if (mscp->targ_status != 0)
+/**************************************************************************\
+* I have no information for any possible value of target status field     *
+* other than 0 means no error!! So I guess any error is unexpected in that *
+* event!!                                                                 *
+\**************************************************************************/
+
+                       {       
+                               if (uha_debug & UHA_SHOWMISC)
+                               {
+                                       printf("unexpected targ_status: %x\n",
+                                               mscp->targ_status);
+                               }
+                               xs->error = XS_DRIVER_STUFFUP;
+                       }
+               }
+       }
+done:   xs->flags |= ITSDONE;
+       uha_free_mscp(unit,mscp, xs->flags);
+       if(xs->when_done)
+               (*(xs->when_done))(xs->done_arg,xs->done_arg2);
+}
+
+/***********************************************\
+* A mscp (and hence a mbx-out is put onto the    *
+* free list.                                    *
+\***********************************************/
+uha_free_mscp(unit,mscp, flags)
+struct mscp *mscp;
+{
+       unsigned int opri;
+       
+       if(scsi_debug & PRINTROUTINES)
+               printf("mscp%d(0x%x)> ",unit,flags);
+       if (!(flags & SCSI_NOMASK)) 
+               opri = splbio();
+
+       mscp->next = uha_data[unit].free_mscp;
+       uha_data[unit].free_mscp = mscp;
+       mscp->flags = MSCP_FREE;
+       /***********************************************\
+       * If there were none, wake abybody waiting for  *
+       * one to come free, starting with queued entries*
+       \***********************************************/
+       if (!mscp->next) {
+               wakeup(&uha_data[unit].free_mscp);
+       }
+       if (!(flags & SCSI_NOMASK)) 
+               splx(opri);
+}
+
+/***********************************************\
+* Get a free mscp (and hence mbox-out entry)     *
+\***********************************************/
+struct mscp *
+uha_get_mscp(unit,flags)
+{
+       unsigned opri;
+       struct mscp *rc;
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("<mscp%d(0x%x) ",unit,flags);
+       if (!(flags & SCSI_NOMASK)) 
+               opri = splbio();
+       /***********************************************\
+       * If we can and have to, sleep waiting for one  *
+       * to come free                                  *
+       \***********************************************/
+       while ((!(rc = uha_data[unit].free_mscp)) && (!(flags & SCSI_NOSLEEP)))
+       {
+               sleep(&uha_data[unit].free_mscp, PRIBIO);
+       }
+       if (rc) 
+       {
+               uha_data[unit].free_mscp = rc->next;
+               rc->flags = MSCP_ACTIVE;
+       }
+       if (!(flags & SCSI_NOMASK)) 
+               splx(opri);
+       return(rc);
+}
+               
+
+
+/***********************************************\
+* Start the board, ready for normal operation   *
+\***********************************************/
+
+uha_init(unit)
+int    unit;
+{      
+       unsigned char ad[4];
+       volatile unsigned char model;
+       volatile unsigned char submodel;
+       unsigned char config_reg1;
+       unsigned char config_reg2;
+       unsigned char dma_ch;
+       unsigned char irq_ch;
+       unsigned char uha_id;
+       int     port = uha_data[unit].baseport;
+       int     i;
+       int     resetcount = FUDGE(delaycount) * 4000;
+
+       model = inb(port + UHA_ID0);
+       submodel = inb(port + UHA_ID1);
+                if ((model != 0x56) & (submodel != 0x40))
+                     { printf("ultrastor 14f not responding\n");
+                       return(ENXIO); }
+
+       printf("uha%d reading board settings, ",unit);
+
+       config_reg1 = inb(port + UHA_CONF1);
+       config_reg2 = inb(port + UHA_CONF2);
+       dma_ch = (config_reg1 & 0xc0);
+       irq_ch = (config_reg1 & 0x30);
+       uha_id = (config_reg2 & 0x07);
+
+       switch(dma_ch)
+       {
+       case    UHA_DMA_CH5:
+               uha_data[unit].dma = 5;
+               printf("dma=5 ");
+               break;
+       case    UHA_DMA_CH6:
+               uha_data[unit].dma = 6;
+               printf("dma=6 ");
+               break;
+       case    UHA_DMA_CH7:
+               uha_data[unit].dma = 7;
+               printf("dma=7 ");
+               break;
+       default:
+               printf("illegal dma jumper setting\n");
+               return(EIO);
+       }
+       switch(irq_ch)
+       {
+       case    UHA_IRQ10:
+               uha_data[unit].vect = 10;
+               printf("int=10 ");
+               break;
+       case    UHA_IRQ11:
+               uha_data[unit].vect = 11;
+               printf("int=11 ");
+               break;
+       case    UHA_IRQ14:
+               uha_data[unit].vect = 14;
+               printf("int=14 ");
+               break;
+       case    UHA_IRQ15:
+               uha_data[unit].vect = 15;
+               printf("int=15 ");
+               break;
+       default:
+               printf("illegal int jumper setting\n");
+               return(EIO);
+       }
+       /* who are we on the scsi bus */
+       printf("id=%x\n",uha_id);
+       uha_data[unit].our_id = uha_id;
+
+       
+       /***********************************************\
+       * link up all our MSCPs into a free list         *
+       \***********************************************/
+       for (i=0; i < NUM_CONCURRENT; i++)
+       {
+               uha_data[unit].mscps[i].next = uha_data[unit].free_mscp;
+               uha_data[unit].free_mscp = &uha_data[unit].mscps[i];
+               uha_data[unit].free_mscp->flags = MSCP_FREE;
+       }
+
+       /***********************************************\
+       * Note that we are going and return (to probe)  *
+       \***********************************************/
+       outb(port + UHA_LINT, UHA_ASRST);       
+       while( (resetcount--) && (!(inb(port + UHA_LINT))));
+       if(resetcount == -1)
+       {
+               printf("uha%d: board timed out during reset\n",unit);
+               return(ENXIO);
+       }
+
+       outb(port + UHA_SMASK, 0x81); /* make sure interrupts are enabled */
+       uha_data[unit].flags |= UHA_INIT;
+       return(0);
+}
+
+
+
+#ifndef min
+#define min(x,y) (x < y ? x : y)
+#endif  min
+
+
+void uhaminphys(bp)
+struct  buf *bp;
+{
+#ifdef  MACH
+#if     !defined(OSF)
+       bp->b_flags |= B_NPAGES;                /* can support scat/gather */
+#endif  /* defined(OSF) */
+#endif  MACH
+       if(bp->b_bcount > ((UHA_NSEG-1) * PAGESIZ))
+       {
+               bp->b_bcount = ((UHA_NSEG-1) * PAGESIZ);
+       }
+}
+
+/***********************************************\
+* start a scsi operation given the command and  *
+* the data address. Also needs the unit, target *
+* and lu                                        *
+\***********************************************/
+int     uha_scsi_cmd(xs)
+struct scsi_xfer *xs;
+{
+       struct  scsi_sense_data *s1,*s2;
+       struct mscp *mscp;
+       struct uha_dma_seg *sg;
+       int     seg;    /* scatter gather seg being worked on */
+       int i   = 0;
+       int rc  =  0;
+       int     thiskv;
+       unsigned long int        thisphys,nextphys;
+       int     unit =xs->adapter;
+       int     bytes_this_seg,bytes_this_page,datalen,flags;
+       struct  iovec   *iovp;
+       int     s;
+       unsigned int stat;
+       int     port = uha_data[unit].baseport;
+       unsigned long int templen;
+
+
+       if(scsi_debug & PRINTROUTINES)
+               printf("uha_scsi_cmd ");
+       /***********************************************\
+       * get a mscp (mbox-out) to use. If the transfer  *
+       * is from a buf (possibly from interrupt time)  *
+       * then we can't allow it to sleep               *
+       \***********************************************/
+       flags = xs->flags;
+       if(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */
+       if(flags & ITSDONE)
+       {
+               printf("Already done?");
+               xs->flags &= ~ITSDONE;
+       }
+       if(!(flags & INUSE))
+       {
+               printf("Not in use?");
+               xs->flags |= INUSE;
+       }
+       if (!(mscp = uha_get_mscp(unit,flags)))
+       {
+               xs->error = XS_DRIVER_STUFFUP;
+               return(TRY_AGAIN_LATER);
+       }
+
+cheat = mscp;
+       if(uha_debug & UHA_SHOWMSCPS)
+                               printf("<start mscp(%x)>",mscp);
+       if(scsi_debug & SHOWCOMMANDS)
+       {
+               uha_show_scsi_cmd(xs);
+       }
+       mscp->xs = xs;
+       /***********************************************\
+       * Put all the arguments for the xfer in the mscp *
+       \***********************************************/
+
+       if (flags & SCSI_RESET)
+       {
+               mscp->opcode = 0x04;
+               mscp->ca = 0x01;
+       }
+       else
+       {
+               mscp->opcode = 0x02;
+               mscp->ca = 0x01;
+       }               
+
+       if (flags & SCSI_DATA_IN)
+       {
+               mscp->xdir = 0x01;
+       }
+       if (flags & SCSI_DATA_OUT)
+       {
+               mscp->xdir = 0x02;
+       }
+
+       if (xs->lu != 0)
+       {
+               xs->error = XS_DRIVER_STUFFUP;
+               uha_free_mscp(unit,mscp,flags);
+               return(HAD_ERROR);
+       }
+       
+       mscp->dcn       = 0x00;
+       mscp->chan      = 0x00;
+       mscp->target    = xs->targ;
+       mscp->lun       = xs->lu;
+       mscp->link.addr[0]      = 0x00;
+       mscp->link.addr[1]      = 0x00;
+       mscp->link.addr[2]      = 0x00;
+       mscp->link.addr[3]      = 0x00;
+       mscp->link_id   = 0x00;
+       mscp->cdblen             =       xs->cmdlen;
+       scratch                 =       KVTOPHYS(&(mscp->mscp_sense));
+       mscp->sense.addr[0]     =       (scratch & 0xff);
+       mscp->sense.addr[1]     =       ((scratch >> 8) & 0xff);
+       mscp->sense.addr[2]     =       ((scratch >> 16) & 0xff);
+       mscp->sense.addr[3]     =       ((scratch >> 24) & 0xff);
+       mscp->senselen          =       sizeof(mscp->mscp_sense);
+       mscp->ha_status         =       0x00;
+       mscp->targ_status       =       0x00;
+
+       if(xs->datalen)
+       { /* should use S/G only if not zero length */
+               scratch                 =       KVTOPHYS(mscp->uha_dma);
+               mscp->data.addr[0]      =       (scratch & 0xff);
+               mscp->data.addr[1]      =       ((scratch >> 8) & 0xff);
+               mscp->data.addr[2]      =       ((scratch >> 16) & 0xff);
+               mscp->data.addr[3]      =       ((scratch >> 24) & 0xff);
+               sg              =       mscp->uha_dma ;
+               seg             =       0;
+               mscp->sgth      =       0x01;
+
+               if(flags & SCSI_DATA_UIO)
+               {
+                       iovp = ((struct uio *)xs->data)->uio_iov;
+                       datalen = ((struct uio *)xs->data)->uio_iovcnt;
+                       xs->datalen = 0;
+                       while ((datalen) && (seg < UHA_NSEG))
+                       {
+                               scratch  = (unsigned long)iovp->iov_base;
+                               sg->addr.addr[0] = (scratch & 0xff); 
+                               sg->addr.addr[1] = ((scratch >> 8) & 0xff);
+                               sg->addr.addr[2] = ((scratch >> 16) & 0xff);
+                               sg->addr.addr[3] = ((scratch >> 24) & 0xff);
+                               xs->datalen += *(unsigned long *)sg->len.len = iovp->iov_len; 
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("(0x%x@0x%x)"
+                                                       ,iovp->iov_len
+                                                       ,iovp->iov_base);
+                               sg++;
+                               iovp++;
+                               seg++;
+                               datalen--;
+                       }
+               }
+               else
+               {
+                       /***********************************************\
+                       * Set up the scatter gather block               *
+                       \***********************************************/
+               
+                       if(scsi_debug & SHOWSCATGATH)
+                               printf("%d @0x%x:- ",xs->datalen,xs->data);
+                       datalen         =       xs->datalen;
+                       thiskv          =       (int)xs->data;
+                       thisphys        =       KVTOPHYS(thiskv);
+                       templen         =       0;
+               
+                       while ((datalen) && (seg < UHA_NSEG))
+                       {
+                               bytes_this_seg  = 0;
+       
+                               /* put in the base address */
+                               sg->addr.addr[0] = (thisphys & 0xff);
+                               sg->addr.addr[1] = ((thisphys >> 8) & 0xff);
+                               sg->addr.addr[2] = ((thisphys >> 16) & 0xff);
+                               sg->addr.addr[3] = ((thisphys >> 24) & 0xff);
+               
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("0x%x",thisphys);
+       
+                               /* do it at least once */
+                               nextphys = thisphys;    
+                               while ((datalen) && (thisphys == nextphys))
+                               /*********************************************\
+                               * This page is contiguous (physically) with   *
+                               * the the last, just extend the length        *
+                               \*********************************************/
+                               {
+                                       /* how far to the end of the page */
+                                       nextphys = (thisphys & (~(PAGESIZ - 1)))
+                                                               + PAGESIZ;
+                                       bytes_this_page = nextphys - thisphys;
+                                       /**** or the data ****/
+                                       bytes_this_page = min(bytes_this_page
+                                                               ,datalen);
+                                       bytes_this_seg  += bytes_this_page;
+                                       datalen         -= bytes_this_page;
+               
+                                       /* get more ready for the next page */
+                                       thiskv  = (thiskv & (~(PAGESIZ - 1)))
+                                                               + PAGESIZ;
+                                       if(datalen)
+                                               thisphys = KVTOPHYS(thiskv);
+                               }
+                               /********************************************\
+                               * next page isn't contiguous, finish the seg *
+                               \********************************************/
+                               if(scsi_debug & SHOWSCATGATH)
+                                       printf("(0x%x)",bytes_this_seg);
+                               sg->len.len[0] = (bytes_this_seg & 0xff);
+                               sg->len.len[1] = ((bytes_this_seg >> 8) & 0xff);
+                               sg->len.len[2] = ((bytes_this_seg >> 16) & 0xff);
+                               sg->len.len[3] = ((bytes_this_seg >> 24) & 0xff);
+                               templen += bytes_this_seg;
+                               sg++;
+                               seg++;
+                       }
+               } /*end of iov/kv decision */
+               mscp->datalen.len[0] = (templen & 0xff);
+               mscp->datalen.len[1] = ((templen >> 8) & 0xff);
+               mscp->datalen.len[2] = ((templen >> 16) & 0xff);
+               mscp->datalen.len[3] = ((templen >> 24) & 0xff);
+               mscp->sg_num = seg;
+
+               if(scsi_debug & SHOWSCATGATH)
+                       printf("\n");
+               if (datalen)
+               { /* there's still data, must have run out of segs! */
+                       printf("uha_scsi_cmd%d: more than %d DMA segs\n",
+                               unit,UHA_NSEG);
+                       xs->error = XS_DRIVER_STUFFUP;
+                       uha_free_mscp(unit,mscp,flags);
+                       return(HAD_ERROR);
+               }
+
+       }
+       else
+       {       /* No data xfer, use non S/G values */
+               mscp->data.addr[0] = 0x00;
+               mscp->data.addr[1] = 0x00;
+               mscp->data.addr[2] = 0x00;
+               mscp->data.addr[3] = 0x00;
+               mscp->datalen.len[0] = 0x00;
+               mscp->datalen.len[1] = 0x00;
+               mscp->datalen.len[2] = 0x00;
+               mscp->datalen.len[3] = 0x00;
+               mscp->xdir = 0x03;
+               mscp->sgth = 0x00;
+               mscp->sg_num = 0x00;    
+       }
+
+       /***********************************************\
+       * Put the scsi command in the mscp and start it  *
+       \***********************************************/
+       bcopy(xs->cmd, mscp->cdb, xs->cmdlen); 
+
+       /***********************************************\
+       * Usually return SUCCESSFULLY QUEUED            *
+       \***********************************************/
+       if (!(flags & SCSI_NOMASK))
+       {
+               s = splbio();
+               uha_send_mbox(unit,mscp);
+               uha_add_timeout(mscp,xs->timeout);
+               splx(s);
+               if(scsi_debug & TRACEINTERRUPTS)
+                       printf("cmd_sent ");
+               return(SUCCESSFULLY_QUEUED);
+       }
+       /***********************************************\
+       * If we can't use interrupts, poll on completion*
+       \***********************************************/
+       uha_send_mbox(unit,mscp);
+       if(scsi_debug & TRACEINTERRUPTS)
+               printf("cmd_wait ");
+       do
+       {
+               if(uha_poll(unit,xs->timeout))
+               {
+                       if (!(xs->flags & SCSI_SILENT)) printf("cmd fail\n");
+                       if(!(uha_abort(unit,mscp)))
+                       {
+                               printf("abort failed in wait\n");
+                               uha_free_mscp(unit,mscp,flags);
+                       }
+                       xs->error = XS_DRIVER_STUFFUP;
+                       splx(s);
+                       return(HAD_ERROR);
+               }
+       } while (!(xs->flags & ITSDONE));/* something (?) else finished */
+       splx(s);
+scsi_debug = 0;uha_debug = 0;
+       if(xs->error)
+       {
+               return(HAD_ERROR);
+       }
+       return(COMPLETE);
+}
+
+/*
+ *                +----------+    +----------+    +----------+
+ * uha_soonest--->|    later |--->|     later|--->|     later|--->0
+ *                | [Delta]  |    | [Delta]  |    | [Delta]  |
+ *           0<---|sooner    |<---|sooner    |<---|sooner    |<---uha_latest
+ *                +----------+    +----------+    +----------+
+ *
+ *     uha_furtherest = sum(Delta[1..n])
+ */
+uha_add_timeout(mscp,time)
+struct  mscp     *mscp;
+int     time;
+{
+       int     timeprev;
+       struct mscp *prev;
+       int     s = splbio();
+
+       if(prev = uha_latest) /* yes, an assign */
+       {
+               timeprev = uha_furtherest;
+       }
+       else
+       {
+               timeprev = 0;
+       }
+       while(prev && (timeprev > time)) 
+       {
+               timeprev -= prev->delta;
+               prev = prev->sooner;
+       }
+       if(prev)
+       {
+               mscp->delta = time - timeprev;
+               if( mscp->later = prev->later) /* yes an assign */
+               {
+                       mscp->later->sooner = mscp;
+                       mscp->later->delta -= mscp->delta;
+               }
+               else
+               {
+                       uha_furtherest = time;
+                       uha_latest = mscp;
+               }
+               mscp->sooner = prev;
+               prev->later = mscp;
+       }
+       else
+       {
+               if( mscp->later = uha_soonest) /* yes, an assign*/
+               {
+                       mscp->later->sooner = mscp;
+                       mscp->later->delta -= time;
+               }
+               else
+               {
+                       uha_furtherest = time;
+                       uha_latest = mscp;
+               }
+               mscp->delta = time;
+               mscp->sooner = (struct mscp *)0;
+               uha_soonest = mscp;
+       }
+       splx(s);
+}
+
+uha_remove_timeout(mscp)
+struct  mscp     *mscp;
+{
+       int     s = splbio();
+
+       if(mscp->sooner)
+       {
+               mscp->sooner->later = mscp->later;
+       }
+       else
+       {
+               uha_soonest = mscp->later;
+       }
+       if(mscp->later)
+       {
+               mscp->later->sooner = mscp->sooner;
+               mscp->later->delta += mscp->delta;
+       }
+       else
+       {
+               uha_latest = mscp->sooner;
+               uha_furtherest -= mscp->delta;
+       }
+       mscp->sooner = mscp->later = (struct mscp *)0;
+       splx(s);
+}
+
+
+extern int      hz;
+#define ONETICK 500 /* milliseconds */
+#define SLEEPTIME ((hz * 1000) / ONETICK)
+uha_timeout(arg)
+int     arg;
+{
+       struct  mscp  *mscp;
+       int     unit;
+       int     s       = splbio();
+       unsigned int stat;
+       int     port = uha_data[unit].baseport;
+
+       while( mscp = uha_soonest )
+       {
+               if(mscp->delta <= ONETICK)
+               /***********************************************\
+               * It has timed out, we need to do some work     *
+               \***********************************************/
+               {
+                       unit = mscp->xs->adapter;
+                       printf("uha%d:%d device timed out\n",unit
+                                       ,mscp->xs->targ);
+                       if(uha_debug & UHA_SHOWMSCPS)
+                               uha_print_active_mscp();
+
+                       /***************************************\
+                       * Unlink it from the queue              *
+                       \***************************************/
+                       uha_remove_timeout(mscp);
+
+                       if((uha_abort(unit,mscp) !=1) || (mscp->flags = MSCP_ABORTED))
+                       {
+                               printf("AGAIN");
+                               mscp->xs->retries = 0; /* I MEAN IT ! */
+                               uha_done(unit,mscp,FAIL);
+                       }
+                       else    /* abort the operation that has timed out */
+                       {
+                               printf("\n");
+                               uha_add_timeout(mscp,2000 + ONETICK);
+                               mscp->flags = MSCP_ABORTED;
+                       }
+               }
+               else
+               /***********************************************\
+               * It has not timed out, adjust and leave        *
+               \***********************************************/
+               {
+                       mscp->delta -= ONETICK;
+                       uha_furtherest -= ONETICK;
+                       break;
+               }
+       }
+       splx(s);
+       timeout(uha_timeout,arg,SLEEPTIME);
+}
+
+uha_show_scsi_cmd(struct scsi_xfer *xs)
+{
+       u_char  *b = (u_char *)xs->cmd;
+       int i = 0;
+       if(!(xs->flags & SCSI_RESET))
+       {
+               printf("uha%d:%d:%d-"
+                       ,xs->adapter
+                       ,xs->targ
+                       ,xs->lu);
+               while(i < xs->cmdlen )
+               {
+                       if(i) printf(",");
+                       printf("%x",b[i++]);
+               }
+               printf("-\n");
+       }
+       else
+       {
+               printf("uha%d:%d:%d-RESET-\n" 
+                       ,xs->adapter 
+                       ,xs->targ
+                       ,xs->lu
+               );
+       }
+}
+uha_print_mscp(mscp)
+struct  mscp *mscp;
+{
+       printf("mscp:%x op:%x cmdlen:%d senlen:%d\n"
+               ,mscp
+               ,mscp->opcode
+               ,mscp->cdblen
+               ,mscp->senselen);
+       printf("        sg:%d sgnum:%x datlen:%d hstat:%x tstat:%x delta:%d flags:%x\n"
+               ,mscp->sgth
+               ,mscp->sg_num
+               ,mscp->datalen
+               ,mscp->ha_status
+               ,mscp->targ_status
+               ,mscp->delta
+               ,mscp->flags);
+       uha_show_scsi_cmd(mscp->xs);
+}
+
+uha_print_active_mscp()
+{
+       struct  mscp *mscp;
+       mscp = uha_soonest;
+
+       while(mscp)
+       {
+               uha_print_mscp(mscp);
+               mscp = mscp->later;
+       }
+       printf("Furtherest = %d\n",uha_furtherest);
+}
diff --git a/usr/src/sys.386bsd/scsi/README b/usr/src/sys.386bsd/scsi/README
new file mode 100644 (file)
index 0000000..16e0998
--- /dev/null
@@ -0,0 +1,182 @@
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+This release consists of the following files 
+(relative to the base of the kernel tree)
+
+
+MAKEDEV
+
+scsi
+scsi/README
+scsi/scsiconf.h
+scsi/scsiconf.c
+scsi/scsi_all.h
+
+scsi/scsi_disk.h
+scsi/sd.c
+
+scsi/scsi_tape.h
+scsi/st.c
+
+sys/chio.h
+scsi/scsi_changer.h
+scsi/ch.c
+
+sys/cdio.h
+scsi/scsi_cd.h
+scsi/cd.c
+
+i386/conf/SCSITEST
+i386/isa/aha1542.c
+
+README.AHA1742
+i386/conf/AHBTEST
+i386/isa/aha1742.c
+
+i386/conf/UHATEST
+i386/isa/ultra14f.c
+
+i386/conf/BTTEST
+i386/isa/bt742a.c
+
+
+
+----------------------------------------------------------------
+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 *
+Kodak IL900 scanner *
+Exabyte tape changer device.
+----------------------------------------------------------------
+
+
+There are also working bottom end drivers for:
+----------------------------------------------------------------
+adaptec 1542 (and 1742 in 1542 mode)
+bustec 742a
+adaptec 174x 
+Ultrastore 14f                         
+----------------------------------------------------------------
+
+
+Work is proceeding on the following bottom end drivers:
+----------------------------------------------------------------
+Future Domain (1680)**                 hosler@tfs.com & me
+Future Domain (8  bit)****             rpr@oce.nl
+WD7000**                               terry@icarus.weber.edu
+seagate st01/st02****                  overby@aspen.cray.com ?
+----------------------------------------------------------------
+* drivers not made public (proprietary.. proof that the concept works though)
+** driver not yet released but working.
+*** just a dream so far.
+**** some amount more than just a dream so far.
+
+
+################## 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.
+
+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)
+
+--------------getting started------------
+It should be possible to use the /dev entries for as0 as if they were 
+/dev entries for sd0 and the old as bootblocks should
+continue to work if you are using an adaptec 1542b.
+
+--------------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
+
+
+The tape devices are as follows:
+rst0   basic raw device, will rewind on close
+nrst0  will not rewind on close
+erst0  will rewind and EJECTon close
+nerst0  will not rewind and WILL eject (some devices may rewind anyhow)
+
+------------future enhancements--------------
+Some people have indicated that they would like to have the SCSI ID
+encoded into the minor number in some way, and
+this may be supported at some timein the future, using
+minor numbers greater than 128. (or maybe a different major number)
+
+I will also be writing (probably) a generic scsi-error
+handling routine that will be table driven, so that the routine can
+be removed from each individual driver. With enough care,
+two similar devices with different error codes (quite common) could run
+the same driver but use different error tables.
+
+--------------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 and sys/chio (based after sys/mtio for
+the ioctls for mag tapes (including st).
+
+-----------cd-rom-----------------
+The cd rom driver ha been tested by a number of people and
+grefen@wilbur.zdv.uni-mainz.de has completed the audio play
+functions.
+He tells me he has some Public Domain package that
+allows an control of the cd player from an Xwindow
+but I don't have it.
+
+-------------media changer---------------
+Once again courtesy of grefen@wilbur.zdv.uni-mainz.de.
+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.
+
+-----------booting from an AHA-174x---------
+For some reason I have not yet worked out,
+the BIOS-based bootblocks I have posted will not boot
+from the aha1742 in extended mode. (it can't be serious
+because the MACH version works) This is in fact not a 
+problem because the aha1742 driver will force the board into extended
+mode during probe, so it can be left in standard mode during the boot.
+During the next reboot, the bios will place it back in standard mode
+ready for the NEXT boot.
+
diff --git a/usr/src/sys.386bsd/scsi/README.AHA1742 b/usr/src/sys.386bsd/scsi/README.AHA1742
new file mode 100644 (file)
index 0000000..3362f1f
--- /dev/null
@@ -0,0 +1,24 @@
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+
+Just a note:
+The new bootblocks do not work with the 1742 in extended mode:
+They DO however love it in standard mode.
+
+The driver  automatically switches the board to extended mode when
+it finds it
+so leave your 1742 board in standard mode and just boot with the new bootblocks.
+
+When unix starts the board will magically be in extended mode 
+and when it is rebooted the bios will take it back to standard mode again.
+
+Eventually I will fix the bootblocks, but this works just as well.
+
+julian
+
+
diff --git a/usr/src/sys.386bsd/scsi/cd.c b/usr/src/sys.386bsd/scsi/cd.c
new file mode 100644 (file)
index 0000000..8f961db
--- /dev/null
@@ -0,0 +1,1720 @@
+/*
+ * 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
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ */
+
+#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>
+
+long int cdstrats,cdqueues;
+
+
+#include <ddb.h>
+#if    NDDB > 0
+int    Debugger();
+#else  NDDB > 0
+#define Debugger()
+#endif NDDB > 0
+
+
+#define PAGESIZ        4096
+#define SECSIZE 2048   /* XXX */ /* default only */
+#define        CDOUTSTANDING   2
+#define CDQSIZE                4
+#define        CD_RETRIES      4
+
+#define        UNITSHIFT       3
+#define PARTITION(z)   (minor(z) & 0x07)
+#define        RAW_PART        3
+#define UNIT(z)                (  (minor(z) >> UNITSHIFT) )
+
+
+extern int hz;
+int    cd_done();
+int    cdstrategy();
+int    cd_debug = 0;
+
+struct buf             cd_buf_queue[NCD];
+struct scsi_xfer       cd_scsi_xfer[NCD][CDOUTSTANDING]; /* XXX */
+struct scsi_xfer       *cd_free_xfer[NCD];
+int                    cd_xfer_block_wait[NCD];
+
+struct cd_data
+{
+       int     flags;
+#define        CDVALID         0x02            /* PARAMS LOADED        */
+#define        CDINIT          0x04            /* device has been init'd */
+#define        CDWAIT          0x08            /* device has someone waiting */
+#define CDHAVELABEL    0x10            /* have read the label */
+       struct  scsi_switch *sc_sw;     /* address of scsi low level switch */
+       int     ctlr;                   /* so they know which one we want */
+       int     targ;                   /* our scsi target ID */
+       int     lu;                     /* out scsi lu */
+       int     cmdscount;              /* cmds allowed outstanding by board*/
+       struct  cd_parms
+       {
+               int     blksize;
+               u_long  disksize;               /* total number sectors */
+       }params;
+       struct  disklabel       disklabel;
+       int     partflags[MAXPARTITIONS];       /* per partition flags */
+#define CDOPEN 0x01
+       int             openparts;              /* one bit for each open partition */
+}cd_data[NCD];
+
+#define CD_STOP                0
+#define CD_START       1
+#define CD_EJECT       -2
+
+
+static int     next_cd_unit = 0;
+/***********************************************************************\
+* The routine called by the low level scsi routine when it discovers   *
+* A device suitable for this driver                                    *
+\***********************************************************************/
+int    cdattach(ctlr,targ,lu,scsi_switch)
+struct scsi_switch *scsi_switch;
+{
+       int     unit,i;
+       unsigned char *tbl;
+       struct cd_data *cd;
+       struct cd_parms *dp;
+
+       unit = next_cd_unit++;
+       cd = cd_data + unit;
+       dp  = &(cd->params);
+       if(scsi_debug & PRINTROUTINES) printf("cdattach: "); 
+       /*******************************************************\
+       * Check we have the resources for another drive         *
+       \*******************************************************/
+       if( unit >= NCD)
+       {
+               printf("Too many scsi CDs..(%d > %d) reconfigure kernel",(unit + 1),NCD);
+               return(0);
+       }
+       /*******************************************************\
+       * Store information needed to contact our base driver   *
+       \*******************************************************/
+       cd->sc_sw       =       scsi_switch;
+       cd->ctlr        =       ctlr;
+       cd->targ        =       targ;
+       cd->lu          =       lu;
+       cd->cmdscount = CDOUTSTANDING; /* XXX (ask the board) */
+
+
+       i = cd->cmdscount;
+       while(i-- )
+       {
+               cd_scsi_xfer[unit][i].next = cd_free_xfer[unit];
+               cd_free_xfer[unit] = &cd_scsi_xfer[unit][i];
+       }
+       /*******************************************************\
+       * 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 present\n");
+       }
+       else
+       {
+               printf("drive empty\n");
+       }
+       cd->flags |= CDINIT;
+       return;
+
+}
+
+
+
+/*******************************************************\
+*      open the device. Make sure the partition info   *
+* is a up-to-date as can be.                           *
+\*******************************************************/
+cdopen(dev)
+{
+       int errcode = 0;
+       int unit, part;
+       struct cd_parms cd_parms;
+       struct cd_data *cd ;
+
+       unit = UNIT(dev);
+       part = PARTITION(dev);
+       cd = cd_data + unit;
+       if(scsi_debug & (PRINTROUTINES | TRACEOPENS))
+               printf("cdopen: dev=0x%x (unit %d (of %d),partition %d)\n"
+                               ,   dev,      unit,   NCD,         part);
+       /*******************************************************\
+       * Check the unit is legal                               *
+       \*******************************************************/
+       if ( unit >= NCD )
+       {
+               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 (! (cd->flags & CDINIT))
+               return(ENXIO);
+
+       /*******************************************************\
+       * If it's been invalidated, and not everybody has       *
+       * closed it then forbid re-entry.                       *
+       *       (may have changed media)                        *
+       \*******************************************************/
+       if ((! (cd->flags & CDVALID))
+          && ( 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 CDVALID flag is not yet set     *
+       \*******************************************************/
+       if (cd_req_sense(unit, SCSI_SILENT) != 0) {
+               if(scsi_debug & TRACEOPENS)
+                       printf("not reponding\n");
+               return(ENXIO);
+       }
+       if(scsi_debug & TRACEOPENS)
+               printf("Device present\n");
+       /*******************************************************\
+       * In case it is a funny one, tell it to start           *
+       * not needed for hard drives                            *
+       \*******************************************************/
+       cd_start_unit(unit,part,CD_START);
+        cd_prevent_unit(unit,PR_PREVENT,SCSI_SILENT);
+       if(scsi_debug & TRACEOPENS)
+               printf("started ");
+       /*******************************************************\
+       * Load the physical device parameters                   *
+       \*******************************************************/
+       cd_get_parms(unit, 0);
+       if(scsi_debug & TRACEOPENS)
+               printf("Params loaded ");
+       /*******************************************************\
+       * Load the partition info if not already loaded         *
+       \*******************************************************/
+       cdgetdisklabel(unit);
+       if(scsi_debug & TRACEOPENS)
+               printf("Disklabel fabricated ");
+       /*******************************************************\
+       * Check the partition is legal                          *
+       \*******************************************************/
+       if (( part >= cd->disklabel.d_npartitions ) 
+               && (part != RAW_PART))
+       {
+               if(scsi_debug & TRACEOPENS)
+                       printf("partition %d > %d\n",part
+                               ,cd->disklabel.d_npartitions);
+               cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT);
+               return(ENXIO);
+       }
+       /*******************************************************\
+       *  Check that the partition exists                      *
+       \*******************************************************/
+       if (( cd->disklabel.d_partitions[part].p_fstype != FS_UNUSED )
+               || (part == RAW_PART))
+       {
+               cd->partflags[part] |= CDOPEN;
+               cd->openparts |= (1 << part);
+               if(scsi_debug & TRACEOPENS)
+                       printf("open complete\n");
+               cd->flags |= CDVALID;
+       }
+       else
+       {
+               if(scsi_debug & TRACEOPENS)
+                       printf("part %d type UNUSED\n",part);
+               cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT);
+               return(ENXIO);
+       }
+       return(0);
+}
+
+/*******************************************************\
+* Get ownership of a scsi_xfer structure               *
+* If need be, sleep on it, until it comes free         *
+\*******************************************************/
+struct scsi_xfer *cd_get_xs(unit,flags)
+int    flags;
+int    unit;
+{
+       struct scsi_xfer *xs;
+       int     s;
+
+       if(flags & (SCSI_NOSLEEP |  SCSI_NOMASK))
+       {
+               if (xs = cd_free_xfer[unit])
+               {
+                       cd_free_xfer[unit] = xs->next;
+                       xs->flags = 0;
+               }
+       }
+       else
+       {
+               s = SPLCD();
+               while (!(xs = cd_free_xfer[unit]))
+               {
+                       cd_xfer_block_wait[unit]++;  /* someone waiting! */
+                       sleep((caddr_t)&cd_free_xfer[unit], PRIBIO+1);
+                       cd_xfer_block_wait[unit]--;
+               }
+               cd_free_xfer[unit] = xs->next;
+               splx(s);
+               xs->flags = 0;
+       }
+       return(xs);
+}
+
+/*******************************************************\
+* Free a scsi_xfer, wake processes waiting for it      *
+\*******************************************************/
+cd_free_xs(unit,xs,flags)
+struct scsi_xfer *xs;
+int    unit;
+int    flags;
+{
+       int     s;
+       
+       if(flags & SCSI_NOMASK)
+       {
+               if (cd_xfer_block_wait[unit])
+               {
+                       printf("doing a wakeup from NOMASK mode\n");
+                       wakeup((caddr_t)&cd_free_xfer[unit]);
+               }
+               xs->next = cd_free_xfer[unit];
+               cd_free_xfer[unit] = xs;
+       }
+       else
+       {
+               s = SPLCD();
+               if (cd_xfer_block_wait[unit])
+                       wakeup((caddr_t)&cd_free_xfer[unit]);
+               xs->next = cd_free_xfer[unit];
+               cd_free_xfer[unit] = xs;
+               splx(s);
+       }
+}
+
+/*******************************************************\
+* 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_data[UNIT(bp->b_dev)].sc_sw->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.                          *
+\*******************************************************/
+
+int    cdstrategy(bp)
+struct buf     *bp;
+{
+       struct  buf     *dp;
+       unsigned int opri;
+       struct cd_data *cd ;
+       int     unit;
+
+       cdstrats++;
+       unit = UNIT((bp->b_dev));
+       cd = cd_data + unit;
+       if(scsi_debug & PRINTROUTINES) printf("\ncdstrategy ");
+       if(scsi_debug & SHOWREQUESTS) printf("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->flags & CDVALID))
+       {
+               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)
+       {
+               if (!(cd->flags & CDHAVELABEL))
+               {
+                       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,&cd->disklabel,1) <= 0)
+                       goto done;
+               /* otherwise, process transfer request */
+       }
+
+       opri = SPLCD();
+       dp = &cd_buf_queue[unit];
+
+       /*******************************************************\
+       * 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;
+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;
+}
+
+/***************************************************************\
+* 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 cd_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 cd_done*/
+cdstart(unit)
+int    unit;
+{
+       register struct buf     *bp = 0;
+       register struct buf     *dp;
+       struct  scsi_xfer       *xs;
+       struct  scsi_rw_big     cmd;
+       int                     blkno, nblk;
+       struct cd_data *cd = cd_data + unit;
+       struct partition *p ;
+
+       if(scsi_debug & PRINTROUTINES) printf("cdstart%d ",unit);
+       /*******************************************************\
+       * See if there is a buf to do and we are not already    *
+       * doing one                                             *
+       \*******************************************************/
+       if(!cd_free_xfer[unit])
+       {
+               return;    /* none for us, unit already underway */
+       }
+
+       if(cd_xfer_block_wait[unit])    /* there is one, but a special waits */
+       {
+               return; /* give the special that's waiting a chance to run */
+       }
+
+
+       dp = &cd_buf_queue[unit];
+       if ((bp = dp->b_actf) != NULL)  /* yes, an assign */
+       {
+               dp->b_actf = bp->av_forw;
+       }
+       else
+       { 
+               return;
+       }
+
+       xs=cd_get_xs(unit,0);   /* ok we can grab it */
+       xs->flags = INUSE;    /* Now ours */
+       /***************************************************************\
+       * Should reject all queued entries if CDVALID is not true       *
+       \***************************************************************/
+       if(!(cd->flags & CDVALID))
+       {
+               goto bad; /* no I/O.. media changed or something */
+       }
+
+       /*******************************************************\
+       * We have a buf, now we should move the data into       *
+       * a scsi_xfer definition and try start it               *
+       \*******************************************************/
+       /*******************************************************\
+       *  First, translate the block to absolute               *
+       * and put it in terms of the logical blocksize of the   *
+       * device..                                              *
+       \*******************************************************/
+       p = cd->disklabel.d_partitions + PARTITION(bp->b_dev);
+       blkno = ((bp->b_blkno / (cd->params.blksize/512)) + p->p_offset);
+       nblk = (bp->b_bcount + (cd->params.blksize - 1)) / (cd->params.blksize);
+
+       /*******************************************************\
+       *  Fill out the scsi command                            *
+       \*******************************************************/
+       bzero(&cmd, sizeof(cmd));
+       cmd.op_code     =       READ_BIG;
+       cmd.addr_3      =       (blkno & 0xff000000) >> 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);
+       /*******************************************************\
+       * Fill out the scsi_xfer structure                      *
+       *       Note: we cannot sleep as we may be an interrupt *
+       \*******************************************************/
+       xs->flags       |=      SCSI_NOSLEEP;
+       xs->adapter     =       cd->ctlr;
+       xs->targ        =       cd->targ;
+       xs->lu          =       cd->lu;
+       xs->retries     =       CD_RETRIES;
+       xs->timeout     =       10000;/* 10000 millisecs for a disk !*/
+       xs->cmd         =       (struct scsi_generic *)&cmd;
+       xs->cmdlen      =       sizeof(cmd);
+       xs->resid       =       bp->b_bcount;
+       xs->when_done   =       cd_done;
+       xs->done_arg    =       unit;
+       xs->done_arg2   =       (int)xs;
+       xs->error       =       XS_NOERROR;
+       xs->bp          =       bp;
+       xs->data        =       (u_char *)bp->b_un.b_addr;
+       xs->datalen     =       bp->b_bcount;
+
+       /*******************************************************\
+       * Pass all this info to the scsi driver.                *
+       \*******************************************************/
+       if ( (*(cd->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED)
+       {
+               printf("cd%d: oops not queued",unit);
+               goto bad;
+       }       
+       cdqueues++;
+       return;
+bad:   xs->error = XS_DRIVER_STUFFUP;
+       cd_done(unit,xs);
+}
+
+/*******************************************************\
+* This routine is called by the scsi interrupt when    *
+* the transfer is complete. (or failed)                        *
+\*******************************************************/
+int    cd_done(unit,xs)
+int    unit;
+struct scsi_xfer       *xs;
+{
+       struct  buf             *bp;
+       int     retval;
+
+       if(scsi_debug & PRINTROUTINES) printf("cd_done%d ",unit);
+       if (! (xs->flags & INUSE))      /* paranoia always pays off */
+               panic("scsi_xfer not in use!");
+       if(bp = xs->bp)
+       {
+               switch(xs->error)
+               {
+               case    XS_NOERROR:
+                       bp->b_error = 0;
+                       bp->b_resid = 0;
+                       break;
+
+               case    XS_SENSE:
+                       retval = (cd_interpret_sense(unit,xs));
+                       if(retval)
+                       {
+                               bp->b_flags |= B_ERROR;
+                               bp->b_error = retval;
+                       }
+                       break;
+
+               case    XS_TIMEOUT:
+                       printf("cd%d timeout\n",unit);
+
+               case    XS_BUSY:        
+                       /***********************************\
+                       * Just resubmit it straight back to *
+                       * the SCSI driver to try it again   *
+                       \***********************************/
+                       if(xs->retries--)
+                       {
+                               xs->error = XS_NOERROR;
+                               xs->flags &= ~ITSDONE;
+                               if ( (*(cd_data[unit].sc_sw->scsi_cmd))(xs)
+                                       == SUCCESSFULLY_QUEUED)
+                               {       /* shhh! don't wake the job, ok? */
+                                       /* don't tell cdstart either, */
+                                       return;
+                               }
+                               /* xs->error is set by the scsi driver */
+                       } /* Fall through */
+
+               case    XS_DRIVER_STUFFUP:
+                       bp->b_flags |= B_ERROR;
+                       bp->b_error = EIO;
+                       break;
+               default:
+                       printf("cd%d: unknown error category from scsi driver\n"
+                               ,unit);
+               }       
+               biodone(bp);
+               cd_free_xs(unit,xs,0);
+               cdstart(unit);  /* If there's anything waiting.. do it */
+       }
+       else /* special has finished */
+       {
+               wakeup(xs);
+       }
+}
+/*******************************************************\
+* Perform special action on behalf of the user         *
+* Knows about the internals of this device             *
+\*******************************************************/
+cdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
+{
+       int error = 0;
+       unsigned int opri;
+       unsigned char unit, part;
+       register struct cd_data *cd;
+
+
+       /*******************************************************\
+       * Find the device that the user is talking about        *
+       \*******************************************************/
+       unit = UNIT(dev);
+       part = PARTITION(dev);
+       cd = &cd_data[unit];
+       if(scsi_debug & PRINTROUTINES) printf("cdioctl%d ",unit);
+
+       /*******************************************************\
+       * If the device is not valid.. abandon ship             *
+       \*******************************************************/
+       if (!(cd_data[unit].flags & CDVALID))
+               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;
+
+        case DIOCWDINFO:
+        case DIOCSDINFO:
+                if ((flag & FWRITE) == 0)
+                        error = EBADF;
+                else
+                        error = setdisklabel(&cd->disklabel,
+                                       (struct disklabel *)addr,
+                         /*(cd->flags & DKFL_BSDLABEL) ? cd->openparts : */0,
+                               0);
+                if (error == 0) {
+                       cd->flags |= CDHAVELABEL;
+               }
+                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.sotc = 0;
+                       data.page.audio.immed = 1;
+                       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 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.sotc = 0;
+                       data.page.audio.immed = 1;
+                       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;
+                       int 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:
+               {
+                       struct ioc_toc_header th;
+                       if( error = cd_read_toc(unit,0,0,&th,sizeof(th)))
+                               break;
+                       th.len=(th.len&0xff)<<8+((th.len>>8)&0xff);
+                       bcopy(&th,addr,sizeof(th));
+               }
+               break;
+       case CDIOREADTOCENTRYS:
+               {
+                       struct ioc_read_toc_entry *te=  
+                                       (struct ioc_read_toc_entry *)addr;
+                       struct cd_toc_entry data[65];
+                       struct ioc_toc_header *th;
+                       int len=te->data_len;
+                       th=(struct ioc_toc_header *)data;
+
+                        if(len>sizeof(data) || len<sizeof(struct cd_toc_entry)) {
+                                error=EINVAL;
+                                break;
+                        }
+                       if(error = cd_read_toc(unit,te->address_format,
+                                                   te->starting_track,
+                                                   data,
+                                                   len))
+                               break;
+                       len=MIN(len,((((th->len&0xff)<<8)+((th->len>>8)))+
+                                                               sizeof(*th)));
+                       if(copyout(th,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;
+               }
+               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].volume = arg->vol[LEFT_PORT];
+                       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 = 15;
+                       data.page.audio.port[RIGHT_PORT].channels = 15;
+                       data.page.audio.port[2].channels = 15;
+                       data.page.audio.port[3].channels = 15;
+                       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 = cd_start_unit(unit,part,CD_START);
+               break;
+       case CDIOCSTOP:
+               error = cd_start_unit(unit,part,CD_STOP);
+               break;
+       case CDIOCEJECT:
+               error = cd_start_unit(unit,part,CD_EJECT);
+               break;
+       case CDIOCSETDEBUG:
+               scsi_debug = 0xfff; cd_debug = 0xfff;
+               break;
+       case CDIOCCLRDEBUG:
+               scsi_debug = 0; cd_debug = 0;
+               break;
+       case CDIOCRESET:
+               return(cd_reset(unit));
+               break;
+       default:
+               error = ENOTTY;
+               break;
+       }
+       return (error);
+}
+
+
+/*******************************************************\
+* Load the label information on the named device       *
+*                                                      *
+* EVENTUALLY take information about different          *
+* data tracks from the TOC and put it in the disklabel *
+\*******************************************************/
+int cdgetdisklabel(unit)
+unsigned char  unit;
+{
+       /*unsigned int n, m;*/
+       char *errstring;
+       struct dos_partition *dos_partition_p;
+       struct cd_data *cd = cd_data + unit;
+
+       /*******************************************************\
+       * If the inflo is already loaded, use it                *
+       \*******************************************************/
+       if(cd->flags & CDHAVELABEL) return;
+
+       bzero(&cd->disklabel,sizeof(struct disklabel));
+       /*******************************************************\
+       * make partition 3 the whole disk in case of failure    *
+       *   then get pdinfo                                     *
+       \*******************************************************/
+       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;
+
+       cd->disklabel.d_npartitions = 1;
+        cd->disklabel.d_partitions[0].p_offset = 0;
+        cd->disklabel.d_partitions[0].p_size = cd->params.disksize;
+        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)           *
+       \*******************************************************/
+       cd->flags |= CDHAVELABEL;
+       return(ESUCCESS);
+}
+
+/*******************************************************\
+* Find out form the device what it's capacity is       *
+\*******************************************************/
+cd_size(unit, flags)
+{
+       struct  scsi_read_cd_cap_data   rdcap;
+       struct  scsi_read_cd_capacity   scsi_cmd;
+       int size;
+       int     blksize;
+
+       /*******************************************************\
+       * 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                                      *
+       \*******************************************************/
+       if (cd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       &rdcap,
+                       sizeof(rdcap),  
+                       2000,
+                       flags) != 0)
+       {
+               printf("could not get size of unit %d\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(cd_debug)printf("cd%d: %d %d byte blocks\n",unit,size,blksize);
+       cd_data[unit].params.disksize = size;
+       cd_data[unit].params.blksize = blksize;
+       return(size);
+}
+       
+/*******************************************************\
+* Check with the device that it is ok, (via scsi driver)*
+\*******************************************************/
+cd_req_sense(unit, flags)
+{
+       struct  scsi_sense_data sense_data;
+       struct  scsi_sense scsi_cmd;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = REQUEST_SENSE;
+       scsi_cmd.length = sizeof(sense_data);
+
+       if (cd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       &sense_data,
+                       sizeof(sense_data),
+                       2000,
+                       flags) != 0)
+       {
+               return(ENXIO);
+       }
+       else 
+               return(0);
+}
+
+/*******************************************************\
+* Get the requested page into the buffer given         *
+\*******************************************************/
+cd_get_mode(unit,data,page)
+int    unit;
+struct cd_mode_data *data;
+int    page;
+{
+       struct scsi_mode_sense scsi_cmd;
+       int     retval;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       bzero(data,sizeof(*data));
+       scsi_cmd.op_code = MODE_SENSE;
+       scsi_cmd.page_code = page;
+       scsi_cmd.length = sizeof(*data) & 0xff;
+       retval = cd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       data,
+                       sizeof(*data),
+                       20000,  /* should be immed */
+                       0);
+       return (retval);
+}
+/*******************************************************\
+* Get the requested page into the buffer given         *
+\*******************************************************/
+cd_set_mode(unit,data)
+int    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.pf = 1;
+       scsi_cmd.length = sizeof(*data) & 0xff;
+       data->header.data_length = 0;
+       /*show_mem(data,sizeof(*data));/**/
+       return (cd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       data,
+                       sizeof(*data),
+                       20000,  /* should be immed */
+                       0)
+       ); 
+}
+/*******************************************************\
+* Get scsi driver to send a "start playing" command    *
+\*******************************************************/
+cd_play(unit,blk,len)
+int    unit,blk,len;
+{
+       struct scsi_play scsi_cmd;
+       int     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;
+       retval = cd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       200000, /* should be immed */
+                       0);
+       return(retval);
+}
+/*******************************************************\
+* Get scsi driver to send a "start playing" command    *
+\*******************************************************/
+cd_play_big(unit,blk,len)
+int    unit,blk,len;
+{
+       struct scsi_play_big scsi_cmd;
+       int     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;
+       retval = cd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       20000,  /* should be immed */
+                       0);
+       return(retval);
+}
+/*******************************************************\
+* Get scsi driver to send a "start playing" command    *
+\*******************************************************/
+cd_play_tracks(unit,strack,sindex,etrack,eindex)
+int    unit,strack,sindex,etrack,eindex;
+{
+       struct scsi_play_track scsi_cmd;
+       int     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;
+       retval = cd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       20000,  /* should be immed */
+                       0);
+       return(retval);
+}
+/*******************************************************\
+* Get scsi driver to send a "start up" command         *
+\*******************************************************/
+cd_pause(unit,go)
+int    unit,go;
+{
+       struct scsi_pause scsi_cmd;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = PAUSE;
+       scsi_cmd.resume = go;
+
+       return (cd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       2000,
+                       0));
+}
+/*******************************************************\
+* Get scsi driver to send a "start up" command         *
+\*******************************************************/
+cd_reset(unit)
+int    unit;
+{
+       return(cd_scsi_cmd(unit,0,0,0,0,2000,SCSI_RESET));
+}
+/*******************************************************\
+* Get scsi driver to send a "start up" command         *
+\*******************************************************/
+cd_start_unit(unit,part,type)
+{
+       struct scsi_start_stop scsi_cmd;
+
+        if(type==CD_EJECT && (cd_data[unit].openparts&~(1<<part)) == 0 ) {
+               cd_prevent_unit(unit,CD_EJECT,0);
+       }
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = START_STOP;
+       scsi_cmd.start = type==CD_START?1:0;
+       scsi_cmd.loej  = type==CD_EJECT?1:0;
+
+       if (cd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       2000,
+                       0) != 0) {
+               return(ENXIO);
+       } else 
+               return(0);
+}
+/*******************************************************\
+* Prevent or allow the user to remove the disk          *
+\*******************************************************/
+cd_prevent_unit(unit,type,flags)
+int     unit,type,flags;
+{
+        struct  scsi_prevent    scsi_cmd;
+
+        if(type==CD_EJECT || type==PR_PREVENT || cd_data[unit].openparts == 0 ) {
+                bzero(&scsi_cmd, sizeof(scsi_cmd));
+                scsi_cmd.op_code = PREVENT_ALLOW;
+                scsi_cmd.prevent=type==CD_EJECT?PR_ALLOW:type;
+                if (cd_scsi_cmd(unit,
+                        &scsi_cmd,
+                        sizeof(struct   scsi_prevent),
+                        0,
+                        0,
+                        5000,
+                        0) != 0)
+                {
+                 if(!(flags & SCSI_SILENT))
+                         printf("cannot prevent/allow on cd%d\n", unit);
+                 return(0);
+                }
+        }
+        return(1);
+}
+
+/******************************************************\
+* Read Subchannel                                     *
+\******************************************************/
+
+cd_read_subchannel(unit,mode,format,track,data,len)
+int unit,mode,format,len;
+struct cd_sub_channel_info *data;
+{
+       struct scsi_read_subchannel scsi_cmd;
+       int error;
+
+       bzero(&scsi_cmd,sizeof(scsi_cmd));
+
+       scsi_cmd.op_code=READ_SUBCHANNEL;
+        if(mode==CD_MSF_FORMAT)
+               scsi_cmd.msf=1;
+       scsi_cmd.subQ=1;
+       scsi_cmd.subchan_format=format;
+       scsi_cmd.track=track;
+       scsi_cmd.data_len[0]=(len)>>8;
+       scsi_cmd.data_len[1]=(len)&0xff;
+       return cd_scsi_cmd(unit,
+               &scsi_cmd,
+               sizeof(struct   scsi_read_subchannel),
+               data,
+               len,
+               5000,
+               0);
+}
+
+/*******************************************************\
+* Read Table of contents                                *
+\*******************************************************/
+cd_read_toc(unit,mode,start,data,len)
+int unit,mode,start,len;
+struct cd_toc_entry *data;
+{
+       struct scsi_read_toc scsi_cmd;
+       int error;
+       int ntoc;
+       
+       bzero(&scsi_cmd,sizeof(scsi_cmd));
+       /*if(len!=sizeof(struct ioc_toc_header))
+          ntoc=((len)-sizeof(struct ioc_toc_header))/sizeof(struct cd_toc_entry);
+         else*/
+          ntoc=len;
+
+       scsi_cmd.op_code=READ_TOC;
+        if(mode==CD_MSF_FORMAT)
+                scsi_cmd.msf=1;
+       scsi_cmd.from_track=start;
+       scsi_cmd.data_len[0]=(ntoc)>>8;
+       scsi_cmd.data_len[1]=(ntoc)&0xff;
+        return cd_scsi_cmd(unit,
+                &scsi_cmd,
+                sizeof(struct   scsi_read_toc),
+                data,
+                len,
+                5000,
+                0);
+}
+
+
+#define b2tol(a)       (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
+
+/*******************************************************\
+* Get the scsi driver to send a full inquiry to the    *
+* device and use the results to fill out the disk      *
+* parameter structure.                                 *
+\*******************************************************/
+
+int    cd_get_parms(unit, flags)
+{
+       struct cd_data *cd = cd_data + unit;
+
+       /*******************************************************\
+       * First check if we have it all loaded                  *
+       \*******************************************************/
+               if(cd->flags & CDVALID) return(0);
+       /*******************************************************\
+       * give a number of sectors so that sec * trks * cyls    *
+       * is <= disk_size                                       *
+       \*******************************************************/
+       if(cd_size(unit, flags))
+       {
+               cd->flags |= CDVALID;
+               return(0);
+       }
+       else
+       {
+               return(ENXIO);
+       }
+}
+
+/*******************************************************\
+* close the device.. only called if we are the LAST    *
+* occurence of an open device                          *
+\*******************************************************/
+cdclose(dev)
+dev_t dev;
+{
+       unsigned char unit, part;
+       unsigned int old_priority;
+
+       unit = UNIT(dev);
+       part = PARTITION(dev);
+       if(scsi_debug & TRACEOPENS)
+               printf("closing cd%d part %d\n",unit,part);
+       cd_data[unit].partflags[part] &= ~CDOPEN;
+       cd_data[unit].openparts &= ~(1 << part);
+               cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT);
+       return(0);
+}
+
+/*******************************************************\
+* ask the scsi driver to perform a command for us.     *
+* Call it through the switch table, and tell it which  *
+* sub-unit we want, and what target and lu we wish to  *
+* talk to. Also tell it where to find the command      *
+* how long int is.                                     *
+* Also tell it where to read/write the data, and how   *
+* long the data is supposed to be                      *
+\*******************************************************/
+int    cd_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags)
+
+int    unit,flags;
+struct scsi_generic *scsi_cmd;
+int    cmdlen;
+int    timeout;
+u_char *data_addr;
+int    datalen;
+{
+       struct  scsi_xfer *xs;
+       int     retval;
+       int     s;
+       struct cd_data *cd = cd_data + unit;
+
+       if(scsi_debug & PRINTROUTINES) printf("\ncd_scsi_cmd%d ",unit);
+       if(cd->sc_sw)   /* If we have a scsi driver */
+       {
+               xs = cd_get_xs(unit,flags); /* should wait unless booting */
+               if(!xs)
+               {
+                       printf("cd_scsi_cmd%d: controller busy"
+                                       " (this should never happen)\n",unit); 
+                               return(EBUSY);
+               }
+               xs->flags |= INUSE;
+               /*******************************************************\
+               * Fill out the scsi_xfer structure                      *
+               \*******************************************************/
+               xs->flags       |=      flags;
+               xs->adapter     =       cd->ctlr;
+               xs->targ        =       cd->targ;
+               xs->lu          =       cd->lu;
+               xs->retries     =       CD_RETRIES;
+               xs->timeout     =       timeout;
+               xs->cmd         =       scsi_cmd;
+               xs->cmdlen      =       cmdlen;
+               xs->data        =       data_addr;
+               xs->datalen     =       datalen;
+               xs->resid       =       datalen;
+               xs->when_done   =       (flags & SCSI_NOMASK)
+                                       ?(int (*)())0
+                                       :cd_done;
+               xs->done_arg    =       unit;
+               xs->done_arg2   =       (int)xs;
+retry:         xs->error       =       XS_NOERROR;
+               xs->bp          =       0;
+               retval = (*(cd->sc_sw->scsi_cmd))(xs);
+               switch(retval)
+               {
+               case    SUCCESSFULLY_QUEUED:
+                       s = splbio();
+                       while(!(xs->flags & ITSDONE))
+                               sleep(xs,PRIBIO+1);
+                       splx(s);
+
+               case    HAD_ERROR:
+                       /*printf("err = %d ",xs->error);*/
+                       switch(xs->error)
+                       {
+                       case    XS_NOERROR:
+                               retval = ESUCCESS;
+                               break;
+                       case    XS_SENSE:
+                               retval = (cd_interpret_sense(unit,xs));
+                               break;
+                       case    XS_DRIVER_STUFFUP:
+                               retval = EIO;
+                               break;
+
+
+                       case    XS_BUSY:
+                       case    XS_TIMEOUT:
+                               if(xs->retries-- )
+                               {
+                                       xs->flags &= ~ITSDONE;
+                                       goto retry;
+                               }
+                               retval = EIO;
+                               break;
+                       default:
+                               retval = EIO;
+                               printf("cd%d: unknown error category from scsi driver\n"
+                                       ,unit);
+                       }       
+                       break;
+               case    COMPLETE:
+                       retval = ESUCCESS;
+                       break;
+               case    TRY_AGAIN_LATER:
+                       if(xs->retries-- )
+                       {
+                               if(tsleep( 0,PRIBIO + 2,"retry",hz * 2))
+                               {
+                                       xs->flags &= ~ITSDONE;
+                                       goto retry;
+                               }
+                       }
+                       retval = EIO;
+                       break;
+               default:
+                       retval = EIO;
+               }
+               cd_free_xs(unit,xs,flags);
+               cdstart(unit);          /* check if anything is waiting fr the xs */
+       }
+       else
+       {
+               printf("cd%d: not set up\n",unit);
+               return(EINVAL);
+       }
+       return(retval);
+}
+/***************************************************************\
+* Look at the returned sense and act on the error and detirmine        *
+* The unix error number to pass back... (0 = report no error)  *
+\***************************************************************/
+
+int    cd_interpret_sense(unit,xs)
+int    unit;
+struct scsi_xfer *xs;
+{
+       struct  scsi_sense_data *sense;
+       int     key;
+       int     silent;
+
+       /***************************************************************\
+       * If the flags say errs are ok, then always return ok.          *
+       \***************************************************************/
+       if (xs->flags & SCSI_ERR_OK) return(ESUCCESS);
+       silent = (xs->flags & SCSI_SILENT);
+
+       sense = &(xs->sense);
+       switch(sense->error_class)
+       {
+       case 7:
+               {
+               key=sense->ext.extended.sense_key;
+               switch(key)
+               {
+               case    0x0:
+                       return(ESUCCESS);
+               case    0x1:
+                       if(!silent)
+                       {
+                               printf("cd%d: soft error(corrected) ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(ESUCCESS);
+               case    0x2:
+                       if(!silent)printf("cd%d: not ready\n ",
+                               unit); 
+                       return(ENODEV);
+               case    0x3:
+                       if(!silent)
+                       {
+                               printf("cd%d: medium error ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(EIO);
+               case    0x4:
+                       if(!silent)printf("cd%d: non-media hardware failure\n ",
+                               unit); 
+                       return(EIO);
+               case    0x5:
+                       if(!silent)printf("cd%d: illegal request\n ",
+                               unit); 
+                       return(EINVAL);
+               case    0x6:
+                       if(!silent)printf("cd%d: Unit attention.\n ", unit); 
+                       if (cd_data[unit].openparts)
+                       cd_data[unit].flags &= ~(CDVALID | CDHAVELABEL);
+                       {
+                               return(EIO);
+                       }
+                       return(ESUCCESS);
+               case    0x7:
+                       if(!silent)
+                       {
+                               printf("cd%d: attempted protection violation ",
+                                               unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(EACCES);
+               case    0x8:
+                       if(!silent)
+                       {
+                               printf("cd%d: block wrong state (worm)\n ",
+                               unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(EIO);
+               case    0x9:
+                       if(!silent)printf("cd%d: vendor unique\n",
+                               unit); 
+                       return(EIO);
+               case    0xa:
+                       if(!silent)printf("cd%d: copy aborted\n ",
+                               unit); 
+                       return(EIO);
+               case    0xb:
+                       if(!silent)printf("cd%d: command aborted\n ",
+                               unit); 
+                       return(EIO);
+               case    0xc:
+                       if(!silent)
+                       {
+                               printf("cd%d: search returned\n ",
+                                       unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(ESUCCESS);
+               case    0xd:
+                       if(!silent)printf("cd%d: volume overflow\n ",
+                               unit); 
+                       return(ENOSPC);
+               case    0xe:
+                       if(!silent)
+                       {
+                               printf("cd%d: verify miscompare\n ",
+                               unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(EIO);
+               case    0xf:
+                       if(!silent)printf("cd%d: unknown error key\n ",
+                               unit); 
+                       return(EIO);
+               }
+               break;
+       }
+       case 0:
+       case 1:
+       case 2:
+       case 3:
+       case 4:
+       case 5:
+       case 6:
+               {
+                       if(!silent)printf("cd%d: error class %d code %d\n",
+                               unit,
+                               sense->error_class,
+                               sense->error_code);
+               if(sense->valid)
+                       if(!silent)printf("block no. %d (decimal)\n",
+                       (sense->ext.unextended.blockhi <<16),
+                       + (sense->ext.unextended.blockmed <<8),
+                       + (sense->ext.unextended.blocklow ));
+               }
+               return(EIO);
+       }
+}
+
+
+
+
+int
+cdsize(dev_t dev)
+{
+       return (-1);
+}
+
+show_mem(address,num)
+unsigned char   *address;
+int     num;
+{
+       int x,y;
+       printf("------------------------------");
+       for (y = 0; y<num; y += 1)
+       {
+               if(!(y % 16))
+                       printf("\n%03d: ",y);
+               printf("%02x ",*address++);
+       }
+       printf("\n------------------------------\n");
+}
+
diff --git a/usr/src/sys.386bsd/scsi/ch.c b/usr/src/sys.386bsd/scsi/ch.c
new file mode 100644 (file)
index 0000000..1e7469b
--- /dev/null
@@ -0,0 +1,1015 @@
+/* 
+ */
+/*
+ * HISTORY
+ * 
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ * 
+ */
+
+#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>
+
+#if defined(OSF)
+#define SECSIZE        512
+#endif /* defined(OSF) */
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_changer.h>
+#include <scsi/scsiconf.h>
+
+
+struct  scsi_xfer ch_scsi_xfer[NCH];
+int     ch_xfer_block_wait[NCH];
+
+
+#define PAGESIZ        4096
+#define STQSIZE                4
+#define        CH_RETRIES      4
+
+
+#define MODE(z)                (  (minor(z) & 0x0F) )
+#define UNIT(z)                (  (minor(z) >> 4) )
+
+#ifndef        MACH
+#define ESUCCESS 0
+#endif MACH
+
+int    ch_info_valid[NCH];     /* the info about the device is valid */
+int    ch_initialized[NCH] ;
+int    ch_debug = 1;
+
+int chattach();
+int ch_done();
+struct ch_data
+{
+       int     flags;
+       struct  scsi_switch *sc_sw;     /* address of scsi low level switch */
+       int     ctlr;                   /* so they know which one we want */
+       int     targ;                   /* our scsi target ID */
+       int     lu;                     /* out scsi lu */
+       short   chmo;                   /* Offset of first CHM */
+       short   chms;                   /* No. of CHM */
+       short   slots;                  /* No. of Storage Elements */
+       short   sloto;                  /* Offset of first SE */
+       short   imexs;                  /* No. of Import/Export Slots */
+       short   imexo;                  /* Offset of first IM/EX */
+       short   drives;                 /* No. of CTS */
+       short   driveo;                 /* Offset of first CTS */
+       short   rot;                    /* CHM can rotate */
+       u_long  op_matrix;              /* possible opertaions */
+       u_short lsterr;         /* details of lasterror */
+       u_char  stor;                   /* posible Storage locations */
+}ch_data[NCH];
+
+#define CH_OPEN                0x01
+#define CH_KNOWN       0x02
+
+static int     next_ch_unit = 0;
+/***********************************************************************\
+* The routine called by the low level scsi routine when it discovers   *
+* A device suitable for this driver                                    *
+\***********************************************************************/
+
+int    chattach(ctlr,targ,lu,scsi_switch)
+struct scsi_switch *scsi_switch;
+{
+       int     unit,i,stat;
+       unsigned char *tbl;
+
+       if(scsi_debug & PRINTROUTINES) printf("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",(unit + 1),NCH);
+               return(0);
+       }
+       /*******************************************************\
+       * Store information needed to contact our base driver   *
+       \*******************************************************/
+       ch_data[unit].sc_sw     =       scsi_switch;
+       ch_data[unit].ctlr      =       ctlr;
+       ch_data[unit].targ      =       targ;
+       ch_data[unit].lu        =       lu;
+
+       /*******************************************************\
+       * 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, %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;
+       }
+       else
+       {
+               printf("        ch%d: scsi changer :- offline\n", unit);
+               stat=CH_OPEN;
+       }
+       ch_initialized[unit] = stat;
+
+       return;
+
+}
+
+
+
+/*******************************************************\
+*      open the device.                                *
+\*******************************************************/
+chopen(dev)
+{
+       int errcode = 0;
+       int unit,mode;
+
+       unit = UNIT(dev);
+       mode = MODE(dev);
+
+       /*******************************************************\
+       * Check the unit is legal                               *
+       \*******************************************************/
+       if ( unit >= NCH )
+       {
+               printf("ch %d  > %d\n",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);
+               errcode = ENXIO;
+               goto bad;
+       }
+       
+       if(ch_debug||(scsi_debug & (PRINTROUTINES | TRACEOPENS)))
+               printf("chopen: dev=0x%x (unit %d (of %d))\n"
+                               ,   dev,      unit,   NCH);
+       /*******************************************************\
+       * Make sure the device has been initialised             *
+       \*******************************************************/
+
+       if (!ch_initialized[unit])
+               return(ENXIO);
+       if (ch_initialized[unit]!=CH_KNOWN) {
+               if((ch_mode_sense(unit,  SCSI_NOSLEEP |  SCSI_NOMASK /*| SCSI_SILENT*/)))
+               {
+                       ch_initialized[unit]=CH_KNOWN;
+               }
+         else
+               {
+                       printf("  ch%d: scsi changer :- offline\n", unit);
+                       return(ENXIO);
+               }
+       }
+       /*******************************************************\
+       * Check that it is still responding and ok.             *
+       \*******************************************************/
+
+       if(ch_debug || (scsi_debug & TRACEOPENS))
+               printf("device is ");
+       if (!(ch_req_sense(unit, 0)))
+       {
+               errcode = ENXIO;
+               if(ch_debug || (scsi_debug & TRACEOPENS))
+                       printf("not responding\n");
+               goto bad;
+       }
+       if(ch_debug || (scsi_debug & TRACEOPENS))
+               printf("ok\n");
+
+       if(!(ch_test_ready(unit,0)))
+       {
+               printf("ch%d not ready\n",unit);
+               return(EIO);
+       }
+
+       ch_info_valid[unit] = TRUE;
+
+       /*******************************************************\
+       * Load the physical device parameters                   *
+       \*******************************************************/
+
+       ch_data[unit].flags = CH_OPEN;
+       return(errcode);
+bad:
+       return(errcode);
+}
+
+/*******************************************************\
+* close the device.. only called if we are the LAST    *
+* occurence of an open device                          *
+\*******************************************************/
+chclose(dev)
+{
+       unsigned char unit,mode;
+
+       unit = UNIT(dev);
+       mode = MODE(dev);
+
+       if(scsi_debug & TRACEOPENS)
+               printf("Closing device");
+       ch_data[unit].flags = 0;
+       return(0);
+}
+
+
+
+/***************************************************************\
+* chstart                                                       *
+* 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.                                     *
+\***************************************************************/
+/* chstart() is called at splbio */
+chstart(unit)
+{
+       int                     drivecount;
+       register struct buf     *bp = 0;
+       register struct buf     *dp;
+       struct  scsi_xfer       *xs;
+       int                     blkno, nblk;
+
+
+       if(scsi_debug & PRINTROUTINES) printf("chstart%d ",unit);
+       /*******************************************************\
+       * See if there is a buf to do and we are not already    *
+       * doing one                                             *
+       \*******************************************************/
+       xs=&ch_scsi_xfer[unit];
+       if(xs->flags & INUSE)
+       {
+               return;    /* unit already underway */
+       }
+       if(ch_xfer_block_wait[unit]) /* a special awaits, let it proceed first */
+       {
+               wakeup(&ch_xfer_block_wait[unit]);
+               return;
+       }
+
+       return;
+
+}
+
+
+/*******************************************************\
+* This routine is called by the scsi interrupt when    *
+* the transfer is complete.
+\*******************************************************/
+int    ch_done(unit,xs)
+int    unit;
+struct scsi_xfer       *xs;
+{
+       struct  buf             *bp;
+       int     retval;
+
+       if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("ch_done%d ",unit);
+       if (! (xs->flags & INUSE))
+               panic("scsi_xfer not in use!");
+       wakeup(xs);
+}
+/*******************************************************\
+* Perform special action on behalf of the user         *
+* Knows about the internals of this device             *
+\*******************************************************/
+chioctl(dev, cmd, arg, mode)
+dev_t dev;
+int cmd;
+caddr_t arg;
+{
+       /* struct ch_cmd_buf *args;*/
+       union scsi_cmd *scsi_cmd;
+       register i,j;
+       unsigned int opri;
+       int errcode = 0;
+       unsigned char unit;
+       int number,flags,ret;
+
+       /*******************************************************\
+       * Find the device that the user is talking about        *
+       \*******************************************************/
+       flags = 0;      /* give error messages, act on errors etc. */
+       unit = UNIT(dev);
+
+       switch(cmd)
+       {
+               case CHIOOP: {
+                       struct chop *ch=(struct chop *) arg;
+                       if (ch_debug)
+                               printf("[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 EINVAL;
+       }
+
+       return(ret?ESUCCESS:EIO);
+}
+
+ch_getelem(unit,stat,type,from,data,flags)
+int unit,from,flags;
+short *stat;
+char *data;
+{
+       struct scsi_read_element_status scsi_cmd;
+       char elbuf[32];
+       int ret;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = READ_ELEMENT_STATUS;
+       scsi_cmd.element_type_code=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=ch_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       elbuf,
+                       32,
+                       100000,
+                       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;
+}
+
+ch_move(unit,stat,chm,from,to,flags)
+int unit,chm,from,to,flags;
+short *stat;
+{
+       struct scsi_move_medium scsi_cmd;
+       int 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=ch_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       NULL,
+                       0,
+                       100000,
+                       flags) !=ESUCCESS)) {
+                       *stat=ch_data[unit].lsterr;
+                       return ret;
+                       }
+       return ret;
+}
+
+ch_position(unit,stat,chm,to,flags)
+int unit,chm,to,flags;
+short *stat;
+{
+       struct scsi_position_to_element scsi_cmd;
+       int 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=ch_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       NULL,
+                       0,
+                       100000,
+                       flags) !=ESUCCESS)) {
+                       *stat=ch_data[unit].lsterr;
+                       return ret;
+                       }
+       return ret;
+}
+
+/*******************************************************\
+* Check with the device that it is ok, (via scsi driver)*
+\*******************************************************/
+ch_req_sense(unit, flags)
+int    flags;
+{
+       struct  scsi_sense_data sense;
+       struct scsi_sense scsi_cmd;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = REQUEST_SENSE;
+       scsi_cmd.length = sizeof(sense);
+
+       if (ch_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(struct   scsi_sense),
+                       &sense,
+                       sizeof(sense),
+                       100000,
+                       flags | SCSI_DATA_IN) != 0)
+       {
+               return(FALSE);
+       }
+       else 
+               return(TRUE);
+}
+
+/*******************************************************\
+* Get scsi driver to send a "are you ready" command    *
+\*******************************************************/
+ch_test_ready(unit,flags)
+int    unit,flags;
+{
+       struct scsi_test_unit_ready scsi_cmd;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = TEST_UNIT_READY;
+
+       if (ch_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(struct   scsi_test_unit_ready),
+                       0,
+                       0,
+                       100000,
+                       flags) != 0) {
+               return(FALSE);
+       } else 
+               return(TRUE);
+}
+
+
+#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.                                 *
+\*******************************************************/
+ch_mode_sense(unit, flags)
+int    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;
+       int i,l;
+
+       /*******************************************************\
+       * First check if we have it all loaded                  *
+       \*******************************************************/
+       if (ch_info_valid[unit]==CH_KNOWN) return(TRUE);
+       /*******************************************************\
+       * First do a mode sense                                 *
+       \*******************************************************/
+       ch_info_valid[unit] &= ~CH_KNOWN;
+       for(l=1;l>=0;l--) {
+               bzero(&scsi_cmd, sizeof(scsi_cmd));
+               scsi_cmd.op_code = MODE_SENSE;
+               scsi_cmd.dbd = l;
+               scsi_cmd.page_code = 0x3f;      /* All Pages */
+               scsi_cmd.length = sizeof(scsi_sense);
+       /*******************************************************\
+       * do the command, but we don't need the results         *
+       * just print them for our interest's sake               *
+       \*******************************************************/
+               if (ch_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(struct   scsi_mode_sense),
+                       &scsi_sense,
+                       sizeof(scsi_sense),
+                       5000,
+                       flags | SCSI_DATA_IN) == 0) {
+                               ch_info_valid[unit] = CH_KNOWN;
+                               break;
+                       }
+       }
+       if (ch_info_valid[unit]!=CH_KNOWN)   {
+               if(!(flags & SCSI_SILENT))
+                       printf("could not mode sense for unit %d\n", unit);
+               return(FALSE);
+       }
+       l=scsi_sense[0]-3;
+       b=&scsi_sense[4];
+       /*****************************\
+       * To avoid alignment problems *
+       \*****************************/
+/*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;) {
+               int pc=(*b++)&0x3f;
+               int pl=*b++;
+               u_char *bb=b;
+               switch(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;
+       }
+       if (ch_debug)
+       {
+               printf("unit %d: cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n",
+                               unit,
+                               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(TRUE);
+}
+
+/*******************************************************\
+* ask the scsi driver to perform a command for us.     *
+* Call it through the switch table, and tell it which  *
+* sub-unit we want, and what target and lu we wish to  *
+* talk to. Also tell it where to find the command      *
+* how long int is.                                     *
+* Also tell it where to read/write the data, and how   *
+* long the data is supposed to be                      *
+\*******************************************************/
+int    ch_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags)
+
+int    unit,flags;
+struct scsi_generic *scsi_cmd;
+int    cmdlen;
+int    timeout;
+u_char *data_addr;
+int    datalen;
+{
+       struct  scsi_xfer *xs;
+       int     retval;
+       int     s;
+
+       if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("\nch_scsi_cmd%d %x",
+                               unit,scsi_cmd->opcode);
+       if(ch_data[unit].sc_sw) /* If we have a scsi driver */
+       {
+
+               xs = &(ch_scsi_xfer[unit]);
+               if(!(flags & SCSI_NOMASK))
+                       s = splbio();
+               ch_xfer_block_wait[unit]++;     /* there is someone waiting */
+               while (xs->flags & INUSE)
+               {
+                       sleep(&ch_xfer_block_wait[unit],PRIBIO+1);
+               }
+               ch_xfer_block_wait[unit]--;
+               xs->flags = INUSE;
+               if(!(flags & SCSI_NOMASK))
+                       splx(s);
+
+               /*******************************************************\
+               * Fill out the scsi_xfer structure                      *
+               \*******************************************************/
+               xs->flags       |=      flags;
+               xs->adapter     =       ch_data[unit].ctlr;
+               xs->targ        =       ch_data[unit].targ;
+               xs->lu          =       ch_data[unit].lu;
+               xs->retries     =       CH_RETRIES;
+               xs->timeout     =       timeout;
+               xs->cmd         =       scsi_cmd;
+               xs->cmdlen      =       cmdlen;
+               xs->data        =       data_addr;
+               xs->datalen     =       datalen;
+               xs->resid       =       datalen;
+               xs->when_done   =       (flags & SCSI_NOMASK)
+                                       ?(int (*)())0
+                                       :ch_done;
+               xs->done_arg    =       unit;
+               xs->done_arg2   =       (int)xs;
+retry:         xs->error       =       XS_NOERROR;
+               xs->bp          =       0;
+               ch_data[unit].lsterr=0;
+               retval = (*(ch_data[unit].sc_sw->scsi_cmd))(xs);
+               switch(retval)
+               {
+               case    SUCCESSFULLY_QUEUED:
+                       while(!(xs->flags & ITSDONE))
+                               sleep(xs,PRIBIO+1);
+
+               case    HAD_ERROR:
+               case    COMPLETE:
+                       switch(xs->error)
+                       {
+                       case    XS_NOERROR:
+                               retval = ESUCCESS;
+                               break;
+                       case    XS_SENSE:
+                               retval = (ch_interpret_sense(unit,xs));
+                               break;
+                       case    XS_DRIVER_STUFFUP:
+                               retval = EIO;
+                               break;
+                       case    XS_TIMEOUT:
+                               if(xs->retries-- )
+                               {
+                                       xs->flags &= ~ITSDONE;
+                                       goto retry;
+                               }
+                               retval = EIO;
+                               break;
+                       case    XS_BUSY:
+                               if(xs->retries-- )
+                               {
+                                       xs->flags &= ~ITSDONE;
+                                       goto retry;
+                               }
+                               retval = EIO;
+                               break;
+                       default:
+                               retval = EIO;
+                               printf("st%d: unknown error category from scsi driver\n"
+                                       ,unit);
+                               break;
+                       }       
+                       break;
+               case    TRY_AGAIN_LATER:
+                       if(xs->retries-- )
+                       {
+                               xs->flags &= ~ITSDONE;
+                               goto retry;
+                       }
+                       retval = EIO;
+                       break;
+               default:
+                       retval = EIO;
+               }
+               xs->flags = 0;  /* it's free! */
+               chstart(unit);
+       }
+       else
+       {
+               printf("chd: not set up\n",unit);
+               return(EINVAL);
+       }
+       return(retval);
+}
+/***************************************************************\
+* Look at the returned sense and act on the error and detirmine        *
+* The unix error number to pass back... (0 = report no error)  *
+\***************************************************************/
+
+int    ch_interpret_sense(unit,xs)
+int    unit;
+struct scsi_xfer       *xs;
+{
+       struct  scsi_sense_data *sense;
+       int     key;
+       int     silent = xs->flags & SCSI_SILENT;
+
+       /***************************************************************\
+       * If errors are ok, report a success                            *
+       \***************************************************************/
+       if(xs->flags & SCSI_ERR_OK) return(ESUCCESS);
+
+       /***************************************************************\
+       * Get the sense fields and work out what CLASS                  *
+       \***************************************************************/
+       sense = &(xs->sense);
+       switch(sense->error_class)
+       {
+       /***************************************************************\
+       * If it's class 7, use the extended stuff and interpret the key *
+       \***************************************************************/
+       case 7:
+               {
+               key=sense->ext.extended.sense_key;
+               if(sense->ext.extended.ili)
+                       if(!silent)
+                       {
+                               printf("length error ");
+                       }
+                       if(sense->valid)
+                               xs->resid = ntohl(*((long *)sense->ext.extended.info));
+                               if(xs->bp)
+                               {
+                                       xs->bp->b_flags |= B_ERROR;
+                                       return(ESUCCESS);
+                               }
+               if(sense->ext.extended.eom)
+                       if(!silent) printf("end of medium ");
+               if(sense->ext.extended.filemark)
+                       if(!silent) printf("filemark ");
+               if(ch_debug)
+               {
+                       printf("code%x class%x valid%x\n"
+                                       ,sense->error_code
+                                       ,sense->error_class
+                                       ,sense->valid);
+                       printf("seg%x key%x ili%x eom%x fmark%x\n"
+                                       ,sense->ext.extended.segment
+                                       ,sense->ext.extended.sense_key
+                                       ,sense->ext.extended.ili
+                                       ,sense->ext.extended.eom
+                                       ,sense->ext.extended.filemark);
+                       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: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n"
+                                       ,sense->ext.extended.extra_bytes[0]
+                                       ,sense->ext.extended.extra_bytes[1]
+                                       ,sense->ext.extended.extra_bytes[2]
+                                       ,sense->ext.extended.extra_bytes[3]
+                                       ,sense->ext.extended.extra_bytes[4]
+                                       ,sense->ext.extended.extra_bytes[5]
+                                       ,sense->ext.extended.extra_bytes[6]
+                                       ,sense->ext.extended.extra_bytes[7]
+                                       ,sense->ext.extended.extra_bytes[8]
+                                       ,sense->ext.extended.extra_bytes[9]
+                                       ,sense->ext.extended.extra_bytes[10]
+                                       ,sense->ext.extended.extra_bytes[11]
+                                       ,sense->ext.extended.extra_bytes[12]
+                                       ,sense->ext.extended.extra_bytes[13]
+                                       ,sense->ext.extended.extra_bytes[14]
+                                       ,sense->ext.extended.extra_bytes[15]);
+                                   
+               }
+               switch(key)
+               {
+               case    0x0:
+                       return(ESUCCESS);
+               case    0x1:
+                       if(!silent)
+                       {
+                               printf("st%d: soft error(corrected) ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(ESUCCESS);
+               case    0x2:
+                       if(!silent) printf("st%d: not ready\n ", unit); 
+                       ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
+                                                               sense->ext.extended.info[13] ;
+                       return(ENODEV);
+               case    0x3:
+                       if(!silent)
+                       {
+                               printf("st%d: medium error ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(EIO);
+               case    0x4:
+                       if(!silent) printf("st%d: non-media hardware failure\n ",
+                               unit); 
+                       ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
+                                                               sense->ext.extended.info[13] ;
+                       return(EIO);
+               case    0x5:
+                       if(!silent) printf("st%d: illegal request\n ", unit); 
+                       ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
+                                                               sense->ext.extended.info[13] ;
+                       return(EINVAL);
+               case    0x6:
+                       if(!silent) printf("st%d: Unit attention.\n ", unit); 
+                       ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
+                                                               sense->ext.extended.info[13] ;
+                       ch_info_valid[unit] = FALSE;
+                       if (ch_data[unit].flags & CH_OPEN) /* TEMP!!!! */
+                               return(EIO);
+                       else
+                               return(ESUCCESS);
+               case    0x7:
+                       if(!silent)
+                       {
+                               printf("st%d: attempted protection violation "
+                                                               , unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(EACCES);
+               case    0x8:
+                       if(!silent)
+                       {
+                               printf("st%d: block wrong state (worm)\n "
+                                                       , unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(EIO);
+               case    0x9:
+                       if(!silent) printf("st%d: vendor unique\n",
+                               unit); 
+                       return(EIO);
+               case    0xa:
+                       if(!silent) printf("st%d: copy aborted\n ",
+                               unit); 
+                       return(EIO);
+               case    0xb:
+                       if(!silent) printf("st%d: command aborted\n ",
+                               unit); 
+                       ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
+                                                               sense->ext.extended.info[13] ;
+                       return(EIO);
+               case    0xc:
+                       if(!silent)
+                       {
+                               printf("st%d: search returned\n ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(ESUCCESS);
+               case    0xd:
+                       if(!silent) printf("st%d: volume overflow\n ",
+                               unit); 
+                       return(ENOSPC);
+               case    0xe:
+                       if(!silent)
+                       {
+                               printf("st%d: verify miscompare\n ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(EIO);
+               case    0xf:
+                       if(!silent) printf("st%d: unknown error key\n ",
+                               unit); 
+                       return(EIO);
+               }
+               break;
+       }
+       /***************************************************************\
+       * If it's NOT class 7, just report it.                          *
+       \***************************************************************/
+       case 0:
+       case 1:
+       case 2:
+       case 3:
+       case 4:
+       case 5:
+       case 6:
+               {
+                       if(!silent) printf("st%d: error class %d code %d\n",
+                               unit,
+                               sense->error_class,
+                               sense->error_code);
+               if(sense->valid)
+                       if(!silent) printf("block no. %d (decimal)\n",
+                       (sense->ext.unextended.blockhi <<16),
+                       + (sense->ext.unextended.blockmed <<8),
+                       + (sense->ext.unextended.blocklow ));
+               }
+               return(EIO);
+       }
+}
+
+
+
diff --git a/usr/src/sys.386bsd/scsi/scsi_all.h b/usr/src/sys.386bsd/scsi/scsi_all.h
new file mode 100644 (file)
index 0000000..430c70c
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * HISTORY
+ * $Log: scsi_all.h,v $
+ * Revision 1.2  1992/11/20  23:07:13  julian
+ * add a definition for device type T_NODEVICE
+ *
+ * Revision 1.1  1992/09/26  22:14:02  julian
+ * Initial revision
+ *
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ * 
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * SCSI command format
+ */
+
+
+struct scsi_generic
+{
+       u_char  opcode;
+       u_char  bytes[11];
+};
+
+struct scsi_test_unit_ready
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       u_char  unused[3];
+       u_char  link:1;
+       u_char  flag:4;
+       u_char  :3;
+};
+
+struct scsi_send_diag
+{
+       u_char  op_code;
+       u_char  uol:1;
+       u_char  dol:1;
+       u_char  selftest:1;
+       u_char  :1;
+       u_char  pf:1;
+       u_char  lun:3;
+       u_char  unused[1];
+       u_char  paramlen[2];
+       u_char  link:1;
+       u_char  flag:4;
+       u_char  :3;
+};
+
+struct scsi_sense
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;  
+       u_char  unused[2];
+       u_char  length;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_inquiry
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;  
+       u_char  unused[2];
+       u_char  length;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_mode_sense
+{
+       u_char  op_code;
+       u_char  :3;
+       u_char  dbd:1;
+       u_char  rsvd:1;
+       u_char  lun:3;  
+       u_char  page_code:6;
+       u_char  page_ctrl:2;
+       u_char  unused;
+       u_char  length;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_mode_sense_big
+{
+       u_char  op_code;
+       u_char  :3;
+       u_char  dbd:1;
+       u_char  rsvd:1;
+       u_char  lun:3;  
+       u_char  page_code:6;
+       u_char  page_ctrl:2;
+       u_char  unused[4];
+       u_char  length[2];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_mode_select
+{
+       u_char  op_code;
+       u_char  sp:1;
+       u_char  :3;
+       u_char  pf:1;
+       u_char  lun:3;  
+       u_char  unused[2];
+       u_char  length;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_mode_select_big
+{
+       u_char  op_code;
+       u_char  sp:1;
+       u_char  :3;
+       u_char  pf:1;
+       u_char  lun:3;  
+       u_char  unused[5];
+       u_char  length[2];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_reserve
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;  
+       u_char  unused[2];
+       u_char  length;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_release
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;  
+       u_char  unused[2];
+       u_char  length;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_prevent
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       u_char  unused[2];
+       u_char  prevent:1;
+       u_char  :7;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+#define        PR_PREVENT 1
+#define PR_ALLOW   0
+
+/*
+ * 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        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_type:5;
+       u_char  device_qualifier:3;
+       u_char  dev_qual2:7;
+       u_char  removable:1;
+       u_char  ansii_version:3;
+       u_char  :5;
+       u_char  response_format;
+       u_char  additional_length;
+       u_char  unused[2];
+       u_char  :3;
+       u_char  can_link:1;
+       u_char  can_sync:1;
+       u_char  :3;
+       char    vendor[8];
+       char    product[16];
+       char    revision[4];
+       u_char  extra[8];
+};
+
+
+struct scsi_sense_data
+{
+       u_char  error_code:4;
+       u_char  error_class:3;
+       u_char  valid:1;
+       union
+       {
+               struct
+               {
+                       u_char  blockhi:5;
+                       u_char  vendor:3;
+                       u_char  blockmed;
+                       u_char  blocklow;
+               } unextended;
+               struct
+               {
+                       u_char  segment;
+                       u_char  sense_key:4;
+                       u_char  :1;
+                       u_char  ili:1;
+                       u_char  eom:1;
+                       u_char  filemark:1;
+                       u_char  info[4];
+                       u_char  extra_len;
+                       /* allocate enough room to hold new stuff
+                       u_char  cmd_spec_info[4];
+                       u_char  add_sense_code;
+                       u_char  add_sense_code_qual;
+                       u_char  fru;
+                       u_char  sense_key_spec_1:7;
+                       u_char  sksv:1;
+                       u_char  sense_key_spec_2;
+                       u_char  sense_key_spec_3;
+                       ( by increasing 16 to 26 below) */
+                       u_char  extra_bytes[26];
+               } extended;
+       }ext;
+};
+struct scsi_sense_data_new
+{
+       u_char  error_code:7;
+       u_char  valid:1;
+       union
+       {
+               struct
+               {
+                       u_char  blockhi:5;
+                       u_char  vendor:3;
+                       u_char  blockmed;
+                       u_char  blocklow;
+               } unextended;
+               struct
+               {
+                       u_char  segment;
+                       u_char  sense_key:4;
+                       u_char  :1;
+                       u_char  ili:1;
+                       u_char  eom:1;
+                       u_char  filemark:1;
+                       u_char  info[4];
+                       u_char  extra_len;
+                       u_char  cmd_spec_info[4];
+                       u_char  add_sense_code;
+                       u_char  add_sense_code_qual;
+                       u_char  fru;
+                       u_char  sense_key_spec_1:7;
+                       u_char  sksv:1;
+                       u_char  sense_key_spec_2;
+                       u_char  sense_key_spec_3;
+                       u_char  extra_bytes[16];
+               } extended;
+       }ext;
+};
+
+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
diff --git a/usr/src/sys.386bsd/scsi/scsi_cd.h b/usr/src/sys.386bsd/scsi/scsi_cd.h
new file mode 100644 (file)
index 0000000..c35bdff
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * 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.
+ *
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ */
+
+
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+/*
+ * SCSI command format
+ */
+
+struct scsi_read_capacity_cd
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       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  link:1;
+       u_char  flag:1;
+       u_char  :6;     
+};
+
+struct scsi_pause
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       u_char  unused[6];
+       u_char  resume:1;
+       u_char  :7;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+#define        PA_PAUSE        1
+#define PA_RESUME      0
+
+struct scsi_play_msf
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       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  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_play_track
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       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  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_play
+{
+       u_char  op_code;
+       u_char  reladdr:1;
+       u_char  :4;
+       u_char  lun:3;
+       u_char  blk_addr[4];
+       u_char  unused;
+       u_char  xfer_len[2];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_play_big   
+{
+       u_char  op_code;
+       u_char  reladdr:1;
+       u_char  :4;
+       u_char  lun:3;
+       u_char  blk_addr[4];
+       u_char  xfer_len[4];
+       u_char  unused;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_play_rel_big
+{
+       u_char  op_code;
+       u_char  reladdr:1;
+       u_char  :4;
+       u_char  lun:3;
+       u_char  blk_addr[4];
+       u_char  xfer_len[4];
+       u_char  track;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_read_header
+{
+       u_char  op_code;
+       u_char  :1;
+       u_char  msf:1;
+       u_char  :3;
+       u_char  lun:3;
+       u_char  blk_addr[4];
+       u_char  unused;
+       u_char  data_len[2];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_read_subchannel
+{
+       u_char  op_code;
+       u_char  :1;
+       u_char  msf:1;
+       u_char  :3;
+       u_char  lun:3;
+       u_char  :6;
+       u_char  subQ:1;
+       u_char  :1;
+       u_char  subchan_format;
+       u_char  unused[2];
+       u_char  track;
+       u_char  data_len[2];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_read_toc
+{
+       u_char  op_code;
+       u_char  :1;
+       u_char  msf:1;
+       u_char  :3;
+       u_char  lun:3;
+       u_char  unused[4];
+       u_char  from_track;
+       u_char  data_len[2];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+;
+
+struct scsi_read_cd_capacity
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       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  link:1;
+       u_char  flag:1;
+       u_char  :6;     
+};
+
+/*
+ * 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 cd_inquiry_data /* in case there is some special info */
+{
+       u_char  device_type:5;
+       u_char  device_qualifier:3;
+       u_char  dev_qual2:7;
+       u_char  removable:1;
+       u_char  ansii_version:3;
+       u_char  :5;
+       u_char  response_format;
+       u_char  additional_length;
+       u_char  unused[2];
+       u_char  :3;
+       u_char  can_link:1;
+       u_char  can_sync:1;
+       u_char  :3;
+       char    vendor[8];
+       char    product[16];
+       char    revision[4];
+       u_char  extra[8];
+};
+
+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
+{
+#define        AUDIO_PAGE      0x0e
+       struct  audio_page
+       {
+               u_char  page_code:6;
+               u_char  :1;
+               u_char  ps:1;
+               u_char  param_len;
+               u_char  :1;
+               u_char  sotc:1;
+               u_char  immed:1;
+               u_char  :5;
+               u_char  unused[2];
+               u_char  format_lba:4;
+               u_char  :3;
+               u_char  apr_valid:1;
+               u_char  lb_per_sec[2];
+               struct  port_control
+               {
+                       u_char  channels:4;
+#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  :4;
+                       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;
+};
+
diff --git a/usr/src/sys.386bsd/scsi/scsi_changer.h b/usr/src/sys.386bsd/scsi/scsi_changer.h
new file mode 100644 (file)
index 0000000..63a5b13
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * HISTORY
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ * 
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * SCSI command format
+ */
+struct scsi_read_element_status
+{
+        u_char  op_code;
+       u_char  element_type_code:4;
+       u_char  voltag:1;
+        u_char  lun:3;
+       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  link:1;
+        u_char  flag:1;
+        u_char  :6;
+};
+#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  :5;
+       u_char  lun:3;
+       u_char  transport_element_address[2];
+       u_char  source_address[2];
+       u_char  destination_address[2];
+       u_char  rsvd[2];
+       u_char  invert:1;
+       u_char  :7;
+        u_char  link:1;
+        u_char  flag:1;
+        u_char  :6;
+};
+
+struct scsi_position_to_element
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       u_char  transport_element_address[2];
+       u_char  source_address[2];
+       u_char  rsvd[2];
+       u_char  invert:1;
+       u_char  :7;
+        u_char  link:1;
+        u_char  flag:1;
+        u_char  :6;
+};
+       
+/*
+ * 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  :5;
+       u_char avoltag:1;
+       u_char pvoltag:1;
+       u_char element_descriptor_length[2];
+       u_char rsvd;
+       u_char byte_count_of_descriptor_data[3];
+};
+
diff --git a/usr/src/sys.386bsd/scsi/scsi_disk.h b/usr/src/sys.386bsd/scsi/scsi_disk.h
new file mode 100644 (file)
index 0000000..35a3b78
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * HISTORY
+ * $Log: scsi_disk.h,v $
+ * Revision 1.2  1992/10/13  03:14:21  julian
+ * added the load-eject field in 'start/stop' for removable devices.
+ *
+ * Revision 1.1  1992/09/26  22:11:29  julian
+ * Initial revision
+ *
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ * 
+ */
+
+/*
+ * SCSI interface description
+ */
+
+/*
+ * Some lines of this file comes from a file of the name "scsi.h"
+ * distributed by OSF as part of mach2.5,
+ *  so the following disclaimer has been kept.
+ *
+ * Copyright 1990 by Open Software Foundation,
+ * Grenoble, FRANCE
+ *
+ *             All Rights Reserved
+ * 
+ *   Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OSF or Open Software
+ * Foundation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission.
+ * 
+ *   OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+ * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Largely written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with 
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+/*
+ * SCSI command format
+ */
+
+
+struct scsi_reassign_blocks
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;  
+       u_char  unused[3];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_rw
+{
+       u_char  op_code;
+       u_char  addr_2:5;       /* Most significant */
+       u_char  lun:3;
+       u_char  addr_1;
+       u_char  addr_0;         /* least significant */
+       u_char  length;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+struct scsi_rw_big
+{
+       u_char  op_code;
+       u_char  rel_addr:1;
+       u_char  :4;     /* Most significant */
+       u_char  lun:3;
+       u_char  addr_3;
+       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  link:1;
+       u_char  flag:1;
+       u_char  :4;
+       u_char  vendor:2;
+};
+
+struct scsi_read_capacity
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       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  link:1;
+       u_char  flag:1;
+       u_char  :6;     
+};
+
+struct scsi_start_stop
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       u_char  unused[2];
+       u_char  start:1;
+       u_char  loej:1;
+       u_char  :6;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+};
+
+
+
+/*
+ * 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:6;    /* page code (should be 3)            */
+          u_char :2;           
+          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 reserved1:4;
+          u_char surf:1;
+          u_char rmb:1;
+          u_char hsec:1;
+          u_char ssec:1;
+          u_char reserved2;
+          u_char reserved3;
+       } disk_format;
+       struct page_rigid_geometry {
+          u_char pg_code:7;    /* page code (should be 4)            */
+          u_char mbone:1;      /* must be one                        */
+          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;
+} ;
diff --git a/usr/src/sys.386bsd/scsi/scsi_tape.h b/usr/src/sys.386bsd/scsi/scsi_tape.h
new file mode 100644 (file)
index 0000000..2b8a82f
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * HISTORY
+ * $Log: scsi_tape.h,v $
+ * Revision 1.2  1993/01/26  18:39:08  julian
+ * add the 'write protected' bit in the device status struct.
+ *
+ * Revision 1.1  1992/09/26  22:10:21  julian
+ * Initial revision
+ *
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ * 
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * SCSI command format
+ */
+
+
+struct scsi_rw_tape
+{
+       u_char  op_code;
+       u_char  fixed:1;
+       u_char  :4;     
+       u_char  lun:3;
+       u_char  len[3];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+} rw_tape;
+
+struct scsi_space
+{
+       u_char  op_code;
+       u_char  code:2;
+       u_char  :3;
+       u_char  lun:3;
+       u_char  number[3];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+} 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  :5;
+       u_char  lun:3;
+       u_char  number[3];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+} write_filemarks;
+
+struct scsi_rewind
+{
+       u_char  op_code;
+       u_char  immed:1;
+       u_char  :4;
+       u_char  lun:3;
+       u_char  unused[3];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+} rewind;
+
+struct scsi_load
+{
+       u_char  op_code;
+       u_char  immed:1;
+       u_char  :4;
+       u_char  lun:3;
+       u_char  unused[2];
+       u_char  load:1;
+       u_char  reten:1;
+       u_char  :6;
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+} load;
+#define LD_UNLOAD 0
+#define LD_LOAD 1
+
+struct scsi_blk_limits
+{
+       u_char  op_code;
+       u_char  :5;
+       u_char  lun:3;
+       u_char  unused[3];
+       u_char  link:1;
+       u_char  flag:1;
+       u_char  :6;
+} 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 */
+};
+
+struct scsi_mode_header_tape
+{
+       u_char  data_length;    /* Sense data length */
+       u_char  medium_type;
+       u_char  speed:4;
+       u_char  buf_mode:3;
+       u_char  write_protected:1;
+       u_char  blk_desc_len;
+};
+
+
+#define QIC_120     0x0f
+#define QIC_150     0x10
+#define QIC_320     0x11
+#define QIC_525     0x11
+#define QIC_1320     0x12
+
+
diff --git a/usr/src/sys.386bsd/scsi/scsiconf.c b/usr/src/sys.386bsd/scsi/scsiconf.c
new file mode 100644 (file)
index 0000000..509e69c
--- /dev/null
@@ -0,0 +1,758 @@
+/*
+ * 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.
+ *
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+/*
+$Log:
+*
+*/
+#include <sys/types.h>
+#include "st.h"
+#include "sd.h"
+#include "ch.h"
+#include "cd.h"
+
+#ifdef MACH
+#include <i386/machparam.h>
+#endif MACH
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#if !defined(OSF) && !defined(__386BSD__)
+#include "bll.h"
+#include "cals.h"
+#include "kil.h"
+#else
+#define        NBLL 0
+#define        NCALS 0
+#define        NKIL 0
+#endif /* !defined(OSF) && !defined(__386BSD__) */
+
+#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
+
+/***************************************************************\
+* 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;
+       int     (*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         *
+\***************************************************************/
+static struct scsidevs 
+{
+       int type;
+       int removable;
+       char    *manufacturer;
+       char    *model;
+       char    *version;
+       int     (*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
+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
+       { T_READONLY,T_REMOV,"SONY    ","CD-ROM CDU-8012 "
+                       ,"3.1a",cdattach,"cd",SC_ONE_LU },
+#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();
+
+/* controls debug level within the scsi subsystem */
+/* see scsiconf.h for values                     */
+int    scsi_debug      =       0x0;
+int    scsibus         =       0x0; /* This is the Nth scsibus */
+
+/***************************************************************\
+* The routine called by the adapter boards to get all their    *
+* devices configured in.                                       *
+\***************************************************************/
+scsi_attachdevs( unit, scsi_addr, scsi_switch)
+int    unit,scsi_addr;
+struct scsi_switch     *scsi_switch;
+{
+       int     targ,lun;
+       struct  scsidevs        *bestmatch = (struct scsidevs *)0;
+       struct  predefined *predef;
+       int maybe_more;
+
+#ifdef SCSI_DELAY
+#if    SCSI_DELAY > 2
+       printf("waiting for scsi devices to settle\n");
+#else  SCSI_DELAY > 2
+#define        SCSI_DELAY 15
+#endif SCSI_DELAY > 2
+#else
+#define SCSI_DELAY 2
+#endif SCSI_DELAY
+       spinwait(1000 * SCSI_DELAY);
+       targ = 0;
+       while(targ < 8)
+       {
+               maybe_more = 0; /* by default only check 1 lun */
+               if (targ == scsi_addr) 
+               {
+                       targ++;
+                       continue;
+               }
+               lun = 0;
+               while(lun < 8)
+               {
+                       predef = scsi_get_predef(scsibus
+                                               ,targ
+                                               ,lun
+                                               ,&maybe_more);
+                       bestmatch = scsi_probedev(unit
+                                               ,targ
+                                               ,lun 
+                                               ,scsi_switch
+                                               ,&maybe_more);
+                       if((bestmatch) && (predef)) /* both exist */
+                       {
+                               if(bestmatch->attach_rtn 
+                                   != predef->attach_rtn)
+                               {
+                                   printf("Clash in found/expected devices\n");
+                                   printf("will link in FOUND\n");
+                               }
+                               (*(bestmatch->attach_rtn))(unit,
+                                               targ,
+                                               lun,
+                                               scsi_switch);
+                       }
+                       if((bestmatch) && (!predef)) /* just FOUND */
+                       {
+                               (*(bestmatch->attach_rtn))(unit,
+                                               targ,
+                                               lun,
+                                               scsi_switch);
+                       }
+                       if((!bestmatch) && (predef)) /* just predef */
+                       {
+                               (*(predef->attach_rtn))(unit,
+                                               targ,
+                                               lun,
+                                               scsi_switch);
+                       }
+                       if(!(maybe_more)) /* nothing suggests we'll find more */
+                       {
+                               break;  /* nothing here, skip to next targ */
+                       }
+                       /* otherwise something says we should look further*/
+                       lun++;
+               }
+               targ++;
+       }
+#if NGENSCSI > 0
+       /***************************************************************\
+       * If available hook up the generic scsi driver, letting it      *
+       * know which target is US. (i.e. illegal or at least special)   *
+       \***************************************************************/
+       genscsi_attach(unit,scsi_addr,0,scsi_switch);
+#endif
+       scsibus++;      /* next time we are on the NEXT scsi bus */
+}
+
+/***********************************************\
+* given a target and lu, check if there is a   *
+* predefined device for that address           *
+\***********************************************/
+struct predefined      *scsi_get_predef(unit,target,lu,maybe_more)
+int    unit,target,lu,*maybe_more;
+{
+       int 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("  dev%d,lu%d: %s - PRECONFIGURED -\n"
+                       ,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(unit,target,lu,scsi_switch, maybe_more)
+
+struct scsi_switch *scsi_switch;
+int    unit,target,lu;
+int *maybe_more;
+{
+       struct  scsidevs        *bestmatch = (struct scsidevs *)0;
+       char    *dtype=(char *)0,*desc;
+       char    *qtype;
+       static  struct scsi_inquiry_data        inqbuf; 
+       int     len,qualifier,type,remov;
+       char    manu[32];
+       char    model[32];
+       char    version[32];
+
+
+       bzero(&inqbuf,sizeof(inqbuf));
+       /***********************************************\
+       * Ask the device what it is                     *
+       \***********************************************/
+#ifdef DEBUG
+       if((target == 0) && (lu == 0))
+               scsi_debug = 0xfff;
+       else
+               scsi_debug = 0;
+#endif DEBUG
+       if(scsi_ready(  unit,
+                       target,
+                       lu,
+                       scsi_switch,
+                       SCSI_NOSLEEP | SCSI_NOMASK) != COMPLETE)
+       {
+               return(struct scsidevs *)0;
+       }
+       if(scsi_inquire(unit,
+                       target,
+                       lu,
+                       scsi_switch,
+                       &inqbuf,
+                       SCSI_NOSLEEP | SCSI_NOMASK) != COMPLETE)
+       {
+               return(struct scsidevs *)0;
+       }
+
+       /***********************************************\
+       * note what BASIC type of device it is          *
+       \***********************************************/
+       if(scsi_debug & SHOWINQUIRY)
+       {
+               desc=(char *)&inqbuf;
+               printf("inq: %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
+               desc[0], desc[1], desc[2], desc[3],
+               desc[4], desc[5], desc[6], desc[7],
+               desc[8], desc[9], desc[10], desc[11],
+               desc[12]);
+       }
+
+       type = inqbuf.device_type;
+       qualifier = inqbuf.device_qualifier;
+       remov = inqbuf.removable;
+
+       /* Check for a non-existent unit.  If the device is returning
+        * this much, then we must set the flag that has
+        * the searcher keep looking on other luns.
+        */
+       if (qualifier == 3 && type == T_NODEVICE)
+       {
+               *maybe_more = 1;
+               return (struct scsidevs *)0;
+       }
+
+       /* Any device qualifier that has
+        * the top bit set (qualifier&4 != 0) is vendor specific and
+        * won't match in this switch.
+        */
+
+       switch(qualifier)
+       {
+               case 0:
+               qtype="";
+               break;
+               case 1:
+               qtype=", Unit not Connected!";
+               break;
+               case 2:
+               qtype=", Reserved Peripheral Qualifier!";
+               break;
+               case 3:
+               qtype=", The Target can't support this Unit!";
+               break;
+
+               default:
+               dtype="vendor specific";
+               qtype="";
+               *maybe_more = 1;
+               break;
+       }
+
+       if (dtype == 0)
+               switch(type)
+               {
+                       case T_DIRECT:
+                               dtype="direct";
+                               break;
+                       case T_SEQUENTIAL:
+                               dtype="sequential";
+                               break;
+                       case T_PRINTER:
+                               dtype="printer";
+                               break;
+                       case T_PROCESSOR:
+                               dtype="processor";
+                               break;
+                       case T_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;
+                       default:
+                               dtype="unknown";
+                               break;
+               }
+
+       /***********************************************\
+       * Then if it's advanced enough, more detailed   *
+       * information                                   *
+       \***********************************************/
+       if(inqbuf.ansii_version > 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("  dev%d,lu%d: type %d:%d(%s%s),%s '%s%s%s' scsi%d\n"
+               ,target
+               ,lu
+               ,qualifier,type
+               ,dtype,qtype
+               ,remov?"removable":"fixed"
+               ,manu
+               ,model
+               ,version
+               ,inqbuf.ansii_version
+       );
+       /***********************************************\
+       * Try make as good a match as possible with     *
+       * available sub drivers                         *
+       \***********************************************/
+       bestmatch = (selectdev(unit,target,lu,&scsi_switch,
+               qualifier,type,remov,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(unit,target,lu,dvr_switch,qualifier,type,remov,manu,model,rev)
+int    unit,target,lu;
+struct scsi_switch *dvr_switch;
+int    qualifier,type,remov;
+char   *manu,*model,*rev;
+{
+       int     numents = (sizeof(knowndevs)/sizeof(struct scsidevs)) - 1;
+       int     count = 0;
+       int                     bestmatches = 0;
+       struct  scsidevs        *bestmatch = (struct scsidevs *)0;
+       struct  scsidevs        *thisentry = knowndevs;
+
+       type |= (qualifier << 5);
+
+       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)
+               printf("        No explicit device driver match for \"%s %s\".\n",
+               manu, model);
+
+       return(bestmatch);
+}
+
+static int recurse = 0;
+/***********************************************\
+* Do a scsi operation asking a device if it is *
+* ready. Use the scsi_cmd routine in the switch *
+* table.                                       *
+\***********************************************/
+scsi_ready(unit,target,lu,scsi_switch, flags)
+struct scsi_switch *scsi_switch;
+{
+       struct  scsi_test_unit_ready scsi_cmd;
+       struct  scsi_xfer scsi_xfer;
+       volatile int rval;
+       int     key;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       bzero(&scsi_xfer, sizeof(scsi_xfer));
+       scsi_cmd.op_code = TEST_UNIT_READY;
+
+       scsi_xfer.flags=flags | INUSE;
+       scsi_xfer.adapter=unit;
+       scsi_xfer.targ=target;
+       scsi_xfer.lu=lu;
+       scsi_xfer.cmd=(struct scsi_generic *)&scsi_cmd;
+       scsi_xfer.retries=8;
+       scsi_xfer.timeout=10000;
+       scsi_xfer.cmdlen=sizeof(scsi_cmd);
+       scsi_xfer.data=0;
+       scsi_xfer.datalen=0;
+       scsi_xfer.resid=0;
+       scsi_xfer.when_done=0;
+       scsi_xfer.done_arg=0;
+retry: scsi_xfer.error=0;
+       /*******************************************************\
+       * do not use interrupts                                 *
+       \*******************************************************/
+       rval = (*(scsi_switch->scsi_cmd))(&scsi_xfer);
+       if (rval != COMPLETE)
+       {
+               if(scsi_debug)
+               {
+                       printf("scsi error, rval = 0x%x\n",rval);
+                       printf("code from driver: 0x%x\n",scsi_xfer.error);
+               }
+               switch(scsi_xfer.error)
+               {
+               case    XS_SENSE:
+               /*******************************************************\
+               * Any sense value is illegal except UNIT ATTENTION      *
+               * In which case we need to check again to get the       *
+               * correct response.                                     *
+               *( especially exabytes)                                 *
+               \*******************************************************/
+                       if(scsi_xfer.sense.error_class == 7 )
+                       {
+                               key = scsi_xfer.sense.ext.extended.sense_key ;
+                               switch(key)
+                               { 
+                               case    2:      /* not ready BUT PRESENT! */
+                                               return(COMPLETE);
+                               case    6:
+                                       spinwait(1000);
+                                       if(scsi_xfer.retries--)
+                                       {
+                                               scsi_xfer.flags &= ~ITSDONE;
+                                               goto retry;
+                                       }
+                                       return(COMPLETE);
+                               default:
+                                       if(scsi_debug)
+                                               printf("%d:%d,key=%x.",
+                                               target,lu,key);
+                               }
+                       }
+                       return(HAD_ERROR);
+               case    XS_BUSY:
+                       spinwait(1000);
+                       if(scsi_xfer.retries--)
+                       {
+                               scsi_xfer.flags &= ~ITSDONE;
+                               goto retry;
+                       }
+                       return(COMPLETE);       /* it's busy so it's there */
+               case    XS_TIMEOUT:
+               default:
+                       return(HAD_ERROR);
+               }
+       }
+       return(COMPLETE);
+}
+/***********************************************\
+* Do a scsi operation asking a device what it is*
+* Use the scsi_cmd routine in the switch table.        *
+\***********************************************/
+scsi_inquire(unit,target,lu,scsi_switch,inqbuf, flags)
+struct scsi_switch *scsi_switch;
+u_char *inqbuf;
+{
+       struct  scsi_inquiry scsi_cmd;
+       struct  scsi_xfer scsi_xfer;
+       volatile int rval;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       bzero(&scsi_xfer, sizeof(scsi_xfer));
+       scsi_cmd.op_code = INQUIRY;
+       scsi_cmd.length = sizeof(struct scsi_inquiry_data);
+
+       scsi_xfer.flags=flags | SCSI_DATA_IN | INUSE;
+       scsi_xfer.adapter=unit;
+       scsi_xfer.targ=target;
+       scsi_xfer.lu=lu;
+       scsi_xfer.retries=8;
+       scsi_xfer.timeout=10000;
+       scsi_xfer.cmd=(struct scsi_generic *)&scsi_cmd;
+       scsi_xfer.cmdlen= sizeof(struct scsi_inquiry);
+       scsi_xfer.data=inqbuf;
+       scsi_xfer.datalen=sizeof(struct scsi_inquiry_data);
+       scsi_xfer.resid=sizeof(struct scsi_inquiry_data);
+       scsi_xfer.when_done=0;
+       scsi_xfer.done_arg=0;
+retry: scsi_xfer.error=0;
+       /*******************************************************\
+       * do not use interrupts                                 *
+       \*******************************************************/
+       if ((*(scsi_switch->scsi_cmd))(&scsi_xfer) != COMPLETE)
+       {
+               if(scsi_debug) printf("inquiry had error(0x%x) ",scsi_xfer.error);
+               switch(scsi_xfer.error)
+               {
+               case    XS_NOERROR:
+                       break;
+               case    XS_SENSE:
+               /*******************************************************\
+               * Any sense value is illegal except UNIT ATTENTION      *
+               * In which case we need to check again to get the       *
+               * correct response.                                     *
+               *( especially exabytes)                                 *
+               \*******************************************************/
+                       if((scsi_xfer.sense.error_class == 7 )
+                        && (scsi_xfer.sense.ext.extended.sense_key == 6))
+                       { /* it's changed so it's there */
+                               spinwait(1000);
+                               {
+                                       if(scsi_xfer.retries--)
+                                       {
+                                               scsi_xfer.flags &= ~ITSDONE;
+                                               goto retry;
+                                       }
+                               }
+                               return( COMPLETE);
+                       }
+                       return(HAD_ERROR);
+               case    XS_BUSY:
+                       spinwait(1000);
+                       if(scsi_xfer.retries--)
+                       {
+                               scsi_xfer.flags &= ~ITSDONE;
+                               goto retry;
+                       }
+               case    XS_TIMEOUT:
+               default:
+                       return(HAD_ERROR);
+               }
+       }
+       return(COMPLETE);
+}
+
+
+
+
+/***********************************************\
+* Utility routines often used in SCSI stuff    *
+\***********************************************/
+
+/***********************************************\
+* convert a physical address to 3 bytes,       *
+* MSB at the lowest address,                   *
+* LSB at the highest.                          *
+\***********************************************/
+
+lto3b(val, bytes)
+u_char *bytes;
+{
+       *bytes++ = (val&0xff0000)>>16;
+       *bytes++ = (val&0xff00)>>8;
+       *bytes = val&0xff;
+}
+
+/***********************************************\
+* The reverse of lto3b                         *
+\***********************************************/
+_3btol(bytes)
+u_char *bytes;
+{
+       int rc;
+       rc = (*bytes++ << 16);
+       rc += (*bytes++ << 8);
+       rc += *bytes;
+       return(rc);
+}
+
diff --git a/usr/src/sys.386bsd/scsi/scsiconf.h b/usr/src/sys.386bsd/scsi/scsiconf.h
new file mode 100644 (file)
index 0000000..c36efde
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ *
+ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+/***********************************************\
+* these calls are called by the high-end       *
+* drivers to get services from whatever low-end        *
+* drivers they are attached to                 *
+\***********************************************/
+struct scsi_switch
+{
+       int             (*scsi_cmd)();
+       void            (*scsi_minphys)();
+       int             (*open_target_lu)();
+       int             (*close_target_lu)();
+       long    int     (*adapter_info)(); /* see definitions below */
+       u_long  spare[3];
+};
+#define        AD_INF_MAX_CMDS         0x000000FF /* maximum number of entries
+                                               queuable to a device by 
+                                               the adapter */
+/* 24 bits of other adapter charcteristics go here */
+
+/***********************************************\
+* The scsi debug control bits                  *
+\***********************************************/
+extern int     scsi_debug;
+#define PRINTROUTINES  0x01
+#define        TRACEOPENS      0x02
+#define        TRACEINTERRUPTS 0x04
+#define        SHOWREQUESTS    0x08
+#define        SHOWSCATGATH    0x10
+#define        SHOWINQUIRY     0x20
+#define        SHOWCOMMANDS    0x40
+
+
+/********************************/
+/* return values for scsi_cmd() */
+/********************************/
+#define SUCCESSFULLY_QUEUED    0
+#define TRY_AGAIN_LATER                1
+#define        COMPLETE                2
+#define        HAD_ERROR               3
+
+struct scsi_xfer
+{
+       struct  scsi_xfer *next;        /* when free */
+       int     flags;
+       u_char  adapter;
+       u_char  targ;
+       u_char  lu;
+       u_char  retries;        /* the number of times to retry */
+       long    int     timeout;        /* in miliseconds */
+       struct  scsi_generic *cmd;
+       int     cmdlen;
+       u_char  *data;          /* either the dma address OR a uio address */
+       int     datalen;        /* data len (blank if uio)    */
+       int     resid;
+       int     (*when_done)();
+       int     done_arg;
+       int     done_arg2;
+       int     error;
+       struct  buf *bp;
+       struct  scsi_sense_data sense;
+};
+/********************************/
+/* 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        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.       */
+/********************************/
+/* Error values                        */
+/********************************/
+#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?      */
+
diff --git a/usr/src/sys.386bsd/scsi/sd.c b/usr/src/sys.386bsd/scsi/sd.c
new file mode 100644 (file)
index 0000000..f838400
--- /dev/null
@@ -0,0 +1,1463 @@
+/*
+ * 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.
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ *
+ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+#define SPLSD splbio
+#define ESUCCESS 0
+#include <sd.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/errno.h>
+#include <sys/disklabel.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+
+long int sdstrats,sdqueues;
+
+
+#include <ddb.h>
+#if    NDDB > 0
+int    Debugger();
+#else  NDDB > 0
+#define Debugger()
+#endif NDDB > 0
+
+
+#define PAGESIZ        4096
+#define SECSIZE 512
+#define PDLOCATION     29      
+#define BOOTRECORDSIGNATURE                    (0x55aa & 0x00ff)
+#define        SDOUTSTANDING   2
+#define SDQSIZE                4
+#define        SD_RETRIES      4
+
+#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 )
+
+struct buf sd_buf_queue[NSD];
+int    sd_done();
+int    sdstrategy();
+
+int    sd_debug = 0;
+
+struct scsi_xfer       *sd_free_xfer[NSD];
+int                    sd_xfer_block_wait[NSD];
+
+struct sd_data
+{
+       int     flags;
+#define        SDVALID         0x02            /* PARAMS LOADED        */
+#define        SDINIT          0x04            /* device has been init'd */
+#define        SDWAIT          0x08            /* device has someone waiting */
+#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_switch *sc_sw;     /* address of scsi low level switch */
+       int     ctlr;                   /* so they know which one we want */
+       int     targ;                   /* our scsi target ID */
+       int     lu;                     /* out scsi lu */
+       long int        ad_info;        /* info about the adapter */
+       int     cmdscount;              /* cmds allowed outstanding by board*/
+       int     wlabel;                 /* label is writable */
+       struct  disk_parms
+       {
+               u_char  heads;          /* Number of heads */
+               u_short cyls;           /* Number of cylinders */
+               u_char  sectors;/*dubious*/     /* Number of sectors/track */
+               u_short secsiz;         /* Number of bytes/sector */
+               u_long  disksize;               /* total number sectors */
+       }params;
+       struct  disklabel       disklabel;
+       struct  dos_partition dosparts[NDOSPART]; /* DOS view of disk */
+       int     partflags[MAXPARTITIONS];       /* per partition flags */
+#define SDOPEN 0x01
+       int             openparts;              /* one bit for each open partition */
+       unsigned int    sd_start_of_unix;       /* unix vs dos partitions */
+}sd_data[NSD];
+
+
+static int     next_sd_unit = 0;
+/***********************************************************************\
+* The routine called by the low level scsi routine when it discovers   *
+* A device suitable for this driver                                    *
+\***********************************************************************/
+
+int    sdattach(ctlr,targ,lu,scsi_switch)
+struct scsi_switch *scsi_switch;
+{
+       int     unit,i;
+       unsigned char *tbl;
+       struct sd_data *sd;
+       struct disk_parms *dp;
+       long int        ad_info;
+       struct  scsi_xfer       *sd_scsi_xfer;
+
+       unit = next_sd_unit++;
+       sd = sd_data + unit;
+       dp  = &(sd->params);
+       if(scsi_debug & PRINTROUTINES) printf("sdattach: "); 
+       /*******************************************************\
+       * Check we have the resources for another drive         *
+       \*******************************************************/
+       if( unit >= NSD)
+       {
+               printf("Too many scsi disks..(%d > %d) reconfigure kernel",(unit + 1),NSD);
+               return(0);
+       }
+       /*******************************************************\
+       * Store information needed to contact our base driver   *
+       \*******************************************************/
+       sd->sc_sw       =       scsi_switch;
+       sd->ctlr        =       ctlr;
+       sd->targ        =       targ;
+       sd->lu          =       lu;
+       if(sd->sc_sw->adapter_info)
+       {
+               sd->ad_info = ( (*(sd->sc_sw->adapter_info))(ctlr));
+               sd->cmdscount = sd->ad_info & AD_INF_MAX_CMDS;
+               if(sd->cmdscount > SDOUTSTANDING)
+               {
+                       sd->cmdscount = SDOUTSTANDING;
+               }
+       }
+       else
+       {
+               sd->ad_info = 1;
+               sd->cmdscount = 1;
+       }
+
+       i = sd->cmdscount;
+       sd_scsi_xfer = (struct scsi_xfer *)malloc(sizeof(struct scsi_xfer) * i
+                               ,M_TEMP, M_NOWAIT);
+       while(i-- )
+       {
+               sd_scsi_xfer->next = sd_free_xfer[unit];
+               sd_free_xfer[unit] = sd_scsi_xfer;
+               sd_scsi_xfer++;
+       }
+       /*******************************************************\
+       * 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, cyls %d, heads %d, secs %d, bytes/sec %d\n",
+               unit,
+               (         dp->cyls 
+                       * dp->heads 
+                       * dp->sectors
+                       * dp->secsiz
+               )
+               / (1024 * 1024),
+               dp->cyls,
+               dp->heads,
+               dp->sectors,
+               dp->secsiz);
+       /*******************************************************\
+       * Set up the bufs for this device                       *
+       \*******************************************************/
+       sd->flags |= SDINIT;
+       return;
+
+}
+
+
+
+/*******************************************************\
+*      open the device. Make sure the partition info   *
+* is a up-to-date as can be.                           *
+\*******************************************************/
+sdopen(dev)
+{
+       int errcode = 0;
+       int unit, part;
+       struct disk_parms disk_parms;
+       struct sd_data *sd ;
+
+       unit = UNIT(dev);
+       part = PARTITION(dev);
+       sd = sd_data + unit;
+       if(scsi_debug & (PRINTROUTINES | TRACEOPENS))
+               printf("sdopen: dev=0x%x (unit %d (of %d),partition %d)\n"
+                               ,   dev,      unit,   NSD,         part);
+       /*******************************************************\
+       * 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->flags & SDINIT))
+       {
+               return(ENXIO);
+       }
+
+       /*******************************************************\
+       * If it's been invalidated, and not everybody has       *
+       * closed it then forbid re-entry.                       *
+       \*******************************************************/
+       if ((! (sd->flags & SDVALID))
+          && ( sd->openparts))
+               return(ENXIO);
+       /*******************************************************\
+       * Check that it is still responding and ok.             *
+       * "unit attention errors should occur here if the drive *
+       * has been restarted or the pack changed                *
+       \*******************************************************/
+
+       if(scsi_debug & TRACEOPENS)
+               printf("device is ");
+       if (sd_test_unit_ready(unit,0))
+       {
+               if(scsi_debug & TRACEOPENS) printf("not reponding\n");
+               return(ENXIO);
+       }
+       if(scsi_debug & TRACEOPENS)
+               printf("ok\n");
+       /*******************************************************\
+       * In case it is a funny one, tell it to start           *
+       * not needed for  most hard drives (ignore failure)     *
+       \*******************************************************/
+       sd_start_unit(unit,SCSI_ERR_OK|SCSI_SILENT);
+       if(scsi_debug & TRACEOPENS)
+               printf("started ");
+       /*******************************************************\
+       * Load the physical device parameters                   *
+       \*******************************************************/
+       sd_get_parms(unit, 0);  /* sets SDVALID */
+       if (sd->params.secsiz != SECSIZE)
+       {
+               printf("sd%d: Can't deal with %d bytes logical blocks\n"
+                       ,unit, sd->params.secsiz);
+               Debugger();
+               return(ENXIO);
+       }
+       if(scsi_debug & TRACEOPENS)
+               printf("Params loaded ");
+       /*******************************************************\
+       * Load the partition info if not already loaded         *
+       \*******************************************************/
+       sd_prevent(unit,PR_PREVENT,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */
+       if((errcode = sdgetdisklabel(unit)) && (part != RAW_PART))
+       {
+               sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */
+               return(errcode);
+       }
+       if(scsi_debug & TRACEOPENS)
+               printf("Disklabel loaded ");
+       /*******************************************************\
+       * Check the partition is legal                          *
+       \*******************************************************/
+       if ( part >= MAXPARTITIONS ) {
+               sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */
+               return(ENXIO);
+       }
+       if(scsi_debug & TRACEOPENS)
+               printf("ok");
+       /*******************************************************\
+       *  Check that the partition exists                      *
+       \*******************************************************/
+       if (( sd->disklabel.d_partitions[part].p_size == 0 )
+               && (part != RAW_PART))
+       {
+               sd_prevent(unit,PR_ALLOW,SCSI_ERR_OK|SCSI_SILENT); /* who cares if it fails? */
+               return(ENXIO);
+       }
+       sd->partflags[part] |= SDOPEN;
+       sd->openparts |= (1 << part);
+       if(scsi_debug & TRACEOPENS)
+               printf("open %d %d\n",sdstrats,sdqueues);
+       return(0);
+}
+
+/*******************************************************\
+* Get ownership of a scsi_xfer                         *
+* If need be, sleep on it, until it comes free         *
+\*******************************************************/
+struct scsi_xfer *sd_get_xs(unit,flags)
+int    flags;
+int    unit;
+{
+       struct scsi_xfer *xs;
+       int     s;
+
+       if(flags & (SCSI_NOSLEEP |  SCSI_NOMASK))
+       {
+               if (xs = sd_free_xfer[unit])
+               {
+                       sd_free_xfer[unit] = xs->next;
+                       xs->flags = 0;
+               }
+       }
+       else
+       {
+               s = SPLSD();
+               while (!(xs = sd_free_xfer[unit]))
+               {
+                       sd_xfer_block_wait[unit]++;  /* someone waiting! */
+                       sleep((caddr_t)&sd_free_xfer[unit], PRIBIO+1);
+                       sd_xfer_block_wait[unit]--;
+               }
+               sd_free_xfer[unit] = xs->next;
+               splx(s);
+               xs->flags = 0;
+       }
+       return(xs);
+}
+
+/*******************************************************\
+* Free a scsi_xfer, wake processes waiting for it      *
+\*******************************************************/
+sd_free_xs(unit,xs,flags)
+struct scsi_xfer *xs;
+int    unit;
+int    flags;
+{
+       int     s;
+       
+       if(flags & SCSI_NOMASK)
+       {
+               if (sd_xfer_block_wait[unit])
+               {
+                       printf("doing a wakeup from NOMASK mode\n");
+                       wakeup((caddr_t)&sd_free_xfer[unit]);
+               }
+               xs->next = sd_free_xfer[unit];
+               sd_free_xfer[unit] = xs;
+       }
+       else
+       {
+               s = SPLSD();
+               if (sd_xfer_block_wait[unit])
+                       wakeup((caddr_t)&sd_free_xfer[unit]);
+               xs->next = sd_free_xfer[unit];
+               sd_free_xfer[unit] = xs;
+               splx(s);
+       }
+}
+
+/*******************************************************\
+* 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_sw->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.                          *
+\*******************************************************/
+
+int    sdstrategy(bp)
+struct buf     *bp;
+{
+       struct  buf     *dp;
+       unsigned int opri;
+       struct sd_data *sd ;
+       int     unit;
+
+       sdstrats++;
+       unit = UNIT((bp->b_dev));
+       sd = sd_data + unit;
+       if(scsi_debug & PRINTROUTINES) printf("\nsdstrategy ");
+       if(scsi_debug & SHOWREQUESTS) printf("sd%d: %d bytes @ blk%d\n",
+                                       unit,bp->b_bcount,bp->b_blkno);
+       sdminphys(bp);
+       /*******************************************************\
+       * If the device has been made invalid, error out        *
+       \*******************************************************/
+       if(!(sd->flags & SDVALID))
+       {
+               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 */
+       }
+
+       opri = SPLSD();
+       dp = &sd_buf_queue[unit];
+
+       /*******************************************************\
+       * 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*
+       \*******************************************************/
+       sdstart(unit);
+
+       splx(opri);
+       return;
+bad:
+       bp->b_flags |= B_ERROR;
+done:
+
+       /*******************************************************\
+       * Correctly set the buf to indicate a completed xfer    *
+       \*******************************************************/
+       bp->b_resid = bp->b_bcount;
+       biodone(bp);
+       return;
+}
+
+/***************************************************************\
+* 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 deques the buf and creates a scsi command to perform the  *
+* transfer in the buf. The transfer request will call sd_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 sd_done*/
+sdstart(unit)
+int    unit;
+{
+       int                     drivecount;
+       register struct buf     *bp = 0;
+       register struct buf     *dp;
+       struct  scsi_xfer       *xs;
+       struct  scsi_rw_big     cmd;
+       int                     blkno, nblk;
+       struct sd_data *sd = sd_data + unit;
+       struct partition *p ;
+
+       if(scsi_debug & PRINTROUTINES) printf("sdstart%d ",unit);
+       /*******************************************************\
+       * See if there is a buf to do and we are not already    *
+       * doing one                                             *
+       \*******************************************************/
+       if(!sd_free_xfer[unit])
+       {
+               return;    /* none for us, unit already underway */
+       }
+
+       if(sd_xfer_block_wait[unit])    /* there is one, but a special waits */
+       {
+               return; /* give the special that's waiting a chance to run */
+       }
+
+
+       dp = &sd_buf_queue[unit];
+       if ((bp = dp->b_actf) != NULL)  /* yes, an assign */
+       {
+               dp->b_actf = bp->av_forw;
+       }
+       else
+       { 
+               return;
+       }
+
+       xs=sd_get_xs(unit,0);   /* ok we can grab it */
+       xs->flags = INUSE;    /* Now ours */
+       /*******************************************************\
+       *  If the device has become invalid, abort all the      *
+       * reads and writes until all files have been closed and *
+       * re-openned                                            *
+       \*******************************************************/
+       if(!(sd->flags & SDVALID))
+       {
+               xs->error = XS_DRIVER_STUFFUP;
+               sd_done(unit,xs);  /* clean up (calls sdstart) */
+               return ;
+       }
+       /*******************************************************\
+       * We have a buf, now we should move the data into       *
+       * a scsi_xfer definition and try start it               *
+       \*******************************************************/
+       /*******************************************************\
+       *  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 & 0xff000000) >> 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);
+       /*******************************************************\
+       * Fill out the scsi_xfer structure                      *
+       *       Note: we cannot sleep as we may be an interrupt *
+       \*******************************************************/
+       xs->flags       |=      SCSI_NOSLEEP;
+       xs->adapter     =       sd->ctlr;
+       xs->targ        =       sd->targ;
+       xs->lu          =       sd->lu;
+       xs->retries     =       SD_RETRIES;
+       xs->timeout     =       10000;/* 10000 millisecs for a disk !*/
+       xs->cmd         =       (struct scsi_generic *)&cmd;
+       xs->cmdlen      =       sizeof(cmd);
+       xs->resid       =       bp->b_bcount;
+       xs->when_done   =       sd_done;
+       xs->done_arg    =       unit;
+       xs->done_arg2   =       (int)xs;
+       xs->error       =       XS_NOERROR;
+       xs->bp          =       bp;
+       xs->data        =       (u_char *)bp->b_un.b_addr;
+       xs->datalen     =       bp->b_bcount;
+       /*******************************************************\
+       * Pass all this info to the scsi driver.                *
+       \*******************************************************/
+
+
+
+       if ( (*(sd->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED)
+       {
+               printf("sd%d: oops not queued",unit);
+               xs->error = XS_DRIVER_STUFFUP;
+               sd_done(unit,xs);  /* clean up (calls sdstart) */
+       }
+       sdqueues++;
+}
+
+/*******************************************************\
+* This routine is called by the scsi interrupt when    *
+* the transfer is complete.
+\*******************************************************/
+int    sd_done(unit,xs)
+int    unit;
+struct scsi_xfer       *xs;
+{
+       struct  buf             *bp;
+       int     retval;
+       int     retries = 0;
+
+       if(scsi_debug & PRINTROUTINES) printf("sd_done%d ",unit);
+       if (! (xs->flags & INUSE))
+               panic("scsi_xfer not in use!");
+       if(bp = xs->bp)
+       {
+               switch(xs->error)
+               {
+               case    XS_NOERROR:
+                       bp->b_error = 0;
+                       bp->b_resid = 0;
+                       break;
+
+               case    XS_SENSE:
+                       retval = (sd_interpret_sense(unit,xs));
+                       if(retval)
+                       {
+                               bp->b_flags |= B_ERROR;
+                               bp->b_error = retval;
+                       }
+                       break;
+
+               case    XS_TIMEOUT:
+                       printf("sd%d timeout\n",unit);
+
+               case    XS_BUSY:        /* should retry */ /* how? */
+                       /************************************************/
+                       /* SHOULD put buf back at head of queue         */
+                       /* and decrement retry count in (*xs)           */
+                       /* HOWEVER, this should work as a kludge        */
+                       /************************************************/
+                       if(xs->retries--)
+                       {
+                               xs->error = XS_NOERROR;
+                               xs->flags &= ~ITSDONE;
+                               if ( (*(sd_data[unit].sc_sw->scsi_cmd))(xs)
+                                       == SUCCESSFULLY_QUEUED)
+                               {       /* don't wake the job, ok? */
+                                       return;
+                               }
+                               xs->flags |= ITSDONE;
+                       } /* fall through */
+
+               case    XS_DRIVER_STUFFUP:
+                       bp->b_flags |= B_ERROR;
+                       bp->b_error = EIO;
+                       break;
+               default:
+                       printf("sd%d: unknown error category from scsi driver\n"
+                               ,unit);
+               }       
+               biodone(bp);
+               sd_free_xs(unit,xs,0);
+               sdstart(unit);  /* If there's anything waiting.. do it */
+       }
+       else /* special has finished */
+       {
+               wakeup(xs);
+       }
+}
+/*******************************************************\
+* Perform special action on behalf of the user         *
+* Knows about the internals of this device             *
+\*******************************************************/
+sdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
+{
+       /* struct sd_cmd_buf *args;*/
+       int error = 0;
+       unsigned int opri;
+       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];
+       if(scsi_debug & PRINTROUTINES) printf("sdioctl%d ",unit);
+
+       /*******************************************************\
+       * If the device is not valid.. abandon ship             *
+       \*******************************************************/
+       if (!(sd_data[unit].flags & SDVALID))
+               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,
+                               sd->dosparts);
+                if (error == 0) {
+                       sd->flags |= SDHAVELABEL;
+               }
+                break;
+
+        case DIOCWLABEL:
+               sd->flags &= ~SDWRITEPROT;
+                if ((flag & FWRITE) == 0)
+                        error = EBADF;
+                else
+                        sd->wlabel = *(int *)addr;
+                break;
+
+        case DIOCWDINFO:
+               sd->flags &= ~SDWRITEPROT;
+                if ((flag & FWRITE) == 0)
+                        error = EBADF;
+                else
+               {
+                       if ((error = setdisklabel(&sd->disklabel
+                                               , (struct disklabel *)addr
+                       , /*(sd->flags & SDHAVELABEL) ? sd->openparts :*/ 0
+                                               , sd->dosparts)) == 0)
+                       {
+                               int wlab;
+
+                               sd->flags |= SDHAVELABEL; /* ok write will succeed */
+
+                               /* simulate opening partition 0 so write succeeds */
+                               sd->openparts |= (1 << 0);            /* XXX */
+                               wlab = sd->wlabel;
+                               sd->wlabel = 1;
+                               error = writedisklabel(dev, sdstrategy,
+                                       &sd->disklabel, sd->dosparts);
+                               sd->wlabel = wlab;
+                       }
+               }
+                break;
+
+
+       default:
+               error = ENOTTY;
+               break;
+       }
+       return (error);
+}
+
+
+/*******************************************************\
+* Load the label information on the named device       *
+\*******************************************************/
+int sdgetdisklabel(unit)
+unsigned char  unit;
+{
+       /*unsigned int n, m;*/
+       char *errstring;
+       struct dos_partition *dos_partition_p;
+       struct sd_data *sd = sd_data + unit;
+
+       /*******************************************************\
+       * 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                                     *
+       \*******************************************************/
+       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 = 512; /* 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 */
+       }
+
+       /*******************************************************\
+       * all the generic bisklabel extraction routine          *
+       \*******************************************************/
+       if(errstring = readdisklabel(makedev(0 ,(unit<<UNITSHIFT )+3)
+                                       , sdstrategy
+                                       , &sd->disklabel
+                                       , sd->dosparts
+                                       , 0
+                                       , 0))
+       {
+               printf("sd%d: %s\n",unit, errstring);
+               return(ENXIO);
+       }
+       /*******************************************************\
+       * leave partition 2 "open" for raw I/O                  *
+       \*******************************************************/
+
+       sd->flags |= SDHAVELABEL; /* WE HAVE IT ALL NOW */
+       return(ESUCCESS);
+}
+
+/*******************************************************\
+* Find out from the device what it's capacity is       *
+\*******************************************************/
+sd_size(unit, flags)
+{
+       struct scsi_read_cap_data rdcap;
+       struct scsi_read_capacity scsi_cmd;
+       int 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 (sd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       &rdcap,
+                       sizeof(rdcap),  
+                       2000,
+                       flags) != 0)
+       {
+               printf("could not get size of unit %d\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);
+}
+       
+/*******************************************************\
+* Get scsi driver to send a "are you ready?" command   *
+\*******************************************************/
+sd_test_unit_ready(unit,flags)
+int    unit,flags;
+{
+       struct  scsi_test_unit_ready scsi_cmd;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = TEST_UNIT_READY;
+
+       return (sd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       100000,
+                       flags));
+}
+
+/*******************************************************\
+* Prevent or allow the user to remove the tape         *
+\*******************************************************/
+sd_prevent(unit,type,flags)
+int    unit,type,flags;
+{
+       struct  scsi_prevent    scsi_cmd;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = PREVENT_ALLOW;
+       scsi_cmd.prevent=type;
+       return (sd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       5000,
+                       flags) );
+}
+/*******************************************************\
+* Get scsi driver to send a "start up" command         *
+\*******************************************************/
+sd_start_unit(unit,flags)
+int    unit,flags;
+{
+       struct scsi_start_stop scsi_cmd;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = START_STOP;
+       scsi_cmd.start = 1;
+
+       return (sd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       2000,
+                       flags));
+}
+
+/*******************************************************\
+* Tell the device to map out a defective block         *
+\*******************************************************/
+sd_reassign_blocks(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(sd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       &rbdata,
+                       sizeof(rbdata),
+                       5000,
+                       0));
+}
+
+#define b2tol(a)       (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
+
+/*******************************************************\
+* Get the scsi driver to send a full inquiry to the    *
+* device and use the results to fill out the disk      *
+* parameter structure.                                 *
+\*******************************************************/
+
+int    sd_get_parms(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;
+       int sectors;
+
+       /*******************************************************\
+       * First check if we have it all loaded                  *
+       \*******************************************************/
+               if(sd->flags & SDVALID) return(0);
+       /*******************************************************\
+       * First do a mode sense page 3                          *
+       \*******************************************************/
+       if (sd_debug)
+       {
+               bzero(&scsi_cmd, sizeof(scsi_cmd));
+               scsi_cmd.op_code = MODE_SENSE;
+               scsi_cmd.page_code = 3;
+               scsi_cmd.length = 0x24;
+               /*******************************************************\
+               * do the command, but we don't need the results         *
+               * just print them for our interest's sake               *
+               \*******************************************************/
+               if (sd_scsi_cmd(unit,
+                               &scsi_cmd,
+                               sizeof(scsi_cmd),
+                               &scsi_sense,
+                               sizeof(scsi_sense),
+                               2000,
+                               flags) != 0)
+               {
+                       printf("could not mode sense (3) for unit %d\n", unit);
+                       return(ENXIO);
+               } 
+               printf("unit %d: %d trk/zone, %d alt_sec/zone, %d alt_trk/zone, %d alt_trk/lun\n",
+                       unit,
+                       b2tol(scsi_sense.pages.disk_format.trk_z),
+                       b2tol(scsi_sense.pages.disk_format.alt_sec),
+                       b2tol(scsi_sense.pages.disk_format.alt_trk_z),
+                       b2tol(scsi_sense.pages.disk_format.alt_trk_v));
+               printf("         %d sec/trk, %d bytes/sec, %d interleave, %d %d bytes/log_blk\n",
+                       b2tol(scsi_sense.pages.disk_format.ph_sec_t),
+                       b2tol(scsi_sense.pages.disk_format.bytes_s),
+                       b2tol(scsi_sense.pages.disk_format.interleave),
+                       sd_size(unit, flags),
+                       _3btol(scsi_sense.blk_desc.blklen));
+       }
+
+
+       /*******************************************************\
+       * do a "mode sense page 4"                              *
+       \*******************************************************/
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = MODE_SENSE;
+       scsi_cmd.page_code = 4;
+       scsi_cmd.length = 0x20;
+       /*******************************************************\
+       * If the command worked, use the results to fill out    *
+       * the parameter structure                               *
+       \*******************************************************/
+       if (sd_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       &scsi_sense,
+                       sizeof(scsi_sense),
+                       2000,
+                       flags) != 0)
+       {
+               printf("could not mode sense (4) for unit %d\n", unit);
+               printf(" using ficticious geometry\n");
+               /* use adaptec standard ficticious geometry */
+               sectors = sd_size(unit, flags);
+               disk_parms->heads = 64;
+               disk_parms->sectors = 32;
+               disk_parms->cyls = sectors/(64 * 32);
+               disk_parms->secsiz = SECSIZE;
+       } 
+       else
+       {
+
+               if (sd_debug)
+               {
+               printf("         %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                                       *
+               \*******************************************************/
+               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);
+               sectors /= disk_parms->cyls;
+               sectors /= disk_parms->heads;
+               disk_parms->sectors = sectors; /* dubious on SCSI*/
+       }
+
+       sd->flags |= SDVALID;
+       return(0);
+}
+
+/*******************************************************\
+* close the device.. only called if we are the LAST    *
+* occurence of an open device                          *
+\*******************************************************/
+sdclose(dev)
+dev_t dev;
+{
+       unsigned char unit, part;
+       unsigned int old_priority;
+
+       unit = UNIT(dev);
+       part = PARTITION(dev);
+       sd_data[unit].partflags[part] &= ~SDOPEN;
+       sd_data[unit].openparts &= ~(1 << part);
+       if(sd_data[unit].openparts == 0) /* if all partitions closed */
+       {
+               sd_prevent(unit,PR_ALLOW,SCSI_SILENT|SCSI_ERR_OK);
+       }
+       return(0);
+}
+
+/*******************************************************\
+* ask the scsi driver to perform a command for us.     *
+* Call it through the switch table, and tell it which  *
+* sub-unit we want, and what target and lu we wish to  *
+* talk to. Also tell it where to find the command      *
+* how long int is.                                     *
+* Also tell it where to read/write the data, and how   *
+* long the data is supposed to be                      *
+\*******************************************************/
+int    sd_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags)
+
+int    unit,flags;
+struct scsi_generic *scsi_cmd;
+int    cmdlen;
+int    timeout;
+u_char *data_addr;
+int    datalen;
+{
+       struct  scsi_xfer *xs;
+       int     retval;
+       int     s;
+       struct sd_data *sd = sd_data + unit;
+
+       if(scsi_debug & PRINTROUTINES) printf("\nsd_scsi_cmd%d ",unit);
+       if(sd->sc_sw)   /* If we have a scsi driver */
+       {
+               xs = sd_get_xs(unit,flags); /* should wait unless booting */
+               if(!xs)
+               {
+                       printf("sd_scsi_cmd%d: controller busy"
+                                       " (this should never happen)\n",unit); 
+                               return(EBUSY);
+               }
+               xs->flags |= INUSE;
+               /*******************************************************\
+               * Fill out the scsi_xfer structure                      *
+               \*******************************************************/
+               xs->flags       |=      flags;
+               xs->adapter     =       sd->ctlr;
+               xs->targ        =       sd->targ;
+               xs->lu          =       sd->lu;
+               xs->retries     =       SD_RETRIES;
+               xs->timeout     =       timeout;
+               xs->cmd         =       scsi_cmd;
+               xs->cmdlen      =       cmdlen;
+               xs->data        =       data_addr;
+               xs->datalen     =       datalen;
+               xs->resid       =       datalen;
+               xs->when_done   =       (flags & SCSI_NOMASK)
+                                       ?(int (*)())0
+                                       :sd_done;
+               xs->done_arg    =       unit;
+               xs->done_arg2   =       (int)xs;
+retry:         xs->error       =       XS_NOERROR;
+               xs->bp          =       0;
+               retval = (*(sd->sc_sw->scsi_cmd))(xs);
+               switch(retval)
+               {
+               case    SUCCESSFULLY_QUEUED:
+                       s = splbio();
+                       while(!(xs->flags & ITSDONE))
+                               sleep(xs,PRIBIO+1);
+                       splx(s);
+
+               case    HAD_ERROR:
+                       /*printf("err = %d ",xs->error);*/
+                       switch(xs->error)
+                       {
+                       case    XS_NOERROR:
+                               retval = ESUCCESS;
+                               break;
+                       case    XS_SENSE:
+                               retval = (sd_interpret_sense(unit,xs));
+                               break;
+                       case    XS_DRIVER_STUFFUP:
+                               retval = EIO;
+                               break;
+                       case    XS_TIMEOUT:
+                               if(xs->retries-- )
+                               {
+                                       xs->flags &= ~ITSDONE;
+                                       goto retry;
+                               }
+                               retval = EIO;
+                               break;
+                       case    XS_BUSY:
+                               if(xs->retries-- )
+                               {
+                                       xs->flags &= ~ITSDONE;
+                                       goto retry;
+                               }
+                               retval = EIO;
+                               break;
+                       default:
+                               retval = EIO;
+                               printf("sd%d: unknown error category from scsi driver\n"
+                                       ,unit);
+                       }       
+                       break;
+               case    COMPLETE:
+                       retval = ESUCCESS;
+                       break;
+               case    TRY_AGAIN_LATER:
+                       if(xs->retries-- )
+                       {
+                               xs->flags &= ~ITSDONE;
+                               goto retry;
+                       }
+                       retval = EIO;
+                       break;
+               default:
+                       retval = EIO;
+               }
+               sd_free_xs(unit,xs,flags);
+               sdstart(unit);          /* check if anything is waiting fr the xs */
+       }
+       else
+       {
+               printf("sd%d: not set up\n",unit);
+               return(EINVAL);
+       }
+       return(retval);
+}
+/***************************************************************\
+* Look at the returned sense and act on the error and detirmine        *
+* The unix error number to pass back... (0 = report no error)  *
+\***************************************************************/
+
+int    sd_interpret_sense(unit,xs)
+int    unit;
+struct scsi_xfer *xs;
+{
+       struct  scsi_sense_data *sense;
+       int     key;
+       int     silent;
+
+       /***************************************************************\
+       * If the flags say errs are ok, then always return ok.          *
+       \***************************************************************/
+       if (xs->flags & SCSI_ERR_OK) return(ESUCCESS);
+       silent = (xs->flags & SCSI_SILENT);
+
+       sense = &(xs->sense);
+       switch(sense->error_class)
+       {
+       case 7:
+               {
+               key=sense->ext.extended.sense_key;
+               switch(key)
+               {
+               case    0x0:
+                       return(ESUCCESS);
+               case    0x1:
+                       if(!silent)
+                       {
+                               printf("sd%d: soft error(corrected) ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(ESUCCESS);
+               case    0x2:
+                       if(!silent)printf("sd%d: not ready\n ",
+                               unit); 
+                       return(ENODEV);
+               case    0x3:
+                       if(!silent)
+                       {
+                               printf("sd%d: medium error ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(EIO);
+               case    0x4:
+                       if(!silent)printf("sd%d: non-media hardware failure\n ",
+                               unit); 
+                       return(EIO);
+               case    0x5:
+                       if(!silent)printf("sd%d: illegal request\n ",
+                               unit); 
+                       return(EINVAL);
+               case    0x6:
+                       /***********************************************\
+                       * If we are not open, then this is not an error *
+                       * as we don't have state yet. Either way, make  *
+                       * sure that we don't have any residual state    *
+                       \***********************************************/
+                       if(!silent)printf("sd%d: Unit attention.\n ", unit); 
+                       sd_data[unit].flags &= ~(SDVALID | SDHAVELABEL);
+                       if (sd_data[unit].openparts)
+                       {
+                               return(EIO);
+                       }
+                       return(ESUCCESS); /* not an error if nothing's open */
+               case    0x7:
+                       if(!silent)
+                       {
+                               printf("sd%d: attempted protection violation ",
+                                               unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(EACCES);
+               case    0x8:
+                       if(!silent)
+                       {
+                               printf("sd%d: block wrong state (worm)\n ",
+                               unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(EIO);
+               case    0x9:
+                       if(!silent)printf("sd%d: vendor unique\n",
+                               unit); 
+                       return(EIO);
+               case    0xa:
+                       if(!silent)printf("sd%d: copy aborted\n ",
+                               unit); 
+                       return(EIO);
+               case    0xb:
+                       if(!silent)printf("sd%d: command aborted\n ",
+                               unit); 
+                       return(EIO);
+               case    0xc:
+                       if(!silent)
+                       {
+                               printf("sd%d: search returned\n ",
+                                       unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(ESUCCESS);
+               case    0xd:
+                       if(!silent)printf("sd%d: volume overflow\n ",
+                               unit); 
+                       return(ENOSPC);
+               case    0xe:
+                       if(!silent)
+                       {
+                               printf("sd%d: verify miscompare\n ",
+                               unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24),
+                                       (sense->ext.extended.info[1] <<16),
+                                       (sense->ext.extended.info[2] <<8),
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               printf("\n");
+                       }
+                       return(EIO);
+               case    0xf:
+                       if(!silent)printf("sd%d: unknown error key\n ",
+                               unit); 
+                       return(EIO);
+               }
+               break;
+       }
+       case 0:
+       case 1:
+       case 2:
+       case 3:
+       case 4:
+       case 5:
+       case 6:
+               {
+                       if(!silent)printf("sd%d: error class %d code %d\n",
+                               unit,
+                               sense->error_class,
+                               sense->error_code);
+               if(sense->valid)
+                       if(!silent)printf("block no. %d (decimal)\n",
+                       (sense->ext.unextended.blockhi <<16),
+                       + (sense->ext.unextended.blockmed <<8),
+                       + (sense->ext.unextended.blocklow ));
+               }
+               return(EIO);
+       }
+}
+
+
+
+
+int
+sdsize(dev_t dev)
+{
+       int unit = UNIT(dev), part = PARTITION(dev), val;
+       struct sd_data *sd;
+
+       if (unit >= NSD)
+               return(-1);
+
+       sd = &sd_data[unit];
+       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 || sd->flags & SDWRITEPROT)
+               return (-1);
+
+       return((int)sd->disklabel.d_partitions[part].p_size);
+}
+
+sddump()
+{
+    printf("sddump()        -- not implemented\n");
+    return(-1);
+}
+
+
+
+
+
diff --git a/usr/src/sys.386bsd/scsi/st.c b/usr/src/sys.386bsd/scsi/st.c
new file mode 100644 (file)
index 0000000..e896902
--- /dev/null
@@ -0,0 +1,1774 @@
+/*
+ * 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.
+ *
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+
+/*
+ * 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 <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/mtio.h>
+
+#if defined(OSF)
+#define SECSIZE        512
+#endif /* defined(OSF) */
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_tape.h>
+#include <scsi/scsiconf.h>
+
+
+long int ststrats,stqueues;
+
+
+#define PAGESIZ        4096
+#define STQSIZE                4
+#define        ST_RETRIES      4
+
+
+#define MODE(z)                (  (minor(z)       & 0x03) )
+#define DSTY(z)         ( ((minor(z) >> 2) & 0x03) )
+#define UNIT(z)                (  (minor(z) >> 4) )
+
+#define DSTY_QIC120  3
+#define DSTY_QIC150  2
+#define DSTY_QIC525  1
+
+#define QIC120     0x0f
+#define QIC150     0x10
+#define QIC525     0x11
+
+
+
+
+#ifndef        __386BSD__
+struct buf stbuf[NST][STQSIZE];        /* buffer for raw io (one per device) */
+struct buf *stbuf_free[NST];   /* queue of free buffers for raw io */
+#endif __386BSD__
+struct buf st_buf_queue[NST];
+int    ststrategy();
+void   stminphys();
+struct scsi_xfer st_scsi_xfer[NST];
+int    st_xfer_block_wait[NST];
+
+#if defined(OSF)
+caddr_t                st_window[NST];
+#endif /* defined(OSF) */
+#ifndef        MACH
+#define ESUCCESS 0
+#endif MACH
+
+int    st_info_valid[NST];     /* the info about the device is valid */
+int    st_initialized[NST] ;
+int    st_debug = 0;
+
+int stattach();
+int st_done();
+struct st_data
+{
+       int     flags;
+       struct  scsi_switch *sc_sw;     /* address of scsi low level switch */
+       int     ctlr;                   /* so they know which one we want */
+       int     targ;                   /* our scsi target ID */
+       int     lu;                     /* out scsi lu */
+       int     blkmin;                 /* min blk size */
+       int     blkmax;                 /* max blk size */
+       int     numblks;                /* nominal blocks capacity */
+       int     blksiz;                 /* nominal block size */
+}st_data[NST];
+#define ST_OPEN                0x01
+#define        ST_NOREWIND     0x02
+#define        ST_WRITTEN      0x04
+#define        ST_FIXEDBLOCKS  0x10
+#define        ST_AT_FILEMARK  0x20
+#define        ST_AT_EOM       0x40
+
+#define        ST_PER_ACTION   (ST_AT_FILEMARK | ST_AT_EOM)
+#define        ST_PER_OPEN     (ST_OPEN | ST_NOREWIND | ST_WRITTEN | ST_PER_ACTION)
+#define        ST_PER_MEDIA    ST_FIXEDBLOCKS
+
+static int     next_st_unit = 0;
+/***********************************************************************\
+* The routine called by the low level scsi routine when it discovers   *
+* A device suitable for this driver                                    *
+\***********************************************************************/
+
+int    stattach(ctlr,targ,lu,scsi_switch)
+struct scsi_switch *scsi_switch;
+{
+       int     unit,i;
+       unsigned char *tbl;
+       struct st_data *st;
+
+       if(scsi_debug & PRINTROUTINES) printf("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",(unit + 1),NST);
+               return(0);
+       }
+       st = st_data + unit;
+       /*******************************************************\
+       * Store information needed to contact our base driver   *
+       \*******************************************************/
+       st->sc_sw       =       scsi_switch;
+       st->ctlr        =       ctlr;
+       st->targ        =       targ;
+       st->lu  =       lu;
+
+       /*******************************************************\
+       * 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: scsi tape drive, %d blocks of %d bytes\n",
+                       unit, st->numblks, st->blksiz);
+       }
+       else
+       {
+               printf("        st%d: scsi tape drive :- offline\n", unit);
+       }
+       /*******************************************************\
+       * Set up the bufs for this device                       *
+       \*******************************************************/
+#ifndef        __386BSD__
+       stbuf_free[unit] = (struct buf *)0;
+       for (i = 1; i < STQSIZE; i++)
+       {
+               stbuf[unit][i].b_forw = stbuf_free[unit];
+               stbuf_free[unit]=&stbuf[unit][i];
+       }
+#endif __386BSD__
+       st_buf_queue[unit].b_active     =       0;
+       st_buf_queue[unit].b_actf       =       st_buf_queue[unit].b_actl = 0;
+       st_initialized[unit] = 1;
+
+#if defined(OSF)
+       st_window[unit] = (caddr_t)alloc_kva(SECSIZE*256+PAGESIZ);
+#endif /* defined(OSF) */
+
+       return;
+
+}
+
+
+
+/*******************************************************\
+*      open the device.                                *
+\*******************************************************/
+stopen(dev)
+{
+       int errcode = 0;
+       int unit,mode,dsty;
+        int dsty_code;
+       struct st_data *st;
+       unit = UNIT(dev);
+       mode = MODE(dev);
+       dsty = DSTY(dev);
+       st = st_data + unit;
+
+       /*******************************************************\
+       * Check the unit is legal                               *
+       \*******************************************************/
+       if ( unit >= NST )
+       {
+               errcode = ENXIO;
+               return(errcode);
+       }
+       /*******************************************************\
+       * Only allow one at a time                              *
+       \*******************************************************/
+       if(st->flags & ST_OPEN)
+       {
+               errcode = ENXIO;
+               goto bad;
+       }
+       /*******************************************************\
+       * Set up the mode flags according to the minor number   *
+       * ensure all open flags are in a known state            *
+       \*******************************************************/
+       st->flags &= ~ST_PER_OPEN;
+       switch(mode)
+       {
+       case    2:
+       case    0:
+               st->flags &= ~ST_NOREWIND;
+               break;
+       case    3:
+       case    1:
+               st->flags |= ST_NOREWIND;
+               break;
+       default:
+               printf("st%d: Bad mode (minor number)%d\n",unit,mode);
+               return(EINVAL);
+       }
+        /*******************************************************\
+        * Check density code: 0 is drive default               *
+        \*******************************************************/
+        switch(dsty)
+       {
+       case    0:              dsty_code = 0;          break;
+       case    DSTY_QIC120:    dsty_code = QIC120;     break;
+       case    DSTY_QIC150:    dsty_code = QIC150;     break;
+       case    DSTY_QIC525:    dsty_code = QIC525;     break;
+       default:
+               printf("st%d: Bad density (minor number)%d\n",unit,dsty);
+               return(EINVAL);
+       }
+       if(scsi_debug & (PRINTROUTINES | TRACEOPENS))
+               printf("stopen: dev=0x%x (unit %d (of %d))\n"
+                               ,   dev,      unit,   NST);
+       /*******************************************************\
+       * Make sure the device has been initialised             *
+       \*******************************************************/
+
+       if (!st_initialized[unit])
+               return(ENXIO);
+       /*******************************************************\
+       * Check that it is still responding and ok.             *
+       \*******************************************************/
+
+       if(scsi_debug & TRACEOPENS)
+               printf("device is ");
+       if (!(st_req_sense(unit, 0)))
+       {
+               errcode = ENXIO;
+               if(scsi_debug & TRACEOPENS)
+                       printf("not responding\n");
+               goto bad;
+       }
+       if(scsi_debug & TRACEOPENS)
+               printf("ok\n");
+
+       if(!(st_test_ready(unit,0)))
+       {
+               printf("st%d not ready\n",unit);
+               return(EIO);
+       }
+
+       if(!st_info_valid[unit])                /* is media new? */
+               if(!st_load(unit,LD_LOAD,0))
+               {
+                       return(EIO);
+               }
+
+       if(!st_rd_blk_lim(unit,0))
+       {
+               return(EIO);
+       }
+
+       if(!st_mode_sense(unit,0))
+       {
+               return(EIO);
+       }
+
+       if(!st_mode_select(unit,0,dsty_code))
+       {
+               return(EIO);
+       }
+
+       st_info_valid[unit] = TRUE;
+
+       st_prevent(unit,PR_PREVENT,0); /* who cares if it fails? */
+
+       /*******************************************************\
+       * Load the physical device parameters                   *
+       \*******************************************************/
+       if(scsi_debug & TRACEOPENS)
+               printf("Params loaded ");
+
+
+       st->flags |= ST_OPEN;
+
+bad:
+       return(errcode);
+}
+
+/*******************************************************\
+* close the device.. only called if we are the LAST    *
+* occurence of an open device                          *
+\*******************************************************/
+stclose(dev)
+{
+       unsigned char unit,mode;
+       struct  st_data *st;
+
+       unit = UNIT(dev);
+       mode = MODE(dev);
+       st = st_data + unit;
+
+       if(scsi_debug & TRACEOPENS)
+               printf("Closing device");
+       if(st->flags & ST_WRITTEN)
+       {
+               st_write_filemarks(unit,1,0);
+       }
+       st->flags &= ~ST_WRITTEN;
+       switch(mode)
+       {
+       case    0:
+               st_rewind(unit,FALSE,SCSI_SILENT);
+               st_prevent(unit,PR_ALLOW,SCSI_SILENT);
+               break;
+       case    1:
+               st_prevent(unit,PR_ALLOW,SCSI_SILENT);
+               break;
+       case    2:
+               st_rewind(unit,FALSE,SCSI_SILENT);
+               st_prevent(unit,PR_ALLOW,SCSI_SILENT);
+               st_load(unit,LD_UNLOAD,SCSI_SILENT);
+               break;
+       case    3:
+               st_prevent(unit,PR_ALLOW,SCSI_SILENT);
+               st_load(unit,LD_UNLOAD,SCSI_SILENT);
+               break;
+       default:
+               printf("st%d:close: Bad mode (minor number)%d how's it open?\n"
+                               ,unit,mode);
+               return(EINVAL);
+       }
+       st->flags &= ~ST_PER_OPEN;
+       return(0);
+}
+
+#ifndef        __386BSD__
+/*******************************************************\
+* Get ownership of this unit's buf                     *
+* If need be, sleep on it, until it comes free         *
+\*******************************************************/
+struct buf *
+st_get_buf(unit) {
+       struct buf *rc;
+
+       while (!(rc = stbuf_free[unit]))
+               sleep((caddr_t)&stbuf_free[unit], PRIBIO+1);
+       stbuf_free[unit] = stbuf_free[unit]->b_forw;
+       rc->b_error = 0;
+       rc->b_resid = 0;
+       rc->b_flags = 0;
+       return(rc);
+}
+
+/*******************************************************\
+* Free this unit's buf, wake processes waiting for it  *
+\*******************************************************/
+st_free_buf(unit,bp)
+struct buf *bp;
+{
+       if (!stbuf_free[unit])
+               wakeup((caddr_t)&stbuf_free[unit]);
+       bp->b_forw = stbuf_free[unit];
+       stbuf_free[unit] = bp;
+}
+       
+
+/*******************************************************\
+* Get the buf for this unit and use physio to do it    *
+\*******************************************************/
+stread(dev,uio)
+register short  dev;
+struct uio     *uio;
+{
+       int     unit = UNIT(dev);
+       struct buf *bp = st_get_buf(unit);
+       int rc;
+       rc = physio(ststrategy, bp, dev, B_READ, stminphys, uio);
+       st_free_buf(unit,bp);
+       return(rc);
+}
+
+/*******************************************************\
+* Get the buf for this unit and use physio to do it    *
+\*******************************************************/
+stwrite(dev,uio)
+dev_t          dev;
+struct uio     *uio;
+{
+       int     unit = UNIT(dev);
+       struct buf *bp = st_get_buf(unit);
+       int rc;
+
+       rc = physio(ststrategy, bp, dev, B_WRITE, stminphys, uio);
+       st_free_buf(unit,bp);
+       return(rc);
+}
+
+
+#endif __386BSD__
+/*******************************************************\
+* 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_sw->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.                          *
+\*******************************************************/
+
+int    ststrategy(bp)
+struct buf     *bp;
+{
+       struct  buf     *dp;
+       unsigned char unit;
+       unsigned int opri;
+
+       ststrats++;
+       unit = UNIT((bp->b_dev));
+       if(scsi_debug & PRINTROUTINES) printf("\nststrategy ");
+       if(scsi_debug & SHOWREQUESTS) printf("st%d: %d bytes @ blk%d\n",
+                                       unit,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_data[unit].flags & ST_FIXEDBLOCKS)
+               && bp->b_bcount % st_data[unit].blkmin)
+       {
+               printf("st%d: bad request, must be multiple of %d\n",
+                       unit, st_data[unit].blkmin);
+               bp->b_error = EIO;
+               goto bad;
+       }
+
+#ifdef __386BSD__
+       stminphys(bp);
+#endif __386BSD__
+       opri = splbio();
+       dp = &st_buf_queue[unit];
+
+       /*******************************************************\
+       * Place it in the queue of disk activities for this tape*
+       * at the end                                            *
+       \*******************************************************/
+       while ( dp->b_actf) 
+       {
+               dp = dp->b_actf;
+       }       
+       dp->b_actf = 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*
+       \*******************************************************/
+       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 deques the buf and creates a scsi command to perform the  *
+* transfer in the buf. The transfer request will call st_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 */
+ststart(unit)
+{
+       int                     drivecount;
+       register struct buf     *bp = 0;
+       register struct buf     *dp;
+       struct  scsi_xfer       *xs;
+       struct  scsi_rw_tape    cmd;
+       int                     blkno, nblk;
+       struct  st_data *st;
+
+
+       st = st_data + unit;
+
+       if(scsi_debug & PRINTROUTINES) printf("ststart%d ",unit);
+       /*******************************************************\
+       * See if there is a buf to do and we are not already    *
+       * doing one                                             *
+       \*******************************************************/
+       xs=&st_scsi_xfer[unit];
+       if(xs->flags & INUSE)
+       {
+               return;    /* unit already underway */
+       }
+trynext:
+       if(st_xfer_block_wait[unit]) /* a special awaits, let it proceed first */
+       {
+               wakeup(&st_xfer_block_wait[unit]);
+               return;
+       }
+
+       dp = &st_buf_queue[unit];
+       if ((bp = dp->b_actf) != NULL)
+       {
+               dp->b_actf = bp->b_actf;
+       }
+       else /* no work to do */
+       {
+               return;
+       }
+       xs->flags = INUSE;    /* Now ours */
+
+
+       /*******************************************************\
+       * We have a buf, now we should move the data into       *
+       * a scsi_xfer definition and try start it               *
+       \*******************************************************/
+
+       /*******************************************************\
+       *  If we are at a filemark but have not reported it yet *
+       * then we should report it now                          *
+       \*******************************************************/
+       if(st->flags & ST_AT_FILEMARK)
+       {
+               bp->b_error = 0;
+               bp->b_flags |= B_ERROR; /* EOF*/
+               st->flags &= ~ST_AT_FILEMARK;
+               biodone(bp);
+               xs->flags = 0; /* won't need it now */
+               goto trynext;
+       }
+       /*******************************************************\
+       *  If we are at EOM but have not reported it yet        *
+       * then we should report it now                          *
+       \*******************************************************/
+       if(st->flags & ST_AT_EOM)
+       {
+               bp->b_error = EIO;
+               bp->b_flags |= B_ERROR;
+               st->flags &= ~ST_AT_EOM;
+               biodone(bp);
+               xs->flags = 0; /* won't need it now */
+               goto trynext;
+       }
+       /*******************************************************\
+       *  Fill out the scsi command                            *
+       \*******************************************************/
+       bzero(&cmd, sizeof(cmd));
+       if((bp->b_flags & B_READ) == B_WRITE)
+       {
+               st->flags |= ST_WRITTEN;
+               xs->flags |= SCSI_DATA_OUT;
+       }
+       else
+       {
+               xs->flags |= SCSI_DATA_IN;
+       }
+       cmd.op_code = (bp->b_flags & B_READ) 
+                                       ? READ_COMMAND_TAPE
+                                       : WRITE_COMMAND_TAPE;
+
+       /*******************************************************\
+       * Handle "fixed-block-mode" tape drives by using the    *
+       * block count instead of the length.                    *
+       \*******************************************************/
+       if(st->flags & ST_FIXEDBLOCKS)
+       {
+               cmd.fixed = 1;
+               lto3b(bp->b_bcount/st->blkmin,cmd.len);
+       }
+       else
+       {
+               lto3b(bp->b_bcount,cmd.len);
+       }
+
+       /*******************************************************\
+       * Fill out the scsi_xfer structure                      *
+       *       Note: we cannot sleep as we may be an interrupt *
+       \*******************************************************/
+       xs->flags       |=      SCSI_NOSLEEP;
+       xs->adapter     =       st->ctlr;
+       xs->targ        =       st->targ;
+       xs->lu          =       st->lu;
+       xs->retries     =       1;      /* can't retry on tape*/
+       xs->timeout     =       100000; /* allow 100 secs for retension */
+       xs->cmd         =       (struct scsi_generic *)&cmd;
+       xs->cmdlen      =       sizeof(cmd);
+       xs->data        =       (u_char *)bp->b_un.b_addr;
+       xs->datalen     =       bp->b_bcount;
+       xs->resid       =       bp->b_bcount;
+       xs->when_done   =       st_done;
+       xs->done_arg    =       unit;
+       xs->done_arg2   =       (int)xs;
+       xs->error       =       XS_NOERROR;
+       xs->bp          =       bp;
+       /*******************************************************\
+       * Pass all this info to the scsi driver.                *
+       \*******************************************************/
+
+
+#if defined(OSF)||defined(FIX_ME)
+       if (bp->b_flags & B_PHYS) {
+               xs->data = (u_char*)map_pva_kva(bp->b_proc, bp->b_un.b_addr,
+                       bp->b_bcount, st_window[unit],
+                       (bp->b_flags&B_READ)?B_WRITE:B_READ);
+       } else {
+               xs->data = (u_char*)bp->b_un.b_addr;
+       }
+#endif /* defined(OSF) */
+
+       if ( (*(st->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED)
+       {
+               printf("st%d: oops not queued",unit);
+               xs->error = XS_DRIVER_STUFFUP;
+               st_done(unit,xs);
+       }
+       stqueues++;
+}
+
+/*******************************************************\
+* This routine is called by the scsi interrupt when    *
+* the transfer is complete.
+\*******************************************************/
+int    st_done(unit,xs)
+int    unit;
+struct scsi_xfer       *xs;
+{
+       struct  buf             *bp;
+       int     retval;
+
+       if(scsi_debug & PRINTROUTINES) printf("st_done%d ",unit);
+       if (! (xs->flags & INUSE))
+               panic("scsi_xfer not in use!");
+       if(bp = xs->bp)
+       {
+               switch(xs->error)
+               {
+               case    XS_NOERROR:
+                       bp->b_flags &= ~B_ERROR;
+                       bp->b_error = 0;
+                       bp->b_resid = 0;
+                       break;
+               case    XS_SENSE:
+                       retval = (st_interpret_sense(unit,xs));
+                       if(retval)
+                       {
+                               /***************************************\
+                               * We have a real error, the bit should  *
+                               * be set to indicate this. The return   *
+                               * value will contain the unix error code*
+                               * that the error interpretation routine *
+                               * thought was suitable, so pass this    *
+                               * value back in the buf structure.      *
+                               * Furthermore we return information     *
+                               * saying that no data was transferred   *
+                               \***************************************/
+                               bp->b_flags |= B_ERROR;
+                               bp->b_error = retval;
+                               bp->b_resid =  bp->b_bcount;
+                               st_data[unit].flags 
+                                       &= ~(ST_AT_FILEMARK|ST_AT_EOM);
+                       }
+                       else
+                       {
+                       /***********************************************\
+                       * The error interpretation code has declared    *
+                       * that it wasn't a real error, or at least that *
+                       * we should be ignoring it if it was.           *
+                       \***********************************************/
+                           if(xs->resid && ( xs->resid != xs->datalen ))
+                           {
+                               /***************************************\
+                               * Here we have the tricky part..        *
+                               * We successfully read less data than   *
+                               * we requested. (but not 0)             *
+                               *------for variable blocksize tapes:----*
+                               * UNDER 386BSD:                         *
+                               * We should legitimatly have the error  *
+                               * bit set, with the error value set to  *
+                               * zero.. This is to indicate to the     *
+                               * physio code that while we didn't get  *
+                               * as much information as was requested, *
+                               * we did reach the end of the record    *
+                               * and so physio should not call us      *
+                               * again for more data... we have it all *
+                               * SO SET THE ERROR BIT!                 *
+                               *                                       *
+                               * UNDER MACH:(CMU)                      *
+                               * To indicate the same as above, we     *
+                               * need only have a non 0 resid that is  *
+                               * less than the b_bcount, but the       *
+                               * ERROR BIT MUST BE CLEAR! (sigh)       *
+                               *                                       *
+                               * UNDER OSF1:                           *
+                               * To indicate the same as above, we     *
+                               * need to have a non 0 resid that is    *
+                               * less than the b_bcount, but the       *
+                               * ERROR BIT MUST BE SET! (gasp)(sigh)   *
+                               *                                       *
+                               *-------for fixed blocksize device------*
+                               * We could have read some successful    *
+                               * records before hitting                *
+                               * the EOF or EOT. These must be passed  *
+                               * to the user, before we report the     *
+                               * EOx. Only if there is no data for the *
+                               * user do we report it now. (via an EIO *
+                               * for EOM and resid == count for EOF).  *
+                               * We will report the EOx NEXT time..    *
+                               \***************************************/
+#ifdef MACH /*osf and cmu varieties */
+#ifdef OSF
+                               bp->b_flags |= B_ERROR;
+#else  OSF
+                               bp->b_flags &= ~B_ERROR;
+#endif OSF
+#endif MACH
+#ifdef __386BSD__
+                               bp->b_flags |= B_ERROR;
+#endif __386BSD__
+                               bp->b_error = 0;
+                               bp->b_resid = xs->resid;
+                               if((st_data[unit].flags & ST_FIXEDBLOCKS))
+                               {
+                                       bp->b_resid *= st_data[unit].blkmin;
+                                       if(  (st_data[unit].flags & ST_AT_EOM)
+                                         && (bp->b_resid == bp->b_bcount))
+                                       {
+                                               bp->b_error = EIO;
+                                               st_data[unit].flags
+                                                       &= ~ST_AT_EOM;
+                                       }
+                               }
+                               xs->error = XS_NOERROR;
+                               break;
+                           }
+                           else
+                           {
+                               /***************************************\
+                               * We have come out of the error handler *
+                               * with no error code.. we have also     *
+                               * not had an ili (would have gone to    *
+                               * the previous clause). Now we need to  *
+                               * distiguish between succesful read of  *
+                               * no data (EOF or EOM) and successfull  *
+                               * read of all requested data.           *
+                               * At least all o/s agree that:          *
+                               * 0 bytes read with no error is EOF     *
+                               * 0 bytes read with an EIO is EOM       *
+                               \***************************************/
+
+                               bp->b_resid = bp->b_bcount;
+                               if(st_data[unit].flags & ST_AT_FILEMARK)
+                               {
+                                       st_data[unit].flags &= ~ST_AT_FILEMARK;
+                                       bp->b_flags &= ~B_ERROR;
+                                       bp->b_error = 0;
+                                       break;
+                               }
+                               if(st_data[unit].flags & ST_AT_EOM)
+                               {
+                                       bp->b_flags |= B_ERROR;
+                                       bp->b_error = EIO;
+                                       st_data[unit].flags &= ~ST_AT_EOM;
+                                       break;
+                               }
+                               printf("st%d:error ignored\n" ,unit);
+                           }
+                       }
+                       break;
+
+               case    XS_TIMEOUT:
+                       printf("st%d timeout\n",unit);
+                       break;
+
+               case    XS_BUSY:        /* should retry */ /* how? */
+                       /************************************************/
+                       /* SHOULD put buf back at head of queue         */
+                       /* and decrement retry count in (*xs)           */
+                       /* HOWEVER, this should work as a kludge        */
+                       /************************************************/
+                       if(xs->retries--)
+                       {
+                               xs->flags &= ~ITSDONE;
+                               xs->error = XS_NOERROR;
+                               if ( (*(st_data[unit].sc_sw->scsi_cmd))(xs)
+                                       == SUCCESSFULLY_QUEUED)
+                               {       /* don't wake the job, ok? */
+                                       return;
+                               }
+                               printf("device busy");
+                               xs->flags |= ITSDONE;
+                       }
+
+               case    XS_DRIVER_STUFFUP:
+                       bp->b_flags |= B_ERROR;
+                       bp->b_error = EIO;
+                       break;
+               default:
+                       printf("st%d: unknown error category from scsi driver\n"
+                               ,unit);
+               }       
+               biodone(bp);
+               xs->flags = 0;  /* no longer in use */
+               ststart(unit);          /* If there's another waiting.. do it */
+       }
+       else
+       {
+               wakeup(xs);
+       }
+}
+/*******************************************************\
+* Perform special action on behalf of the user         *
+* Knows about the internals of this device             *
+\*******************************************************/
+stioctl(dev, cmd, arg, mode)
+dev_t dev;
+int cmd;
+caddr_t arg;
+{
+       register i,j;
+       unsigned int opri;
+       int errcode = 0;
+       unsigned char unit;
+       int number,flags,ret;
+
+       /*******************************************************\
+       * Find the device that the user is talking about        *
+       \*******************************************************/
+       flags = 0;      /* give error messages, act on errors etc. */
+       unit = UNIT(dev);
+
+       switch(cmd)
+       {
+
+       case MTIOCGET:
+           {
+               struct mtget *g = (struct mtget *) arg;
+
+               bzero(g, sizeof(struct mtget));
+               g->mt_type = 0x7;       /* Ultrix compat */ /*?*/
+               ret=TRUE;
+               break;
+           }
+
+
+       case MTIOCTOP:
+           {
+               struct mtop *mt = (struct mtop *) arg;
+
+               if (st_debug)
+                       printf("[sctape_sstatus: %x %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 */
+                       ret = st_write_filemarks(unit,number,flags);
+                       st_data[unit].flags &= ~ST_WRITTEN;
+                       break;
+               case MTFSF:     /* forward space file */
+                       ret = st_space(unit,number,SP_FILEMARKS,flags);
+                       break;
+               case MTBSF:     /* backward space file */
+                       ret = st_space(unit,-number,SP_FILEMARKS,flags);
+                       break;
+               case MTFSR:     /* forward space record */
+                       ret = st_space(unit,number,SP_BLKS,flags);
+                       break;
+               case MTBSR:     /* backward space record */
+                       ret = st_space(unit,-number,SP_BLKS,flags);
+                       break;
+               case MTREW:     /* rewind */
+                       ret = st_rewind(unit,FALSE,flags);
+                       break;
+               case MTOFFL:    /* rewind and put the drive offline */
+                       if((ret = st_rewind(unit,FALSE,flags)))
+                       {
+                               st_prevent(unit,PR_ALLOW,0);
+                               ret = st_load(unit,LD_UNLOAD,flags);
+                       }
+                       else
+                       {
+                               printf("rewind failed, unit still loaded\n");
+                       }
+                       break;
+               case MTNOP:     /* no operation, sets status only */
+               case MTCACHE:   /* enable controller cache */
+               case MTNOCACHE: /* disable controller cache */
+                       ret = TRUE;;
+                       break;
+               default:
+                       return EINVAL;
+               }
+               break;
+           }
+       case MTIOCIEOT:
+       case MTIOCEEOT:
+               ret=TRUE;
+               break;
+       }
+
+       return(ret?ESUCCESS:EIO);
+}
+
+
+/*******************************************************\
+* Check with the device that it is ok, (via scsi driver)*
+\*******************************************************/
+st_req_sense(unit, flags)
+int    flags;
+{
+       struct  scsi_sense_data sense;
+       struct  scsi_sense      scsi_cmd;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = REQUEST_SENSE;
+       scsi_cmd.length = sizeof(sense);
+
+       if (st_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       &sense,
+                       sizeof(sense),
+                       100000,
+                       flags | SCSI_DATA_IN) != 0)
+       {
+               return(FALSE);
+       }
+       else 
+               return(TRUE);
+}
+
+/*******************************************************\
+* Get scsi driver to send a "are you ready" command    *
+\*******************************************************/
+st_test_ready(unit,flags)
+int    unit,flags;
+{
+       struct  scsi_test_unit_ready scsi_cmd;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = TEST_UNIT_READY;
+
+       if (st_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       100000,
+                       flags) != 0) {
+               return(FALSE);
+       } else 
+               return(TRUE);
+}
+
+
+#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.   *
+\*******************************************************/
+st_rd_blk_lim(unit, flags)
+int    unit,flags;
+{
+       struct  scsi_blk_limits scsi_cmd;
+       struct scsi_blk_limits_data scsi_blkl;
+       struct st_data *st = st_data + unit;
+       /*******************************************************\
+       * First check if we have it all loaded                  *
+       \*******************************************************/
+       if (st_info_valid[unit]) goto done;
+
+       /*******************************************************\
+       * 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 (st_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       &scsi_blkl,
+                       sizeof(scsi_blkl),
+                       5000,
+                       flags | SCSI_DATA_IN) != 0)
+       {
+               if(!(flags & SCSI_SILENT))
+                       printf("could not get blk limits for unit %d\n", unit);
+               st_info_valid[unit] = FALSE;
+               return(FALSE);
+       } 
+       if (st_debug)
+       {
+               printf(" (%d <= blksiz <= %d\n) ",
+                       b2tol(scsi_blkl.min_length),
+                       _3btol(&scsi_blkl.max_length_2));
+       }
+       st->blkmin = b2tol(scsi_blkl.min_length);
+       st->blkmax = _3btol(&scsi_blkl.max_length_2);
+
+done:
+       if(st->blkmin && (st->blkmin == st->blkmax))
+       {
+               st->flags |= ST_FIXEDBLOCKS;
+       }
+       return(TRUE);
+}
+/*******************************************************\
+* Get the scsi driver to send a full inquiry to the    *
+* device and use the results to fill out the global    *
+* parameter structure.                                 *
+\*******************************************************/
+st_mode_sense(unit, flags)
+int    unit,flags;
+{
+       struct scsi_mode_sense          scsi_cmd;
+       struct 
+       {
+               struct  scsi_mode_header_tape header;
+               struct  blk_desc        blk_desc;
+       }scsi_sense;
+       struct  st_data *st = st_data + unit;
+
+       /*******************************************************\
+       * First check if we have it all loaded                  *
+       \*******************************************************/
+       if (st_info_valid[unit]) return(TRUE);
+       /*******************************************************\
+       * First do a mode sense                                 *
+       \*******************************************************/
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = MODE_SENSE;
+       scsi_cmd.length = sizeof(scsi_sense);
+       /*******************************************************\
+       * do the command, but we don't need the results         *
+       * just print them for our interest's sake               *
+       \*******************************************************/
+       if (st_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       &scsi_sense,
+                       sizeof(scsi_sense),
+                       5000,
+                       flags | SCSI_DATA_IN) != 0)
+       {
+               if(!(flags & SCSI_SILENT))
+                       printf("could not mode sense for unit %d\n", unit);
+               st_info_valid[unit] = FALSE;
+               return(FALSE);
+       } 
+       if (st_debug)
+       {
+               printf("unit %d: %d blocks of %d bytes, write %s, %sbuffered",
+                       unit,
+                       _3btol(&scsi_sense.blk_desc.nblocks),
+                       _3btol(&scsi_sense.blk_desc.blklen),
+                       (scsi_sense.header.write_protected ?
+                               "protected" : "enabled"),
+                       (scsi_sense.header.buf_mode ?
+                               "" : "un")
+                       );
+       }
+       st->numblks = _3btol(&scsi_sense.blk_desc.nblocks);
+       st->blksiz = _3btol(&scsi_sense.blk_desc.blklen);
+       return(TRUE);
+}
+
+/*******************************************************\
+* Get the scsi driver to send a full inquiry to the    *
+* device and use the results to fill out the global    *
+* parameter structure.                                 *
+\*******************************************************/
+st_mode_select(unit, flags, dsty_code)
+int    unit,flags,dsty_code;
+{
+       struct scsi_mode_select scsi_cmd;
+       struct
+       {
+               struct  scsi_mode_header_tape header;
+               struct  blk_desc        blk_desc;
+       }dat;
+       struct  st_data *st = st_data + unit;
+
+       /*******************************************************\
+       * Set up for a mode select                              *
+       \*******************************************************/
+       bzero(&dat, sizeof(dat));
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = MODE_SELECT;
+       scsi_cmd.length = sizeof(dat);
+       dat.header.blk_desc_len = sizeof(struct  blk_desc);
+       dat.header.buf_mode = 1;
+        dat.blk_desc.density = dsty_code;
+       if(st->flags & ST_FIXEDBLOCKS)
+       {
+               lto3b( st->blkmin , dat.blk_desc.blklen);
+       }
+/*     lto3b( st->numblks , dat.blk_desc.nblocks); use defaults!!!!
+       lto3b( st->blksiz , dat.blk_desc.blklen);
+*/
+       /*******************************************************\
+       * do the command                                        *
+       \*******************************************************/
+       if (st_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       &dat,
+                       sizeof(dat),
+                       5000,
+                       flags | SCSI_DATA_OUT) != 0)
+       {
+               if(!(flags & SCSI_SILENT))
+                       printf("could not mode select for unit %d\n", unit);
+               st_info_valid[unit] = FALSE;
+               return(FALSE);
+       } 
+       return(TRUE);
+}
+
+/*******************************************************\
+* skip N blocks/filemarks/seq filemarks/eom            *
+\*******************************************************/
+st_space(unit,number,what,flags)
+int    unit,number,what,flags;
+{
+       struct scsi_space scsi_cmd;
+
+       /* if we are at a filemark now, we soon won't be*/
+       st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM);
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = SPACE;
+       scsi_cmd.code = what;
+       lto3b(number,scsi_cmd.number);
+       if (st_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       600000, /* 10 mins enough? */
+                       flags) != 0)
+       {
+               if(!(flags & SCSI_SILENT))
+                       printf("could not space st%d\n", unit);
+               st_info_valid[unit] = FALSE;
+               return(FALSE);
+       }
+       return(TRUE);
+}
+/*******************************************************\
+* write N filemarks                                    *
+\*******************************************************/
+st_write_filemarks(unit,number,flags)
+int    unit,number,flags;
+{
+       struct scsi_write_filemarks scsi_cmd;
+
+       st_data[unit].flags &= ~(ST_AT_FILEMARK);
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = WRITE_FILEMARKS;
+       lto3b(number,scsi_cmd.number);
+       if (st_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       100000, /* 10 secs.. (may need to repos head )*/
+                       flags) != 0)
+       {
+               if(!(flags & SCSI_SILENT))
+                       printf("could not write_filemarks st%d\n", unit);
+               st_info_valid[unit] = FALSE;
+               return(FALSE);
+       }
+       return(TRUE);
+}
+/*******************************************************\
+* load /unload (with retension if true)                        *
+\*******************************************************/
+st_load(unit,type,flags)
+int    unit,type,flags;
+{
+       struct  scsi_load  scsi_cmd;
+
+       st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM);
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = LOAD_UNLOAD;
+       scsi_cmd.load=type;
+       if (type == LD_LOAD)
+       {
+               /*scsi_cmd.reten=TRUE;*/
+               scsi_cmd.reten=FALSE;
+       }
+       else
+       {
+               scsi_cmd.reten=FALSE;
+       }
+       if (st_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       30000, /* 30 secs */
+                       flags) != 0)
+       {
+               if(!(flags & SCSI_SILENT))
+                       printf("cannot load/unload  st%d\n", unit);
+               st_info_valid[unit] = FALSE;
+               return(FALSE);
+       }
+       return(TRUE);
+}
+/*******************************************************\
+* Prevent or allow the user to remove the tape         *
+\*******************************************************/
+st_prevent(unit,type,flags)
+int    unit,type,flags;
+{
+       struct  scsi_prevent    scsi_cmd;
+
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = PREVENT_ALLOW;
+       scsi_cmd.prevent=type;
+       if (st_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       5000,
+                       flags) != 0)
+       {
+               if(!(flags & SCSI_SILENT))
+                       printf("cannot prevent/allow on st%d\n", unit);
+               st_info_valid[unit] = FALSE;
+               return(FALSE);
+       }
+       return(TRUE);
+}
+/*******************************************************\
+*  Rewind the device                                   *
+\*******************************************************/
+st_rewind(unit,immed,flags)
+int    unit,immed,flags;
+{
+       struct  scsi_rewind     scsi_cmd;
+
+       st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM);
+       bzero(&scsi_cmd, sizeof(scsi_cmd));
+       scsi_cmd.op_code = REWIND;
+       scsi_cmd.immed=immed;
+       if (st_scsi_cmd(unit,
+                       &scsi_cmd,
+                       sizeof(scsi_cmd),
+                       0,
+                       0,
+                       immed?5000:300000, /* 5 sec or 5 min */
+                       flags) != 0)
+       {
+               if(!(flags & SCSI_SILENT))
+                       printf("could not rewind st%d\n", unit);
+               st_info_valid[unit] = FALSE;
+               return(FALSE);
+       }
+       return(TRUE);
+}
+/*******************************************************\
+* ask the scsi driver to perform a command for us.     *
+* Call it through the switch table, and tell it which  *
+* sub-unit we want, and what target and lu we wish to  *
+* talk to. Also tell it where to find the command      *
+* how long int is.                                     *
+* Also tell it where to read/write the data, and how   *
+* long the data is supposed to be                      *
+\*******************************************************/
+int    st_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags)
+
+int    unit,flags;
+struct scsi_generic *scsi_cmd;
+int    cmdlen;
+int    timeout;
+u_char *data_addr;
+int    datalen;
+{
+       struct  scsi_xfer *xs;
+       int     retval;
+       int     s;
+       struct  st_data *st = st_data + unit;
+
+       if(scsi_debug & PRINTROUTINES) printf("\nst_scsi_cmd%d ",unit);
+       if(st->sc_sw)   /* If we have a scsi driver */
+       {
+
+               xs = &(st_scsi_xfer[unit]);
+               if(!(flags & SCSI_NOMASK))
+                       s = splbio();
+               st_xfer_block_wait[unit]++;     /* there is someone waiting */
+               while (xs->flags & INUSE)
+               {
+                       sleep(&st_xfer_block_wait[unit],PRIBIO+1);
+               }
+               st_xfer_block_wait[unit]--;
+               xs->flags = INUSE;
+               if(!(flags & SCSI_NOMASK))
+                       splx(s);
+
+               /*******************************************************\
+               * Fill out the scsi_xfer structure                      *
+               \*******************************************************/
+               xs->flags       |=      flags;
+               xs->adapter     =       st->ctlr;
+               xs->targ        =       st->targ;
+               xs->lu          =       st->lu;
+               xs->retries     =       ST_RETRIES;
+               xs->timeout     =       timeout;
+               xs->cmd         =       scsi_cmd;
+               xs->cmdlen      =       cmdlen;
+               xs->data        =       data_addr;
+               xs->datalen     =       datalen;
+               xs->resid       =       datalen;
+               xs->when_done   =       (flags & SCSI_NOMASK)
+                                       ?(int (*)())0
+                                       :st_done;
+               xs->done_arg    =       unit;
+               xs->done_arg2   =       (int)xs;
+retry:         xs->error       =       XS_NOERROR;
+               xs->bp          =       0;
+               retval = (*(st->sc_sw->scsi_cmd))(xs);
+               switch(retval)
+               {
+               case    SUCCESSFULLY_QUEUED:
+                       s = splbio();
+                       while(!(xs->flags & ITSDONE))
+                               sleep(xs,PRIBIO+1);
+                       splx(s);
+
+               case    HAD_ERROR:
+               case    COMPLETE:
+                       switch(xs->error)
+                       {
+                       case    XS_NOERROR:
+                               retval = ESUCCESS;
+                               break;
+                       case    XS_SENSE:
+                               retval = (st_interpret_sense(unit,xs));
+                               /* only useful for reads */
+                               if (retval)
+                               { /* error... don't care about filemarks */
+                                       st->flags &= ~(ST_AT_FILEMARK
+                                                       | ST_AT_EOM);
+                               }
+                               else
+                               {
+                                       xs->error = XS_NOERROR;
+                                       retval = ESUCCESS;
+                               }
+                               break;
+                       case    XS_DRIVER_STUFFUP:
+                               retval = EIO;
+                               break;
+                       case    XS_TIMEOUT:
+                               if(xs->retries-- )
+                               {
+                                       xs->flags &= ~ITSDONE;
+                                       goto retry;
+                               }
+                               retval = EIO;
+                               break;
+                       case    XS_BUSY:
+                               if(xs->retries-- )
+                               {
+                                       xs->flags &= ~ITSDONE;
+                                       goto retry;
+                               }
+                               retval = EIO;
+                               break;
+                       default:
+                               retval = EIO;
+                               printf("st%d: unknown error category from scsi driver\n"
+                                       ,unit);
+                               break;
+                       }       
+                       break;
+               case    TRY_AGAIN_LATER:
+                       if(xs->retries-- )
+                       {
+                               xs->flags &= ~ITSDONE;
+                               goto retry;
+                       }
+                       retval = EIO;
+                       break;
+               default:
+                       retval = EIO;
+               }
+               xs->flags = 0;  /* it's free! */
+               ststart(unit);
+       }
+       else
+       {
+               printf("st%d: not set up\n",unit);
+               return(EINVAL);
+       }
+       return(retval);
+}
+/***************************************************************\
+* Look at the returned sense and act on the error and detirmine        *
+* The unix error number to pass back... (0 = report no error)  *
+\***************************************************************/
+
+int    st_interpret_sense(unit,xs)
+int    unit;
+struct scsi_xfer       *xs;
+{
+       struct  scsi_sense_data *sense;
+       int     key;
+       int     silent = xs->flags & SCSI_SILENT;
+
+       /***************************************************************\
+       * If errors are ok, report a success                            *
+       \***************************************************************/
+       if(xs->flags & SCSI_ERR_OK) return(ESUCCESS);
+
+       /***************************************************************\
+       * Get the sense fields and work out what CLASS                  *
+       \***************************************************************/
+       sense = &(xs->sense);
+       if(st_debug)
+       {
+               int count = 0;
+               printf("code%x class%x valid%x\n"
+                               ,sense->error_code
+                               ,sense->error_class
+                               ,sense->valid);
+               printf("seg%x key%x ili%x eom%x fmark%x\n"
+                               ,sense->ext.extended.segment
+                               ,sense->ext.extended.sense_key
+                               ,sense->ext.extended.ili
+                               ,sense->ext.extended.eom
+                               ,sense->ext.extended.filemark);
+               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");
+       }
+       switch(sense->error_class)
+       {
+       /***************************************************************\
+       * If it's class 7, use the extended stuff and interpret the key *
+       \***************************************************************/
+       case 7:
+       {
+               if(sense->ext.extended.eom)
+               {
+                       st_data[unit].flags |= ST_AT_EOM;
+               }
+
+               if(sense->ext.extended.filemark)
+               {
+                       st_data[unit].flags |= ST_AT_FILEMARK;
+               }
+
+               if(sense->ext.extended.ili)
+               {
+                       if(sense->valid)
+                       {
+                               /*******************************\
+                               * In all ili cases, note that   *
+                               * the resid is non-0 AND not    *
+                               * unchanged.                    *
+                               \*******************************/
+                               xs->resid
+                                  = ntohl(*((long *)sense->ext.extended.info));
+                               if(xs->bp)
+                               {
+                                       if(xs->resid < 0)
+                                       { /* never on block devices */
+                                               /***********************\
+                                               * it's only really bad  *
+                                               * if we have lost data  *
+                                               * (the record was       *
+                                               * bigger than the read) *
+                                               \***********************/
+                                               return(EIO);
+                                       }
+                               }       
+                       }
+                       else
+                       { /* makes no sense.. complain */
+                               printf("BAD length error?");
+                       }
+               }/* there may be some other error. check the rest */
+
+               key=sense->ext.extended.sense_key;
+               switch(key)
+               {
+               case    0x0:
+                       return(ESUCCESS);
+               case    0x1:
+                       if(!silent)
+                       {
+                               printf("st%d: soft error(corrected) ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(ESUCCESS);
+               case    0x2:
+                       if(!silent) printf("st%d: not ready\n ", unit); 
+                       return(ENODEV);
+               case    0x3:
+                       if(!silent)
+                       {
+                               printf("st%d: medium error ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(EIO);
+               case    0x4:
+                       if(!silent) printf("st%d: non-media hardware failure\n ",
+                               unit); 
+                       return(EIO);
+               case    0x5:
+                       if(!silent) printf("st%d: illegal request\n ", unit); 
+                       return(EINVAL);
+               case    0x6:
+                       if(!silent) printf("st%d: Unit attention.\n ", unit); 
+                       st_data[unit].flags &= ~(ST_AT_FILEMARK|ST_AT_EOM);
+                       st_info_valid[unit] = FALSE;
+                       if (st_data[unit].flags & ST_OPEN) /* TEMP!!!! */
+                               return(EIO);
+                       else
+                               return(ESUCCESS);
+               case    0x7:
+                       if(!silent)
+                       {
+                               printf("st%d: attempted protection violation "
+                                                               , unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(EACCES);
+               case    0x8:
+                       if(!silent)
+                       {
+                               printf("st%d: block wrong state (worm)\n "
+                                                       , unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(EIO);
+               case    0x9:
+                       if(!silent) printf("st%d: vendor unique\n",
+                               unit); 
+                       return(EIO);
+               case    0xa:
+                       if(!silent) printf("st%d: copy aborted\n ",
+                               unit); 
+                       return(EIO);
+               case    0xb:
+                       if(!silent) printf("st%d: command aborted\n ",
+                               unit); 
+                       return(EIO);
+               case    0xc:
+                       if(!silent)
+                       {
+                               printf("st%d: search returned\n ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(ESUCCESS);
+               case    0xd:
+                       if(!silent) printf("st%d: volume overflow\n ",
+                               unit); 
+                       return(ENOSPC);
+               case    0xe:
+                       if(!silent)
+                       {
+                               printf("st%d: verify miscompare\n ", unit); 
+                               if(sense->valid)
+                               {
+                                       printf("block no. %d (decimal)\n",
+                                       (sense->ext.extended.info[0] <<24)|
+                                       (sense->ext.extended.info[1] <<16)|
+                                       (sense->ext.extended.info[2] <<8)|
+                                       (sense->ext.extended.info[3] ));
+                               }
+                               else
+                               {
+                                       printf("\n");
+                               }
+                       }
+                       return(EIO);
+               case    0xf:
+                       if(!silent) printf("st%d: unknown error key\n ",
+                               unit); 
+                       return(EIO);
+               }
+               break;
+       }
+       /***************************************************************\
+       * If it's NOT class 7, just report it.                          *
+       \***************************************************************/
+       case 0:
+       case 1:
+       case 2:
+       case 3:
+       case 4:
+       case 5:
+       case 6:
+               {
+                       if(!silent) printf("st%d: error class %d code %d\n",
+                               unit,
+                               sense->error_class,
+                               sense->error_code);
+               if(sense->valid)
+                       if(!silent) printf("block no. %d (decimal)\n",
+                       (sense->ext.unextended.blockhi <<16),
+                       + (sense->ext.unextended.blockmed <<8),
+                       + (sense->ext.unextended.blocklow ));
+               }
+               return(EIO);
+       }
+}
+
+#if defined(OSF)
+
+stsize(dev_t dev)
+{
+    printf("stsize()        -- not implemented\n");
+    return(0);
+}
+
+stdump()
+{
+    printf("stdump()        -- not implemented\n");
+    return(-1);
+}
+
+#endif /* defined(OSF) */
+
diff --git a/usr/src/sys.386bsd/sys/cdio.h b/usr/src/sys.386bsd/sys/cdio.h
new file mode 100644 (file)
index 0000000..0bd4ea7
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ */
+/* Shared between kernel & process */
+
+struct cd_toc_entry {
+       u_char  :8;
+       u_char  control:4;
+       u_char  addr_type:4;
+       u_char  track;
+       u_char  :8;
+       u_char  addr[4];
+};
+
+struct cd_sub_channel_header {
+       u_char  :8;   
+       u_char  audio_status; 
+#define CD_AS_AUDIO_INVALID        0x00
+#define CD_AS_PLAY_IN_PROGRESS     0x11
+#define CD_AS_PLAY_PAUSED          0x12
+#define CD_AS_PLAY_COMPLETED       0x13
+#define CD_AS_PLAY_ERROR           0x14
+#define CD_AS_NO_STATUS            0x15
+       u_char  data_len[2];
+};
+
+struct cd_sub_channel_position_data {
+       u_char  data_format;
+       u_char  control:4;
+       u_char  addr_type:4;
+       u_char  track_number;
+       u_char  index_number;
+       u_char  absaddr[4];
+       u_char  reladdr[4];
+};
+
+struct cd_sub_channel_media_catalog {
+        u_char  data_format;
+        u_char  :8;
+        u_char  :8;
+        u_char  :8;
+        u_char  :7;
+        u_char  mc_valid:1;
+        u_char  mc_number[15]; 
+};
+
+struct cd_sub_channel_track_info {
+        u_char  data_format;
+        u_char  :8;
+        u_char  track_number;
+        u_char  :8;
+        u_char  :7;
+        u_char  ti_valid:1;   
+        u_char  ti_number[15]; 
+};
+
+struct cd_sub_channel_info {
+       struct cd_sub_channel_header header;
+       union {
+               struct cd_sub_channel_position_data position;
+               struct cd_sub_channel_media_catalog media_catalog;
+               struct cd_sub_channel_track_info track_info;    
+       } what;
+};
+
+/***************************************************************\
+* Ioctls for the CD drive                                      *
+\***************************************************************/
+struct ioc_play_track
+{
+       u_char  start_track;
+       u_char  start_index;
+       u_char  end_track;
+       u_char  end_index;
+};
+
+#define        CDIOCPLAYTRACKS _IOW('c',1,struct ioc_play_track)
+struct ioc_play_blocks
+{
+       int     blk;
+       int     len;
+};
+#define        CDIOCPLAYBLOCKS _IOW('c',2,struct ioc_play_blocks)
+
+struct ioc_read_subchannel {
+       u_char address_format;
+#define CD_LBA_FORMAT  1
+#define CD_MSF_FORMAT  2
+       u_char data_format;
+#define CD_SUBQ_DATA           0
+#define CD_CURRENT_POSITION    1
+#define CD_MEDIA_CATALOG       2
+#define CD_TRACK_INFO          3
+       u_char track;
+       int     data_len;
+       struct  cd_sub_channel_info *data;
+};
+#define CDIOCREADSUBCHANNEL _IOWR('c', 3 , struct ioc_read_subchannel )
+
+
+struct ioc_toc_header {
+       u_short len;
+       u_char  starting_track;
+       u_char  ending_track;
+};
+
+#define CDIOREADTOCHEADER _IOR('c',4,struct ioc_toc_header)
+
+struct ioc_read_toc_entry {
+       u_char  address_format;
+       u_char  starting_track;
+       u_short data_len;
+       struct  cd_toc_entry *data;
+};
+#define CDIOREADTOCENTRYS _IOWR('c',5,struct ioc_read_toc_entry)
+
+struct ioc_patch
+{
+       u_char  patch[4];       /* one for each channel */
+};
+#define        CDIOCSETPATCH   _IOW('c',9,struct ioc_patch)
+struct ioc_vol
+{
+       u_char  vol[4]; /* one for each channel */
+};
+#define        CDIOCGETVOL     _IOR('c',10,struct ioc_vol)
+#define        CDIOCSETVOL     _IOW('c',11,struct ioc_vol)
+#define        CDIOCSETMONO    _IO('c',12)
+#define        CDIOCSETSTERIO  _IO('c',13)
+#define        CDIOCSETMUTE    _IO('c',14)
+#define        CDIOCSETLEFT    _IO('c',15)
+#define        CDIOCSETRIGHT   _IO('c',16)
+#define        CDIOCSETDEBUG   _IO('c',17)
+#define        CDIOCCLRDEBUG   _IO('c',18)
+#define        CDIOCPAUSE      _IO('c',19)
+#define        CDIOCRESUME     _IO('c',20)
+#define        CDIOCRESET      _IO('c',21)
+#define        CDIOCSTART      _IO('c',22)
+#define        CDIOCSTOP       _IO('c',23)
+#define        CDIOCEJECT      _IO('c',24)
+
+
+
diff --git a/usr/src/sys.386bsd/sys/chio.h b/usr/src/sys.386bsd/sys/chio.h
new file mode 100644 (file)
index 0000000..532e6ee
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1982, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by 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.
+ *
+ *     @(#)chio.h      7.6 (Berkeley) 2/5/91
+ *
+ * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
+ * --------------------         -----   ----------------------
+ * CURRENT PATCH LEVEL:         1       00098
+ * --------------------         -----   ----------------------
+ *
+ * 16 Feb 93   Julian Elischer         ADDED for SCSI system
+ */
+
+/* This is a "convertet" mtio.h from 386BSD 
+   Stefan Grefen grefen@goofy.zdv.uni-mainz.de 
+ */
+
+/*
+ * Structures and definitions for changer io control commands
+ */
+#ifndef _CHIO_H_
+#define _CHIO_H_
+
+#define CH_INVERT              0x10000
+#define CH_ADDR_MASK           0xffff
+struct chop {
+       short   ch_op;          /* operations defined below */
+       short   result;         /* The result               */
+       union {
+          struct {
+               int chm;                /* Transport element */
+               int from;
+               int to;
+          } move;
+          struct {
+               int chm;                /* Transport element */
+               int to;
+          } position; 
+          struct {
+               short   chmo;                   /* Offset of first CHM */
+               short   chms;                   /* No. of CHM */
+               short   slots;                  /* No. of Storage Elements */
+                short   sloto;                  /* Offset of first SE */
+                short   imexs;                  /* No. of Import/Export Slots */
+                short   imexo;                  /* Offset of first IM/EX */
+                short   drives;                 /* No. of CTS */
+                short   driveo;                 /* Offset of first CTS */
+                short   rot;                    /* CHM can rotate */
+          } getparam;
+          struct {
+               int type;
+#define CH_CHM 1
+#define CH_STOR        2
+#define CH_IMEX        3
+#define CH_CTS 4
+               int from;
+               struct {
+                       u_char elema_1;
+                       u_char elema_0;
+                       u_char full:1;
+                       u_char rsvd:1;
+                       u_char except:1;
+                       u_char :5;
+                       u_char rsvd2;
+                       union {
+                               struct {
+                               u_char add_sense_code;
+                               u_char add_sense_code_qualifier;
+                               } specs;
+                               short add_sense;
+/* WARINING LSB only */
+#define CH_CHOLDER     0x0290  /* Cartridge holder is missing */
+#define CH_STATUSQ     0x0390  /* Status is questionable */
+#define CH_CTS_CLOSED  0x0490  /* CTS door is closed */
+
+                       } ch_add_sense;
+                       u_char rsvd3[3];
+                       u_char :6;
+                       u_char invert:1;
+                       u_char svalid:1;
+                       u_char source_1;
+                       u_char source_0;
+                       u_char rsvd4[4];
+                       } elem_data;
+               } get_elem_stat;
+       } u;
+};
+
+/* operations */
+#define CHMOVE                         1
+#define CHPOSITION                     2
+#define CHGETPARAM                     3
+#define CHGETELEM                      4
+
+
+/* Changer IO control command */
+#define        CHIOOP  _IOWR('c', 1, struct chop)      /* do a mag tape op */
+#endif