fix so it compiles
[unix-history] / usr / src / sys / pmax / dev / tz.c
/*
* Copyright (c) 1992 Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Ralph Campbell.
*
* %sccs.include.redist.c%
*
* @(#)tz.c 7.2 (Berkeley) %G%
*
* from: $Header: /sprite/src/kernel/dev/RCS/devSCSITape.c,
* v 8.14 89/07/31 17:26:13 mendel Exp $ SPRITE (Berkeley)
*/
/*
* SCSI CCS (Command Command Set) tape driver.
*/
#include "tz.h"
#if NTZ > 0
#include "param.h"
#include "systm.h"
#include "buf.h"
#include "errno.h"
#include "file.h"
#include "ioctl.h"
#include "mtio.h"
#include "syslog.h"
#include "device.h"
#include "scsi.h"
int tzprobe();
void tzstart(), tzdone();
struct driver tzdriver = {
"tz", tzprobe, tzstart, tzdone,
};
struct tz_softc {
struct scsi_device *sc_sd; /* physical unit info */
int sc_flags; /* see below */
struct buf sc_tab; /* queue of pending operations */
struct buf sc_buf; /* buf for doing I/O */
struct buf sc_errbuf; /* buf for doing REQUEST_SENSE */
struct ScsiCmd sc_cmd; /* command for controller */
ScsiGroup0Cmd sc_rwcmd; /* SCSI cmd for read/write */
struct scsi_fmt_cdb sc_cdb; /* SCSI cmd if not read/write */
struct scsi_fmt_sense sc_sense; /* sense data from last cmd */
} tz_softc[NTZ];
/* sc_flags values */
#define TZF_ALIVE 0x01 /* drive found and ready */
#define TZF_SENSEINPROGRESS 0x02 /* REQUEST_SENSE command in progress */
#define TZF_ALTCMD 0x04 /* alternate command in progress */
#define TZF_WRITTEN 0x08 /* tape has been written to */
#define TZF_OPEN 0x10 /* device is open */
#define TZF_WAIT 0x20 /* waiting for sc_tab to drain */
/* bits in minor device */
#define tzunit(x) (minor(x) >> 1) /* tz%d unit number */
#define TZ_NOREWIND 0x1 /* don't rewind on close */
#define INF (daddr_t)1000000L /* a block number that won't exist */
#ifdef DEBUG
int tzdebug = 1;
#endif
/*
* Test to see if device is present.
* Return true if found and initialized ok.
*/
tzprobe(sd)
struct scsi_device *sd;
{
register struct tz_softc *sc = &tz_softc[sd->sd_unit];
register int i;
ScsiInquiryData inqbuf;
ScsiClass7Sense *sp;
printf("tzprobe()\n"); /* XXX */
/* init some parameters that don't change */
sc->sc_sd = sd;
sc->sc_cmd.sd = sd;
sc->sc_cmd.unit = sd->sd_unit;
sc->sc_rwcmd.unitNumber = sd->sd_slave;
/* sc->sc_rwcmd.highAddr = 1; /* count in blocks */
/* try to find out what type of device this is */
sc->sc_flags = TZF_ALTCMD; /* force use of sc_cdb */
sc->sc_cdb.len = sizeof(ScsiGroup0Cmd);
scsiGroup0Cmd(SCSI_INQUIRY, sd->sd_slave, 0, sizeof(inqbuf),
(ScsiGroup0Cmd *)sc->sc_cdb.cdb);
sc->sc_buf.b_flags = B_BUSY | B_READ;
sc->sc_buf.b_bcount = sizeof(inqbuf);
sc->sc_buf.b_un.b_addr = (caddr_t)&inqbuf;
sc->sc_buf.av_forw = (struct buf *)0;
sc->sc_tab.b_actf = sc->sc_tab.b_actl = &sc->sc_buf;
tzstart(sd->sd_unit);
if (biowait(&sc->sc_buf) ||
(i = sizeof(inqbuf) - sc->sc_buf.b_resid) < 5)
goto bad;
if (inqbuf.type != SCSI_TAPE_TYPE)
goto bad;
/* check for device ready to clear UNIT_ATTN */
sc->sc_cdb.len = sizeof(ScsiGroup0Cmd);
scsiGroup0Cmd(SCSI_TEST_UNIT_READY, sd->sd_slave, 0, 0,
(ScsiGroup0Cmd *)sc->sc_cdb.cdb);
sc->sc_buf.b_flags = B_BUSY | B_READ;
sc->sc_buf.b_bcount = 0;
sc->sc_buf.b_un.b_addr = (caddr_t)0;
sc->sc_buf.av_forw = (struct buf *)0;
sc->sc_tab.b_actf = sc->sc_tab.b_actl = &sc->sc_buf;
tzstart(sd->sd_unit);
(void) biowait(&sc->sc_buf);
sc->sc_flags = TZF_ALIVE;
sc->sc_buf.b_flags = 0;
printf("tz%d at %s%d drive %d slave %d\n", sd->sd_unit,
sd->sd_cdriver->d_name, sd->sd_ctlr, sd->sd_drive,
sd->sd_slave);
return (1);
bad:
/* doesn't exist or not a CCS device */
sc->sc_flags = 0;
sc->sc_buf.b_flags = 0;
return (0);
}
/*
* Perform a special tape command on a SCSI Tape drive.
*/
tzcommand(dev, command, code, count)
dev_t dev;
int command;
int code;
int count;
{
register struct tz_softc *sc = &tz_softc[tzunit(dev)];
int s, error;
s = splbio();
while (sc->sc_tab.b_actf) {
sc->sc_flags |= TZF_WAIT;
sleep(&sc->sc_flags, PZERO);
}
sc->sc_flags |= TZF_ALTCMD; /* force use of sc_cdb */
sc->sc_cdb.len = sizeof(ScsiGroup0Cmd);
scsiGroup0Cmd(command, sc->sc_sd->sd_slave,
(code << 16) | ((count >> 8) & 0xFFFF), count & 0xFF,
(ScsiGroup0Cmd *)sc->sc_cdb.cdb);
sc->sc_buf.b_flags = B_BUSY | B_READ;
sc->sc_buf.b_bcount = 0;
sc->sc_buf.b_un.b_addr = (caddr_t)0;
sc->sc_buf.av_forw = (struct buf *)0;
sc->sc_tab.b_actf = sc->sc_tab.b_actl = &sc->sc_buf;
tzstart(sc->sc_sd->sd_unit);
error = biowait(&sc->sc_buf);
sc->sc_flags &= ~TZF_ALTCMD; /* force use of sc_cdb */
sc->sc_buf.b_flags = 0;
splx(s);
return (error);
}
void
tzstart(unit)
int unit;
{
register struct tz_softc *sc = &tz_softc[unit];
register struct buf *bp = sc->sc_tab.b_actf;
register int n;
sc->sc_cmd.buf = bp->b_un.b_addr;
sc->sc_cmd.buflen = bp->b_bcount;
if (sc->sc_flags & (TZF_SENSEINPROGRESS | TZF_ALTCMD)) {
sc->sc_cmd.flags = !(bp->b_flags & B_READ) ?
SCSICMD_DATA_TO_DEVICE : 0;
sc->sc_cmd.cmd = sc->sc_cdb.cdb;
sc->sc_cmd.cmdlen = sc->sc_cdb.len;
} else {
if (bp->b_flags & B_READ) {
sc->sc_cmd.flags = 0;
sc->sc_rwcmd.command = SCSI_READ;
} else {
sc->sc_cmd.flags = SCSICMD_DATA_TO_DEVICE;
sc->sc_rwcmd.command = SCSI_WRITE;
}
sc->sc_cmd.cmd = (u_char *)&sc->sc_rwcmd;
sc->sc_cmd.cmdlen = sizeof(sc->sc_rwcmd);
n = howmany(bp->b_bcount, 512);
sc->sc_rwcmd.midAddr = n >> 16;
sc->sc_rwcmd.lowAddr = n >> 8;
sc->sc_rwcmd.blockCount = n;
if ((bp->b_bcount & (512 - 1)) != 0)
printf("tz%d: partial block xfer -- %x bytes\n",
unit, bp->b_bcount);
}
printf("tzstart(%d) flags %x, addr %x sz %d\n", unit,
sc->sc_flags, sc->sc_cmd.buf, sc->sc_cmd.buflen); /* XXX */
/* tell controller to start this command */
if (sc->sc_cmd.cmd[0] == SCSI_READ)
(*sc->sc_sd->sd_cdriver->d_start)(&sc->sc_cmd);
}
/*
* This is called by the controller driver when the command is done.
*/
void
tzdone(unit, error, resid, status)
int unit;
int error; /* error number from errno.h */
int resid; /* amount not transfered */
int status; /* SCSI status byte */
{
register struct tz_softc *sc = &tz_softc[unit];
register struct buf *bp = sc->sc_tab.b_actf;
extern int cold;
printf("tzdone(%d, %d, %d, %x) %x flags %x\n", unit, error, resid,
status, sc, sc->sc_flags); /* XXX */
if (bp == NULL) {
printf("tz%d: bp == NULL\n", unit);
return;
}
if (sc->sc_flags & TZF_SENSEINPROGRESS) {
sc->sc_flags &= ~TZF_SENSEINPROGRESS;
sc->sc_tab.b_actf = bp = bp->b_actf; /* remove sc_errbuf */
if (bp == 0) {
panic("tzdone"); /* XXX */
}
if (error || (status & SCSI_STATUS_CHECKCOND)) {
#ifdef DEBUG
if (tzdebug)
printf("tz%d: error reading sense data: error %d scsi status 0x%x\n",
unit, error, status);
#endif
/*
* We got an error during the REQUEST_SENSE,
* fill in no sense for data.
*/
sc->sc_sense.sense[0] = 0x70;
sc->sc_sense.sense[2] = SCSI_CLASS7_NO_SENSE;
} else if (!cold
#ifdef DEBUG
|| tzdebug
#endif
) {
printf("tz%d: ", unit);
scsiPrintSense((ScsiClass7Sense *)sc->sc_sense.sense,
sizeof(sc->sc_sense.sense) - resid);
}
} else if (error || (status & SCSI_STATUS_CHECKCOND)) {
#ifdef DEBUG
if (tzdebug)
printf("tz%d: error %d scsi status 0x%x\n",
unit, error, status);
#endif
/* save error info */
sc->sc_sense.status = status;
bp->b_flags |= B_ERROR;
bp->b_error = error;
bp->b_resid = resid;
if (status & SCSI_STATUS_CHECKCOND) {
/*
* Start a REQUEST_SENSE command.
* Since we are called at interrupt time, we can't
* wait for the command to finish; that's why we use
* the sc_flags field.
*/
sc->sc_flags |= TZF_SENSEINPROGRESS;
sc->sc_cdb.len = sizeof(ScsiGroup0Cmd);
scsiGroup0Cmd(SCSI_REQUEST_SENSE, sc->sc_sd->sd_slave,
0, sizeof(sc->sc_sense.sense),
(ScsiGroup0Cmd *)sc->sc_cdb.cdb);
sc->sc_errbuf.b_flags = B_BUSY | B_READ;
sc->sc_errbuf.b_bcount = sizeof(sc->sc_sense.sense);
sc->sc_errbuf.b_un.b_addr = (caddr_t)sc->sc_sense.sense;
sc->sc_errbuf.av_forw = bp;
sc->sc_tab.b_actf = &sc->sc_errbuf;
tzstart(unit);
return;
}
} else {
sc->sc_sense.status = status;
bp->b_resid = resid;
}
sc->sc_tab.b_actf = bp->b_actf;
biodone(bp);
if (sc->sc_tab.b_actf)
tzstart(unit);
else {
sc->sc_tab.b_active = 0;
if (sc->sc_flags & TZF_WAIT) {
sc->sc_flags &= ~TZF_WAIT;
wakeup(&sc->sc_flags);
}
}
}
tzopen(dev, flags)
dev_t dev;
int flags;
{
register int unit = tzunit(dev);
register struct tz_softc *sc = &tz_softc[unit];
int error;
if (unit >= NTZ)
return (ENXIO);
if (!(sc->sc_flags & TZF_ALIVE)) {
/* check again, tape may have been turned off at boot time */
if (!tzprobe(sc->sc_sd))
return (ENXIO);
}
if (sc->sc_flags & TZF_OPEN)
return (EBUSY);
/* clear UNIT_ATTENTION */
error = tzcommand(dev, SCSI_TEST_UNIT_READY, 0, 0);
if (error) {
ScsiClass7Sense *sp = (ScsiClass7Sense *)sc->sc_sense.sense;
/* return error if last error was not UNIT_ATTENTION */
if (!(sc->sc_sense.status & SCSI_STATUS_CHECKCOND) ||
sp->error7 != 0x70 || sp->key != SCSI_CLASS7_UNIT_ATTN)
return (error);
}
#ifdef notdef
if ((flag&FWRITE) && (sc->sc_dsreg&HTDS_WRL)) {
sc->sc_openf = 0;
uprintf("tz%d: no write ring\n", unit);
return (EIO);
}
sc->sc_ctty = (caddr_t)(u.u_procp->p_flag & SCTTY ?
u.u_procp->p_session->s_ttyp : 0);
#endif
sc->sc_flags = TZF_ALIVE | TZF_OPEN;
return (0);
}
tzclose(dev, flag)
dev_t dev;
int flag;
{
register struct tz_softc *sc = &tz_softc[tzunit(dev)];
if (!(sc->sc_flags & TZF_OPEN))
return (0);
if (flag == FWRITE ||
((flag & FWRITE) && (sc->sc_flags & TZF_WRITTEN))) {
(void) tzcommand(dev, SCSI_WRITE_EOF, 0, 1);
}
if ((minor(dev) & TZ_NOREWIND) == 0)
(void) tzcommand(dev, SCSI_REWIND, 0, 0);
sc->sc_flags &= ~(TZF_OPEN | TZF_WRITTEN);
return (0);
}
tzioctl(dev, cmd, data, flag)
dev_t dev;
int cmd;
caddr_t data;
int flag;
{
register struct tz_softc *sc = &tz_softc[tzunit(dev)];
register struct buf *bp = &sc->sc_buf;
struct mtop *mtop;
struct mtget *mtget;
int code, count;
static tzops[] = {
SCSI_WRITE_EOF, SCSI_SPACE, SCSI_SPACE, SCSI_SPACE, SCSI_SPACE,
SCSI_REWIND, SCSI_REWIND, SCSI_TEST_UNIT_READY
};
switch (cmd) {
case MTIOCTOP: /* tape operation */
mtop = (struct mtop *)data;
if ((unsigned)mtop->mt_op < MTREW && mtop->mt_count <= 0)
return (EINVAL);
switch (mtop->mt_op) {
case MTWEOF:
code = 0;
count = mtop->mt_count;
break;
case MTFSF:
code = 1;
count = mtop->mt_count;
break;
case MTBSF:
code = 1;
count = -mtop->mt_count;
break;
case MTFSR:
code = 0;
break;
case MTBSR:
code = 0;
count = -mtop->mt_count;
break;
case MTREW:
case MTOFFL:
case MTNOP:
code = 0;
count = 0;
break;
default:
return (EINVAL);
}
return (tzcommand(dev, tzops[mtop->mt_op], code, count));
case MTIOCGET:
mtget = (struct mtget *)data;
mtget->mt_dsreg = 0;
mtget->mt_erreg = sc->sc_sense.status;
mtget->mt_resid = 0;
mtget->mt_type = 0;
break;
default:
return (EINVAL);
}
return (0);
}
void
tzstrategy(bp)
register struct buf *bp;
{
register int unit = tzunit(bp->b_dev);
register struct tz_softc *sc = &tz_softc[unit];
register struct buf *dp;
register int s;
bp->av_forw = NULL;
dp = &sc->sc_tab;
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;
tzstart(unit);
}
splx(s);
}
#endif