4.3BSD-Reno/src/sys/hpdev/ct.c

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

/*
 * Copyright (c) 1982, 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution is only permitted until one year after the first shipment
 * of 4.4BSD by the Regents.  Otherwise, redistribution and use in source and
 * binary forms are permitted provided that: (1) source distributions retain
 * this entire copyright notice and comment, and (2) distributions including
 * binaries display the following acknowledgement:  This product includes
 * software developed by the University of California, Berkeley and its
 * contributors'' in the documentation or other materials provided with the
 * distribution and in all advertising materials mentioning features or use
 * of this software.  Neither the name of the University nor the names of
 * its contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *	@(#)ct.c	7.1 (Berkeley) 5/8/90
 */

#include "ct.h"
#if NCT > 0
/*
 * CS80 cartridge tape driver (9144, 88140, 9145)
 *
 * Reminder:
 *	C_CC bit (character count option) when used in the CS/80 command
 *	'set options' will cause the tape not to stream.
 *
 * TODO:
 *	make filesystem compatible
 *	make block mode work according to mtio(4) spec. (if possible)
 *	merge with cs80 disk driver
 *	finish support of 9145
 */

#include "param.h"
#include "buf.h"
#include "ioctl.h"
#include "mtio.h"
#include "errno.h"
#include "ctreg.h"
#include "device.h"
#include "user.h"
#include "tty.h"
#include "proc.h"

/* number of eof marks to remember */
#define EOFS	128

int	ctinit(), ctstart(), ctgo(), ctintr();
struct	driver ctdriver = {
	ctinit, "ct", ctstart, ctgo, ctintr,
};

struct	ct_softc {
	struct	hp_device *sc_hd;
	struct	ct_iocmd sc_ioc;
	struct	ct_rscmd sc_rsc;
	struct	ct_stat sc_stat;
	struct	ct_ssmcmd sc_ssmc;
	struct	ct_srcmd sc_src;
	struct	ct_soptcmd sc_soptc;
	struct	ct_ulcmd sc_ul;
	struct	ct_wfmcmd sc_wfm;
	struct	ct_clearcmd sc_clear;
	struct	buf *sc_bp;
	int	sc_blkno;
	int	sc_cmd;
	int	sc_resid;
	char	*sc_addr;
	int	sc_flags;
	short	sc_type;
	short	sc_punit;
	caddr_t	sc_ctty;
	struct	devqueue sc_dq;
	int	sc_eofp;
	int	sc_eofs[EOFS];
} ct_softc[NCT];

/* flags */
#define	CTF_OPEN	0x01
#define	CTF_ALIVE	0x02
#define	CTF_WRT		0x04
#define	CTF_CMD		0x08
#define	CTF_IO		0x10
#define	CTF_BEOF	0x20
#define	CTF_AEOF	0x40
#define	CTF_EOT		0x80
#define	CTF_STATWAIT	0x100
#define CTF_CANSTREAM	0x200
#define	CTF_WRTTN	0x400

struct	ctinfo {
	short	hwid;
	short	punit;
	char	*desc;
} ctinfo[] = {
	CT7946ID,	1,	"7946A",
	CT7912PID,	1,	"7912P",
	CT7914PID,	1,	"7914P",
	CT9144ID,	0,	"9144",
	CT9145ID,	0,	"9145",
};
int	nctinfo = sizeof(ctinfo) / sizeof(ctinfo[0]);

struct	buf cttab[NCT];
struct	buf ctbuf[NCT];

#define	CT_NOREW	4
#define	CT_STREAM	8
#define	UNIT(x)		(minor(x) & 3)
#define	ctpunit(x)	((x) & 7)

#ifdef DEBUG
int ctdebug = 0;
#define CDB_FILES	0x01
#define CT_BSF		0x02
#endif

ctinit(hd)
	register struct hp_device *hd;
{
	register struct ct_softc *sc = &ct_softc[hd->hp_unit];

	sc->sc_hd = hd;
	sc->sc_punit = ctpunit(hd->hp_flags);
	if (ctident(sc, hd) < 0)
		return(0);
	ctreset(sc, hd);
	sc->sc_dq.dq_ctlr = hd->hp_ctlr;
	sc->sc_dq.dq_unit = hd->hp_unit;
	sc->sc_dq.dq_slave = hd->hp_slave;
	sc->sc_dq.dq_driver = &ctdriver;
	sc->sc_flags |= CTF_ALIVE;
	return(1);
}

ctident(sc, hd)
	register struct ct_softc *sc;
	register struct hp_device *hd;
{
	struct ct_describe desc;
	u_char stat, cmd[3];
	char name[7];
	int id, i;

	/*
	 * Read device id and verify that:
	 * 1. It is a CS80 device
	 * 2. It is one of our recognized tape devices
	 * 3. It has the proper physical unit number
	 */
	id = hpibid(hd->hp_ctlr, hd->hp_slave);
	if ((id & 0x200) == 0)
		return(-1);
	for (i = 0; i < nctinfo; i++)
		if (id == ctinfo[i].hwid)
			break;
	if (i == nctinfo || sc->sc_punit != ctinfo[i].punit)
		return(-1);
	id = i;

	/*
	 * Collect device description.
	 * Right now we only need this to differentiate 7945 from 7946.
	 * Note that we always issue the describe command to unit 0.
	 */
	cmd[0] = C_SUNIT(0);
	cmd[1] = C_SVOL(0);
	cmd[2] = C_DESC;
	hpibsend(hd->hp_ctlr, hd->hp_slave, C_CMD, cmd, sizeof(cmd));
	hpibrecv(hd->hp_ctlr, hd->hp_slave, C_EXEC, &desc, 37);
	hpibrecv(hd->hp_ctlr, hd->hp_slave, C_QSTAT, &stat, sizeof(stat));
	bzero(name, sizeof(name));
	if (!stat) {
		register int n = desc.d_name;
		for (i = 5; i >= 0; i--) {
			name[i] = (n & 0xf) + '0';
			n >>= 4;
		}
	}
	switch (ctinfo[id].hwid) {
	case CT7946ID:
		if (bcmp(name, "079450", 6) == 0)
			return(-1);		/* not really a 7946 */
		/* fall into... */
	case CT9144ID:
	case CT9145ID:
		sc->sc_type = CT9144;
		sc->sc_flags |= CTF_CANSTREAM;
		break;

	case CT7912PID:
	case CT7914PID:
		sc->sc_type = CT88140;
		break;
	}
	printf("ct%d: %s %stape\n", hd->hp_unit, ctinfo[id].desc,
	       (sc->sc_flags & CTF_CANSTREAM) ? "streaming " : " ");
	return(id);
}

ctreset(sc, hd)
	register struct ct_softc *sc;
	register struct hp_device *hd;
{
	u_char stat;

	sc->sc_clear.unit = C_SUNIT(sc->sc_punit);
	sc->sc_clear.cmd = C_CLEAR;
	hpibsend(hd->hp_ctlr, hd->hp_slave, C_TCMD, &sc->sc_clear,
		sizeof(sc->sc_clear));
	hpibswait(hd->hp_ctlr, hd->hp_slave);
	hpibrecv(hd->hp_ctlr, hd->hp_slave, C_QSTAT, &stat, sizeof(stat));
	sc->sc_src.unit = C_SUNIT(CTCTLR);
	sc->sc_src.nop = C_NOP;
	sc->sc_src.cmd = C_SREL;
	sc->sc_src.param = C_REL;
	hpibsend(hd->hp_ctlr, hd->hp_slave, C_CMD, &sc->sc_src,
		sizeof(sc->sc_src));
	hpibswait(hd->hp_ctlr, hd->hp_slave);
	hpibrecv(hd->hp_ctlr, hd->hp_slave, C_QSTAT, &stat, sizeof(stat));
	sc->sc_ssmc.unit = C_SUNIT(sc->sc_punit);
	sc->sc_ssmc.cmd = C_SSM;
	sc->sc_ssmc.refm = REF_MASK;
	sc->sc_ssmc.fefm = FEF_MASK;
	sc->sc_ssmc.aefm = AEF_MASK;
	sc->sc_ssmc.iefm = IEF_MASK;
	hpibsend(hd->hp_ctlr, hd->hp_slave, C_CMD, &sc->sc_ssmc,
		sizeof(sc->sc_ssmc));
	hpibswait(hd->hp_ctlr, hd->hp_slave);
	hpibrecv(hd->hp_ctlr, hd->hp_slave, C_QSTAT, &stat, sizeof(stat));
	sc->sc_soptc.unit = C_SUNIT(sc->sc_punit);
	sc->sc_soptc.nop = C_NOP;
	sc->sc_soptc.cmd = C_SOPT;
	sc->sc_soptc.opt = C_SPAR;
	hpibsend(hd->hp_ctlr, hd->hp_slave, C_CMD, &sc->sc_soptc,
		sizeof(sc->sc_soptc));
	hpibswait(hd->hp_ctlr, hd->hp_slave);
	hpibrecv(hd->hp_ctlr, hd->hp_slave, C_QSTAT, &stat, sizeof(stat));
}

/*ARGSUSED*/
ctopen(dev, flag)
	dev_t dev;
{
	register struct ct_softc *sc = &ct_softc[UNIT(dev)];
	u_char stat;
	int cc;

	if (UNIT(dev) >= NCT || (sc->sc_flags & CTF_ALIVE) == 0)
		return(ENXIO);
	if (sc->sc_flags & CTF_OPEN)
		return(EBUSY);
	sc->sc_soptc.unit = C_SUNIT(sc->sc_punit);
	sc->sc_soptc.nop = C_NOP;
	sc->sc_soptc.cmd = C_SOPT;
	if ((dev & CT_STREAM) && (sc->sc_flags & CTF_CANSTREAM))
		sc->sc_soptc.opt = C_SPAR | C_IMRPT;
	else
		sc->sc_soptc.opt = C_SPAR;
	/* 
	 * Check the return of hpibsend() and hpibswait().
	 * Drive could be loading/unloading a tape. If not checked,
	 * driver hangs. 
	 */
	cc = hpibsend(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave,
	              C_CMD, &sc->sc_soptc, sizeof(sc->sc_soptc));
	if (cc != sizeof(sc->sc_soptc))
		return(EBUSY);
	hpibswait(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave);
	cc = hpibrecv(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave, C_QSTAT, 
	              &stat, sizeof(stat));
	if (cc != sizeof(stat))
		return(EBUSY);
	sc->sc_ctty = (caddr_t)(u.u_procp->p_flag&SCTTY ? 
			u.u_procp->p_session->s_ttyvp : 0);
	sc->sc_flags |= CTF_OPEN;
	return(0);
}

/*ARGSUSED*/
ctclose(dev, flag)
	dev_t dev;
{
	register struct ct_softc *sc = &ct_softc[UNIT(dev)];

	if ((sc->sc_flags & (CTF_WRT|CTF_WRTTN)) == (CTF_WRT|CTF_WRTTN) &&
	    (sc->sc_flags & CTF_EOT) == 0 ) { /* XXX return error if EOT ?? */
		ctcommand(dev, MTWEOF, 2);
		ctcommand(dev, MTBSR, 1);
		if (sc->sc_eofp == EOFS - 1)
			sc->sc_eofs[EOFS - 1]--;
		else
			sc->sc_eofp--;
#ifdef DEBUG
		if(ctdebug & CT_BSF)
			printf("ct%d: ctclose backup eofs prt %d blk %d\n",
			       UNIT(dev), sc->sc_eofp, sc->sc_eofs[sc->sc_eofp]);
#endif
	}
	if ((minor(dev) & CT_NOREW) == 0)
		ctcommand(dev, MTREW, 1);
	sc->sc_flags &= ~(CTF_OPEN | CTF_WRT | CTF_WRTTN);
	sc->sc_ctty = NULL;
#ifdef DEBUG
	if (ctdebug & CDB_FILES)
		printf("ctclose: flags %x\n", sc->sc_flags);
#endif
	return(0);	/* XXX */
}

ctcommand(dev, cmd, cnt)
	dev_t dev;
	register int cnt;
{
	register struct ct_softc *sc = &ct_softc[UNIT(dev)];
	register struct buf *bp = &ctbuf[UNIT(dev)];
	register struct buf *nbp = 0;

	if (cmd == MTBSF && sc->sc_eofp == EOFS - 1) {
		cnt = sc->sc_eofs[EOFS - 1] - cnt;
		ctcommand(dev, MTREW, 1);
		ctcommand(dev, MTFSF, cnt);
		cnt = 2;
		cmd = MTBSR;
	}

	if (cmd == MTBSF && sc->sc_eofp - cnt < 0) {
		cnt = 1;
		cmd = MTREW;
	}

	sc->sc_flags |= CTF_CMD;
	sc->sc_bp = bp;
	sc->sc_cmd = cmd;
	bp->b_dev = dev;
	if (cmd == MTFSF) {
		nbp = (struct buf *)geteblk(MAXBSIZE);
		bp->b_un.b_addr = nbp->b_un.b_addr;
		bp->b_bcount = MAXBSIZE;
	}
again:
	bp->b_flags = B_BUSY;
	if (cmd == MTBSF) {
		sc->sc_blkno = sc->sc_eofs[sc->sc_eofp];
		sc->sc_eofp--;
#ifdef DEBUG
		if (ctdebug & CT_BSF)
			printf("ct%d: backup eof pos %d blk %d\n",
			       UNIT(dev), sc->sc_eofp, 
			       sc->sc_eofs[sc->sc_eofp]);
#endif
	}
	ctstrategy(bp);
	iowait(bp);
	if (--cnt > 0)
		goto again;
	bp->b_flags = 0;
	sc->sc_flags &= ~CTF_CMD;
	if (nbp)
		brelse(nbp);
}

ctstrategy(bp)
	register struct buf *bp;
{
	register struct buf *dp;
	register int s, unit;

	unit = UNIT(bp->b_dev);
	dp = &cttab[unit];
	bp->av_forw = NULL;
	s = splbio();
	if (dp->b_actf == NULL)
		dp->b_actf = bp;
	else
		dp->b_actl->av_forw = bp;
	dp->b_actl = bp;
	if (dp->b_active == 0) {
		dp->b_active = 1;
		ctustart(unit);
	}
	splx(s);
}

ctustart(unit)
	register int unit;
{
	register struct ct_softc *sc = &ct_softc[unit];
	register struct buf *bp;

	bp = cttab[unit].b_actf;
	sc->sc_addr = bp->b_un.b_addr;
	sc->sc_resid = bp->b_bcount;
	if (hpibreq(&sc->sc_dq))
		ctstart(unit);
}

ctstart(unit)
	register int unit;
{
	register struct ct_softc *sc = &ct_softc[unit];
	register struct buf *bp;
	register int i;

	bp = cttab[unit].b_actf;
again:
	if ((sc->sc_flags & CTF_CMD) && sc->sc_bp == bp) {
		switch(sc->sc_cmd) {

		case MTFSF:
			bp->b_flags |= B_READ;
			goto mustio;

		case MTBSF:
			goto gotaddr;

		case MTOFFL:
			sc->sc_blkno = 0;
			sc->sc_ul.unit = C_SUNIT(sc->sc_punit);
			sc->sc_ul.cmd = C_UNLOAD;
			hpibsend(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave,
				C_CMD, &sc->sc_ul, sizeof(sc->sc_ul));
			break;

		case MTWEOF:
			sc->sc_blkno++;
			sc->sc_flags |= CTF_WRT;
			sc->sc_wfm.unit = C_SUNIT(sc->sc_punit);
			sc->sc_wfm.cmd = C_WFM;
			hpibsend(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave,
				C_CMD, &sc->sc_wfm, sizeof(sc->sc_wfm));
			ctaddeof(unit);
			break;

		case MTBSR:
			sc->sc_blkno--;
			goto gotaddr;

		case MTFSR:
			sc->sc_blkno++;
			goto gotaddr;

		case MTREW:
			sc->sc_blkno = 0;
#ifdef DEBUG
			if(ctdebug & CT_BSF)
				printf("ct%d: clearing eofs\n", unit);
#endif
			for (i=0; i<EOFS; i++)
				sc->sc_eofs[i] = 0;
			sc->sc_eofp = 0;

gotaddr:
			sc->sc_ioc.saddr = C_SADDR;
			sc->sc_ioc.addr0 = 0;
			sc->sc_ioc.addr = sc->sc_blkno;
			sc->sc_ioc.unit = C_SUNIT(sc->sc_punit);
			sc->sc_ioc.nop2 = C_NOP;
			sc->sc_ioc.slen = C_SLEN;
			sc->sc_ioc.len = 0;
			sc->sc_ioc.nop3 = C_NOP;
			sc->sc_ioc.cmd = C_READ;
			hpibsend(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave,
				C_CMD, &sc->sc_ioc, sizeof(sc->sc_ioc));
			break;
		}
	}
	else {
mustio:
		if ((bp->b_flags & B_READ) &&
		    sc->sc_flags & (CTF_BEOF|CTF_EOT)) {
#ifdef DEBUG
			if (ctdebug & CDB_FILES)
				printf("ctstart: before flags %x\n", sc->sc_flags);
#endif
			if (sc->sc_flags & CTF_BEOF) {
				sc->sc_flags &= ~CTF_BEOF;
				sc->sc_flags |= CTF_AEOF;
#ifdef DEBUG
				if (ctdebug & CDB_FILES)
					printf("ctstart: after flags %x\n", sc->sc_flags);
#endif
			}
			bp->b_resid = bp->b_bcount;
			iodone(bp);
			hpibfree(&sc->sc_dq);
			cttab[unit].b_actf = bp = bp->av_forw;
			if (bp == NULL) {
				cttab[unit].b_active = 0;
				return;
			}
			sc->sc_addr = bp->b_un.b_addr;
			sc->sc_resid = bp->b_bcount;
			if (hpibreq(&sc->sc_dq))
				goto again;
			return;
		}			
		sc->sc_flags |= CTF_IO;
		sc->sc_ioc.unit = C_SUNIT(sc->sc_punit);
		sc->sc_ioc.saddr = C_SADDR;
		sc->sc_ioc.addr0 = 0;
		sc->sc_ioc.addr = sc->sc_blkno;
		sc->sc_ioc.nop2 = C_NOP;
		sc->sc_ioc.slen = C_SLEN;
		sc->sc_ioc.len = sc->sc_resid;
		sc->sc_ioc.nop3 = C_NOP;
		if (bp->b_flags & B_READ)
			sc->sc_ioc.cmd = C_READ;
		else {
			sc->sc_ioc.cmd = C_WRITE;
			sc->sc_flags |= (CTF_WRT | CTF_WRTTN);
		}
		hpibsend(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave, C_CMD,
			&sc->sc_ioc, sizeof(sc->sc_ioc));
	}
	hpibawait(sc->sc_hd->hp_ctlr);
}

ctgo(unit)
	register int unit;
{
	register struct ct_softc *sc = &ct_softc[unit];
	register struct buf *bp;

	bp = cttab[unit].b_actf;
	hpibgo(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave, C_EXEC,
		sc->sc_addr, sc->sc_resid, bp->b_flags & B_READ);
}

/*
 * Hideous grue to handle EOF/EOT (mostly for reads)
 */
cteof(sc, bp)
	register struct ct_softc *sc;
	register struct buf *bp;
{
	long blks;

	/*
	 * EOT on a write is an error.
	 */
	if ((bp->b_flags & B_READ) == 0) {
		bp->b_resid = bp->b_bcount;
		bp->b_flags |= B_ERROR;
		bp->b_error = ENOSPC;
		sc->sc_flags |= CTF_EOT;
		return;
	}
	/*
	 * Use returned block position to determine how many blocks
	 * we really read and update b_resid.
	 */
	blks = sc->sc_stat.c_blk - sc->sc_blkno - 1;
#ifdef DEBUG
	if (ctdebug & CDB_FILES)
		printf("cteof: bc %d oblk %d nblk %d read %d, resid %d\n",
		       bp->b_bcount, sc->sc_blkno, sc->sc_stat.c_blk,
		       blks, bp->b_bcount - CTKTOB(blks));
#endif
	if (blks == -1) { /* 9145 on EOF does not change sc_stat.c_blk */
		blks = 0;
		sc->sc_blkno++;
	}
	else {
		sc->sc_blkno = sc->sc_stat.c_blk;
	}
	bp->b_resid = bp->b_bcount - CTKTOB(blks);
	/*
	 * If we are at physical EOV or were after an EOF,
	 * we are now at logical EOT.
	 */
	if ((sc->sc_stat.c_aef & AEF_EOV) ||
	    (sc->sc_flags & CTF_AEOF)) {
		sc->sc_flags |= CTF_EOT;
		sc->sc_flags &= ~(CTF_AEOF|CTF_BEOF);
	}
	/*
	 * If we were before an EOF or we have just completed a FSF,
	 * we are now after EOF.
	 */
	else if ((sc->sc_flags & CTF_BEOF) ||
		 (sc->sc_flags & CTF_CMD) && sc->sc_cmd == MTFSF) {
		sc->sc_flags |= CTF_AEOF;
		sc->sc_flags &= ~CTF_BEOF;
	}
	/*
	 * Otherwise if we read something we are now before EOF
	 * (and no longer after EOF).
	 */
	else if (blks) {
		sc->sc_flags |= CTF_BEOF;
		sc->sc_flags &= ~CTF_AEOF;
	}
	/*
	 * Finally, if we didn't read anything we just passed an EOF
	 */
	else
		sc->sc_flags |= CTF_AEOF;
#ifdef DEBUG
	if (ctdebug & CDB_FILES)
		printf("cteof: leaving flags %x\n", sc->sc_flags);
#endif
}

ctintr(unit)
	register int unit;
{
	register struct ct_softc *sc = &ct_softc[unit];
	register struct buf *bp;
	u_char stat;

	bp = cttab[unit].b_actf;
	if (bp == NULL) {
		printf("ct%d: bp == NULL\n", unit);
		return;
	}
	if (sc->sc_flags & CTF_IO) {
		sc->sc_flags &= ~CTF_IO;
		if (hpibustart(sc->sc_hd->hp_ctlr))
			ctgo(unit);
		return;
	}
	if ((sc->sc_flags & CTF_STATWAIT) == 0) {
		if (hpibpptest(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave) == 0) {
			sc->sc_flags |= CTF_STATWAIT;
			hpibawait(sc->sc_hd->hp_ctlr);
			return;
		}
	} else
		sc->sc_flags &= ~CTF_STATWAIT;
	hpibrecv(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave, C_QSTAT, &stat, 1);
#ifdef DEBUG
	if (ctdebug & CDB_FILES)
		printf("ctintr: before flags %x\n", sc->sc_flags);
#endif
	if (stat) {
		sc->sc_rsc.unit = C_SUNIT(sc->sc_punit);
		sc->sc_rsc.cmd = C_STATUS;
		hpibsend(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave, C_CMD,
			&sc->sc_rsc, sizeof(sc->sc_rsc));
		hpibrecv(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave, C_EXEC,
			&sc->sc_stat, sizeof(sc->sc_stat));
		hpibrecv(sc->sc_hd->hp_ctlr, sc->sc_hd->hp_slave, C_QSTAT,
			&stat, 1);
#ifdef DEBUG
		if (ctdebug & CDB_FILES)
			printf("ctintr: return stat 0x%x, A%x F%x blk %d\n",
			       stat, sc->sc_stat.c_aef,
			       sc->sc_stat.c_fef, sc->sc_stat.c_blk);
#endif
		if (stat == 0) {
			if (sc->sc_stat.c_aef & (AEF_EOF | AEF_EOV)) {
				cteof(sc, bp);
				ctaddeof(unit);
				goto done;
			}
			if (sc->sc_stat.c_fef & FEF_PF) {
				ctreset(sc, sc->sc_hd);
				ctstart(unit);
				return;
			}
			if (sc->sc_stat.c_fef & FEF_REXMT) {
				ctstart(unit);
				return;
			}
			if (sc->sc_stat.c_aef & 0x5800) {
				if (sc->sc_stat.c_aef & 0x4000)
					tprintf(sc->sc_ctty,
						"ct%d: uninitialized media\n",
						unit);
				if (sc->sc_stat.c_aef & 0x1000)
					tprintf(sc->sc_ctty,
						"ct%d: not ready\n", unit);
				if (sc->sc_stat.c_aef & 0x0800)
					tprintf(sc->sc_ctty,
						"ct%d: write protect\n", unit);
			} else {
				printf("ct%d err: v%d u%d ru%d bn%d, ",
				       unit,
				       (sc->sc_stat.c_vu>>4)&0xF,
				       sc->sc_stat.c_vu&0xF,
				       sc->sc_stat.c_pend,
				       sc->sc_stat.c_blk);
				printf("R0x%x F0x%x A0x%x I0x%x\n",
				       sc->sc_stat.c_ref,
				       sc->sc_stat.c_fef,
				       sc->sc_stat.c_aef,
				       sc->sc_stat.c_ief);
			}
		} else
			printf("ct%d: request status failed\n", unit);
		bp->b_flags |= B_ERROR;
		bp->b_error = EIO;
		goto done;
	} else
		bp->b_resid = 0;
	if (sc->sc_flags & CTF_CMD) {
		switch (sc->sc_cmd) {
		case MTFSF:
			sc->sc_flags &= ~(CTF_BEOF|CTF_AEOF);
			sc->sc_blkno += CTBTOK(sc->sc_resid);
			ctstart(unit);
			return;
		case MTBSF:
			sc->sc_flags &= ~(CTF_AEOF|CTF_BEOF|CTF_EOT);
			break;
		case MTBSR:
			sc->sc_flags &= ~CTF_BEOF;
			if (sc->sc_flags & CTF_EOT) {
				sc->sc_flags |= CTF_AEOF;
				sc->sc_flags &= ~CTF_EOT;
			} else if (sc->sc_flags & CTF_AEOF) {
				sc->sc_flags |= CTF_BEOF;
				sc->sc_flags &= ~CTF_AEOF;
			}
			break;
		case MTWEOF:
			sc->sc_flags &= ~CTF_BEOF;
			if (sc->sc_flags & (CTF_AEOF|CTF_EOT)) {
				sc->sc_flags |= CTF_EOT;
				sc->sc_flags &= ~CTF_AEOF;
			} else
				sc->sc_flags |= CTF_AEOF;
			break;
		case MTREW:
		case MTOFFL:
			sc->sc_flags &= ~(CTF_BEOF|CTF_AEOF|CTF_EOT);
			break;
		}
	} else {
		sc->sc_flags &= ~CTF_AEOF;
		sc->sc_blkno += CTBTOK(sc->sc_resid);
	}
done:
#ifdef DEBUG
	if (ctdebug & CDB_FILES)
		printf("ctintr: after flags %x\n", sc->sc_flags);
#endif
	cttab[unit].b_actf = bp->av_forw;
	iodone(bp);
	hpibfree(&sc->sc_dq);
	if (cttab[unit].b_actf == NULL) {
		cttab[unit].b_active = 0;
		return;
	}
	ctustart(unit);
}

ctread(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	register int unit = UNIT(dev);

	return(physio(ctstrategy, &ctbuf[unit], dev, B_READ, minphys, uio));
}

ctwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	register int unit = UNIT(dev);

	return(physio(ctstrategy, &ctbuf[unit], dev, B_WRITE, minphys, uio));
}

/*ARGSUSED*/
ctioctl(dev, cmd, data, flag)
	dev_t dev;
	caddr_t data;
{
	register struct mtop *op;
	register int cnt;

	switch (cmd) {

	case MTIOCTOP:
		op = (struct mtop *)data;
		switch(op->mt_op) {

		case MTWEOF:
		case MTFSF:
		case MTBSR:
		case MTBSF:
		case MTFSR:
			cnt = op->mt_count;
			break;

		case MTREW:
		case MTOFFL:
			cnt = 1;
			break;

		default:
			return(EINVAL);
		}
		ctcommand(dev, op->mt_op, cnt);
		break;

	case MTIOCGET:
		break;

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

/*ARGSUSED*/
ctdump(dev)
	dev_t dev;
{
	return(ENXIO);
}

ctaddeof(unit)
	int unit;
{
	register struct ct_softc *sc = &ct_softc[unit];
	
	if (sc->sc_eofp == EOFS - 1)
		sc->sc_eofs[EOFS - 1]++;
	else {
		sc->sc_eofp++;
		if (sc->sc_eofp == EOFS - 1)
			sc->sc_eofs[EOFS - 1] = EOFS;
		else
			/* save blkno */
			sc->sc_eofs[sc->sc_eofp] = sc->sc_blkno - 1;
	}
#ifdef DEBUG
	if (ctdebug & CT_BSF)
		printf("ct%d: add eof pos %d blk %d\n",
		       unit, sc->sc_eofp,
		       sc->sc_eofs[sc->sc_eofp]);
#endif
}
#endif