Ultrix-3.1/sys/dev/ts.c
/**********************************************************************
* Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. *
* All Rights Reserved. *
* Reference "/usr/src/COPYRIGHT" for applicable restrictions. *
**********************************************************************/
/*
* ULTRIX-11 TS11 - 1600 BPI tape driver
*
* SCCSID: @(#)ts.c 3.0 4/21/86
*
* Chung-Wu Lee, Dec-04-85
*
* Start supporting up to 4 controller per system.
*
* Chung-Wu Lee, Aug-09-85
*
* add ioctl routine (from 2.9 BSD).
*
* Fred Canter, Aug-07-85
*
* changes for 1K block file system.
*
* Fred Canter
*
* Thanks to Jerry Brenner for most of this driver.
*
*******************************************************************
* *
* If the system has both a TM11 and a TS11, set the TS11 CSR to *
* 172550. This allows the TS11 to be booted. Use ms0 for the TS11 *
* at 172520 or ms6 for the TS11 at 172550. Use the Boot: csr *
* command to tell the stand-alone programs the TS11 address, if *
* it is not at the standard address (172520). *
* *
*******************************************************************
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/dir.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/errlog.h>
#include <sys/devmaj.h>
#include <sys/mtio.h>
#include <sys/ts_info.h>
int cmdpkt[]; /* command packet */
struct device
{
int tsdb; /* TS data buffer and bus address reg */
int tssr; /* TS status register */
};
struct chrdat chrbuf[]; /* characteristics buffer */
struct mespkt mesbuf[]; /* message buffer */
struct buf tstab[];
struct buf ctsbuf[];
struct buf rtsbuf[];
struct compkt *ts_cbp[]; /* command packet buffer pointer, set in tsopen() */
char *ts_ubmo; /* offsets packet addresses if unibus map used */
char *ts_ubcba[]; /* unibus virtual address of command packet buffer */
/* set in tsopen() to ts_cbp - ts_ubmo */
char ts_openf[];
u_short ts_flags[];
daddr_t ts_blkno[];
daddr_t ts_nxrec[];
u_short ts_erreg[];
u_short ts_dsreg[];
short ts_resid[];
/*
* Block device error log buffer, holds one
* error log record until the error retry sequence
* has been completed.
*/
struct tsebuf ts_ebuf[];
int ts_csr[]; /* CSR address now in config file (c.c) */
/* int ts_ivec[]; /* interrupt address now in config file (c.c) */
int nts; /* NIY number of drives, see c.c */
int ts_mcact; /* active flag of ts controller */
/* bit definitions for command word in ts command packet */
#define ACK 0100000 /* acknowledge bit */
#define CVC 040000 /* clear volume check */
#define OPP 020000 /* opposite. reverse recovery */
#define SWB 010000 /* swap bytes. for data xfer */
/* bit definitions for Command mode field during read command */
#define RNEXT 0 /* read next (forward) */
#define RPREV 0400 /* read previous (reverse) */
#define RRPRV 01000 /* reread previous (space rev, read two) */
#define RRNXT 01400 /* reread next (space fwd, read rev) */
/* bit definitions for Command mode field during write command */
#define WNEXT 0 /* Write data next */
#define WDRTY 01000 /* write data retry , space rev, erase, write data) */
/* bit definitions for command mode field during position command */
#define SPCFWD 0 /* space records forward */
#define SPCREV 0400 /* space records reverse */
#define SKTPF 01000 /* skip tape marks forward */
#define SKTPR 01400 /* skip tape marks reverse */
#define RWIND 02000 /* rewind */
/* bit definitions for command mode field during format command */
#define WEOF 0 /* write tape mark */
#define ERAS 0400 /* erase */
#define WEOFE 01000 /* write tape mark entry */
/* bit definitions for command mode field during control command */
#define MBREAL 0 /* message buffer release */
#define REWUNL 0400 /* Rewind and unload */
#define CLEAN 01000 /* clean */
/* additional definitions */
#define IEI 0200 /* interrupt enable bit */
/* command code definitions */
#define NOP 0
#define RCOM 01 /* read command */
#define WCHAR 04 /* write characteristics */
#define WCOM 05 /* write */
#define WSUSM 06 /* write subsystem memory */
#define POSIT 010 /* position command */
#define FORMT 011 /* Format command */
#define CONTRL 012 /* Control command */
#define INIT 013 /* initialize */
#define GSTAT 017 /* get status immediate */
/* definition of tssr bits */
#define SC 0100000 /* special condition */
#define UPE 040000 /* unibus parity error */
#define SPE 020000 /* Serial bus parity error */
#define RMR 010000 /* register modify refused */
#define NXM 04000 /* non-existent memory */
#define NBA 02000 /* Need Buffer address */
#define SSR 0200 /* Sub-System ready */
#define OFL 0100 /* off-line */
/* fatal termination class codes */
#define FTC 030 /* use this as a mask to get codes */
/* code = 00 see error code byte in tsx3 */
/* code = 01 I/O seq Crom or main Crom parity error */
/* code = 10 u-processor Crom parity error,I/O silo parity */
/* serial bus parity, or other fatal */
/* code = 11 A/C low. drive ac low */
/* termination class codes */
#define TCC 016 /* mask for termination class codes */
/* code = 000 normal termination */
/* code = 001 Attention condition
/* code = 010 Tape status alert
/* code = 011 Function reject
/* code = 100 Recoverable error - tape pos = 1 record down from
start of function
/* code = 101 Recoverable error - tape has not moved
/* code = 110 Unrecoverable error - tape position lost
/* code = 111 Fatal controller error - see fatal class bits */
/* definition of message buffer header word */
#define MAKC 0100000 /* acknowledge from controller */
#define MCCF 07400 /* mask for class code field */
/* class codes are */
/* 0 = ATTN, on or ofline
1 = ATTN, microdiagnostic failure
0 = FAIL, serial bus parity error
1 = FAIL, WRT LOCK
2 = FAIL, interlock or non-executable function
3 = FAIL, microdiagnostic error
*/
#define MMSC 037 /* mask for message code field */
/* message codes are
020 = end
021 = FAIL
022 = ERROR
023 = Attention
/* definition of extended status reg 0 bits */
#define TMK 0100000 /* Tape mark detected */
#define RLS 040000 /* Record length short */
#define LET 020000 /* Logical end of Tape */
#define RLL 010000 /* Record length long */
#define WLE 04000 /* Write lock error */
#define NEF 02000 /* Non-executable function */
#define ILC 01000 /* Illegal command */
#define ILA 0400 /* Illegal address */
#define MOT 0200 /* Capistan is moving */
#define ONL 0100 /* On Line */
#define IE 040 /* state of interrupt enable bit */
#define VCK 020 /* Volume Check */
#define PED 010 /* Phase encoded drive */
#define WLK 04 /* Write locked */
#define BOT 02 /* Tape at bot */
#define EOT 01 /* Tape at eot */
/* definitions of xstat1 */
#define DLT 0100000 /* Data late error */
#define COR 020000 /* Correctable data error */
#define CRS 010000 /* Crease detected */
#define TIG 04000 /* Trash in the gap */
#define DBF 02000 /* Deskew Buffer Fail */
#define SCK 01000 /* Speed check */
#define IPR 0200 /* Invalid preamble */
#define SYN 0100 /* Synch Failure */
#define IPO 040 /* invalid postamble */
#define IED 020 /* invalid end data */
#define POS 010 /* postamble short */
#define POL 04 /* postamble long */
#define UNC 02 /* Uncorrectable data error */
#define MTE 01 /* multi track error */
/* Definitions of XSTAT2 bits */
#define OPM 0100000 /* operation in progress (tape moving) */
#define SIP 040000 /* Silo Parity error */
#define BPE 020000 /* Serial bus Parity error at drive */
#define CAF 010000 /* Capstan Acceleration fail */
#define WCF 02000 /* Write card error */
#define DTP 0477 /* mask for Dead track bits */
/* bit definitions for XSTAT3 */
#define LMX 0200 /* Limit exceeded (tension arms) */
#define OPI 0100 /* operation incomplete */
#define REV 040 /* current operation in reverse */
#define CRF 020 /* capstan response fail */
#define DCK 010 /* density check */
#define NOI 04 /* no tape mark or preamble */
#define LXS 02 /* limit exceeded manual recovery */
#define RIB 01 /* Reverse into BOT */
#define SIO 01
#define SSEEK 02
#define SINIT 04
#define SRETRY 010
#define SCOM 020
#define SOK 040
#define SERROR 0100
#define SREWND 0200
#define S_WRITTEN 01 /* last command is write command */
#define S_EOT 010 /* EOT encountered */
#define S_CSE 020 /* clear EOT */
#define S_DEOT 040 /* disable EOT */
#define S_BUSY 0100 /* ctsbuf is busy, switch for clx/cls */
#define S_WAIT 0200 /* ctsbuf is waited, switch for clx/cls */
#define S_BUSYF 01000 /* good termination of busy */
#define S_WAITF 02000 /* good termination of wait */
tsopen(dev, flag)
{
register char *cba;
register int ds;
register struct device *tsaddr;
int ctrl;
/* if unit > nts or not configured then flag an error and return */
ctrl = minor(dev) & 077;
tsaddr = ts_csr[ctrl];
if (ctrl >= nts || tsaddr == 0)
{
u.u_error = ENXIO;
return;
}
/*
* Set up command and message buffer address offset
* if unibus map present. Also setup command packet
* buffer pointer (aligned on mod 4 boundry).
*/
cba = &cmdpkt[5*ctrl];
if(ubmaps) { /* UNIBUS map present (much magic to follow) */
ts_ubmo = (char *)&cfree;
if(((int)ts_ubmo & 2) && (((int)cba & 2) == 0))
cba += 2;
if((((int)ts_ubmo & 2) == 0) && ((int)cba & 2))
cba += 2;
} else {
ts_ubmo = 0;
if((int)cba & 2)
cba += 2;
}
ts_cbp[ctrl] = cba; /* save packet buffer pointer for later use */
ts_ubcba[ctrl] = (char *)cba - ts_ubmo; /* set UB addr of cmd pkt buf */
if (flag&FNDELAY) /* forced open */
{
ts_openf[ctrl] = 1;
return;
}
/* if unit already open then flag an error and return */
if (ts_openf[ctrl] > 0)
{
u.u_error = ETO;
return;
}
if(tstab[ctrl].b_active & SREWND)
{ /* this is a delay loop to allow rewind completion */
/* it allows for 2400 feet of tape plus a fudge factor */
/******** this loop assumes lbolt every 4 seconds *******/
for(ds = 0; ds < 125; ds++)
{
if(tsaddr->tssr & SSR)
{
ds = 0; /* if ready then break */
break;
}
else
sleep(&lbolt, PZERO +1);
}
if(ds)
{
u.u_error = ETOL; /* timeout, off-line */
return;
}
else
tstab[ctrl].b_active = 0; /* rewind complete */
}
/* call init. if init returns 1 then failure.
flag error and return */
if(tsinit(ctrl, 0))
{
u.u_error = ENXIO;
return;
}
/* some house keeping here */
ts_blkno[ctrl] = 0; /* set current block # to 0 */
ts_nxrec[ctrl] = 1000000; /* set max accessable block # */
ts_flags[ctrl] &= S_DEOT;
tstab[ctrl].b_flags |= B_TAPE; /* Say we are a tape so no write
ahead */
if ((ds = tscommand(dev, NOP, 1)) == -1) /* Get drive status */
u.u_error = ENXIO;
/* if drive is off-line or open for write(read/write)
and write locked then fatal error */
if(ds & EOT)
ts_flags[ctrl] |= S_EOT;
if(tsaddr->tssr & OFL)
u.u_error = ETOL;
else if((flag & FWRITE) && (ds & WLK))
u.u_error = ETWL;
if(u.u_error == 0) /* if no error then say opened */
ts_openf[ctrl] = 1;
else
ts_openf[ctrl] = 0;
}
tsclose(dev, flag)
{
int ctrl;
ctrl = minor(dev) & 077;
if (ts_openf[ctrl] <= 0)
return;
if(tstab[ctrl].b_active & SREWND)
{
ts_openf[ctrl] = 0; /* say closed */
return;
}
/* if opened for write or read/write and was
written then write two tape marks
and back up one */
if(((flag & (FWRITE|FREAD)) == FWRITE) ||
((flag & FWRITE) && (ts_flags[ctrl] & S_WRITTEN)))
{
if (tscommand(dev, (FORMT|WEOF), 1) == -1)
goto badcls;
if (tscommand(dev, (FORMT|WEOF), 1) == -1)
goto badcls;
if (tscommand(dev, (SPCREV|POSIT), 1) == -1)
goto badcls;
}
/* if the no-rewind bit in minor is clear then rewind */
if ((minor(dev)&0200) ==0 )
{
if (tscommand(dev, (RWIND|POSIT), 0) == -1)
goto badcls;
ts_flags[ctrl] &= ~S_EOT;
}
ts_openf[ctrl] = 0; /* say closed */
return;
badcls:
u.u_error = ENXIO;
return;
}
tsioctl(dev, cmd, data, flag)
caddr_t data;
dev_t dev;
{
register struct buf *bp;
struct buf *wbp;
register callcount;
int ctrl, fcount;
struct mtop *mtop;
struct mtget *mtget;
/* we depend of the values and order of the MT codes here */
static tsops[] =
{FORMT|WEOF,POSIT|SKTPF,POSIT|SKTPR,POSIT|SPCFWD,POSIT|SPCREV,
POSIT|RWIND,CONTRL|REWUNL,NOP,NOP,NOP,NOP,INIT,INIT};
ctrl = minor(dev) & 077;
bp = &ctsbuf[ctrl];
switch (cmd) {
case MTIOCTOP: /* tape operation */
mtop = (struct mtop *)data;
switch (mtop->mt_op) {
case MTWEOF:
callcount = mtop->mt_count;
fcount = 1;
break;
case MTFSF: case MTBSF:
case MTFSR: case MTBSR:
callcount = 1;
fcount = mtop->mt_count;
break;
case MTREW: case MTOFFL: case MTNOP:
callcount = 1;
fcount = 1;
break;
case MTCSE:
ts_flags[ctrl] |= S_CSE;
return(0);
case MTENAEOT:
ts_flags[ctrl] &= ~S_DEOT;
return(0);
case MTDISEOT:
ts_flags[ctrl] |= S_DEOT;
return(0);
case MTCACHE: case MTNOCACHE:
case MTASYNC: case MTNOASYNC:
return(0);
case MTCLX:
case MTCLS:
if((wbp = tstab[ctrl].b_actf) != 0) {
tstab[ctrl].b_actf = wbp->av_forw;
if (wbp != bp) {
wbp->b_error = EIO;
wbp->b_flags |= B_ERROR;
iodone(wbp);
}
}
if (ts_flags[ctrl]&S_BUSY) {
bp->b_flags &= ~B_BUSY;
ts_flags[ctrl] &= ~S_BUSYF;
iodone(bp);
}
if (ts_flags[ctrl]&S_WAIT) {
bp->b_flags &= ~B_WANTED;
ts_flags[ctrl] &= ~S_WAITF;
wakeup((caddr_t)bp);
}
if(tsinit(ctrl, 0))
{
u.u_error = ENXIO;
return;
}
ts_blkno[ctrl] = 0;
ts_nxrec[ctrl] = 1000000;
ts_flags[ctrl] &= S_DEOT;
tstab[ctrl].b_active = 0;
ts_openf[ctrl] = 0;
tsstart(ctrl);
return(0);
default:
return (ENXIO);
}
if (callcount <= 0 || fcount <= 0)
return (EINVAL);
while (--callcount >= 0) {
if (tscommand(dev, tsops[mtop->mt_op], fcount) == -1)
{
u.u_error = ENXIO;
return(0);
}
if ((mtop->mt_op == MTFSR || mtop->mt_op == MTBSR) &&
bp->b_resid)
return (EIO);
if ((bp->b_flags&B_ERROR) || ts_erreg[ctrl]&BOT)
break;
}
return (geterror(bp));
case MTIOCGET:
mtget = (struct mtget *)data;
mtget->mt_dsreg = 0;
mtget->mt_erreg = ts_erreg[ctrl];
mtget->mt_resid = ts_resid[ctrl];
mtget->mt_softstat = 0;
if (ts_flags[ctrl]&S_EOT)
mtget->mt_softstat |= MT_EOT;
if (ts_flags[ctrl]&S_DEOT)
mtget->mt_softstat |= MT_DISEOT;
mtget->mt_type = MT_ISTS;
break;
default:
return (ENXIO);
}
return (0);
}
static
tscommand(dev, com, count)
{
register struct buf *bp;
int ctrl;
ctrl = minor(dev) & 077;
bp = &ctsbuf[ctrl]; /* set buffer pointer to command table */
spl5(); /* grab some priority for this */
while(bp->b_flags&B_BUSY) /* if command table is busy */
{
bp->b_flags |= B_WANTED; /* then say we want it */
ts_flags[ctrl] |= (S_WAIT|S_WAITF);
sleep((caddr_t)bp, PRIBIO); /* and sleep around */
if ((ts_flags[ctrl]&S_WAITF) == 0)
return(-1);
}
spl0(); /* have the buffer so drop pri */
bp->b_dev = dev; /* associate with this device */
bp->b_resid = com; /* load desired command here */
bp->b_bcount = count;
bp->b_blkno = 0; /* don't need a block number */
bp->b_flags = B_BUSY|B_READ; /* mark command buffer busy */
ts_flags[ctrl] |= (S_BUSY|S_BUSYF);
tsstrategy(bp); /* do it */
iowait(bp); /* wait until it's done */
if ((ts_flags[ctrl]&S_BUSYF) == 0)
return(-1);
if(bp->b_flags&B_WANTED) /* if someone (me) wants it */
{
ts_flags[ctrl] &= ~S_WAIT;
wakeup((caddr_t)bp); /* then say he can have it */
}
bp->b_flags &= B_ERROR; /* say command buffer avail */
return(bp->b_resid); /* return drive status */
}
tsstrategy(bp)
struct buf *bp;
{
register daddr_t *p;
int ctrl;
mapalloc(bp);
ctrl = minor(bp->b_dev) & 077;
if(bp != &ctsbuf[ctrl]) /* if not command buffer */
{
if(ts_openf[ctrl] < 0)
{
bp->b_flags |= B_ERROR; /* mark buffer bad */
iodone(bp);
return;
}
p = &ts_nxrec[ctrl]; /* get pointer to max accessable
block number */
#ifdef UCB_NKB
if(dbtofsb(bp->b_blkno) > *p) /* if requested max access */
#else
if(bp->b_blkno > *p) /* if requested > max access */
#endif UCB_NKB
{
bp->b_flags |= B_ERROR; /* thats a no-no */
bp->b_error = ENXIO; /* make sure it's known */
iodone(bp); /* return buffer */
return; /* bye - bye */
}
#ifdef UCB_NKB
if(dbtofsb(bp->b_blkno) == *p && bp->b_flags&B_READ)
#else
if(bp->b_blkno == *p && bp->b_flags&B_READ)
#endif UCB_NKB
{
/* if requested block = max accessable & read */
if((bp->b_flags&B_PHYS) == 0)
clrbuf(bp); /* zero the buffer */
bp->b_resid = bp->b_bcount; /* didn't read */
iodone(bp); /* return the buffer */
return; /* bye - bye */
}
ts_flags[ctrl] &= ~S_WRITTEN;
if((bp->b_flags&B_READ) == 0) /* if a write command */
{
#ifdef UCB_NKB
*p = dbtofsb(bp->b_blkno) + 1; /* set max access to requested + 1 */
#else
*p = bp->b_blkno +1; /* set max access to requested
+ 1 */
#endif UCB_NKB
ts_flags[ctrl] |= S_WRITTEN; /* say it's been written */
}
}
bp->av_forw = 0; /* clear the available forward link */
spl5(); /* grab some priority for this */
if (tstab[ctrl].b_actf == NULL) /* if no forward link on the table */
tstab[ctrl].b_actf = bp; /* then set this buffer as next */
else
tstab[ctrl].b_actl->av_forw = bp; /* else link this one to
the end */
tstab[ctrl].b_actl = bp; /* set a reverse link in table */
if (tstab[ctrl].b_active == 0) /* if not active then */
tsstart(ctrl); /* start some activity */
spl0();
}
static
tsstart(ctrl)
{
register struct buf *bp;
register struct device *tsaddr;
register struct compkt *combuf;
daddr_t blkno;
int com;
combuf = ts_cbp[ctrl];
loop:
if ((bp = tstab[ctrl].b_actf) == NULL)
return; /* no link so return */
tsaddr = ts_csr[ctrl];
blkno = ts_blkno[ctrl]; /* get current block # */
if(tsaddr->tssr & OFL)
{ /* driver off-line. bad news */
u.u_error = ETOL; /* tell user, tape off-line */
/*
printf("\nMagtape %o offline\n",minor(bp->b_dev)&077);
*/
ts_openf[ctrl] = -1; /* say this is fatal */
bp->b_flags |= B_ERROR; /* mark buffer bad */
goto next; /* start the flush loop */
}
if (bp == &ctsbuf[ctrl]) /* if this is command table */
{
switch(bp->b_resid)
{
case POSIT|SKTPF: case POSIT|SKTPR:
case POSIT|SPCFWD: case POSIT|SPCREV:
combuf->tsba = bp->b_bcount;
case FORMT|WEOF: case POSIT|RWIND: case CONTRL|REWUNL:
break;
case NOP:
bp->b_resid = mesbuf[ctrl].mstsx0;
goto next;
case INIT:
tsinit(ctrl,1);
return;
default:
printf("Bad ioctl on ts\n");
return;
}
tstab[ctrl].b_active = SCOM; /* say just general maint command */
combuf->tscom = (ACK|IEI|bp->b_resid); /* load command */
tsaddr->tsdb = ts_ubcba[ctrl]; /* feed driver packet */
return; /* later */
}
if(ts_openf[ctrl] < 0)
{
bp->b_error = ETPL; /* set fatal flag */
bp->b_flags |= B_ERROR; /* mark buffer bad */
goto next; /* start the flush loop */
}
#ifdef UCB_NKB
if(dbtofsb(bp->b_blkno) > ts_nxrec[ctrl])
#else
if(bp->b_blkno > ts_nxrec[ctrl])
#endif UCB_NKB
{
bp->b_flags |= B_ERROR; /* block # out of range */
goto next; /* start the flush loop */
}
#ifdef UCB_NKB
if(blkno != dbtofsb(bp->b_blkno)) /* if requested != current */
#else
if(blkno != bp->b_blkno) /* if requested != current */
#endif UCB_NKB
{
tstab[ctrl].b_active = SSEEK; /* say this is a seek */
#ifdef UCB_NKB
if(blkno < dbtofsb(bp->b_blkno)) /*we're not there yet so */
#else
if(blkno < bp->b_blkno) /* we're not there yet so */
#endif UCB_NKB
{ /* space forward to it */
combuf->tscom = (ACK|IEI|SPCFWD|POSIT);
#ifdef UCB_NKB
combuf->tsba = dbtofsb(bp->b_blkno) - blkno; /* load block */
#else
combuf->tsba = bp->b_blkno - blkno; /* load block */
#endif UCB_NKB
}
else
{ /* need to backup */
combuf->tscom = (ACK|IEI|SPCREV|POSIT);
#ifdef UCB_NKB
combuf->tsba = blkno - dbtofsb(bp->b_blkno); /*load block */
#else
combuf->tsba = blkno - bp->b_blkno; /* load block */
#endif UCB_NKB
}
tsaddr->tsdb = ts_ubcba[ctrl]; /* feed driver packet */
return; /* bye - bye */
}
tstab[ctrl].b_active = SIO; /* say this is read or write */
combuf->tsba = bp->b_un.b_addr; /* get low order address */
combuf->tsbae = bp->b_xmem; /* set extended bits */
combuf->tswc = bp->b_bcount; /* grab a positive byte count */
combuf->tscom = (ACK|IEI); /* set up acknowledge and IEI */
if ((ts_flags[ctrl]&S_DEOT) == 0) {
if ((mesbuf[ctrl].mstsx0&EOT) && (ts_flags[ctrl]&S_CSE) == 0) {
bp->b_resid = bp->b_bcount;
bp->b_error = ENOSPC;
bp->b_flags |= B_ERROR;
goto next;
}
}
if(bp->b_flags &B_READ)
combuf->tscom |= (RNEXT|RCOM); /* read next record */
else
combuf->tscom |= (WNEXT|WCOM); /* write next record */
tsaddr->tsdb = ts_ubcba[ctrl]; /* feed packet to drive */
ts_mcact |= (1 << ctrl); /* controller active */
el_bdact |= (1 << TS_BMAJ); /* device active */
return;
/* this loop is good for flushing buffers on fatal errors */
next:
tstab[ctrl].b_active = 0;
tstab[ctrl].b_actf = bp->av_forw; /* grab next avail forward */
if (bp == &ctsbuf[ctrl])
ts_flags[ctrl] &= ~S_BUSY;
iodone(bp); /* say current buffer done */
goto loop; /* try some more */
}
tsintr(dev)
{
register struct buf *bp;
register struct device *tsaddr;
register struct compkt *combuf;
int state, *ebp, ctrl, i;
ctrl = minor(dev) & 077;
combuf = ts_cbp[ctrl];
tsaddr = ts_csr[ctrl];
ts_erreg[ctrl] = mesbuf[ctrl].mstsx0;
ts_resid[ctrl] = mesbuf[ctrl].msresid;
if (mesbuf[ctrl].mstsx0&EOT)
ts_flags[ctrl] |= S_EOT;
else
ts_flags[ctrl] &= ~(S_EOT|S_CSE);
if((bp = tstab[ctrl].b_actf) == 0) /* grab a buffer pointer */
{
logsi(tsaddr); /* log stray interrupt */
return;
}
state = tstab[ctrl].b_active; /* get a copy of status */
if(tsaddr->tssr & SC) /* Special Condition Set ?? */
{
/* If initial error, save reg.'s for error log */
/* The two real registers are saved by fmtbde(), */
/* the remaining data is loaded from the buffers. */
if(tstab[ctrl].b_errcnt == 0)
{
fmtbde(bp, &ts_ebuf[ctrl], tsaddr, 2, TSDBOFF);
ts_ebuf[ctrl].ts_bdh.bd_nreg = NTSREG;
ebp = (char *)ts_cbp[ctrl]; /* addr of command packet */
for(i=2; i<6; i++)
ts_ebuf[ctrl].ts_reg[i] = *ebp++;
ebp = &chrbuf[ctrl]; /* addr of characteristics buffer */
for( ; i<10; i++)
ts_ebuf[ctrl].ts_reg[i] = *ebp++;
ebp = &mesbuf[ctrl]; /* addr of message buffer */
for( ; i<17; i++)
ts_ebuf[ctrl].ts_reg[i] = *ebp++;
}
/* look at the Termination Class code */
switch ((tsaddr->tssr & TCC) >> 1)
{
case 07: /* fatal sub-system error */
case 06: /* non-recoverable error */
state = 0; /* this means dead */
break; /* break out */
case 05: /* recoverable. no tape motion */
++tstab[ctrl].b_errcnt;
state = SRETRY; /* do a straight retry */
break;
case 04: /* recoverable. tape position +1 */
if (state == SIO) {
if(++tstab[ctrl].b_errcnt < 10)
{
if(bp->b_flags & B_READ)
/* re-read previous */
combuf->tscom =(ACK|IEI|RRPRV|RCOM);
else
/* write data retry */
combuf->tscom=(ACK|IEI|WDRTY|WCOM);
tsaddr->tsdb = ts_ubcba[ctrl];
return;
}
} else if (ts_openf[ctrl] > 0 && bp != &rtsbuf[ctrl])
ts_openf[ctrl] = -1;
break;
case 03: /* function reject */
/* if VCK then someone diddled
with the drive */
/* this is fatal, but not ioctl */
if (state != SCOM)
state = 0;
break;
case 02: /* Tape Status Alert */
if (state == SCOM)
break;
if(mesbuf[ctrl].mstsx0 & (TMK|EOT))
{ /* End of file/tape set max to this blk */
#ifdef UCB_NKB
ts_nxrec[ctrl] = dbtofsb(bp->b_blkno);
#else
ts_nxrec[ctrl] = bp->b_blkno;
#endif UCB_NKB
state = SOK;
break;
}
else if(mesbuf[ctrl].mstsx0&RLS)
{ /* record length is short. do not
have to treat this as fatal because
the actual byte will be returned */
state = SIO; /* update block cnt */
break;
}
else if(mesbuf[ctrl].mstsx0&RLL)
{
state = SIO;
bp->b_flags |= B_ERROR;
break;
}
break;
case 01: /* Attention Condition */
if(tsaddr->tssr & OFL)
{
printf("\nMagtape %o offline\n", ctrl);
state = 0; /* drive offline */
}
break;
default:
break;
}
if(tstab[ctrl].b_errcnt >= 10 || state == 0)
{
/* bad news. FATAL ERROR or No recovery */
bp->b_flags |= B_ERROR; /* mark this buffer bad */
bp->b_error = ETPL; /* fatal - tape position lost */
ts_openf[ctrl] = -1; /* say no more */
if(state)
tsinit(ctrl, 0); /* init the drive. no rewind*/
else
tsinit(ctrl, 1); /* init the drive. rewind */
state = 0; /* just to be sure */
}
}
if((tstab[ctrl].b_active & SREWND) == 0)
tstab[ctrl].b_active = 0; /* say ok to do more */
ts_mcact &= ~(1 << ctrl); /* controller not active */
if (ts_mcact == 0)
el_bdact &= ~(1 << TS_BMAJ); /* device not active */
/* If possible log the error, if not print it on the console */
if(tstab[ctrl].b_errcnt || state == 0)
{
ts_ebuf[ctrl].ts_bdh.bd_errcnt = tstab[ctrl].b_errcnt;
if(!logerr(E_BD, &ts_ebuf[ctrl], sizeof(struct tsebuf)))
{
/* tssr, mstsx0 */
deverror(bp, ts_ebuf[ctrl].ts_reg[1], ts_ebuf[ctrl].ts_reg[13]);
/* mstsx1, mstsx2 */
deverror(bp, ts_ebuf[ctrl].ts_reg[14], ts_ebuf[ctrl].ts_reg[15]);
/* mstsx3, retry count */
deverror(bp, ts_ebuf[ctrl].ts_reg[16], tstab[ctrl].b_errcnt);
tstab[ctrl].b_errcnt = 0; /* clear errcnt here, */
/* because of state 0 in */
/* SCOM below. */
}
}
if(state == 0)
tstab[ctrl].b_errcnt = 0;
switch (state)
{
case SRETRY: /* straight retry */
break;
case SIO: /* update block # and */
case SOK: /* fall through SCOM */
ts_blkno[ctrl]++;
case SCOM:
tstab[ctrl].b_errcnt = 0; /* no errors */
tstab[ctrl].b_actf = bp->av_forw; /* next buffer */
if(combuf->tscom & POSIT) /* if position */
bp->b_resid = 0; /* this could be
garbage */
else
bp->b_resid = mesbuf[ctrl].msresid; /* get residual
count */
if (bp == &ctsbuf[ctrl])
ts_flags[ctrl] &= ~S_BUSY;
iodone(bp); /* return buffer */
break;
case SSEEK:
#ifdef UCB_NKB
ts_blkno[ctrl] = dbtofsb(bp->b_blkno); /* update blk no */
#else
ts_blkno[ctrl] = bp->b_blkno; /* update blk no */
#endif UCB_NKB
break;
default:
break;
}
tsstart(ctrl); /* start any pending I/O */
}
static
tsinit(ctrl, ini)
int ctrl;
int ini;
{
register struct device *tsaddr;
register struct compkt *combuf;
int ds, cnt;
combuf = ts_cbp[ctrl];
tsaddr = ts_csr[ctrl];
if(ini)
{
tsaddr->tssr = 0; /* init with rewind */
tstab[ctrl].b_active = SREWND;
return(0);
}
else
{
combuf->tscom = (ACK|CVC|INIT); /* init no rewind */
tsaddr->tsdb = ts_ubcba[ctrl]; /* feed drive packet */
for(cnt = 0; cnt < 30000; cnt++)
{ /* wait loop. hopefully */
if(tsaddr->tssr & SSR) /* 3 - 5 msecs long */
break; /* have ready. break out*/
}
}
if(cnt >= 30000)
{ /* wait loop timeout */
ts_iex:
printf("TS11 init failed\n");
deverror(&ctsbuf[ctrl], tsaddr->tssr, mesbuf[ctrl].mstsx0);
return(1); /* bad stuff. say so */
}
/* show where message buffer is */
chrbuf[ctrl].msbptr = (char *)&mesbuf[ctrl] - ts_ubmo;
chrbuf[ctrl].msbae = 0; /* hope no extended addr */
chrbuf[ctrl].msbsiz = 016; /* size of message buf */
chrbuf[ctrl].mschar = 0; /* set characteristics */
combuf->tscom = (ACK|CVC|WCHAR); /* say this is write
characteristics */
combuf->tsba = (char*)&chrbuf[ctrl] - ts_ubmo; /* addr of chars buf */
combuf->tsbae = 0; /* hope no ext addr */
combuf->tswc = 010; /* size of chars buf */
tsaddr->tsdb = ts_ubcba[ctrl]; /* feed packet to drive */
for(cnt = 0; cnt < 30000; cnt++)
{
if(tsaddr->tssr & SSR)
break;
}
if(cnt >= 30000)
goto ts_iex;
if(((tsaddr->tssr & TCC) >>1) > 1)
goto ts_iex;
else
return(0); /* good init */
}
tsread(dev)
{
int ctrl;
ctrl = minor(dev) & 077;
tsphys(dev);
physio(tsstrategy, &rtsbuf[ctrl], dev, B_READ);
}
tswrite(dev)
{
int ctrl;
ctrl = minor(dev) & 077;
tsphys(dev);
physio(tsstrategy, &rtsbuf[ctrl], dev, B_WRITE);
}
static
tsphys(dev)
{
daddr_t a;
int ctrl;
if((ctrl = (minor(dev) & 077)) < nts) /* only if valid drive */
{
#ifdef UCB_NKB
a = dbtofsb(u.u_offset >>9); /* grab the block offset */
#else
a = u.u_offset >>9; /* grab the block offset */
#endif UCB_NKB
ts_blkno[ctrl] = a; /* set current block */
ts_nxrec[ctrl] = a+1; /* set next max block # */
}
}