Research V7 development
[unix-history] / usr / sys / dev / tm.c
#
/*
* TM tape driver
*/
#include "../h/param.h"
#include "../h/buf.h"
#include "../h/dir.h"
#include "../h/conf.h"
#include "../h/file.h"
#include "../h/user.h"
struct device {
int tmer;
int tmcs;
int tmbc;
char *tmba;
int tmdb;
int tmrd;
};
struct buf tmtab;
struct buf ctmbuf;
struct buf rtmbuf;
char t_flags[8];
char t_openf[8];
daddr_t t_blkno[8];
daddr_t t_nxrec[8];
#define TMADDR ((struct device *)0172520)
#define GO 01
#define RCOM 02
#define WCOM 04
#define WEOF 06
#define NOP 0100
#define SFORW 010
#define SREV 012
#define WIRG 014
#define REW 016
#define DENS 060000 /* 9-channel */
#define IENABLE 0100
#define CRDY 0200
#define GAPSD 010000
#define TUR 1
#define HARD 0102200 /* ILC, EOT, NXM */
#define RLE 0100
#define EOF 0040000
#define WL 04
#define SSEEK 1
#define SIO 2
#define SCOM 3
#define T_WRITTEN 1
tmopen(dev, flag)
{
register unit, ds;
unit = minor(dev) & 0177;
if (t_openf[unit]) {
u.u_error = ENXIO;
return;
}
t_blkno[unit] = 0;
t_nxrec[unit] = 65535;
t_flags[unit] = 0;
tmtab.b_flags |= B_TAPE;
ds = tcommand(dev, NOP);
if ((ds&TUR)==0) {
printf("mt%d off line\n",unit);
u.u_error = ENXIO;
}
if (flag && ds&WL) {
printf("mt%d needs write ring\n",unit);
u.u_error = ENXIO;
}
if (u.u_error==0)
t_openf[unit]++;
}
tmclose(dev, flag)
dev_t dev;
int flag;
{
if ( flag == FWRITE ||
((flag&FWRITE) && (t_flags[minor(dev)&0177]&T_WRITTEN))) {
tcommand(dev, WEOF);
tcommand(dev, WEOF);
tcommand(dev, SREV);
}
if ((minor(dev)&0200) == 0)
tcommand(dev, REW);
t_openf[minor(dev)&077] = 0;
}
tcommand(dev, com)
{
register struct buf *bp;
bp = &ctmbuf;
spl5();
while (bp->b_flags&B_BUSY) {
bp->b_flags |= B_WANTED;
sleep((caddr_t)bp, PRIBIO);
}
bp->b_flags = B_BUSY|B_READ;
spl0();
bp->b_dev = dev;
bp->b_resid = com;
bp->b_blkno = 0;
tmstrategy(bp);
iowait(bp);
if (bp->b_flags&B_WANTED)
wakeup((caddr_t)bp);
bp->b_flags = 0;
return(bp->b_resid);
}
tmstrategy(bp)
register struct buf *bp;
{
register daddr_t *p;
if(bp->b_flags&B_PHYS)
mapalloc(bp);
if (bp != &ctmbuf) {
p = &t_nxrec[minor(bp->b_dev)&0177];
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);
bp->b_resid = 0;
iodone(bp);
return;
}
}
if ((bp->b_flags&B_READ) == 0) {
t_flags[minor(bp->b_dev)&0177] |= T_WRITTEN;
*p = bp->b_blkno+1;
}
}
bp->av_forw = 0;
spl5();
if (tmtab.b_actf == NULL)
tmtab.b_actf = bp;
else
tmtab.b_actl->av_forw = bp;
tmtab.b_actl = bp;
if (tmtab.b_active == NULL)
tmstart();
spl0();
}
tmstart()
{
register struct buf *bp;
register int com;
int unit;
register daddr_t *blkno;
loop:
if ((bp = tmtab.b_actf) == 0)
return;
unit = minor(bp->b_dev)&0177;
blkno = &t_blkno[unit];
if (t_openf[unit] < 0 || (TMADDR->tmcs & CRDY) == NULL) {
bp->b_flags |= B_ERROR;
goto next;
}
if (bp == &ctmbuf) {
if (bp->b_resid == NOP) {
bp->b_resid = TMADDR->tmer;
goto next;
}
tmtab.b_active = SCOM;
TMADDR->tmcs = DENS|bp->b_resid|GO| (unit<<8) | IENABLE;
return;
}
com = (unit<<8) | ((bp->b_xmem & 03) << 4) | IENABLE|DENS;
if (*blkno != bp->b_blkno) {
tmtab.b_active = SSEEK;
if (*blkno < bp->b_blkno) {
com |= SFORW|GO;
TMADDR->tmbc = *blkno - bp->b_blkno;
} else {
if (bp->b_blkno == 0)
com |= REW|GO;
else {
com |= SREV|GO;
TMADDR->tmbc = bp->b_blkno - *blkno;
}
}
TMADDR->tmcs = com;
return;
}
tmtab.b_active = SIO;
TMADDR->tmbc = -bp->b_bcount;
TMADDR->tmba = bp->b_un.b_addr;
TMADDR->tmcs = com | ((bp->b_flags&B_READ)? RCOM|GO:
((tmtab.b_errcnt)? WIRG|GO: WCOM|GO));
return;
next:
tmtab.b_actf = bp->av_forw;
iodone(bp);
goto loop;
}
tmintr()
{
register struct buf *bp;
register int unit;
int state;
if ((bp = tmtab.b_actf) == NULL)
return;
unit = minor(bp->b_dev)&0177;
state = tmtab.b_active;
tmtab.b_active = 0;
if (TMADDR->tmcs < 0) { /* error bit */
while(TMADDR->tmrd & GAPSD) ; /* wait for gap shutdown */
if (TMADDR->tmer&EOF) {
t_nxrec[unit] = bp->b_blkno;
state = SCOM;
TMADDR->tmbc = -bp->b_bcount;
goto out;
}
if ((TMADDR->tmer&HARD) == 0 && TMADDR->tmer&RLE) {
state = SIO;
goto out;
}
if ((TMADDR->tmer&(HARD|EOF)) == NULL && state==SIO) {
if (++tmtab.b_errcnt < 2) {
t_blkno[unit]++;
tmtab.b_active = 0;
tmstart();
return;
}
} else
if (t_openf[unit]>0 && bp!=&rtmbuf &&
(TMADDR->tmer&EOF)==0 ) {
t_openf[unit] = -1;
deverror(bp, TMADDR->tmer, 0);
}
bp->b_flags |= B_ERROR;
state = SIO;
}
out:
switch ( state ) {
case SIO:
t_blkno[unit] += (bp->b_bcount>>BSHIFT);
case SCOM:
tmtab.b_errcnt = 0;
tmtab.b_actf = bp->av_forw;
bp->b_resid = -TMADDR->tmbc;
iodone(bp);
break;
case SSEEK:
t_blkno[unit] = bp->b_blkno;
break;
default:
return;
}
tmstart();
}
tmread(dev)
{
tmphys(dev);
physio(tmstrategy, &rtmbuf, dev, B_READ);
}
tmwrite(dev)
{
tmphys(dev);
physio(tmstrategy, &rtmbuf, dev, B_WRITE);
}
tmphys(dev)
{
register unit;
daddr_t a;
unit = minor(dev) & 0177;
if(unit < 8) {
a = u.u_offset >> 9;
t_blkno[unit] = a;
t_nxrec[unit] = a+1;
}
}