USG_PG3/usr/source/io2/tm.c
#
/*
* TM tape driver
* Conventions:
* minor devices 0-3 rewind
* minor devices 4-7 no rewind
*/
#include "../head/param.h"
#include "../head/systm.h"
#include "../head/buf.h"
#include "../head/bufx.h"
#include "../head/conf.h"
#include "../head/file.h"
#include "../head/user.h"
#include "../head/userx.h"
#include "../head/elog.h"
struct tmregs {
int tmer;
int tmcs;
int tmbc;
int tmba;
int tmdb;
int tmrd;
};
#define NTM 4
#define TMADDR 0172520
struct devtab tmtab;
struct buf rtmbuf;
struct buf ctmbuf;
struct iostat tmstat[NTM];
struct errtab tmetab { etabinit(E_BLK,NTM,TM,tmstat) };
char t_openf[NTM];
char *t_blkno[NTM];
char *t_nxrec[NTM];
#define GO 01
#define RCOM 02
#define WCOM 04
#define WEOF 06
#define SFORW 010
#define SREV 012
#define WIRG 014
#define REW 016
#define DENS 060000 /* 9-channel */
#define IENABLE 0100
#define CRDY 0200
#define TUR 1
#define WRL 4
#define SELR 0100
#define GSD 010000
#define HARD 0102200 /* ILC, EOT, NXM */
#define EOF 0040000
#define NOP 0
#define SSEEK 01
#define SIO 02
#define SBACK 04
#define TCMD 010
#define SBAD 020
tmopen(dev, flag)
{
register int unit, ds, i;
unit = dev.d_minor&03;
if(unit >= NTM || t_openf[unit]) {
u.u_error = ENXIO;
return;
}
flag =& FWRITE;
for(i = 0; i < 75; i++) { /* only wait 5 min for TUR */
t_blkno[unit] = 0;
t_nxrec[unit] = 65535L;
ds = tcommand(unit, NOP);
if((ds&SELR)==0 || (flag && (ds&WRL)) || t_openf[unit]) {
u.u_error = ENXIO;
return;
}
if(ds&TUR) {
if(u.u_error == 0)
t_openf[unit]++;
return;
}
sleep(&lbolt, -1);
}
u.u_error = ENXIO;
}
tmclose(dev, flag)
{
register int unit;
unit = dev.d_minor&03;
flag =& FWRITE;
if (flag){
tcommand(unit, WEOF);
tcommand(unit, WEOF);
}
if ((dev.d_minor&04) == 0)
tcommand(unit, REW);
else if(flag)
tcommand(unit, SREV);
t_openf[unit] = 0;
}
tcommand(unit, com)
{
register struct buf *bp;
bp = &ctmbuf;
spl5();
while(bp->b_flags&B_BUSY) {
bp->b_flags =| B_WANTED;
sleep(bp, PRIBIO);
}
spl0();
bp->b_flags = B_BUSY|B_READ;
bp->b_dev = unit;
bp->b_blkno = 0;
bp->b_resid = com;
tmstrategy(bp);
iowait(bp);
if(bp->b_flags&B_WANTED)
wakeup(bp);
bp->b_flags = 0;
return(bp->b_resid);
}
tmstrategy(abp)
struct buf *abp;
{
register struct buf *bp;
register char **p;
bp = abp;
if(bp->b_flags&B_PHYS)
mapalloc(bp);
p = &t_nxrec[bp->b_dev.d_minor&03];
if (*p < bp->b_blkno || (*p == bp->b_blkno && bp->b_flags&B_READ)) {
if (bp->b_flags&B_READ)
bp->b_resid = bp->b_wcount;
else {
bp->b_flags =| B_ERROR;
bp->b_error = ENXIO;
}
iodone(bp);
return;
}
if ((bp->b_flags&B_READ)==0)
*p = bp->b_blkno + 1;
bp->av_forw = 0;
spl5();
if (tmtab.d_actf==0)
tmtab.d_actf = bp;
else
tmtab.d_actl->av_forw = bp;
tmtab.d_actl = bp;
if (tmtab.d_active==0)
tmstart();
spl0();
}
tmstart()
{
register struct buf *bp;
register int com, unit;
char *blkno;
loop:
if ((bp = tmtab.d_actf) == 0)
return;
unit = bp->b_dev.d_minor&03;
com = (unit<<8);
TMADDR->tmcs = com; /* gives unit time to be selected */
blkno = t_blkno[unit];
tmetab.e_aunit = &tmstat[unit];
if(bp == &ctmbuf) {
if(bp->b_resid == NOP) {
bp->b_resid = TMADDR->tmer;
tmtab.d_actf = bp->av_forw;
iodone(bp);
goto loop;
}
tmtab.d_active = TCMD;
tmetab.e_aunit->io_misc++;
TMADDR->tmbc = 0;
TMADDR->tmcs = com|bp->b_resid|DENS|IENABLE|GO;
return;
}
if (t_openf[unit] < 0 || (TMADDR->tmcs & CRDY)==0) {
if((t_openf[unit] != -2) || (bp->b_flags&B_READ)) {
if (t_openf[unit] == -2)
bp->b_resid = bp->b_wcount;
else
bp->b_flags =| B_ERROR;
tmtab.d_actf = bp->av_forw;
iodone(bp);
goto loop;
}
}
com =| DENS | ((bp->b_xmem & 03)<<4) | IENABLE ;
if (blkno != bp->b_blkno) {
tmtab.d_active = SSEEK;
if (blkno < bp->b_blkno) {
com =| SFORW|GO;
TMADDR->tmbc = blkno - bp->b_blkno;
} else {
com =| SREV|GO;
TMADDR->tmbc = bp->b_blkno - blkno;
if(t_openf[unit] == -2)
tmtab.d_active = SBACK;
}
tmetab.e_aunit->io_misc++;
}
else {
tmtab.d_active = SIO;
tmetab.e_aunit->io_ops++;
blkacty =| (1<<TM);
TMADDR->tmbc = bp->b_wcount << 1;
TMADDR->tmba = bp->b_addr; /* core address */
com =| ((bp->b_flags&B_READ)? RCOM|GO:
((tmtab.d_errcnt)? WIRG|GO: WCOM|GO));
}
TMADDR->tmcs = com;
}
tmintr()
{
register struct buf *bp;
register int unit, state;
struct tmregs tmregs[0];
if ((bp = tmtab.d_actf)==0)
return;
blkacty =& ~(1<<TM);
state = tmtab.d_active;
unit = bp->b_dev.d_minor&03;
if (TMADDR->tmcs < 0) { /* error bit */
while(TMADDR->tmrd & GSD) ; /* wait for gap shutdown */
if(state&(SSEEK|TCMD) || TMADDR->tmer&HARD) {
t_openf[unit] = -1;
state = SBAD;
}
else if((TMADDR->tmer&EOF) == 0) { /* soft errors */
if (tmtab.d_errcnt < 10) {
fmtblk(&tmetab,TMADDR,sizeof(tmregs[0])/2,bp);
if(tmtab.d_errcnt++ == 0)
logerr(&tmetab,E_FIRST);
t_blkno[unit]++;
tmtab.d_active = 0;
tmstart();
return;
}
else
state = SBAD;
}
else if(bp != &rtmbuf) { /* EOF */
if(state&SBACK)
t_openf[unit] = 1;
else
t_openf[unit] = -2;
}
if(state&SBAD) {
bp->b_flags =| B_ERROR;
fmtblk(&tmetab,TMADDR,sizeof(tmregs[0])/2,bp);
}
else
TMADDR->tmbc = bp->b_wcount << 1;
}
if(state&(SIO|TCMD|SBAD)) {
if(tmetab.e_emsg != NULL)
logerr(&tmetab,E_RETRY);
tmtab.d_errcnt = 0;
t_blkno[unit]++;
tmtab.d_actf = bp->av_forw;
tmtab.d_active = 0;
bp->b_resid = TMADDR->tmbc >> 1;
iodone(bp);
} else
t_blkno[unit] = bp->b_blkno;
tmstart();
}
tmread(dev)
{
tmphys(dev);
physio(tmstrategy, &rtmbuf, dev, B_READ, 0);
}
tmwrite(dev)
{
tmphys(dev);
physio(tmstrategy, &rtmbuf, dev, B_WRITE, 0);
}
tmphys(dev)
{
register unit, a;
unit = dev.d_minor&03;
a = lshift(u.u_offset, -9);
t_blkno[unit] = a;
t_nxrec[unit] = ++a;
}