Ultrix-3.1/sys/dev/ht.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 TM02/3 - 800/1600 BPI tape driver
 *
 * SCCSID: @(#)ht.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/conf.h>
#include <sys/dir.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/errlog.h>
#include <sys/devmaj.h>
#include <sys/mtio.h>

/*
 * SOFT ERROR NO LOG
 *
 * If SENOLOG is defined, a soft write error
 * that is recovered on the first retry will
 * NOT be logged. This is done to cut down on
 * the number of nuisance error log entries
 * for soft tape errors. If a tape problem is
 * suspected, SENOLOG should be removed so that
 * all tape errors will be logged.
 */
#define	SENOLOG	1

struct	device
{
	int	htcs1;	/* HT control & status 1 register */
	int	htwc;	/* HT word count register */
	caddr_t	htba;	/* HT bus address register */
	int	htfc;	/* HT frame count register */
	int	htcs2;	/* HT control & status 2 register */
	int	htds;	/* HT drive status register */
	int	hter;	/* HT error register */
	int	htas;	/* HT attention summary register */
	int	htck;	/* HT character check register */
	int	htdb;	/* HT data buffer register */
	int	htmr;	/* HT maintenance register */
	int	htdt;	/* HT drive type register */
	int	htsn;	/* HT serial number register */
	int	httc;	/* HT tape control register */
	int	htbae;	/* HT bus address extension register */
	int	htcs3;	/* HT control & status 3 register */
};

struct	buf	httab;
struct	buf	rhtbuf;
struct	buf	chtbuf[];

#define	INF	1000000

char	ht_openf[];
u_short	ht_flags[];
daddr_t	ht_blkno[];
daddr_t	ht_nxrec[];
u_short	ht_erreg[];
u_short	ht_dsreg[];
short	ht_resid[];

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

struct
{
	struct elrhdr ht_hdr;	/* record header */
	struct el_bdh ht_bdh;	/* block device header */
	int ht_reg[NHTREG];	/* device registers at error time */
} ht_ebuf;

int	io_csr[];	/* CSR address now in config file (c.c) */
char	io_bae[];	/* (c.c) - bus address BAE register */
int	nht;		/* number of drives, see c.c */

#define	GO	01
#define	WCOM	060
#define	RCOM	070
#define	NOP	0
#define	SENSE	0
#define REWOFFL	02
#define	WEOF	026
#define	SFORW	030
#define	SREV	032
#define	ERASE	024
#define	REW	06
#define	DCLR	010
#define P800	01300		/* 800 + pdp11 mode */
#define	P1600	02300		/* 1600 + pdp11 mode */
#define	IENABLE	0100
#define	RDY	0200
#define	BOT	02
#define	TM	04
#define	DRY	0200
#define EOT	02000
#define	PEF	0200
#define	INC	0100
#define CS	02000
#define COR	0100000
#define PES	040
#define WRL	04000
#define MOL	010000
#define ERR	040000
#define FCE	01000
#define	TRE	040000
#define HARD	064023	/* UNS|OPI|NEF|FMT|RMR|ILR|ILF */

#define	CLR	040	/* controller clear (in cs2) */

#define	NED	010000

#define	SIO	1
#define	SSFOR	2
#define	SSREV	3
#define SRETRY	4
#define SCOM	5
#define SOK	6
 
#define	CLR_DRV	0	/* clear drive only */
#define	CLR_DC	1	/* clear both drive and controller */

#define H_WRITTEN 01	/* last command is write command */
#define H_EOT 010	/* EOT encountered */
#define H_CSE 020	/* clear EOT */
#define H_DEOT 040	/* disable EOT */
#define	H_BUSY 0100	/* chtbuf is busy, switch for clx/cls */
#define	H_WAIT 0200	/* chtbuf is waited, switch for clx/cls */
#define	H_BUSYF 01000	/* good termination of busy */
#define	H_WAITF 02000	/* good termination of wait */
htopen(dev, flag)
{
	register struct device *htaddr;
	register unit, ds;

	htaddr = io_csr[HT_RMAJ];
	httab.b_flags |= B_TAPE;
	unit = minor(dev) & 077;
	if (unit >= nht) {
		u.u_error = ENXIO;
		return;
	}
	if (flag&FNDELAY) {
		ht_openf[unit] = 1;
		return;
	}
	if (ht_openf[unit] > 0) {
		u.u_error = ETO;
		return;
	}
	ht_blkno[unit] = 0;
	ht_nxrec[unit] = INF;
	ht_flags[unit] &= H_DEOT;
	ht_openf[unit] = 0;
	if(htaddr->htcs1 & TRE)
		htinit(CLR_DC);
	if ((ds = hcommand(dev, NOP, 1)) == -1) {
		u.u_error = ENXIO;
	}
	if (ds & EOT)
		ht_flags[unit] |= H_EOT;
	if ((ds & MOL) == 0) {
/*		printf("ht%d off line\n", unit);	*/
		u.u_error = ETOL;
	}
	if((flag & FWRITE) && (ds & WRL)) {
/*		printf("ht%d needs write ring\n", unit);	*/
		u.u_error = ETWL;
	}
	if (u.u_error==0)
		ht_openf[unit] = 1;
	else
		ht_openf[unit] = 0;
}

htclose(dev, flag)
{
	register int unit;

	unit = minor(dev) & 077;
	if (ht_openf[unit] <= 0)
		return;
	if(((flag & (FWRITE|FREAD)) == FWRITE) ||
	  ((flag&FWRITE) && (ht_flags[unit]&H_WRITTEN))) {
		if (hcommand(dev, WEOF, 1) == -1)
			goto badcls;
		if (hcommand(dev, WEOF, 1) == -1)
			goto badcls;
		if (hcommand(dev, SREV, 1) == -1)
			goto badcls;
	}
	if ((minor(dev)&0200) == 0) {
		if (hcommand(dev, REW, 0) == -1)
			goto badcls;
		ht_flags[unit] &= ~H_EOT;
	}
	ht_openf[unit] = 0;
	return;

badcls:
	u.u_error = ENXIO;
	return;
}

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

	unit = minor(dev) & 077;
	bp = &chtbuf[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:
			callcount = 1;
			fcount = 1;
			break;

		case MTCSE:
			ht_flags[unit] |= H_CSE;
			return(0);

		case MTENAEOT:
			ht_flags[unit] &= ~H_DEOT;
			return(0);

		case MTDISEOT:
			ht_flags[unit] |= H_DEOT;
			return(0);

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

		case MTCLX:
		case MTCLS:
			if ((wbp = httab.b_actf) != NULL) {
				httab.b_actf = wbp->av_forw;
				if (wbp != bp) {
					wbp->b_error = EIO;
					wbp->b_flags |= B_ERROR;
					iodone(wbp);
				}
			}
			if (ht_flags[unit]&H_BUSY) {
				bp->b_flags &= ~B_BUSY;
				ht_flags[unit] &= ~H_BUSYF;
				iodone(bp);
			}
			if (ht_flags[unit]&H_WAIT) {
				bp->b_flags &= ~B_WANTED;
				ht_flags[unit] &= ~H_WAITF;
				wakeup((caddr_t)bp);
			}
			if (mtop->mt_op == MTCLX)
				htinit(CLR_DRV);
			else
				htinit(CLR_DC);
			httab.b_active = 0;
			ht_flags[unit] &= H_DEOT;
			ht_blkno[unit] = 0;
			ht_nxrec[unit] = INF;
			ht_openf[unit] = 0;
			htstart();
			return(0);

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

	case MTIOCGET:
		mtget = (struct mtget *)data;
		mtget->mt_dsreg = ht_dsreg[unit];
		mtget->mt_erreg = ht_erreg[unit];
		mtget->mt_resid = ht_resid[unit];
		mtget->mt_softstat = 0;
		if(ht_flags[unit]&H_EOT)
			mtget->mt_softstat |= MT_EOT;
		if(ht_flags[unit]&H_DEOT)
			mtget->mt_softstat |= MT_DISEOT;
		mtget->mt_type = MT_ISHT;
		break;

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

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

	unit = minor(dev)&077;
	bp = &chtbuf[unit];
	spl5();
	while(bp->b_flags&B_BUSY) {
		bp->b_flags |= B_WANTED;
		ht_flags[unit] |= (H_WAIT|H_WAITF);
		sleep((caddr_t)bp, PRIBIO);
		if ((ht_flags[unit]&H_WAITF) == 0)
			return(-1);
	}
	spl0();
	bp->b_dev = dev;
	bp->b_resid = com;
	bp->b_bcount = count;
	bp->b_blkno = 0;
	bp->b_flags = B_BUSY|B_READ;
	ht_flags[unit] |= (H_BUSY|H_BUSYF);
	htstrategy(bp);
	iowait(bp);
	if ((ht_flags[unit]&H_BUSYF) == 0)
		return(-1);
	if(bp->b_flags&B_WANTED) {
		ht_flags[unit] &= ~H_WAIT;
		wakeup((caddr_t)bp);
	}
	bp->b_flags &= B_ERROR;
	return(bp->b_resid);
}

htstrategy(bp)
register struct buf *bp;
{
	register struct device *htaddr;
	register daddr_t *p;

	htaddr = io_csr[HT_RMAJ];
	if(!io_bae[HT_BMAJ])
		mapalloc(bp);
	if(bp != &chtbuf[minor(bp->b_dev)&077]) {
		p = &ht_nxrec[minor(bp->b_dev)&077];
#ifdef	UCB_NKB
		if(dbtofsb(bp->b_blkno) > *p) {
#else
		if(bp->b_blkno > *p) {
#endif	UCB_NKB
			bp->b_flags |= B_ERROR;
			bp->b_error = ENXIO;
			iodone(bp);
			return;
		}
#ifdef	UCB_NKB
		if(dbtofsb(bp->b_blkno) == *p && bp->b_flags&B_READ) {
#else
		if(bp->b_blkno == *p && bp->b_flags&B_READ) {
#endif	UCB_NKB
			if((bp->b_flags&B_PHYS) == 0)
				clrbuf(bp);
			bp->b_resid = bp->b_bcount;
			iodone(bp);
			return;
		}
		ht_flags[minor(bp->b_dev)&077] &= ~H_WRITTEN;
		if ((bp->b_flags&B_READ)==0) {
#ifdef	UCB_NKB
			*p = dbtofsb(bp->b_blkno) + 1;
#else
			*p = bp->b_blkno + 1;
#endif	UCB_NKB
			ht_flags[minor(bp->b_dev)&077] |= H_WRITTEN;
		}
	}
	bp->av_forw = NULL;
	spl5();
	if (httab.b_actf == NULL)
		httab.b_actf = bp;
	else
		httab.b_actl->av_forw = bp;
	httab.b_actl = bp;
	if (httab.b_active==0)
		htstart();
	spl0();
}

static
htstart()
{
	register struct buf *bp;
	register struct device *htaddr;
	register unit;
	int den;
	daddr_t blkno;

	htaddr = io_csr[HT_RMAJ];
    loop:
	if ((bp = httab.b_actf) == NULL)
		return;
	unit = minor(bp->b_dev) & 0177;
	htaddr->htcs2 = ((unit>>3)&07);
	den = P1600 | (unit&07);
	if(unit > 077)
		den = P800 | (unit&07);
	if((htaddr->httc&03777) != den)
		htaddr->httc = den;
	if (htaddr->htcs2 & NED || (htaddr->htds&MOL)==0) {
		ht_openf[unit&077] = -1;
		bp->b_error = ETOL;
/*		goto abort;	*/
	}
	unit &= 077;
	blkno = ht_blkno[unit];
	if (bp == &chtbuf[unit]) {
		if (bp->b_resid==NOP) {
			bp->b_resid = htaddr->htds;
			goto next;
		}
		httab.b_active = SCOM;
		htaddr->htfc = -bp->b_bcount;
		htaddr->htcs1 = bp->b_resid|IENABLE|GO;
		return;
	}
	if(ht_openf[unit] < 0) {
		bp->b_error = ETPL;
		goto abort;
	}
#ifdef	UCB_NKB
	if(dbtofsb(bp->b_blkno) > ht_nxrec[unit])
#else
	if(bp->b_blkno > ht_nxrec[unit])
#endif	UCB_NKB
		goto abort;
#ifdef	UCB_NKB
	if (blkno == dbtofsb(bp->b_blkno)) {
#else
	if (blkno == bp->b_blkno) {
#endif	UCB_NKB
		httab.b_active = SIO;
		el_bdact |= (1 << HT_BMAJ);	/* device is active */
		htaddr->htba = bp->b_un.b_addr;
		if(io_bae[HT_BMAJ])
			htaddr->htbae = bp->b_xmem;
		htaddr->htfc = -bp->b_bcount;
		htaddr->htwc = -(bp->b_bcount>>1);
		den = ((bp->b_xmem&3) << 8) | IENABLE | GO;
		if((ht_flags[unit]&H_DEOT) == 0) {
			if((htaddr->htds&EOT) && (ht_flags[unit]&H_CSE) == 0) {
				bp->b_resid = bp->b_bcount;
				bp->b_error = ENOSPC;
				bp->b_flags |= B_ERROR;
				goto abort;
			}
		}
		if(bp->b_flags & B_READ)
			den |= RCOM;
		else
			den |= WCOM;
		htaddr->htcs1 = den;
	} else {
#ifdef	UCB_NKB
		if (blkno < dbtofsb(bp->b_blkno)) {
#else
		if (blkno < bp->b_blkno) {
#endif	UCB_NKB
			httab.b_active = SSFOR;
#ifdef	UCB_NKB
			htaddr->htfc = blkno - dbtofsb(bp->b_blkno);
#else
			htaddr->htfc = blkno - bp->b_blkno;
#endif	UCB_NKB
			htaddr->htcs1 = SFORW|IENABLE|GO;
		} else {
			httab.b_active = SSREV;
#ifdef	UCB_NKB
			htaddr->htfc = dbtofsb(bp->b_blkno) - blkno;
#else
			htaddr->htfc = bp->b_blkno - blkno;
#endif	UCB_NKB
			htaddr->htcs1 = SREV|IENABLE|GO;
		}
	}
	return;

    abort:
	bp->b_flags |= B_ERROR;

    next:
	httab.b_active = 0;
	httab.b_actf = bp->av_forw;
	if (bp == &chtbuf[unit])
		ht_flags[unit] &= ~H_BUSY;
	iodone(bp);
	goto loop;
}

htintr()
{
	register struct buf *bp;
	register struct device *htaddr;
	register int unit;
	int	state;
	int	err;
	int	nreg;

	htaddr = io_csr[HT_RMAJ];
	if ((bp = httab.b_actf)==NULL) {
		logsi(htaddr);
		return;
		}
	unit = minor(bp->b_dev) & 077;
	ht_dsreg[unit] = htaddr->htds;
	ht_erreg[unit] = htaddr->hter;
	ht_resid[unit] = htaddr->htfc;
	state = httab.b_active;
	httab.b_active = 0;
	if(htaddr->htds&EOT)
		ht_flags[unit] |= H_EOT;
	else
		ht_flags[unit] &= ~(H_EOT|H_CSE);
	if (htaddr->htcs1&TRE) {
		if(httab.b_errcnt == 0) {
			if(io_bae[HT_BMAJ])
				nreg = NHTREG;
			else
				nreg = (NHTREG - 2);
			fmtbde(bp, &ht_ebuf, htaddr, nreg, HTDBOFF);
			}
		err = htaddr->hter;
		if (htaddr->htcs2&077400 || (err&HARD))
			state = 0;
		if (bp == &rhtbuf)
			err &= ~FCE;
		if ((bp->b_flags&B_READ) && (htaddr->htds&PES))
			err &= ~(CS|COR);
		if((htaddr->htds&MOL) == 0) {
			if(ht_openf[unit])
				ht_openf[unit] = -1;
		}
		else if(htaddr->htds&TM) {
			htaddr->htwc = -(bp->b_bcount>>1);
#ifdef	UCB_NKB
			ht_nxrec[unit] = dbtofsb(bp->b_blkno);
#else
			ht_nxrec[unit] = bp->b_blkno;
#endif	UCB_NKB
			state = SOK;
		}
		else if(state && err == 0)
			state = SOK;
		htinit(CLR_DC);
		if (state==SIO && ++httab.b_errcnt < 10) {
			httab.b_active = SRETRY;
			ht_blkno[unit]++;
			htaddr->htfc = -1;
			htaddr->htcs1 = SREV|IENABLE|GO;
			return;
		}
		if (state!=SOK) {
			bp->b_error = ETPL;
			bp->b_flags |= B_ERROR;
			ht_openf[unit] = -1;
			state = SIO;
		}
	} else if (htaddr->htcs1 < 0) {	/* SC */
		if(htaddr->htds & ERR)
			htinit(CLR_DC);
	}
	switch(state) {
	case SIO:
	case SOK:
		ht_blkno[unit]++;

	case SCOM:
		if(httab.b_errcnt || (bp->b_flags & B_ERROR))
		{
			ht_ebuf.ht_bdh.bd_errcnt = httab.b_errcnt;
#ifdef SENOLOG
			if((ht_ebuf.ht_reg[5] & PES)
			  && (httab.b_errcnt == 1)
			  && ((ht_ebuf.ht_reg[6] & ~(CS|COR|PEF|INC)) == 0))
				goto nolog;
#endif
			if(!logerr(E_BD, &ht_ebuf, sizeof(ht_ebuf)))
			    deverror(bp, ht_ebuf.ht_reg[6], ht_ebuf.ht_reg[4]);
		}
	nolog:
		httab.b_errcnt = 0;
		el_bdact &= ~(1 << HT_BMAJ);	/* device no longer active */
		httab.b_actf = bp->av_forw;
		if (bp == &chtbuf[unit])
			ht_flags[unit] &= ~H_BUSY;
		iodone(bp);
		bp->b_resid = (-htaddr->htwc)<<1;
		break;

	case SRETRY:
		if((bp->b_flags&B_READ)==0) {
			httab.b_active = SSFOR;
			htaddr->htcs1 = ERASE|IENABLE|GO;
			return;
		}

	case SSFOR:
	case SSREV:
		if(htaddr->htds & TM) {
			if(state == SSREV) {
#ifdef	UCB_NKB
				ht_nxrec[unit] = dbtofsb(bp->b_blkno) - htaddr->htfc;
#else
				ht_nxrec[unit] = bp->b_blkno - htaddr->htfc;
#endif	UCB_NKB
				ht_blkno[unit] = ht_nxrec[unit];
			} else {
#ifdef	UCB_NKB
				ht_nxrec[unit] = dbtofsb(bp->b_blkno) + htaddr->htfc - 1;
#else
				ht_nxrec[unit] = bp->b_blkno + htaddr->htfc - 1;
#endif	UCB_NKB
				ht_blkno[unit] = ht_nxrec[unit]+1;
			}
		} else
#ifdef	UCB_NKB
			ht_blkno[unit] = dbtofsb(bp->b_blkno);
#else
			ht_blkno[unit] = bp->b_blkno;
#endif	UCB_NKB
		break;

	default:
		return;
	}
	htstart();
}

static
htinit(flag)
{
	register struct device *htaddr;
	register ocs2;
	register omttc;
	
	htaddr = io_csr[HT_RMAJ];
	omttc = htaddr->httc & 03777;	/* preserve old slave select, dens, format */
	ocs2 = htaddr->htcs2 & 07;	/* preserve old unit */

	if (flag == CLR_DC)
		htaddr->htcs2 = CLR;
	htaddr->htcs2 = ocs2;
	htaddr->httc = omttc;
	htaddr->htcs1 = DCLR|GO;
}

htread(dev)
{
	htphys(dev);
	physio(htstrategy, &rhtbuf, dev, B_READ);
}

htwrite(dev)
{
	htphys(dev);
	physio(htstrategy, &rhtbuf, dev, B_WRITE);
}

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

	unit = minor(dev) & 077;
	if(unit < nht) {
#ifdef	UCB_NKB
		a = dbtofsb(u.u_offset >> 9);
#else
		a = u.u_offset >> 9;
#endif	UCB_NKB
		ht_blkno[unit] = a;
		ht_nxrec[unit] = a+1;
	}
}