Ultrix-3.1/sys/dev/tm.c

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


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

/*
 * ULTRIX-11 TM11 - 800 BPI tape driver
 *
 * SCCSID: @(#)tm.c	3.0	4/21/86
 *
 * 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 3/11/82
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/dir.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/errlog.h>
#include <sys/devmaj.h>
#include <sys/mtio.h>

struct device {
	int	tmer;	/* TM status register */
	int	tmcs;	/* TM command register */
	int	tmbc;	/* TM byte record count register */
	char	*tmba;	/* TM current memory address register */
	int	tmdb;	/* TM data buffer register */
	int	tmrd;	/* TM read data lines */
};
/*
 * Fred Canter 8/30/81
 *
 * `tmtab' is now in data space instead of bss,
 * so that tapes which look like the TM11
 * will work. For some reason, if tmtab is in bss space
 * it gets corrupted and the tape hangs, thinks its open
 * but really is not ?
 * What makes this even stranger is that it works fine
 * on a real DEC TM11 tape !
 */
struct	buf	tmtab = 0;
struct	buf	ctmbuf[];
struct	buf	rtmbuf;

char	tm_openf[];
u_short	tm_flags[];
daddr_t	tm_blkno[];
daddr_t	tm_nxrec[];
u_short	tm_erreg[];
u_short	tm_dsreg[];
short	tm_resid[];

/*
 * Block device error log buffer, holds one
 * error log record until error retry sequence
 * has been completed.
 */

struct
{
	struct elrhdr tm_hdr;	/* record header */
	struct el_bdh tm_bdh;	/* block device header */
	int tm_reg[NTMREG];	/* device registers at error time */
} tm_ebuf;

int	io_csr[];	/* CSR address now in config file (c.c) */
int	ntm;		/* number of drives, see c.c */

int	wakeup();
int	hz;

#define	OFFL	0
#define	GO	01
#define	RCOM	02
#define	RWS	02
#define	WCOM	04
#define	WEOF	06
#define	SELR	0100
#define	NOP	0100
#define	SFORW	010
#define	SREV	012
#define	WIRG	014
#define	FUNC	016
#define	REW	016
#define	DCLR	010000
#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	01000
#define	EOF	0040000
#define	EOT	0002000
#define	BOT	0000040
#define	WL	04

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

#define	INF	1000000

#define T_WRITTEN 01	/* last command is write command */
#define	T_EOT	  010	/* EOT encountered */
#define	T_CSE	  020	/* clear EOT */
#define	T_DEOT	  040	/* disable EOT */
#define	T_BUSY	  0100	/* ctnbuf is busy, switch for clx/cls */
#define	T_WAIT	  0200	/* ctmbuf is waited, switch for clx/cls */
#define	T_BUSYF	  01000	/* good termination of busy */
#define	T_WAITF	  02000	/* good termination of wait */

tmopen(dev, flag)
{
	register unit, ds;

	unit = minor(dev) & 07;
	if (unit >= ntm) {
		u.u_error = ENXIO;
		return;
	}
	if (flag&FNDELAY) {
		tm_openf[unit] = 1;
		return;
	}
	if (tm_openf[unit] > 0) {
		u.u_error = ETO;
		return;
	}
	tm_blkno[unit] = 0;
	tm_nxrec[unit] = 65535;
	tm_flags[unit] &= T_DEOT;

	tmtab.b_flags |= B_TAPE;
	if ((ds = tcommand(dev, NOP, 1)) == -1) {
		u.u_error = ENXIO;
		return;
	}
	if (ds & EOT)
		tm_flags[unit] |= T_EOT;
	if ((ds&TUR)==0) {
/*		printf("mt%d off line\n",unit);	*/
		u.u_error = ETOL;
	}
	if ((flag & FWRITE) && (ds & WL)) {
/*		printf("mt%d needs write ring\n",unit);	*/
		u.u_error = ETWL;
	}
	if (u.u_error==0)
		tm_openf[unit] = 1;
	else
		tm_openf[unit] = 0;
}

tmclose(dev, flag)
dev_t dev;
int flag;
{
	register int unit;

	unit = minor(dev) & 07;
	if (tm_openf[unit] <= 0)
		return;
	if(((flag & (FWRITE|FREAD)) == FWRITE) ||
	   ((flag&FWRITE) && (tm_flags[unit]&T_WRITTEN))) {
		if (tcommand(dev, WEOF, 1) == -1)
			goto badcls;
		if (tcommand(dev, WEOF, 1) == -1)
			goto badcls;
		if (tcommand(dev, SREV, 1) == -1)
			goto badcls;
	}
	if ((minor(dev)&0200) == 0) {
		if (tcommand(dev, REW, 0) == -1)
			goto badcls;
		tm_flags[unit] &= ~T_EOT;
	}
	tm_openf[unit] = 0;
	return;

badcls:
	u.u_error = ENXIO;
	return;
}

tmioctl(dev, cmd, data, flag)
	caddr_t data;
	dev_t dev;
{
	int unit;
	register struct buf *bp;
	struct buf *wbp;
	register callcount;
	int fcount;
	struct mtop *mtop;
	struct mtget *mtget;
	struct device *tmaddr;
	/* we depend of the values and order of the MT codes here */
	static tmops[] =
	   {WEOF,SFORW,SREV,SFORW,SREV,REW,OFFL,NOP,NOP,NOP,NOP,DCLR,DCLR,
	    NOP,NOP,NOP,NOP};

	tmaddr = io_csr[TM_RMAJ];
	unit = minor(dev) & 07;
	bp = &ctmbuf[unit];
	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:
			callcount = mtop->mt_count;
			fcount = INF;
			break;

		case MTFSR: case MTBSR:
			callcount = 1;
			fcount = mtop->mt_count;
			break;

		case MTREW: case MTOFFL: case MTNOP:
			callcount = 1;
			fcount = 1;
			break;

		case MTCACHE: case MTNOCACHE:
		case MTASYNC: case MTNOASYNC:
			return(0);

		case MTCSE:
			tm_flags[unit] |= T_CSE;
			return(0);

		case MTENAEOT:
			tm_flags[unit] &= ~T_DEOT;
			return(0);

		case MTDISEOT:
			tm_flags[unit] |= T_DEOT;
			return(0);

		case MTCLX:
			return(0);

		case MTCLS:
			if ((wbp = tmtab.b_actf) != NULL) {
				tmtab.b_actf = wbp->av_forw;
				if (wbp != bp) {
					wbp->b_error = EIO;
					wbp->b_flags |= B_ERROR;
					iodone(wbp);
				}
			}
			if (tm_flags[unit]&T_BUSY) {
				bp->b_flags &= ~B_BUSY;
				tm_flags[unit] &= ~T_BUSYF;
				iodone(bp);
			}
			if (tm_flags[unit]&T_WAIT) {
				bp->b_flags &= ~B_WANTED;
				tm_flags[unit] &= ~T_WAITF;
				wakeup((caddr_t)bp);
			}
			tmaddr->tmcs = DCLR;
			tm_blkno[unit] = 0;
			tm_nxrec[unit] = 65535;
			tm_flags[unit] &= T_DEOT;
			tmtab.b_active == NULL;
			tmstart();
			return(0);

		default:
			return (ENXIO);
		}
		if (callcount <= 0 || fcount <= 0)
			return (EINVAL);
		while (--callcount >= 0) {
			if (tcommand(dev, tmops[mtop->mt_op], fcount) == -1) {
				u.u_error = ENXIO;
				return;
			}
			if ((mtop->mt_op == MTFSR || mtop->mt_op == MTBSR) &&
			    bp->b_resid)
				return (EIO);
			if ((bp->b_flags&B_ERROR) || tm_erreg[unit]&BOT)
				break;
		}
		return (geterror(bp));

	case MTIOCGET:
		mtget = (struct mtget *)data;
		mtget->mt_dsreg = tm_dsreg[unit];
		mtget->mt_erreg = tm_erreg[unit];
		mtget->mt_resid = tm_resid[unit];
		mtget->mt_softstat = 0;
		if (tm_flags[unit]&T_EOT)
			mtget->mt_softstat |= MT_EOT;
		if (tm_flags[unit]&T_DEOT)
			mtget->mt_softstat |= MT_DISEOT;
		mtget->mt_type = MT_ISTM;
		break;

	default:
		return (ENXIO);
	}
	return (0);
}

static
tcommand(dev, com, count)
{
	register struct buf *bp;
	int	unit;

	unit = minor(dev) & 07;
	bp = &ctmbuf[unit];
	spl5();
	while (bp->b_flags&B_BUSY) {
		bp->b_flags |= B_WANTED;
		tm_flags[unit] |= (T_WAIT|T_WAITF);
		sleep((caddr_t)bp, PRIBIO);
		if ((tm_flags[unit]&T_WAITF) == 0)
			return(-1);
	}
	bp->b_flags = B_BUSY|B_READ;
	spl0();
	bp->b_dev = dev;
	bp->b_resid = com;
	bp->b_bcount = count;
	bp->b_blkno = 0;
	tm_flags[unit] |= (T_BUSY|T_BUSYF);
	tmstrategy(bp);
	iowait(bp);
	if ((tm_flags[unit]&T_BUSYF) == 0)
		return(-1);
	if (bp->b_flags&B_WANTED) {
		tm_flags[unit] &= ~T_WAIT;
		wakeup((caddr_t)bp);
	}
	bp->b_flags &= B_ERROR;
	return(bp->b_resid);
}

tmstrategy(bp)
register struct buf *bp;
{
	register daddr_t *p;
	register struct device *tmaddr;
	int	unit;

	tmaddr = io_csr[TM_RMAJ];
	mapalloc(bp);
	unit = minor(bp->b_dev)&07;
	if (bp != &ctmbuf[unit]) {
		p = &tm_nxrec[unit];
#ifdef	UCB_NKB
		if (*p <= dbtofsb(bp->b_blkno)) {
#else
		if (*p <= bp->b_blkno) {
#endif	UCB_NKB
#ifdef	UCB_NKB
			if (*p < dbtofsb(bp->b_blkno)) {
#else
			if (*p < bp->b_blkno) {
#endif	UCB_NKB
				bp->b_flags |= B_ERROR;
				bp->b_error = ENXIO;
				iodone(bp);
				return;
			}
			if (bp->b_flags&B_READ) {
				if((bp->b_flags&B_PHYS) == 0)
					clrbuf(bp);
				bp->b_resid = bp->b_bcount;
				iodone(bp);
				return;
			}
		}
		tm_flags[unit] &= ~T_WRITTEN;
		if ((bp->b_flags&B_READ) == 0) {
			tm_flags[unit] |= T_WRITTEN;
#ifdef	UCB_NKB
			*p = dbtofsb(bp->b_blkno) + 1;
#else
			*p = bp->b_blkno+1;
#endif	UCB_NKB
		}
	}
	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();
}

static
tmstart()
{
	register struct buf *bp;
	register struct device *tmaddr;
	register daddr_t *blkno;
	int com;
	int unit;

	tmaddr = io_csr[TM_RMAJ];
    loop:
	if ((bp = tmtab.b_actf) == 0)
		return;
	unit = minor(bp->b_dev)&07;
	blkno = &tm_blkno[unit];
	if (tm_openf[unit] < 0 || (tmaddr->tmcs & CRDY) == NULL) {
		bp->b_flags |= B_ERROR;
		bp->b_error = ETOL;
		goto next;
	}
	if (bp == &ctmbuf[unit]) {
		if (bp->b_resid == NOP) {
			tmaddr->tmcs = (unit<<8);
			bp->b_resid = tmaddr->tmer;
			goto next;
		}
		tmtab.b_active = SCOM;
		tmaddr->tmbc = -bp->b_bcount;
		tmaddr->tmcs = DENS|bp->b_resid|GO| (unit<<8) | IENABLE;
		return;
	}
	com = (unit<<8) | ((bp->b_xmem & 03) << 4) | IENABLE|DENS;
#ifdef	UCB_NKB
	if (*blkno != dbtofsb(bp->b_blkno)) {
#else
	if (*blkno != bp->b_blkno) {
#endif	UCB_NKB
		tmtab.b_active = SSEEK;
#ifdef	UCB_NKB
		if (*blkno < dbtofsb(bp->b_blkno)) {
#else
		if (*blkno < bp->b_blkno) {
#endif	UCB_NKB
			com |= SFORW|GO;
#ifdef	UCB_NKB
			tmaddr->tmbc = *blkno - dbtofsb(bp->b_blkno);
#else
			tmaddr->tmbc = *blkno - bp->b_blkno;
#endif	UCB_NKB
		} else {
			if (bp->b_blkno == 0)
				com |= REW|GO;
			else {
				com |= SREV|GO;
#ifdef	UCB_NKB
				tmaddr->tmbc = dbtofsb(bp->b_blkno) - *blkno;
#else
				tmaddr->tmbc = bp->b_blkno - *blkno;
#endif	UCB_NKB
			}
		}
		tmaddr->tmcs = com;
		return;
	}
	if ((tm_flags[unit]&T_DEOT) == 0) {
		if ((tmaddr->tmer&EOT) && (tm_flags[unit]&T_CSE) == 0) {
			bp->b_resid = bp->b_bcount;
			bp->b_error = ENOSPC;
			bp->b_flags |= B_ERROR;
			goto next;
		}
	}
	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));
	el_bdact |= (1 << TM_BMAJ);	/* device is active */
	return;

next:
	tmtab.b_active == NULL;
	tmtab.b_actf = bp->av_forw;
	if (bp == &ctmbuf[unit])
		tm_flags[unit] &= ~T_BUSY;
	iodone(bp);
	goto loop;
}

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

	tmaddr = io_csr[TM_RMAJ];
	if(((tmaddr->tmcs & FUNC) == REW) && (tmaddr->tmer & RWS))
		return;
	if ((bp = tmtab.b_actf) == NULL) {
		logsi(tmaddr);
		return;
		}
	unit = minor(bp->b_dev)&07;
	tm_erreg[unit] = tmaddr->tmer;
	tm_resid[unit] = -tmaddr->tmbc;
	state = tmtab.b_active;
	tmtab.b_active = 0;
	if (tmaddr->tmer&EOT)
		tm_flags[unit] |= T_EOT;
	else
		tm_flags[unit] &= ~(T_EOT|T_CSE);
	if (tmaddr->tmcs < 0) {		/* error bit */
		if(tmtab.b_errcnt == 0)
			fmtbde(bp, &tm_ebuf, tmaddr, NTMREG, TMDBOFF);
		while(tmaddr->tmrd & GAPSD) ; /* wait for gap shutdown */
		if (tmaddr->tmer&EOT)
			goto out;
		if (tmaddr->tmer&EOF) {
#ifdef	UCB_NKB
			tm_nxrec[unit] = dbtofsb(bp->b_blkno);
#else
			tm_nxrec[unit] = bp->b_blkno;
#endif	UCB_NKB
			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 < 10) {
				tm_blkno[unit]++;
				el_bdact &= ~(1 << TM_BMAJ);
				tmtab.b_active = 0;
				tmstart();
				return;
			}
		} else
			if (tm_openf[unit]>0 && bp!=&rtmbuf &&
				(tmaddr->tmer&EOF)==0 )
				tm_openf[unit] = -1;
		bp->b_flags |= B_ERROR;
		bp->b_error = ETPL;
		state = SIO;
	}
out:
	switch ( state ) {
	case SIO:
		tm_blkno[unit] += (bp->b_bcount>>BSHIFT);
	case SCOM:
		if(tmtab.b_errcnt || (bp->b_flags & B_ERROR))
		{
			tm_ebuf.tm_bdh.bd_errcnt = tmtab.b_errcnt;
			if(!logerr(E_BD, &tm_ebuf, sizeof(tm_ebuf)))
			    deverror(bp, tm_ebuf.tm_reg[0], tm_ebuf.tm_reg[1]);
		}
		tmtab.b_errcnt = 0;
		tmtab.b_actf = bp->av_forw;
		bp->b_resid = -tmaddr->tmbc;
		el_bdact &= ~(1 << TM_BMAJ);
		if (bp == &ctmbuf[unit])
			tm_flags[unit] &= ~T_BUSY;
		iodone(bp);
		break;
	case SSEEK:
#ifdef	UCB_NKB
		tm_blkno[unit] = dbtofsb(bp->b_blkno);
#else
		tm_blkno[unit] = bp->b_blkno;
#endif	UCB_NKB
		el_bdact &= ~(1 << TM_BMAJ);
		break;
	default:
		return;
	}
	tmstart();
}

tmread(dev)
{
	tmphys(dev);
	physio(tmstrategy, &rtmbuf, dev, B_READ);
}

tmwrite(dev)
{
	tmphys(dev);
	physio(tmstrategy, &rtmbuf, dev, B_WRITE);
}

static
tmphys(dev)
{
	register unit;
	daddr_t a;

	unit = minor(dev) & 07;
	if(unit < ntm) {
#ifdef	UCB_NKB
		a = dbtofsb(u.u_offset >> 9);
#else
		a = u.u_offset >> 9;
#endif	UCB_NKB
		tm_blkno[unit] = a;
		tm_nxrec[unit] = a+1;
	}
}