date and time created 90/11/08 20:14:00 by bill
authorBill Joy <bill@ucbvax.Berkeley.EDU>
Fri, 9 Nov 1990 12:14:00 +0000 (04:14 -0800)
committerBill Joy <bill@ucbvax.Berkeley.EDU>
Fri, 9 Nov 1990 12:14:00 +0000 (04:14 -0800)
SCCS-vsn: sys/i386/isa/wt.c 1.1

usr/src/sys/i386/isa/wt.c [new file with mode: 0644]

diff --git a/usr/src/sys/i386/isa/wt.c b/usr/src/sys/i386/isa/wt.c
new file mode 100644 (file)
index 0000000..70ea0e8
--- /dev/null
@@ -0,0 +1,1136 @@
+#include "wt.h"
+#if NWT > 0
+/* 
+ * Mach Operating System
+ * Copyright (c) 1989 Carnegie-Mellon University
+ * All rights reserved.  The CMU software License Agreement specifies
+ * the terms and conditions for use and redistribution.
+ *     @(#)wt.c        1.1 (Berkeley) %G%
+ */
+/* 
+ * HISTORY
+ * $Log:       wt.c,v $
+ * Revision 2.2.1.3  90/01/08  13:29:38  rvb
+ *     Add Intel copyright.
+ *     [90/01/08            rvb]
+ * 
+ * Revision 2.2.1.2  89/12/21  18:00:09  rvb
+ *     Change WTPRI to make the streamer tape read/write
+ *     interruptible.          [lin]
+ * 
+ * Revision 2.2.1.1  89/11/10  09:49:49  rvb
+ *     ORC likes their streamer port at 0x288.
+ *     [89/11/08            rvb]
+ * 
+ * Revision 2.2  89/09/25  12:33:02  rvb
+ *     Driver was provided by Intel 9/18/89.
+ *     [89/09/23            rvb]
+ * 
+ */
+
+/*
+ *
+ *  Copyright 1988, 1989 by Intel Corporation
+ *
+ *     Support Bell Tech QIC-02 and WANGTEK QIC-36 or QIC-02
+ */
+
+#include <sys/errno.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/dir.h>
+#include <sys/user.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <machine/isa/wtreg.h>
+
+#ifdef ORC
+unsigned wtport = 0x288;       /* base I/O port of controller  */
+#else  ORC
+unsigned wtport = 0x300;       /* base I/O port of controller  */
+#endif ORC
+                               /* standard = 0x300             */
+                               /* alternate = 0x338            */
+
+unsigned wtchan = 1;           /* DMA channel number           */
+                               /* stardard = 1                 */
+                               /* hardware permits 1, 2 or 3.  */
+                               /* (Avoid DMA 2: used by disks) */
+
+int    first_wtopen_ever = 1;
+
+
+#define        ERROR           1       /* return from tape routines */
+#define        SUCCESS         0       /* return from tape routines */
+
+int    wci = 0;
+int    exflag = 0;
+int    bytes = 0;
+
+static unsigned char eqdma = 0x8;
+static unsigned char pagereg = 0x83;
+static unsigned char dmareg = 2;
+static unsigned char dma_write = 0x49;
+static unsigned char dma_read = 0x45;
+static unsigned char dma_done = 2;
+static unsigned char mode = 0;
+static unsigned char mbits;    /* map bits into each other */
+static long bufptr;
+static unsigned numbytes;
+/*
+_wci           dw      0       ; interrupt chain finished normally
+_exflag                dw      0       ; exception variable
+_bytes         dw      0       ; current bytes
+
+eqdma          db      8h      ; enable dma command: ch1,ch2=8h, ch3=10h
+pagereg                db      83h     ; ch1=83h, ch2=81h, ch3=82h
+dmareg         db      2       ; ch1=2, ch2=4, ch3=6
+dma_write      db      49h     ; write dma command: 48h+_wtchan
+dma_read       db      45h     ; read dma command: 44h+_wtchan
+dma_done       db      2       ; dma done flag: 1<<_wtchan
+mode           db      0       ; dma operation mode
+lbufptr                dw      0       ; buffer pointer to data buffers, low word
+hbufptr                dw      0       ; buffer pointer to data buffers, high word
+numbytes       dw      0       ; number of bytes to read or write (new)
+*/
+
+#define PAGESIZ                4096
+#define HZ             60
+
+/* tape controller ports */
+#define STATPORT       wtport
+#define CTLPORT                STATPORT
+#define CMDPORT                (wtport+1)
+#define DATAPORT       CMDPORT
+
+/* defines for reading out status from wangtek tape controller */
+#define READY          0x01    /* ready bit define        */
+#define EXCEP          0x02    /* exception bit define    */
+#define STAT           (READY|EXCEP)
+#define        RESETMASK       0x7
+#define        RESETVAL        (RESETMASK & ~EXCEP)
+
+/* tape controller control bits (CTLPORT) */
+#define        ONLINE  0x01
+#define        RESET   0x02
+#define        REQUEST 0x04            /* request command */
+#define        CMDOFF  0xC0
+
+/* QIC-02 commands (CMDPORT) */
+#define        RDDATA  0x80            /* read data */
+#define        READFM  0xA0            /* read file mark */
+#define        WRTDATA 0x40            /* write data */
+#define        WRITEFM 0x60            /* write file mark */
+#define        RDSTAT  0xC0            /* read status command */
+#define        REWIND  0x21            /* rewind command (position+bot) */
+
+/* 8237 DMA controller regs */
+#define        STATUSREG       0x8
+#define MASKREG                0xA
+#define MODEREG                0xB
+#define CLEARFF                0xC
+
+/* streamer tape block size */
+#define BLKSIZE        512
+
+/* Tape characteristics */
+#define        NBPS            512     /* 512-byte blocks */
+#define        ERROR           1       /* return from tape routines */
+#define        SUCCESS         0       /* return from tape routines */
+
+/* Minor devs */
+#define        TP_REWCLOSE(d)  ((minor(d)&04) == 0) /* Rewind tape on close if read/write */
+#define        TP_DENS(dev)    ((minor(dev) >> 3) & 03) /* set density */
+#define TPHOG(d)       0       /* use Hogproc during tape I/O  */
+
+/* defines for wtflags */
+#define        TPINUSE 0x0001          /* tape is already open */
+#define        TPREAD  0x0002          /* tape is only open for reading */
+#define        TPWRITE 0x0004          /* tape is only open for writing */
+#define        TPSTART 0x0008          /* tape must be rewound and reset */
+#define        TPDEAD  0x0010          /* tape drive does not work or driver error */
+#define        TPSESS  0x0020          /* no more reads or writes allowed in session */
+                               /* for example, when tape has to be changed */
+#define        TPSTOP  0x0040          /* Stop command outstanding */
+#define        TPREW   0x0080          /* Rewind command outstanding, see wtdsl2() */
+#define        TPVOL   0x0100          /* Read file mark, or hit end of tape */
+#define        TPWO    0x0200          /* write command outstanding */
+#define        TPRO    0x0400          /* read command outstanding */
+#define TPWANY 0x0800          /* write command requested */
+#define TPRANY 0x1000          /* read command requested */
+#define        TPWP    0x2000          /* write protect error seen */
+
+unsigned int   wtflags = TPSTART;      /* state of tape drive */
+
+struct buf     rwtbuf;         /* header for raw i/o */
+struct  proc   *myproc;        /* process which opened tape driver */
+
+char wtimeron;                 /* wtimer() active flag */
+char wtio;                     /* dma (i/o) active flag */
+char isrlock;                  /* isr() flag */
+
+struct proc * Hogproc; /* no Hogproc on Microport */
+#define        ftoseg(x)       ((unsigned) (x >> 16))
+#define        seterror(err)   u.u_error = err
+
+struct wtstatus {
+       ushort  wt_err;         /* code for error encountered */
+       ushort  wt_ercnt;       /* number of error blocks */
+       ushort  wt_urcnt;       /* number of underruns */
+}      wterror;
+
+/* defines for wtstatus.wt_err */
+#define        TP_POR          0x100   /* Power on/reset occurred */
+#define        TP_RES1         0x200   /* Reserved for end of media */
+#define        TP_RES2         0x400   /* Reserved for bus parity */
+#define        TP_BOM          0x800   /* Beginning of media */
+#define        TP_MBD          0x1000  /* Marginal block detected */
+#define        TP_NDT          0x2000  /* No data detected */
+#define        TP_ILL          0x4000  /* Illegal command */
+#define        TP_ST1          0x8000  /* Status byte 1 bits */
+#define        TP_FIL          0x01    /* File mark detected */
+#define        TP_BNL          0x02    /* Bad block not located */
+#define        TP_UDA          0x04    /* Unrecoverable data error */
+#define        TP_EOM          0x08    /* End of media */
+#define        TP_WRP          0x10    /* Write protected cartridge */
+#define        TP_USL          0x20    /* Unselected drive */
+#define        TP_CNI          0x40    /* Cartridge not in place */
+#define        TP_ST0          0x80    /* Status byte 0 bits */
+
+/* Grounds for reporting I/O error to user */
+#define        TP_ERR0         (TP_BNL|TP_UDA|TP_WRP|TP_CNI|TP_FIL|TP_EOM|TP_USL)
+#define        TP_ERR1         (TP_MBD|TP_NDT|TP_ILL)
+/* TP_ILL should never happen! */
+/*
+#define        TP_ERR0         0x7f
+#define        TP_ERR1         0x7700
+*/
+
+/* defines for reading out status from wangtek tape controller */
+#define READY          0x01    /* ready bit define        */
+#define EXCEP          0x02    /* exception bit define    */
+
+/* sleep priority */
+#define WTPRI  (PZERO+10)
+
+char   pagebuf[NBPS];          /* buffer of size NBPS */
+unsigned long  pageaddr;       /* physical addr of pagebuf */
+                               /* pageaddr is used with DMA controller */
+time_t Hogtime;                        /* lbolt when Hog timer started */
+extern time_t  lbolt;
+
+#define        debug   printf
+
+/*
+ * Strategy routine.
+ *
+ * Arguments:
+ *  Pointer to buffer structure
+ * Function:
+ *  Start transfer.
+ *
+ * It would be nice to have this multiple-threaded.
+ * There is a version of dump from Berkeley that works with multiple processes
+ * trading off with disk & tape I/O.
+ */
+
+int
+wtstrategy(bp)
+register struct buf *bp;
+{
+       unsigned ucnt1, ucnt2, finished;
+       unsigned long adr1, adr2;
+
+       adr1 = kvtop(bp->b_un.b_addr);
+#ifdef DEBUG
+       debug("bpaddr %x\n", adr1);
+#endif
+       ucnt1 = bp->b_bcount;
+       ucnt2 = 0;
+       adr2 = 0;
+#ifdef DEBUG
+       debug("WTstart: adr1 %lx cnt %x\n", adr1, ucnt1);
+#endif
+       if (ftoseg(adr1) != ftoseg(adr1 + (unsigned) ucnt1 - 1))
+       {
+               adr2 = (adr1 & 0xffff0000L) + 0x10000L;
+               ucnt2 = (adr1 + ucnt1) - adr2;
+               ucnt1 -= ucnt2;
+       }
+       /* at file marks and end of tape, we just return '0 bytes available' */
+       if (wtflags & TPVOL) {
+               bp->b_resid = bp->b_bcount;
+               goto xit;
+       }
+       if ((Hogproc == (struct proc *) 0) && TPHOG(bp->b_dev))
+       {
+#ifdef DEBUG
+               printf("setting Hogproc\n");
+#endif
+               Hogtime = 0;
+               Hogproc = myproc;
+       }
+       if (bp->b_flags & B_READ) {
+               int     bad = 0;
+
+               /* For now, we assume that all data will be copied out */
+               /* If read command outstanding, just skip down */
+               if (!(wtflags & TPRO)) {
+                       if (ERROR == wtsense(TP_WRP))   /* clear status */
+                               goto errxit;
+#ifdef DEBUG
+                       debug("WTread: Start read\n");
+#endif
+                       if (!(wtflags & TPREAD) || (wtflags & TPWANY) ||
+                           (rstart() == ERROR))  {
+#ifdef DEBUG
+                               debug("Tpstart: read init error\n"); /* */
+#endif
+                               goto errxit;
+                       }
+                       wtflags |= TPRO|TPRANY;
+               }
+
+               finished = 0;
+               /* Take a deep breath */
+               if (ucnt1) {
+                       if ((rtape(adr1, ucnt1) == ERROR) &&
+                                       (wtsense(TP_WRP) == ERROR))
+                               goto endio;
+                       /* wait for it */
+                       bad = pollrdy();
+                       finished = bytes;
+                       if (bad)
+                               goto endio;
+               }
+               /* if a second I/O region, start it */
+               if (ucnt2) {
+                       if ((rtape(adr2, ucnt2) == ERROR) &&
+                                       (wtsense(TP_WRP) == ERROR))
+                               ucnt2 = 0;      /* don't poll for me */
+                       }
+
+               /* if second i/o pending wait for it */
+               if (ucnt2) {
+                       pollrdy();
+                       /* whether pollrdy is ok or not */
+                       finished += bytes;
+               }
+       } else {
+               if (wtflags & TPWP)     /* write protected */
+                       goto errxit;
+
+               /* If write command outstanding, just skip down */
+               if (!(wtflags & TPWO)) {
+                       if (ERROR == wtsense(0))        /* clear status */
+                       {
+#ifdef DEBUG
+                               debug("TPstart: sense 0\n");
+#endif
+                               goto errxit;
+                       }
+                       if (!(wtflags & TPWRITE) || (wtflags & TPRANY) ||
+                           (wstart() == ERROR))  {
+#ifdef DEBUG
+                               debug("Tpstart: write init error\n"); /* */
+#endif
+                               wtsense(0);
+
+errxit:                                bp->b_flags |= B_ERROR;
+                               bp->b_resid = bp->b_bcount;
+                               goto xit;
+                       }
+                       wtflags |= TPWO|TPWANY;
+               } 
+
+               /* and hold your nose */
+               if (ucnt1 && ((wtape(adr1, ucnt1) == ERROR)
+                               && (wtsense(0) == ERROR)))
+                       finished = bytes;
+
+               else if (ucnt2 &&
+                       (((ucnt1 && pollrdy()) ||
+                               (wtape(adr2, ucnt2) == ERROR)) &&
+                               (wtsense(0) == ERROR)))
+                       finished = ucnt1 + NBPS + bytes;
+               /* All writes and/or copyins were fine! */
+               else
+                       finished = bp->b_bcount;
+               pollrdy();
+       }
+
+       endio:
+       wterror.wt_err = 0;
+       if (exflag && wtsense((bp->b_flags & B_READ) ? TP_WRP : 0)) {
+               if ((wterror.wt_err & TP_ST0) 
+                       && (wterror.wt_err & (TP_FIL|TP_EOM))) {
+#ifdef DEBUG
+                       debug("WTsta: Hit end of tape\n"); /* */
+#endif
+                       wtflags |= TPVOL;
+                       if (wterror.wt_err & TP_FIL) {
+                               if (wtflags & TPRO)
+                                       /* interrupter is bogus */
+                                       rstart();  /* restart read command */
+                               else
+                                       wtflags &= ~TPWO;
+                               finished += NBPS; 
+                       }
+               /* Reading file marks or writing end of tape return 0 bytes */
+               } else  {
+                       bp->b_flags |= B_ERROR;
+                       wtflags &= ~(TPWO|TPRO);
+               }
+       }
+
+       bp->b_resid = bp->b_bcount - finished;
+xit:
+       biodone(bp);
+       if (wtimeron)
+               Hogtime = lbolt;
+       else if (Hogproc == myproc)
+               Hogproc = (struct proc *) 0;
+}
+
+/*
+ * simulate an interrupt periodically while I/O is going
+ * this is necessary in case interrupts get eaten due to
+ * multiple devices on a single IRQ line
+ */
+wtimer()
+{
+       /* If I/O going and not in isr(), simulate interrupt
+        * If no I/O for at least 1 second, stop being a Hog
+        * If I/O done and not a Hog, turn off wtimer()
+        */
+       if (wtio && !isrlock)
+               isr();
+
+       if ((Hogproc == myproc) && Hogtime && (lbolt-Hogtime > HZ))
+               Hogproc = (struct proc *) 0;
+
+       if (wtio || (Hogproc == myproc))
+               timeout(wtimer, (caddr_t) 0, HZ);
+       else
+               wtimeron = 0;
+}
+
+
+wtrawio(bp)
+struct buf     *bp;
+{
+       wtstrategy(bp);
+       biowait(bp);
+       return(0);
+}
+
+wt_minphys(bp)
+struct buf     *bp;
+{
+       if (bp->b_bcount > PAGESIZ)
+               bp->b_bcount = PAGESIZ;
+}
+
+/*
+ * raw read routine
+ */
+wtread(dev, uio)
+struct uio     *uio;
+{
+       if (wtflags & TPSESS) {
+               seterror(EIO);
+               return(EIO);
+       }
+       physio(wtrawio, &rwtbuf, dev, B_READ, wt_minphys, uio);
+       return(0);
+}
+
+/*
+ * raw write routine
+ */
+wtwrite(dev, uio)
+struct uio     *uio;
+{
+       if (wtflags & TPSESS) {
+               seterror(EIO);
+               return(EIO);
+       }
+       physio(wtrawio, &rwtbuf, dev, B_WRITE, wt_minphys, uio);
+       return(0);
+}
+
+
+
+/*
+ * ioctl routine
+ *  for user level QIC commands only
+ */
+wtioctl(dev, cmd, arg, mode)
+int dev, cmd;
+unsigned long arg;
+int mode;
+{
+       if (cmd == WTQICMD)
+       {
+               if ((qicmd((int)arg) == ERROR) || (rdyexc(HZ) == ERROR))
+               {
+                       wtsense(0);
+                       seterror(EIO);
+               }
+               return;
+       }
+       seterror(EINVAL);
+}
+
+/*
+ * open routine
+ * called on every device open
+ */
+wtopen(dev, flag)
+int    dev, flag;
+{
+       if (first_wtopen_ever) {
+               wtinit();
+               first_wtopen_ever = 0;
+       }
+#ifdef DEBUG
+       printf("wtopen ...\n");
+#endif
+       if (!pageaddr) {
+               nodev();
+               return(ENXIO);
+       }
+       if (wtflags & (TPINUSE)) {
+               seterror(ENXIO);
+               return(ENXIO);
+       }
+       if (wtflags & (TPDEAD)) {
+               seterror(EIO);
+               return(EIO);
+       }
+       /* If a rewind from the last session is going on, wait */
+       while(wtflags & TPREW) {
+#ifdef DEBUG
+               debug("Waiting for rew to finish\n");
+#endif
+               delay(1000000); /* delay one second */
+       }
+       /* Only do reset and select when tape light is off, and tape is rewound.
+        * This allows multiple volumes. */
+       if (wtflags & TPSTART) { 
+               if (t_reset() != SUCCESS) {
+                       nodev();
+                       return(ENXIO);
+               }
+#ifdef DEBUG
+               debug("reset done. calling wtsense\n");
+#endif
+               if (wtsense(TP_WRP) == ERROR) {
+                       seterror(EIO);
+                       return (EIO);
+               }
+#ifdef DEBUG
+               debug("wtsense done\n");
+#endif
+               wtflags &= ~TPSTART;    
+       }
+
+       wtflags = TPINUSE;
+       if (flag & FREAD)
+               wtflags |= TPREAD;
+       if (flag & FWRITE)
+               wtflags |= TPWRITE;
+       rwtbuf.b_flags = 0;
+       myproc = u.u_procp;             /* for comparison */
+       switch(TP_DENS(dev)) {
+case 0:
+cmds(0x28);
+break;
+case 1:
+cmds(0x29);
+break;
+case 2:
+cmds(0x27);
+break;
+case 3:
+cmds(0x24);
+       }
+       return(0);
+}
+
+/*
+ * close routine
+ * called on last device close
+ * If not rewind-on-close, leave read or write command intact.
+ */
+wtclose(dev)
+{
+       int wtdsl2();
+
+#ifdef DEBUG
+       debug("WTclose:\n");
+#endif
+       if (Hogproc == myproc)
+               Hogproc = (struct proc *) 0;
+       if (!exflag && (wtflags & TPWANY) && !(wtflags & (TPSESS|TPDEAD))) {
+               if (!(wtflags & TPWO))
+                       wstart();
+#ifdef DEBUG
+               debug("WT: Writing file mark\n");
+#endif
+               wmark();        /* write file mark */
+#ifdef DEBUG
+               debug("WT: Wrote file mark, going to wait\n");
+#endif
+               if (rdyexc(HZ/10) == ERROR) {
+                       wtsense(0);
+                       }
+               }
+       if (TP_REWCLOSE(dev) || (wtflags & (TPSESS|TPDEAD))) {
+       /* rewind tape to beginning of tape, deselect tape, and make a note */
+       /* don't wait until rewind, though */
+               /* Ending read or write causes rewind to happen, if no error,
+                * and READY and EXCEPTION stay up until it finishes */
+               if (wtflags & (TPRO|TPWO))
+               {
+#ifdef DEBUG
+                       debug("End read or write\n");
+#endif
+                       rdyexc(HZ/10);
+                       ioend();
+                       wtflags &= ~(TPRO|TPWO);
+               }
+               else    wtwind();
+               wtflags |= TPSTART | TPREW;
+               timeout(wtdsl2, 0, HZ);
+       }
+       else if (!(wtflags & (TPVOL|TPWANY)))
+       {
+               /* space forward to after next file mark no writing done */
+               /* This allows skipping data without reading it.*/
+#ifdef DEBUG
+               debug("Reading past file mark\n");
+#endif
+               if (!(wtflags & TPRO))
+                       rstart();
+               rmark();
+               if (rdyexc(HZ/10))
+               {
+                       wtsense(TP_WRP);
+               }
+       }
+       wtflags &= TPREW|TPDEAD|TPSTART|TPRO|TPWO;
+       return(0);
+}
+
+/* return ERROR if user I/O request should receive an I/O error code */
+
+wtsense(ignor)
+{
+       wtflags &= ~(TPRO|TPWO);
+#ifdef DEBUGx
+       debug("WTsense: start ");
+#endif
+       if (rdstatus(&wterror) == ERROR)
+       {
+#ifdef DEBUG
+               debug("WTsense: Can't read status\n");
+#endif
+               return(ERROR);
+       }
+#ifdef DEBUG
+       if (wterror.wt_err & (TP_ST0|TP_ST1))
+       {
+               debug("Tperror: status %x error %d underruns %d\n",
+                       wterror.wt_err, wterror.wt_ercnt, wterror.wt_urcnt);
+       }
+       else
+               debug("done. no error\n");
+#endif
+       wterror.wt_err &= ~ignor;       /* ignore certain errors */
+       reperr(wterror.wt_err);
+       if (((wterror.wt_err & TP_ST0) && (wterror.wt_err & TP_ERR0)) ||
+                   ((wterror.wt_err & TP_ST1) && (wterror.wt_err & TP_ERR1)))
+                       return  ERROR;
+
+       return SUCCESS;
+}
+
+/* lifted from tdriver.c from Wangtek */
+reperr(srb0)
+int srb0;
+{
+       int s0 = srb0 & (TP_ERR0|TP_ERR1);      /* find out which exception to report */
+       if (s0) {
+               if (s0 & TP_USL) 
+                       sterr("Drive not online");
+               else if (s0 & TP_CNI) 
+                       sterr("No cartridge");
+               else if ((s0 & TP_WRP) && !(wtflags & TPWP))
+               {
+                       sterr("Tape is write protected");
+                       wtflags |= TPWP;
+               }
+               /*
+               if (s0 & TP_FIL)
+                       sterr("Filemark detected");
+               */
+               else if (s0 & TP_BNL)
+                       sterr("Block in error not located");
+               else if (s0 & TP_UDA)
+                       sterr("Unrecoverable data error");
+               /*
+               else if (s0 & TP_EOM)
+                       sterr("End of tape");
+               */
+               else if (s0 & TP_NDT)
+                       sterr("No data detected");
+               /*
+               if (s0 & TP_POR)
+                       sterr("Reset occured");
+               */
+               else if (s0 & TP_BOM)
+                       sterr("Beginning of tape");
+               else if (s0 & TP_ILL)
+                       sterr("Illegal command");
+       }
+}
+       
+sterr(errstr)
+char   *errstr;
+{
+       printf("Streamer: %s\n", errstr);
+}
+
+/* Wait until rewind finishes, and deselect drive */
+wtdsl2() {
+       int     stat;
+
+       stat = inb(wtport) & (READY|EXCEP);
+#ifdef DEBUG
+       debug("Timeout: Waiting for rewind to finish: stat %x\n", stat);
+#endif
+       switch (stat) {
+               /* They're active low, ya'know */
+               case READY|EXCEP:
+                       timeout(wtdsl2, (caddr_t) 0, HZ);
+                       return;
+               case EXCEP: 
+                       wtflags &= ~TPREW;
+                       return;
+               case READY:
+               case    0:
+                       wtflags &= ~TPREW;
+                       sterr("Rewind failed");
+                       wtsense(TP_WRP);
+                       return;
+                       }
+       }
+
+wtwind() {
+#ifdef DEBUG
+       debug("WT: About to rewind\n");
+#endif
+       rwind();        /* actually start rewind */
+}
+
+wtintr(vec) {
+       if (wtflags & (TPWO|TPRO))
+       {
+               isrlock = 1;
+               if (wtio) isr();
+               isrlock = 0;
+       }
+}
+
+wtinit() {
+       if (wtchan < 1 || wtchan > 3)
+       {
+               sterr("Bad DMA channel, cannot init driver");
+               return;
+       }
+       wtlinit();      /* init assembly language variables */
+       pageset();
+}
+
+rdyexc(ticks)
+{
+       int s;
+#ifdef DEBUG
+       int os = 0xffff;                /* force printout first time */
+#endif
+       for (;;) {                      /* loop until ready or exception */
+               s=(inb(wtport) & 0xff); /* read the status register */
+#ifdef DEBUG
+               if (os != s) {
+                       debug("Status reg = %x\n", s); /* */
+                       os = s;
+                       }
+#endif
+               if (!(s & EXCEP))       /* check if exception have occured */
+                       break;
+               if (!(s & READY))       /* check if controller is ready */
+                       break;
+               s = splbio();
+               delay((ticks/HZ)*1000000); /* */
+               splx(s);
+       }
+#ifdef DEBUG
+       debug("Status reg = %x on return\n", s); /* */
+#endif
+       return((s & EXCEP)?SUCCESS:ERROR);  /* return exception if it occured */
+}
+
+pollrdy()
+{
+       int      sps;
+#ifdef DEBUG
+       debug("Pollrdy\n");
+#endif
+       sps = splbio();
+       while (wtio)
+               sleep(&wci, WTPRI);
+       splx(sps);
+#ifdef DEBUG
+       debug("Finish poll, wci %d exflag %d\n", wci, exflag);
+#endif
+       return exflag;
+}
+
+wtdma()                /* start up i/o operation, called from dma() in wtlib1.s */
+{
+       wtio = 1;
+       if (!wtimeron)
+       {
+               wtimeron = 1;
+               timeout(wtimer, (caddr_t) 0, HZ/2);
+       }
+}
+
+wtwake()       /* end i/o operation, called from isr() in wtlib1.s */
+{
+       wtio = 0;
+       wakeup(&wci);
+}
+
+pageset()
+{
+       unsigned long pp;
+
+       pp = (unsigned long) pagebuf;
+       pageaddr = kvtop(pp);
+#ifdef DEBUG
+       debug("pageset: addr %lx\n", pageaddr);
+#endif
+}
+
+
+
+#define near
+
+static near
+sendcmd()
+{
+       /* desired command in global mbits */
+
+       outb(CTLPORT, mbits | REQUEST);         /* set request */
+       while (inb(STATPORT) & READY);          /* wait for ready */
+       outb(CTLPORT, mbits & ~REQUEST);        /* reset request */
+       while ((inb(STATPORT) & READY) == 0);   /* wait for not ready */
+}
+
+static near            /* execute command */
+cmds(cmd)
+{
+       register s;
+
+       do s = inb(STATPORT);
+       while ((s & STAT) == STAT);     /* wait for ready */
+
+       if ((s & EXCEP) == 0)           /* if exception */
+               return ERROR;           /* error */
+       
+       outb(CMDPORT, cmd);             /* output the command   */
+
+       outb(CTLPORT, mbits=ONLINE);    /* set & send ONLINE    */
+       sendcmd();
+
+       return SUCCESS;
+}
+
+qicmd(cmd)
+{
+       return cmds(cmd);
+}
+
+rstart()
+{
+       return cmds(RDDATA);
+}
+
+rmark()
+{
+       return cmds(READFM);
+}
+
+wstart()
+{
+       return cmds(WRTDATA);
+}
+
+ioend()
+{
+       register s;
+       register rval = SUCCESS;
+
+       do s = inb(STATPORT);
+       while ((s & STAT) == STAT);     /* wait for ready */
+
+       if ((s & EXCEP) == 0)           /* if exception */
+               rval = ERROR;           /* error */
+       
+       mbits &= ~ONLINE;
+       outb(CTLPORT, mbits);           /* reset ONLINE */
+       outb(MASKREG, wtchan+4);        /* turn off dma */
+       outb(CLEARFF, 0);               /* reset direction flag */
+
+       return rval;
+}
+
+wmark()
+{
+       register s;
+
+       if (cmds(WRITEFM) == ERROR)
+               return ERROR;
+
+       do s = inb(STATPORT);
+       while ((s & STAT) == STAT);     /* wait for ready */
+
+       if ((s & EXCEP) == 0)           /* if exception */
+               return ERROR;           /* error */
+
+       return SUCCESS;
+}
+
+rwind()
+{
+       register s;
+
+       mbits = CMDOFF;
+
+       do s = inb(STATPORT);
+       while ((s & STAT) == STAT);     /* wait for ready */
+
+       outb(CMDPORT, REWIND);
+       sendcmd();
+
+       return SUCCESS;
+}
+
+rdstatus(stp)
+char *stp;             /* pointer to 6 byte buffer */
+{
+       register s;
+       int n;
+
+       do s = inb(STATPORT);
+       while ((s & STAT) == STAT);     /* wait for ready or exception */
+
+       outb(CMDPORT, RDSTAT);
+       sendcmd();                      /* send read status command */
+
+       for (n=0; n<6; n++)
+       {
+#ifdef DEBUGx
+               debug("rdstatus: waiting, byte %d\n", n);
+#endif
+               do s = inb(STATPORT);
+               while ((s & STAT) == STAT);     /* wait for ready */
+#ifdef DEBUGx
+               debug("rdstatus: done\n");
+#endif
+               if ((s & EXCEP) == 0)           /* if exception */
+                       return ERROR;           /* error */
+
+               *stp++ = inb(DATAPORT);         /* read status byte */
+
+               outb(CTLPORT, mbits | REQUEST); /* set request */
+#ifdef DEBUGx
+               debug("rdstatus: waiting after request, byte %d\n", n);
+#endif
+               while ((inb(STATPORT)&READY) == 0);     /* wait for not ready */
+               for (s=100; s>0; s--);          /* wait an additional time */
+
+               outb(CTLPORT, mbits & ~REQUEST);/* unset request */
+#ifdef DEBUGx
+               debug("rdstatus: done\n");
+#endif
+       }
+       return SUCCESS;
+}
+
+t_reset()
+{
+       register i;
+       mbits |= RESET;
+       outb(CTLPORT, mbits);           /* send reset */
+       delay(20);
+       mbits &= ~RESET;
+       outb(CTLPORT, mbits);           /* turn off reset */
+       if ((inb(STATPORT) & RESETMASK) == RESETVAL)
+               return SUCCESS;
+       return ERROR;
+}
+
+static
+dma()
+{
+       int x=splbio();
+       wtdma();
+       outb(CLEARFF, 0);
+       outb(MODEREG, mode);    /* set dma mode */
+       outb(dmareg, bufptr & 0xFF);
+       outb(dmareg, (bufptr>>8) & 0xFF);
+       outb(pagereg, (bufptr>>16) & 0xFF);                         
+       outb(dmareg+1, (BLKSIZE-1) & 0xFF);
+       outb(dmareg+1, (BLKSIZE-1) >> 8);
+       outb(wtport, eqdma+ONLINE);
+       outb(MASKREG, wtchan);  /* enable command to 8237, start dma */
+       splx(x);
+}
+
+static near
+wtstart(buf, cnt)
+long buf;
+int cnt;
+{
+       register s;
+
+       bufptr = buf;           /* init statics */
+       numbytes = cnt;
+       wci = 0;                /* init flags */
+       exflag = 0;
+       bytes = 0;              /* init counter */
+
+       do s = inb(STATPORT) & STAT;
+       while (s == STAT);      /* wait for ready or error */
+
+       if (s & EXCEP)          /* no error */
+       {
+               dma();
+               return SUCCESS;
+       }
+       return ERROR;           /* error */
+}
+
+rtape(buf, cnt)
+long buf;                      /* physical address */
+int cnt;                       /* number of bytes */
+{
+       mode = dma_read;
+       return wtstart(buf,cnt);
+}
+
+wtape(buf, cnt)
+long buf;                      /* physical address */
+int cnt;                       /* number of bytes */
+{
+       mode = dma_write;
+       return wtstart(buf,cnt);
+}
+
+isr()
+{
+       int stat = inb(wtport);
+       if (!(stat & EXCEP))    /* exception during I/O */
+       {
+               if (bytes + BLKSIZE >= numbytes) wci = 1;
+               exflag = 1;
+               goto isrwake;
+       }
+       if ((stat & READY) || !(inb(STATUSREG) & dma_done))
+               return;
+       exflag = 0;
+       outb(wtport, ONLINE);
+       bytes += BLKSIZE;
+       if (bytes >= numbytes)  /* normal completion of I/O */
+       {
+               wci = 1;
+isrwake:
+               outb(MASKREG, 4+wtchan);        /* turn off dma */
+               wtwake();                       /* wake up user level */
+       }
+       else
+       {                       /* continue I/O */
+               bufptr += BLKSIZE;
+               dma();
+       }
+}
+
+wtlinit()
+{
+       switch (wtchan) {
+       case 1:
+               return;
+       case 2:
+               pagereg = 0x81;
+               dma_done = 4;
+               break;
+       case 3:
+               eqdma = 0x10;
+               pagereg = 0x82;
+               dma_done = 8;
+               break;
+       }
+       dma_write = wtchan+0x48;
+       dma_read = wtchan+0x44;
+       dmareg = wtchan+wtchan;
+}
+
+/*
+ * delay i microseconds
+ */
+delay(i)
+register int i;
+{
+       while (i-- > 0)
+               DELAY(1000);
+}
+
+wtsize()
+{
+}
+
+wtdump()
+{
+}
+
+#include "machine/isa/device.h"
+#include "machine/isa/icu.h"
+
+int    wtprobe(), wtattach();
+struct isa_driver wtdriver = {
+       wtprobe, wtattach, "wt",
+};
+
+wtprobe(dvp)
+       struct isa_device *dvp;
+{
+       int val,i,s;
+
+#ifdef lint
+       wtintr(0);
+#endif
+
+       wtport = dvp->id_iobase;
+       if(t_reset() != SUCCESS) return(0);
+       return(1);
+}
+
+wtattach() { }
+
+#endif NWT