Source to i386/isa/wt.c
/*-
* 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