SRI-NOSC/dmr/tm.c

Compare this file to the similar file:
Show the results in this format:

#
/*
 */

/*
 * TM tape driver
 */

#include "param.h"
#include "buf.h"
#include "conf.h"
#include "user.h"

struct {
	int tmer;
	int tmcs;
	int tmbc;
	int tmba;
	int tmdb;
	int tmrd;
};

struct	devtab	tmtab;
struct	buf	rtmbuf;

#ifdef NOSC	/* extra flag in rtmbuf.b_flags to handle odd-length writes */
#define B_ODDLEN 0100000
#endif NOSC
#define B_SCOM	 0040000

char	t_openf[8];
char	*t_blkno[8];
char	*t_nxrec[8];

#define	TMADDR	0172520

#define NOREW	020

#define	GO	01
#define NOCMD	000
#define	RCOM	002
#define	WCOM	004
#define	WEOF	006
#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	EOF	0040000

#define	SSEEK	1
#define	SIO	2
#define SCOM	3

tmopen(dev, flag)
{
	register dminor;

	dminor = dev.d_minor&07;
	if (t_openf[dminor])
		u.u_error = ENXIO;
	else {
		t_openf[dminor]++;
		t_blkno[dminor] = 0;
		t_nxrec[dminor] = 65535;
	}
}

tmclose(dev, flag)
{
	register int dminor;

	dminor = dev.d_minor&07;
	if (flag) {
		tcommand(dminor, WEOF);
		tcommand(dminor, WEOF);
		tcommand(dminor, dev&NOREW ? SREV : REW);
	} else {
		if((dev&NOREW)==0)
			tcommand(dminor, REW);
		else if(t_openf[dminor] != -1)
			tcommand(dminor, SFORW);
	}
	t_openf[dminor] = 0;
}

tcommand(dev, com)
{
	register struct buf *bp;

	bp = &rtmbuf;
	spl6();
	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_SCOM | B_BUSY | B_READ;
	tmqup(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&07];
	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;
	tmqup(bp);
}

tmqup(abp)
struct buf *abp;
{
	register struct buf *bp;

	bp = abp;
	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;
	int unit;
	register char *blkno;

    loop:
	if ((bp = tmtab.d_actf) == 0)
		return;
	com = ((unit=bp->b_dev.d_minor&07)<<8) | ((bp->b_xmem & 03) << 4) | DENS;
	if(bp->b_flags&B_SCOM) {
		if(bp->b_resid == NOCMD) {
			/* don't want IENABLE bit or else */
			/* interrupt will occur immediately */
			TMADDR->tmcs = com;
			bp->b_resid = TMADDR->tmer;
			tmdone();
			goto loop;
		}
		tmtab.d_active = SCOM;
		TMADDR->tmbc = 1;	/* i.e. infinity, or until eof */
		TMADDR->tmcs = bp->b_resid | com | IENABLE | GO;
		return;
	}
	if (t_openf[unit] < 0 || (TMADDR->tmcs & CRDY)==0) {
		bp->b_flags =| B_ERROR;
		tmdone();
		goto loop;
	}
	blkno = t_blkno[unit];
	if (blkno != bp->b_blkno) {
		tmtab.d_active = SSEEK;
		if (blkno < bp->b_blkno) {
			com =| SFORW|IENABLE|GO;
			TMADDR->tmbc = blkno - bp->b_blkno;
		} else {
			com =| SREV|IENABLE|GO;
			TMADDR->tmbc = bp->b_blkno - blkno;
		}
		TMADDR->tmcs = com;
		return;
	}
	tmtab.d_active = SIO;
	TMADDR->tmbc = bp->b_wcount << 1;
#ifdef NOSC
	if(bp->b_flags & B_ODDLEN) TMADDR->tmbc++;
#endif NOSC
	TMADDR->tmba = bp->b_addr;		/* core address */
	TMADDR->tmcs = com | ((bp->b_flags&B_READ)? RCOM|IENABLE|GO:
	    ((tmtab.d_errcnt)? WIRG|IENABLE|GO: WCOM|IENABLE|GO));
}

tmdone()
{
	register struct buf *bp;

	bp = tmtab.d_actf;
	tmtab.d_errcnt = 0;
	tmtab.d_actf = bp->av_forw;
	tmtab.d_active = 0;
	iodone(bp);
}

tmintr()
{
	register struct buf *bp;
	register int unit;

	if ((bp = tmtab.d_actf)==0)
		return;
	unit = bp->b_dev.d_minor&07;
	/* ignore errors on SCOM */
	if (tmtab.d_active == SCOM) {
		bp->b_resid = TMADDR->tmbc -1;	/* return skip count */
		tmdone();
		tmstart();
		return;
	}
	if (TMADDR->tmcs < 0) {		/* error bit */
		while(TMADDR->tmrd & GAPSD) ; /* wait for gap shutdown */
		if ((TMADDR->tmer&(HARD|EOF))==0 && tmtab.d_active==SIO) {
			if (++tmtab.d_errcnt < 10) {
				t_blkno[unit]++;
				tmtab.d_active = 0;
				tmstart();
				return;
			}
/*
			deverror(bp, TMADDR->tmer, 0);
 */
		} else
			if(bp != &rtmbuf && (TMADDR->tmer&EOF)==0)
				t_openf[unit] = -1;
/*
			else
				deverror(bp, TMADDR->tmer, 0);
 */
		bp->b_flags =| B_ERROR;
		tmtab.d_active = SIO;
	}
	if (tmtab.d_active == SIO) {
		t_blkno[unit]++;
		bp->b_resid = TMADDR->tmbc;
		tmdone();
	} else
		t_blkno[unit] = bp->b_blkno;
	tmstart();
}
#ifndef NOSC
tmread(dev)
{
	tmphys(dev);
	physio(tmstrategy, &rtmbuf, dev, B_READ);
	u.u_count = -rtmbuf.b_resid;
}

tmwrite(dev)
{
	tmphys(dev);
	physio(tmstrategy, &rtmbuf, dev, B_WRITE);
	u.u_count = 0;
}

tmphys(dev)
{
	register unit, a;

	unit = dev.d_minor&07;
	a = lshift(u.u_offset, -9);
	t_blkno[unit] = a;
	t_nxrec[unit] = ++a;
}
#endif NOSC
#ifdef NOSC
		/* Special NOSC code to permit odd-length reads and writes */
tmread(dev)
{
	physio(tmstrategy, &rtmbuf, dev, tmphys(dev)|B_READ);
	u.u_count = -rtmbuf.b_resid;
}

tmwrite(dev)
{
	physio(tmstrategy, &rtmbuf, dev, tmphys(dev)|B_WRITE);
	u.u_count = 0;
}

tmphys(dev)
{
	register unit, a;

	unit = dev.d_minor&07;
	a = lshift(u.u_offset, -9);
	t_blkno[unit] = a;
	t_nxrec[unit] = ++a;
	if(u.u_count & 01) {
		u.u_count++;
		return (B_ODDLEN);
	}
	return 0;
}
#endif NOSC