Ausam/sys/dmr/ht.c
#
/*
* TU16 driver by Larry Wehr PY 1A111 x7982
* Handles one TM02 controller, up to 4 TU16 transports
* minor device classes:
* bits 0,1: slave select
* bit 2 off: rewind on close; on: position after first TM
* bit 3 off: 800 bpi; on: 1600 bpi
*
* Modified by Rob Mathews (Stanford Univ. (415)497-1703 ) to
* elaborate the error handling somewhat, to make cooked I/O
* a little more intelligent about TM's, and to allow raw I/O
* to read TM's as zero-length records.
*
*/
#include "../defines.h"
#include "../param.h"
#include "../buf.h"
#include "../conf.h"
#include "../user.h"
#define NUNIT 4
struct {
int htcs1, htwc, htba, htfc;
int htcs2, htds, hter, htas;
int htck, htdb, htmr, htdt;
int htsn, httc, htbae, htcs3;
};
struct devtab httab;
struct buf rhtbuf, chtbuf;
int h_openf[NUNIT]; /* 0 if closed, httc contents if open,
RAWTM set iff TM on last raw read,
and top bit set if fatal error. */
char *h_blkno[NUNIT],
*h_tmblk[NUNIT];
int htdebug; /* nonzero to debug */
#define HTADDR 0172440
/* htcs1 */
#define NOP 0
#define GO 01
#define REW 06
#define DCLR 010
#define ERASE 024
#define WEOF 026
#define SFORW 030
#define SREV 032
#define IENABLE 0100
#define RDY 0200
#define TRE 040000
/* httc */
#define FATERR 0100000 /* Software bits */
#define RAWTM 040000
#define P800 01300 /* 800 + pdp11 mode */
#define P1600 02300 /* 1600+pdp11 mode */
/* htds */
#define TM 04
#define PES 040
#define DRY 0200
#define EOT 02000
#define WRL 04000
#define MOL 010000
#define PIP 020000
#define ERR 040000
/* hter */
#define HARD 064073 /* UNS|OPI|NEF|FMT|CPAR|DPAR|RMR|ILR|ILF */
#define FCE 01000
#define CS 02000
#define COR 0100000
/* htcs2 */
#define CHARD 077400 /* Hard controller errors. */
/* Operation states */
#define SSEEK 1
#define SIO 2
#define SABORT 3
#define SRETRY 4
#define SCOM 5
#define SATMK 6
htopen(dev, flag)
{
register unit, ds;
unit = dev&03;
if (unit >= NUNIT || h_openf[unit]) {
u.u_error = ENXIO;
return;
}
h_openf[unit] = (dev&010 ? P1600 : P800)|unit;
h_blkno[unit] = 0;
h_tmblk[unit] = -1;
ds = hcommand(dev, NOP);
if ((ds&MOL)==0 || (flag && (ds&WRL)))
{ u.u_error = ENXIO;
h_openf[unit] = 0;
}
}
htclose(dev, flag)
{
register int rdev, unit;
rdev = dev;
unit = rdev&03;
if (flag) {
hcommand(rdev, WEOF);
hcommand(rdev, WEOF);
}
/* Rewind if specified. Otherwise, skip as required. */
hcommand(rdev,
!(rdev&04)? REW
: flag ? SREV
: h_openf[unit]&RAWTM ? NOP
: SFORW);
h_openf[unit] = 0;
}
hcommand(dev, com)
{
register struct buf *bp;
bp= &chtbuf;
spl5();
while(bp->b_flags&B_BUSY) {
bp->b_flags =| B_WANTED;
sleep(bp, PRIBIO);
}
spl0();
bp->b_dev = dev;
bp->b_resid = com;
bp->b_blkno = 0;
bp->b_flags = B_BUSY|B_READ;
htstrategy(bp);
iowait(bp);
if(bp->b_flags&B_WANTED)
wakeup(bp);
bp->b_flags = 0;
return(bp->b_resid);
}
htstrategy(abp)
struct buf *abp;
{
register struct buf *bp;
register char **p;
bp = abp;
p = &h_tmblk[bp->b_dev&03];
if (*p <= bp->b_blkno) {
if (*p < bp->b_blkno) {
bp->b_flags =| B_ERROR;
iodone(bp);
return;
}
if (bp->b_flags&B_READ) {
clrbuf(bp);
iodone(bp);
return;
}
}
if ((bp->b_flags&B_READ)==0)
*p = bp->b_blkno + 1;
bp->av_forw = 0;
spl5();
if (httab.d_actf==0)
httab.d_actf = bp;
else
httab.d_actl->av_forw = bp;
httab.d_actl = bp;
if (httab.d_active==0)
htstart();
spl0();
}
htstart()
{
register struct buf *bp;
register int unit;
register char *blkno;
loop:
if ((bp = httab.d_actf) == 0)
return;
unit = bp->b_dev&03;
HTADDR->htcs2 = 0;
HTADDR->httc = h_openf[unit] =& ~RAWTM;
/* If rewinding, must await completion. */
if (HTADDR->htds&PIP)
{ HTADDR->htas = 1;
HTADDR->htcs1 = TRE|IENABLE;
return;
}
HTADDR->htcs1 = TRE|DCLR|GO;
blkno = h_blkno[unit];
if (bp == &chtbuf) {
if (bp->b_resid==NOP) {
bp->b_resid = HTADDR->htds;
goto next;
}
httab.d_active = SCOM;
HTADDR->htfc = 0;
HTADDR->htcs1 = bp->b_resid|IENABLE|GO;
return;
}
if(h_openf[unit]&FATERR)
goto abort;
if (blkno == bp->b_blkno) {
httab.d_active = SIO;
rhstart(bp, &HTADDR->htfc, bp->b_wcount<<1, &HTADDR->htbae);
} else {
httab.d_active = SSEEK;
if (blkno < bp->b_blkno) {
HTADDR->htfc = blkno - bp->b_blkno;
HTADDR->htcs1 = SFORW|IENABLE|GO;
} else {
HTADDR->htfc = bp->b_blkno - blkno;
HTADDR->htcs1 = SREV|IENABLE|GO;
}
}
return;
abort:
bp->b_flags =| B_ERROR;
next:
httab.d_active = 0;
httab.d_actf = bp->av_forw;
iodone(bp);
goto loop;
}
htintr()
{
register struct buf *bp;
register int unit, state;
if ((bp = httab.d_actf)==0) return;
unit = bp->b_dev&03;
state = httab.d_active;
/* A very useful tool for debugging........................ */
if( htdebug != 0 )
printf("\nbn%d cb%d\nds%o er%o fc%o s%o\n",
bp->b_blkno, h_blkno[unit],
HTADDR->htds, HTADDR->hter, HTADDR->htfc, state);
/* ........................................................ */
/* Handle errors and TM's whenever not backing
over one (cooked TM recovery). */
if ((HTADDR->htcs1&TRE) ||
(HTADDR->htds&EOT) || /* for now... */
(HTADDR->htds&TM && state!=SATMK))
/* Screen out fatal errors (tape position lost). */
if (HTADDR->htcs2&CHARD ||
HTADDR->hter&HARD ||
HTADDR->htds&EOT || /* for now... */
state==SSEEK && (HTADDR->hter&~FCE)!=0 ||
state!=SIO && state!=SCOM && state!=SSEEK)
{ /* Fatal. Abort all operations and chain ahead. */
h_openf[unit] =| FATERR;
state = SABORT; /* for below */
deverror(bp, HTADDR->hter, HTADDR->htcs2);
bp->b_flags =| B_ERROR;
}
/* Nonfatal. Deal with it. */
else switch(state)
{
case SIO:
/* Screen out actual errors: anything if
writing; any remaining controller errors;
if reading, things not corrected; if
reading raw, not FCE's; and in any event
no TM-caused FCE's. */
if ((bp->b_flags&B_READ)==0 ||
HTADDR->htcs2.hibyte ||
HTADDR->hter&~(HTADDR->htds&PES ? CS|COR : 0)
&~(bp== &rhtbuf || HTADDR->htds&TM ? FCE : 0))
/* It's an error. Retry if not too many. */
if (httab.d_errcnt++ < 10)
{ httab.d_active = SRETRY;
HTADDR->htcs1 = TRE|DCLR|GO;
HTADDR->htfc = -1;
HTADDR->htcs1 = SREV|IENABLE|GO;
return;
}
/* Too many errors. */
else
{ deverror(bp, HTADDR->hter, HTADDR->htcs2);
bp->b_flags =| B_ERROR;
break;
}
/* Not actually an error. Get out of here
unless cooked TM, which is handled as
for a seek. Note: NRZ TM causes fc
to be set to 2!!! */
/* If raw read, all is okay as is. */
if (!(HTADDR->htds&TM)) break;
/* If raw TM, zero fc (to be sure). Otherwise,
set it to 1 for the seek logic. */
if ((HTADDR->htfc = bp== &rhtbuf ? 0 : 1)==0)
{ h_openf[unit] =| RAWTM;
break;
}
case SSEEK:
/* Must be cooked (no SSEEK on raw) TM on a
forward skip (FCE above). Confine tape
position to before the TM. */
h_blkno[unit] = bp->b_blkno + HTADDR->htfc - 1;
bp->b_flags =| B_ERROR;
httab.d_active = SATMK;
HTADDR->htcs1 = TRE|DCLR|GO;
HTADDR->htfc = -1;
HTADDR->htcs1 = SREV|IENABLE|GO;
return;
case SCOM:
/* Ignore everything. */
;
};
/* Either no error or completely processed error. */
switch(state)
{
case SRETRY:
if ((bp->b_flags&B_READ)==0)
{ httab.d_active = SSEEK;
HTADDR->htcs1 = ERASE|IENABLE|GO;
return;
}
case SSEEK:
h_blkno[unit] = bp->b_blkno;
break;
case SIO:
h_blkno[unit]++;
case SCOM:
case SABORT:
case SATMK:
httab.d_active = httab.d_errcnt = 0;
httab.d_actf = bp->av_forw;
bp->b_resid = HTADDR->htfc;
iodone(bp);
};
/* Start an operation (perhaps the old same one). */
htstart();
}
htread(dev)
{
htphys(dev);
physio(htstrategy, &rhtbuf, dev, B_READ);
u.u_count = u.u_arg[1] - rhtbuf.b_resid;
}
htwrite(dev)
{
htphys(dev);
physio(htstrategy, &rhtbuf, dev, B_WRITE);
u.u_count = 0;
}
htphys(dev)
{
register unit, a;
unit = dev&03;
a = u.u_offset >> 9 ;
h_blkno[unit] = a;
h_tmblk[unit] = ++a;
}
#ifdef POWER_FAIL
htpowf()
{
register *p;
for ( p = h_openf ; p < h_openf[NUNIT] ; p++ )
p =| FATERR;
htstart();
}
#endif POWER_FAIL