V10/sys/io/ta.c
/*
* 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);
}