Source to i386/isa/wt.c


Enter a symbol's name here to quickly find it.

/*-
 * Copyright (c) 1991, 1993
 *	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.
 *
 *	@(#)wt.c	8.1 (Berkeley) 6/11/93
 */

/*
 *
 * Copyright (c) 1989 Carnegie-Mellon University.
 * All rights reserved.
 *
 * Authors: Robert Baron
 * 
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  [email protected]
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

#include "wt.h"
#if NWT > 0
/* 
 * 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/param.h>
#include <sys/buf.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/user.h>

#include <i386/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))

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;
	int	bad;

	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) {
		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;
		bad = pollrdy();
	}

	endio:
	if(bad == EIO) bad = 0;
	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);
		}
	}

	if(bad) {
		bp->b_flags |= B_ERROR;
		bp->b_error = bad;
	}
	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) {
		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) {
		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);
			return(EIO);
		}
		return(0);
	}
	return(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) {
		return(ENXIO);
	}
	if (wtflags & (TPINUSE)) {
		return(ENXIO);
	}
	if (wtflags & (TPDEAD)) {
		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) {
			return(ENXIO);
		}
#ifdef DEBUG
		debug("reset done. calling wtsense\n");
#endif
		if (wtsense(TP_WRP) == ERROR) {
			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 = curproc;		/* 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(unit) {
	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) {
		int error;

		if (error = tsleep((caddr_t)&wci, WTPRI | PCATCH,
			"wtpoll", 0)) {
			splx(sps);
			return(error);
		}
	}
	splx(sps);
#ifdef DEBUG
	debug("Finish poll, wci %d exflag %d\n", wci, exflag);
#endif
	return (EIO);
}

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 <i386/isa/isa_device.h>
#include <i386/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