V10/sys/io/ta.c

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

/*
 * DSA tape class driver
 * drives TMSCP tapes
 */

#include "sys/param.h"
#include "sys/buf.h"
#include "sys/ta.h"
#include "sys/mscp.h"
#include "sys/user.h"
#include "sys/file.h"
#include "sys/conf.h"
#include "sys/mtio.h"

extern struct msaddr taaddr[];
extern struct tatape tatape[];
extern int tacnt;
static long tarefno;			/* ref seq num */
extern struct buf tabuf[];

static tacmd(), taonline(), tasonl(), tacinit();

int taopen(), taread(), tawrite(), taioctl(), tastrategy(), taclose();
struct bdevsw tabdev = bdinit(taopen, taclose, tastrategy, B_TAPE);
struct cdevsw tacdev = cdinit(taopen, taclose, taread, tawrite, taioctl);

/*
 * minor device number split
 *
 * NOCACHE disables write-behind caching; probably useless
 * DENS selects one of the eight TMSCP density values;
 * the drive is instructed to use 1<<DENS(dev) in whatever
 * density language it believes
 * here are some languages:
 * for 9-track tapes, (0) 800 bpi (1) PE (2) GCR
 * for old cartridge tapes (TK50), (3) block tape
 * for new cartridge tapes (TK70), (0) low density (1) high density
 * for other TMSCP devices, (0) is usually the right number
 */
#define	UNIT(d)	((d)&07)
#define	DENS(d)	(((d)&070)>>3)
#define	NOCACHE(d) ((d)&0100)
#define	NOREW(d) ((d)&0200)

/*
 * reused bits of buf/iobuf struct
 */

#define	b_next	av_forw		/* next buffer in queue */
#define	b_pkt	av_back		/* pointer to mscp command */
#define	b_crf	b_resid		/* saved refno for pending command */

/*
 * flags in tatape.flags
 */

#define	ONLINE	01		/* drive is online */
#define	WONLINE	02		/* waiting for online */
#define	OPEN	04		/* someone has drive open */
#define	WRITTEN	010		/* some data was written */
#define	WPROT	020		/* drive is write protected */
#define	NEEDCMD	040		/* waiting for some command to start */
#define	CMDDONE	0100		/* command is finished */
#define	CMDERR	0200		/* command finished, and error */
#define	UNHAPPY	0400		/* needs `cache data loss' clear */
#define	WONLY	01000		/* tape was opened only for writing */

/*
 * random numbers
 */

#define	PRIONL	(PZERO-1)
#define	IDTA	1		/* connection ID for MSCP */

/*
 * open a drive
 */

int taseql(), tadg();

taopen(dev, flag)
dev_t dev;
{
	register int unit;
	register struct tatape *ta;
	register struct msaddr *rp;
	int wasoff;

	unit = UNIT(dev);
	if (unit > tacnt) {
		u.u_error = ENXIO;
		return;
	}
	ta = &tatape[unit];
	rp = &taaddr[unit];
	if (ta->flags & OPEN) {
		u.u_error = EBUSY;
		return;
	}
	if (rp->ctype < 0 || rp->ctype >= nmsport
	||  (ta->port = msportsw[rp->ctype]) == NULL) {
		u.u_error = ENXIO;
		return;
	}
	ta->flags |= OPEN;
	ta->flags &=~ (WRITTEN|WPROT|WONLY);
	if ((*ta->port->mp_init)(rp->ctl, rp->ctype, 0, IDTA, taseql, tadg) == 0) {
		u.u_error = ENXIO;
		ta->flags &=~ OPEN;
		return;
	}
	tacinit(ta, rp);
	spl6();
	wasoff = (ta->flags & ONLINE) == 0;
	if ((ta->flags & ONLINE) == 0)
		taonline(ta, rp);
	spl0();
	if ((ta->flags & ONLINE) == 0) {
		u.u_error = ENXIO;
		ta->flags &=~ OPEN;
		return;
	}
	if ((ta->flags & WPROT) && (flag & FWRITE)) {
		u.u_error = ENODEV;
		if (!NOREW(dev))
			tacmd(unit, 1, OPAVL, 0, 0);
		ta->flags &=~ (OPEN|ONLINE);
		return;
	}
	ta->dens = (1<<DENS(dev));
	if (tacmd(unit, 0, OPGUS, 0, 0)		/* fill in language in dens */
	||  tacmd(unit, 0, OPSUC, NOCACHE(dev)?0:UFWBK, wasoff?ta->dens:0)) {
		u.u_error = ENXIO;
		if (!NOREW(dev))
			tacmd(unit, 1, OPAVL, 0, 0);
		ta->flags &=~ (OPEN|ONLINE);
		return;
	}
	if ((flag & (FREAD|FWRITE)) == FWRITE)
		ta->flags |= WONLY;
}

taclose(dev)
{
	register struct tatape *ta;
	int unit;

	unit = UNIT(dev);
	ta = &tatape[unit];
	if (ta->flags & ONLINE) {
		if (ta->flags & (WONLY|WRITTEN)) {
			tacmd(unit, 0, OPWRM, 0, 0);
			tacmd(unit, 0, OPWRM, 0, 0);
			tacmd(unit, 0, OPPOS, 0, -1);
		}
		/* annoyance: OPAVL always rewinds. */
		if (!NOREW(dev))
			tacmd(unit, 1, OPAVL, 0, 0);
	}
	ta->flags &=~ OPEN;
}

/*
 * send some non-io command to a tape drive
 * op is the MSCP opcode;
 * p0 and p1 are some parameters
 * if async == 0, don't return until command does
 */

#define	TWPRI	PZERO
#define	TCPRI	(PZERO+1)

static
tacmd(dev, async, op, p0, p1)
{
	register struct mscmd *mp;
	register struct tatape *ta;
	register struct msaddr *rp;
	register s;
	register err;

	ta = &tatape[dev];
	rp = &taaddr[dev];
	if ((ta->flags & ONLINE) == 0)
		return (1);
	mp = (*ta->port->mp_get)(rp->ctl);
	bzero((caddr_t)mp, sizeof(struct mscmd)); /* clear reserved fields */
	mp->m_crf = ++tarefno;
	mp->m_unit = rp->unit;
	mp->m_opcd = op;
	mp->m_mod = MDCSX;
	switch (op) {
	case OPPOS:		/* position: type, nobjs */
		if (p1 < 0) {
			p1 = -p1;
			mp->m_mod |= MDREV;
		}
		if (p0 == -1) {		/* -1 == rewind */
			mp->m_mod |= MDREW;
			mp->m_mod |= p1;	/* hack */
		}
		else if (p0 == 0)	/* 0 == skip files */
			mp->m_fcnt = p1;
		else {			/* 1 == skip records */
			mp->m_mod |= MDOBJ;
			mp->m_rcnt = p1;
		}
		break;

	case OPSUC:		/* set unit char: unit flags, density */
		mp->m_unfl |= p0;
		mp->m_fmt = p1;
		break;

	case OPGUS:		/* get unit status */
		mp->m_mod = p0;	/* wretched TK50 */
		break;

	default:		/* anything else: modifiers, 0 */
		mp->m_mod |= p0;
		break;
	}
	s = spl6();
	while (ta->cmdp) {
		ta->flags |= NEEDCMD;
		sleep((caddr_t)ta, TWPRI);
	}
	ta->cmdp = mp;
	ta->flags &=~ (CMDERR|CMDDONE);
	if (ta->flags & UNHAPPY) {
		ta->flags &=~ UNHAPPY;
		mp->m_mod |= MDCDL;
	}
	(*ta->port->mp_send)(rp->ctl, IDTA, mp);
	if (async == 0) {
		while ((ta->flags & CMDDONE) == 0)
			if (tsleep((caddr_t)ta, TCPRI, 0) == TS_SIG)
				break;
	}
	err = ((ta->flags & CMDERR) != 0);
	ta->cmdp = NULL;
	if (ta->flags & NEEDCMD) {
		ta->flags &=~ NEEDCMD;
		wakeup((caddr_t)ta);
	}
	splx(s);
	return (err);
}

int tastrategy();

taread(dev)
{
	physio(tastrategy, &tabuf[UNIT(dev)], dev, B_READ, minphys);
}

tawrite(dev)
{
	physio(tastrategy, &tabuf[UNIT(dev)], dev, B_WRITE, minphys);
}

/*
 * strategy routine;
 * send the packet right away
 */

tastrategy(bp)
register struct buf *bp;
{
	register struct tatape *ta;
	register struct mscmd *mp;
	register int unit;
	register struct msaddr *rp;
	int count;

	unit = UNIT(minor(bp->b_dev));
	ta = &tatape[unit];
	rp = &taaddr[unit];
	count = bp->b_bcount;
	spl6();
	if ((ta->flags & ONLINE) == 0 && taonline(ta, rp) == 0) {
		bp->b_flags |= B_ERROR;
		iodone(bp);
		spl0();
		return;
	}
	mp = (*ta->port->mp_get)(rp->ctl);
	mp->m_crf = ++tarefno;
	mp->m_unit = rp->unit;
	mp->m_opcd = (bp->b_flags & B_READ) ? OPRD : OPWR;
	mp->m_mod = MDCSX;
	if (ta->flags & UNHAPPY && (bp->b_flags & B_READ) == 0) {
		ta->flags &=~ UNHAPPY;
		mp->m_mod |= MDCDL;
	}
	mp->m_unfl = 0;
	mp->m_bcnt = count;
	/* seek on block device? later */
	(*ta->port->mp_map)(rp->ctl, mp, bp);
	bp->b_pkt = (struct buf *)mp;
	bp->b_crf = mp->m_crf;
	bp->b_next = NULL;
	if (ta->actf)
		ta->actl->b_next = bp;
	else
		ta->actf = bp;
	ta->actl = bp;
	if ((bp->b_flags & B_READ) == 0)
		ta->flags |= WRITTEN;
	(*ta->port->mp_send)(rp->ctl, IDTA, mp);
	spl0();
}

/*
 * ioctl
 * a subset of the berkeley ones
 */

taioctl(dev, cmd, addr, flag)
dev_t dev;
caddr_t addr;
{
	struct mtop mt;
	int func, p0, p1;

	switch (cmd) {
	default:
		u.u_error = ENOTTY;
		break;
	/* does anything use the other ones? */

	case MTIOCTOP:
		if (copyin(addr, (caddr_t)&mt, sizeof(struct mtop)) < 0) {
			u.u_error = EFAULT;
			return;
		}
		if (mt.mt_op == MTWEOF) {	/* oddball */
			if ((flag & FWRITE) == 0) {
				u.u_error = EBADF;
				return;
			}
			while (mt.mt_count-- > 0)
				if (tacmd(UNIT(dev), 0, OPWRM, 0, 0)) {
					u.u_error = EIO;
					return;
				}
			return;
		}
		if (mt.mt_op == MTRST) {	/* another oddball */
			(*tatape[UNIT(dev)].port->mp_init)(taaddr[UNIT(dev)].ctl, taaddr[UNIT(dev)].ctype, 1, IDTA, taseql, tadg);
			return;
		}
		p1 = mt.mt_count;
		func = OPPOS;
		switch (mt.mt_op) {	/* arcane arguments to tapos */
		case MTBSF:
			p1 = -p1;
		case MTFSF:
			p0 = 0;
			break;

		case MTBSR:
			p1 = -p1;
		case MTFSR:
			p0 = 1;
			break;

		case MTREW:
			p1 = 0;
			p0 = -1;
			break;

		case MTOFFL:
			func = OPAVL;
			p1 = 0;
			p0 = MDUNL;
			break;

		case MTNOP:
			return;	/* silly */
		}
		if (tacmd(UNIT(dev), 0, func, p0, p1))
			u.u_error = EIO;
		break;
	}
}

/*
 * here when the port gets a sequential message
 */
taseql(ctl, type, ep)
int ctl, type;
register struct msend *ep;
{
	register struct buf *bp;
	register struct tatape *ta;
	register int unit;
	register struct buf *obp;
	int sts;

	if (ep->m_opcd == 0 && ep->m_sts == STRST) {
		tareset(ctl, type);
		return;
	}
	/* get rid of this wretched loop somehow */
	for (unit = 0; unit < tacnt; unit++)
		if (taaddr[unit].ctl == ctl
		&&  taaddr[unit].ctype == type
		&&  taaddr[unit].unit == ep->m_unit)
			break;
	if (unit >= tacnt) {
		printf("tmscp stray unit: ctl%d typ%d ta%d sts %o opcode %o\n",
			ctl, type, ep->m_unit, ep->m_sts, ep->m_opcd);
		return;
	}
	ta = &tatape[unit];
	if (ep->m_flgs & EFCDL)
		ta->flags |= UNHAPPY;
	switch (ep->m_opcd & 0377) {
	case OPEND:		/* eg invalid command */
		printf("tmscp ctl%d ta%d ill cmd crf %d off %d\n", ctl,
			ep->m_unit, ep->m_crf, ep->m_sts>>8);
		if (ta->cmdp && ta->cmdp->m_crf == ep->m_crf)
			goto fincmd;
		/* else maybe it's a transfer; fall in */
	case OPRD|OPEND:
	case OPWR|OPEND:
		for (bp = ta->actf, obp = NULL; bp; obp = bp, bp = bp->b_next)
			if (ep->m_crf == bp->b_crf)
				break;
		if (bp == NULL) {
			printf("ta%d stray end: crf %d sts x%x opcode 0%o\n",
				unit, ep->m_crf, ep->m_sts, ep->m_opcd & 0377);
			return;
		}
		if (obp)
			obp->b_next = bp->b_next;
		else
			ta->actf = bp->b_next;
		if (bp == ta->actl)
			ta->actl = obp;
		sts = ep->m_sts & STMSK;
		if (sts == STAVL || sts == STOFL)
			ta->flags &=~ ONLINE;	/* help! */
		bp->b_resid = bp->b_bcount - ep->m_bcnt;
		if (sts == STTPM)
			sts = STSUC;	/* tape mark -> empty read */
		if (sts != STSUC) {
			bp->b_flags |= B_ERROR;
			switch (sts) {
			case STRDT:
			case STOFL:
			case STAVL:
			case STWPR:	/* well ... */
				break;
			default:
				printf("err on ta%d block %D: sts 0%o flgs 0%o\n", unit, bp->b_blkno, ep->m_sts, ep->m_flgs);
			}
		}
		if (ep->m_flgs & EFEOT && (bp->b_flags & B_READ) == 0) {
			bp->b_error = ENOSPC;
			bp->b_flags |= B_ERROR;
		}
		(*ta->port->mp_unmap)(ctl, (struct mscmd *)bp->b_pkt);
		iodone(bp);
		return;

	case OPGUS|OPEND:
		ta->dens &= TFMASK;
		ta->dens |= ep->m_menu & ~TFMASK;
		goto fincmd;

	case OPAVL|OPEND:
		if (ep->m_sts == STSUC)	/* sic */
			ta->flags &=~ ONLINE;
	case OPWRM|OPEND:
	case OPPOS|OPEND:
	case OPFLS|OPEND:
	case OPSUC|OPEND:
	fincmd:
		if ((ep->m_sts & STMSK) == STAVL || (ep->m_sts & STMSK) == STOFL)
			ta->flags &=~ ONLINE;
		if (ta->cmdp) {
			ta->flags |= CMDDONE;
			if ((ep->m_sts & STMSK) != STSUC) {
				printf("ta%d: cmd 0%o sts 0%o flgs 0%o\n", unit, ep->m_opcd&0377, ep->m_sts, ep->m_flgs);
				ta->flags |= CMDERR;
			}
			wakeup((caddr_t)ta);
		}
		return;

	case OPONL|OPEND:
		tasonl(ta, ep);
		return;

	case OPSCC|OPEND:
		if ((ep->m_sts & STMSK) != STSUC)
			printf("tmscp ctl%d typ%d: bad init\n", ctl, type);
		return;

	default:
		printf("stray tmscp msg ta%d opcd 0%o sts x%x\n",
			unit, ep->m_opcd&0377, ep->m_sts&0177777);
		return;
	}
}

/*
 * controller was reset
 * discard all pending io,
 * awake all sleepers,
 * mark everything offline
 */

tareset(ctl, type)
int ctl, type;
{
	register int unit;
	register struct tatape *ta;
	register struct buf *bp, *nbp;

	for (unit = 0; unit < tacnt; unit++)  {
		if (taaddr[unit].ctl != ctl || taaddr[unit].ctype != type)
			continue;
		ta = &tatape[unit];
		for (bp = ta->actf; bp; bp = nbp) {
			nbp = bp->b_next;
			(*ta->port->mp_unmap)(ctl, (struct mscmd *)bp->b_pkt);
			bp->b_flags |= B_ERROR;
			iodone(bp);
		}
		ta->actf = ta->actl = NULL;
		ta->flags &=~ (ONLINE|WONLINE);
		if (ta->cmdp)
			ta->flags |= CMDDONE|CMDERR;
		wakeup((caddr_t)ta);
	}
}

/*
 * here with a datagram message
 * explanations really shouldn't be in the driver
 */

static char *taevents[] = {
	"ok",
	"inv cmd",
	"op aborted",
	"offline",
	"available",
	"med fmt",
	"write prot",
	"comp err",
	"data err",
	"host buf access err",
	"cntl err",
	"drive err",
};
#define	MAXEVT	0xb

tadg(ctl, type, ep)
int ctl, type;
register struct mserl *ep;
{

	if (ep->l_evnt == STSEX)	/* boring, at least for now */
		return;
	printf("ta%d ctl%d typ%d seq %d: %s err; fmt x%x ev x%x fl x%x\n",
		ep->l_unit, ctl, type, ep->l_seq,	/* phys unit, not log */
		ep->l_flgs&(LFSUC|LFCON) ? "soft" : "hard",
		ep->l_fmt, ep->l_evnt, ep->l_flgs&0377);
	if ((ep->l_evnt & STMSK) <= MAXEVT)
		printf("%s; ", taevents[ep->l_evnt & STMSK]);
	switch (ep->l_fmt) {
	case FMCNT:
		/* now the thing should be marked disastrously bad */
		printf("oops\n");
		break;

	case FMBAD:
		printf("host mem access; addr x%x\n", ep->l_badr);
		break;

	case FMTAPE:
		printf("lvl x%x retry x%x\n", ep->l_lvl, ep->l_rtry);
		break;

	default:
		printf("\n");
		break;
	}
}

/*
 * unit is believed offline
 * try to bring it on
 */

static
taonline(ta, rp)
register struct tatape *ta;
register struct msaddr *rp;
{
	register struct mscmd *mp;
	int s;

	s = spl6();
	if ((ta->flags & WONLINE) == 0) {
		mp = (*ta->port->mp_get)(rp->ctl);
		bzero((caddr_t)mp, sizeof(struct mscmd)); /* clear reserved fields */
		mp->m_crf = ++tarefno;
		mp->m_unit = rp->unit;
		mp->m_opcd = OPONL;
		mp->m_mod = MDCSX|MDXCL;
		if (ta->flags & UNHAPPY) {
			ta->flags &=~ UNHAPPY;
			mp->m_mod |= MDCDL;
		}
		mp->m_unfl = 0;
		mp->m_fmt = 0;	/* ta->dens? */
		(*ta->port->mp_send)(rp->ctl, IDTA, mp);
		ta->flags |= WONLINE;
	}
	while (ta->flags & WONLINE)
		tsleep((caddr_t)ta, PRIONL, 60);
	splx(s);
	if ((ta->flags & ONLINE) == 0)
		return (0);
	return (1);
}

static
tasonl(ta, ep)
register struct tatape *ta;
register struct msend *ep;
{

	if (ta->flags & WONLINE) {
		ta->flags &=~ WONLINE;
		wakeup((caddr_t)ta);
	}
	if ((ep->m_sts & STMSK) != STSUC)
		return;
	ta->flags |= ONLINE;
	if (ep->m_unfl & UFWPH)
		ta->flags |= WPROT;
}

/*
 * controller init
 * set characteristics to turn off host timeouts
 */

static
tacinit(ta, rp)
struct tatape *ta;
struct msaddr *rp;
{
	register struct mscmd *mp;
	register int s;

	mp = (*ta->port->mp_get)(rp->ctl);
	bzero((caddr_t)mp, sizeof(struct mscmd)); /* clear reserved fields */
	mp->m_crf = ++tarefno;
	mp->m_opcd = OPSCC;
	mp->m_mod = MDCSX;
	mp->m_cntf = CFMSC | CFTHS;
	mp->m_vrsn = MSCPVER;
	s = spl6();
	(*ta->port->mp_send)(rp->ctl, IDTA, mp);
	splx(s);
}