851 lines
21 KiB
C
851 lines
21 KiB
C
/* $NetBSD: tz.c,v 1.8 1995/09/18 03:04:55 jonathan Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1992, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* Ralph Campbell.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* @(#)tz.c 8.4 (Berkeley) 1/11/94
|
|
*
|
|
* 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 <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/file.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mtio.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/tprintf.h>
|
|
|
|
#include <pmax/dev/device.h>
|
|
#include <pmax/dev/scsi.h>
|
|
|
|
int tzprobe();
|
|
void tzstart(), tzdone();
|
|
|
|
struct pmax_driver tzdriver = {
|
|
"tz", tzprobe, tzstart, tzdone,
|
|
};
|
|
|
|
struct tz_softc {
|
|
struct pmax_scsi_device *sc_sd; /* physical unit info */
|
|
int sc_flags; /* see below */
|
|
int sc_tapeid; /* tape drive id */
|
|
int sc_blklen; /* 0 = variable len records */
|
|
long sc_numblks; /* number of blocks on tape */
|
|
tpr_t sc_ctty; /* terminal for error messages */
|
|
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 */
|
|
struct ScsiTapeModeSelectHdr sc_mode; /* SCSI_MODE_SENSE data */
|
|
char sc_modelen; /* SCSI_MODE_SENSE data length */
|
|
} 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 */
|
|
#define TZF_SEENEOF 0x40 /* seen file mark on read */
|
|
|
|
/* bits in minor device */
|
|
#define tzunit(x) (minor(x) >> 4) /* tz%d unit number */
|
|
#define TZ_NOREWIND 0x01 /* don't rewind on close */
|
|
#define TZ_HIDENSITY 0x02
|
|
#define TZ_EXSFMK 0x04
|
|
#define TZ_FIXEDBLK 0x08
|
|
|
|
#ifdef DEBUG
|
|
int tzdebug = 0;
|
|
#endif
|
|
|
|
void tzstrategy __P((register struct buf *bp));
|
|
|
|
/*
|
|
* Test to see if device is present.
|
|
* Return true if found and initialized ok.
|
|
*/
|
|
tzprobe(sd)
|
|
struct pmax_scsi_device *sd;
|
|
{
|
|
register struct tz_softc *sc = &tz_softc[sd->sd_unit];
|
|
register int i;
|
|
ScsiInquiryData inqbuf;
|
|
ScsiClass7Sense *sp;
|
|
|
|
/* 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_cmd.flags = 0;
|
|
sc->sc_rwcmd.unitNumber = sd->sd_slave;
|
|
|
|
/* 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.b_actf = (struct buf *)0;
|
|
sc->sc_buf.b_actb = &sc->sc_tab.b_actf;
|
|
sc->sc_tab.b_actf = &sc->sc_buf;
|
|
sc->sc_tab.b_actb = &sc->sc_buf.b_actf;
|
|
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 || !inqbuf.rmb)
|
|
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.b_actf = (struct buf *)0;
|
|
sc->sc_buf.b_actb = &sc->sc_tab.b_actf;
|
|
sc->sc_tab.b_actf = &sc->sc_buf;
|
|
sc->sc_tab.b_actb = &sc->sc_buf.b_actf;
|
|
tzstart(sd->sd_unit);
|
|
(void) biowait(&sc->sc_buf);
|
|
|
|
sc->sc_flags = TZF_ALIVE;
|
|
sc->sc_modelen = 12;
|
|
sc->sc_buf.b_flags = 0;
|
|
printf("tz%d at %s%d drive %d slave %d", sd->sd_unit,
|
|
sd->sd_cdriver->d_name, sd->sd_ctlr, sd->sd_drive,
|
|
sd->sd_slave);
|
|
if (i == 5 && inqbuf.version == 1 && inqbuf.qualifier == 0x50) {
|
|
printf(" TK50\n");
|
|
sc->sc_tapeid = MT_ISTK50;
|
|
} else if (i >= 5 && inqbuf.version == 1 && inqbuf.qualifier == 0 &&
|
|
inqbuf.length == 0) {
|
|
/* assume Emultex MT02 controller */
|
|
printf(" MT02\n");
|
|
sc->sc_tapeid = MT_ISMT02;
|
|
} else if (inqbuf.version > 2 || i < 36) {
|
|
printf(" GENERIC SCSI tape device: qual 0x%x, ver %d\n",
|
|
inqbuf.qualifier, inqbuf.version);
|
|
sc->sc_tapeid = 0;
|
|
} else {
|
|
char vid[9], pid[17], revl[5];
|
|
|
|
bcopy((caddr_t)inqbuf.vendorID, (caddr_t)vid, 8);
|
|
bcopy((caddr_t)inqbuf.productID, (caddr_t)pid, 16);
|
|
bcopy((caddr_t)inqbuf.revLevel, (caddr_t)revl, 4);
|
|
for (i = 8; --i > 0; )
|
|
if (vid[i] != ' ')
|
|
break;
|
|
vid[i+1] = 0;
|
|
for (i = 16; --i > 0; )
|
|
if (pid[i] != ' ')
|
|
break;
|
|
pid[i+1] = 0;
|
|
for (i = 4; --i > 0; )
|
|
if (revl[i] != ' ')
|
|
break;
|
|
revl[i+1] = 0;
|
|
printf(" %s %s rev %s\n", vid, pid, revl);
|
|
|
|
if (bcmp("EXB-8200", pid, 8) == 0) {
|
|
sc->sc_tapeid = MT_ISEXABYTE;
|
|
sc->sc_modelen = 17;
|
|
} else if (bcmp("VIPER 150", pid, 9) == 0) {
|
|
sc->sc_tapeid = MT_ISVIPER1;
|
|
} else if (bcmp("Python 25501", pid, 12) == 0) {
|
|
sc->sc_tapeid = MT_ISPYTHON;
|
|
} else if (bcmp("HP35450A", pid, 8) == 0) {
|
|
#if 0
|
|
/* XXX "extra" stat makes the HP drive happy at boot time */
|
|
stat = scsi_test_unit_rdy(ctlr, slave, unit);
|
|
#endif
|
|
sc->sc_tapeid = MT_ISHPDAT;
|
|
} else if (bcmp("123107 SCSI", pid, 11) == 0) {
|
|
sc->sc_tapeid = MT_ISMFOUR;
|
|
} else {
|
|
printf("tz%d: assuming GENERIC SCSI tape device\n",
|
|
sd->sd_unit,
|
|
inqbuf.type, inqbuf.qualifier, inqbuf.version);
|
|
sc->sc_tapeid = 0;
|
|
}
|
|
}
|
|
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, data)
|
|
dev_t dev;
|
|
int command;
|
|
int code;
|
|
int count;
|
|
caddr_t data;
|
|
{
|
|
register struct tz_softc *sc = &tz_softc[tzunit(dev)];
|
|
register ScsiGroup0Cmd *c;
|
|
int s, error;
|
|
|
|
s = splbio();
|
|
/* wait for pending operations to finish */
|
|
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);
|
|
c = (ScsiGroup0Cmd *)sc->sc_cdb.cdb;
|
|
c->command = command;
|
|
c->unitNumber = sc->sc_sd->sd_slave;
|
|
c->highAddr = code;
|
|
c->midAddr = count >> 16;
|
|
c->lowAddr = count >> 8;
|
|
c->blockCount = count;
|
|
c->control = 0;
|
|
if (command == SCSI_MODE_SELECT)
|
|
sc->sc_buf.b_flags = B_BUSY;
|
|
else {
|
|
sc->sc_buf.b_flags = B_BUSY | B_READ;
|
|
#if 0
|
|
/* this seems to work but doesn't give us a speed advantage */
|
|
if (command == SCSI_TEST_UNIT_READY)
|
|
sc->sc_cmd.flags |= SCSICMD_USE_SYNC;
|
|
#endif
|
|
}
|
|
sc->sc_buf.b_bcount = data ? count : 0;
|
|
sc->sc_buf.b_un.b_addr = data;
|
|
sc->sc_buf.b_actf = (struct buf *)0;
|
|
sc->sc_buf.b_actb = &sc->sc_tab.b_actf;
|
|
sc->sc_tab.b_actf = &sc->sc_buf;
|
|
sc->sc_tab.b_actb = &sc->sc_buf.b_actf;
|
|
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;
|
|
sc->sc_cmd.flags = 0;
|
|
if (sc->sc_buf.b_resid)
|
|
printf("tzcommand: resid %d\n", sc->sc_buf.b_resid); /* XXX */
|
|
if (error == 0)
|
|
switch (command) {
|
|
case SCSI_SPACE:
|
|
case SCSI_WRITE_EOF:
|
|
case SCSI_REWIND:
|
|
sc->sc_flags &= ~TZF_SEENEOF;
|
|
}
|
|
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)) {
|
|
if (bp->b_flags & B_READ)
|
|
sc->sc_cmd.flags &= ~SCSICMD_DATA_TO_DEVICE;
|
|
else
|
|
sc->sc_cmd.flags |= SCSICMD_DATA_TO_DEVICE;
|
|
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;
|
|
sc->sc_flags &= ~TZF_WRITTEN;
|
|
} else {
|
|
sc->sc_cmd.flags = SCSICMD_DATA_TO_DEVICE;
|
|
sc->sc_rwcmd.command = SCSI_WRITE;
|
|
sc->sc_flags |= TZF_WRITTEN;
|
|
}
|
|
sc->sc_cmd.cmd = (u_char *)&sc->sc_rwcmd;
|
|
sc->sc_cmd.cmdlen = sizeof(sc->sc_rwcmd);
|
|
if (sc->sc_blklen) {
|
|
/* fixed sized records */
|
|
n = bp->b_bcount / sc->sc_blklen;
|
|
if (bp->b_bcount % sc->sc_blklen) {
|
|
tprintf(sc->sc_ctty,
|
|
"tz%d: I/O not block aligned %d/%ld\n",
|
|
unit, sc->sc_blklen, bp->b_bcount);
|
|
tzdone(unit, EIO, bp->b_bcount, 0);
|
|
}
|
|
sc->sc_rwcmd.highAddr = 1;
|
|
} else {
|
|
/* variable sized records */
|
|
n = bp->b_bcount;
|
|
sc->sc_rwcmd.highAddr = 0;
|
|
}
|
|
sc->sc_rwcmd.midAddr = n >> 16;
|
|
sc->sc_rwcmd.lowAddr = n >> 8;
|
|
sc->sc_rwcmd.blockCount = n;
|
|
}
|
|
|
|
/* tell controller to start this command */
|
|
(*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;
|
|
register struct buf *dp;
|
|
extern int cold;
|
|
|
|
if (bp == NULL) {
|
|
printf("tz%d: bp == NULL\n", unit);
|
|
return;
|
|
}
|
|
if (sc->sc_flags & TZF_SENSEINPROGRESS) {
|
|
sc->sc_flags &= ~TZF_SENSEINPROGRESS;
|
|
*bp->b_actb = dp = bp->b_actf; /* remove sc_errbuf */
|
|
#ifdef DIAGNOSTIC
|
|
if (!dp)
|
|
panic("tzdone");
|
|
#endif
|
|
dp->b_actb = bp->b_actb;
|
|
bp = dp;
|
|
|
|
if (error || (status & SCSI_STATUS_CHECKCOND)) {
|
|
printf("tz%d: error reading sense data: error %d scsi status 0x%x\n",
|
|
unit, error, status);
|
|
/*
|
|
* 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) {
|
|
ScsiClass7Sense *sp;
|
|
long resid;
|
|
|
|
sp = (ScsiClass7Sense *)sc->sc_sense.sense;
|
|
if (sp->error7 != 0x70)
|
|
goto prerr;
|
|
if (sp->valid) {
|
|
resid = (sp->info1 << 24) | (sp->info2 << 16) |
|
|
(sp->info3 << 8) | sp->info4;
|
|
if (sc->sc_blklen)
|
|
resid *= sc->sc_blklen;
|
|
} else
|
|
resid = 0;
|
|
switch (sp->key) {
|
|
case SCSI_CLASS7_NO_SENSE:
|
|
/*
|
|
* Hit a filemark, end of media, or
|
|
* end of record.
|
|
* Fixed length blocks, an error.
|
|
*/
|
|
if (sp->endOfMedia) {
|
|
bp->b_error = ENOSPC;
|
|
bp->b_resid = resid;
|
|
break;
|
|
}
|
|
if (sc->sc_blklen && sp->badBlockLen) {
|
|
tprintf(sc->sc_ctty,
|
|
"tz%d: Incorrect Block Length, expected %d got %d\n",
|
|
unit, sc->sc_blklen, resid);
|
|
break;
|
|
}
|
|
if (resid < 0) {
|
|
/*
|
|
* Variable length records but
|
|
* attempted to read less than a
|
|
* full record.
|
|
*/
|
|
tprintf(sc->sc_ctty,
|
|
"tz%d: Partial Read of Variable Length Tape Block, expected %d read %d\n",
|
|
unit, bp->b_bcount - resid,
|
|
bp->b_bcount);
|
|
bp->b_resid = 0;
|
|
break;
|
|
}
|
|
if (sp->fileMark)
|
|
sc->sc_flags |= TZF_SEENEOF;
|
|
/*
|
|
* Attempting to read more than a record is
|
|
* OK. Just record how much was actually read.
|
|
*/
|
|
bp->b_flags &= ~B_ERROR;
|
|
bp->b_error = 0;
|
|
bp->b_resid = resid;
|
|
break;
|
|
|
|
case SCSI_CLASS7_UNIT_ATTN:
|
|
if (!(sc->sc_flags & TZF_OPEN))
|
|
break;
|
|
|
|
default:
|
|
prerr:
|
|
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 (!cold && 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_PHYS | 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.b_actf = bp;
|
|
sc->sc_errbuf.b_actb = bp->b_actb;
|
|
*bp->b_actb = &sc->sc_errbuf;
|
|
bp->b_actb = &sc->sc_errbuf.b_actf;
|
|
tzstart(unit);
|
|
return;
|
|
}
|
|
} else {
|
|
sc->sc_sense.status = status;
|
|
bp->b_resid = resid;
|
|
}
|
|
|
|
if (dp = bp->b_actf)
|
|
dp->b_actb = bp->b_actb;
|
|
else
|
|
sc->sc_tab.b_actb = bp->b_actb;
|
|
*bp->b_actb = dp;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
tzopen(dev, flags, type, p)
|
|
dev_t dev;
|
|
int flags, type;
|
|
struct proc *p;
|
|
{
|
|
register int unit = tzunit(dev);
|
|
register struct tz_softc *sc = &tz_softc[unit];
|
|
int error;
|
|
|
|
if (unit >= NTZ || sc->sc_sd == NULL)
|
|
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, 0);
|
|
while (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);
|
|
|
|
/*
|
|
* Try it again just to be sure and
|
|
* try to negotiate synchonous transfers.
|
|
*/
|
|
error = tzcommand(dev, SCSI_TEST_UNIT_READY, 0, 0, 0);
|
|
}
|
|
|
|
/* get the current mode settings */
|
|
error = tzcommand(dev, SCSI_MODE_SENSE, 0,
|
|
sc->sc_modelen, (caddr_t)&sc->sc_mode);
|
|
if (error)
|
|
return (error);
|
|
|
|
/* check for write protected tape */
|
|
if ((flags & FWRITE) && sc->sc_mode.writeprot) {
|
|
uprintf("tz%d: write protected\n", unit);
|
|
return (EACCES);
|
|
}
|
|
|
|
/* set record length */
|
|
switch (sc->sc_tapeid) {
|
|
case MT_ISAR:
|
|
case MT_ISHPDAT:
|
|
case MT_ISVIPER1:
|
|
sc->sc_blklen = 512;
|
|
break;
|
|
|
|
case MT_ISEXABYTE:
|
|
#if 0
|
|
if (minor(dev) & TZ_FIXEDBLK)
|
|
sc->sc_blklen = 1024;
|
|
else
|
|
sc->sc_blklen = st_exblklen;
|
|
#endif
|
|
break;
|
|
|
|
case MT_ISPYTHON:
|
|
case MT_ISMFOUR:
|
|
case MT_ISTK50:
|
|
sc->sc_blklen = 0;
|
|
break;
|
|
|
|
default:
|
|
sc->sc_blklen = (sc->sc_mode.block_size2 << 16) |
|
|
(sc->sc_mode.block_size1 << 8) | sc->sc_mode.block_size0;
|
|
}
|
|
|
|
/* save total number of blocks on tape */
|
|
sc->sc_numblks = (sc->sc_mode.blocks_2 << 16) |
|
|
(sc->sc_mode.blocks_1 << 8) | sc->sc_mode.blocks_0;
|
|
|
|
/* setup for mode select */
|
|
sc->sc_mode.len = 0;
|
|
sc->sc_mode.media = 0;
|
|
sc->sc_mode.bufferedMode = 1;
|
|
sc->sc_mode.blocks_0 = 0;
|
|
sc->sc_mode.blocks_1 = 0;
|
|
sc->sc_mode.blocks_2 = 0;
|
|
sc->sc_mode.block_size0 = sc->sc_blklen >> 16;
|
|
sc->sc_mode.block_size1 = sc->sc_blklen >> 8;
|
|
sc->sc_mode.block_size2 = sc->sc_blklen;
|
|
|
|
/* check for tape density changes */
|
|
switch (sc->sc_tapeid) {
|
|
case MT_ISAR:
|
|
if (minor(dev) & TZ_HIDENSITY)
|
|
sc->sc_mode.density = 0x5;
|
|
else {
|
|
if (flags & FWRITE) {
|
|
uprintf("Can only write QIC-24\n");
|
|
return (EIO);
|
|
}
|
|
sc->sc_mode.density = 0x4;
|
|
}
|
|
break;
|
|
|
|
case MT_ISMT02:
|
|
/*
|
|
* The tape density is set automatically when the tape
|
|
* is loaded. We only need to change it if we are writing.
|
|
*/
|
|
if (flags & FWRITE) {
|
|
if (minor(dev) & TZ_HIDENSITY)
|
|
sc->sc_mode.density = 0;
|
|
else
|
|
sc->sc_mode.density = 0x4;
|
|
}
|
|
break;
|
|
|
|
case MT_ISEXABYTE:
|
|
#if 0
|
|
if (minor(dev) & TZ_HIDENSITY)
|
|
uprintf("EXB-8200 density support only\n");
|
|
sc->sc_mode.vupb = (u_char)st_exvup;
|
|
sc->sc_mode.rsvd5 = 0;
|
|
sc->sc_mode.p5 = 0;
|
|
sc->sc_mode.motionthres = (u_char)st_exmotthr;
|
|
sc->sc_mode.reconthres = (u_char)st_exreconthr;
|
|
sc->sc_mode.gapthres = (u_char)st_exgapthr;
|
|
#endif
|
|
break;
|
|
|
|
case MT_ISHPDAT:
|
|
case MT_ISVIPER1:
|
|
case MT_ISPYTHON:
|
|
case MT_ISTK50:
|
|
if (minor(dev) & TZ_HIDENSITY)
|
|
uprintf("tz%d: Only one density supported\n", unit);
|
|
break;
|
|
|
|
case MT_ISMFOUR:
|
|
break; /* XXX could do density select? */
|
|
}
|
|
|
|
/* set the current mode settings */
|
|
error = tzcommand(dev, SCSI_MODE_SELECT, 0,
|
|
sc->sc_modelen, (caddr_t)&sc->sc_mode);
|
|
if (error)
|
|
return (error);
|
|
|
|
sc->sc_ctty = tprintf_open(p);
|
|
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)];
|
|
int error = 0;
|
|
|
|
if (!(sc->sc_flags & TZF_OPEN))
|
|
return (0);
|
|
if (flag == FWRITE ||
|
|
((flag & FWRITE) && (sc->sc_flags & TZF_WRITTEN))) {
|
|
error = tzcommand(dev, SCSI_WRITE_EOF, 0, 1, 0);
|
|
#if 0
|
|
/*
|
|
* Cartridge tapes don't do double EOFs on EOT.
|
|
*/
|
|
switch (sc->sc_tapeid) {
|
|
case MT_ISAR:
|
|
case MT_ISMT02:
|
|
break;
|
|
|
|
default:
|
|
error = tzcommand(dev, SCSI_WRITE_EOF, 0, 1, 0);
|
|
if (minor(dev) & TZ_NOREWIND)
|
|
(void) tzcommand(dev, SCSI_SPACE, 0, -1, 0);
|
|
}
|
|
#endif
|
|
}
|
|
if ((minor(dev) & TZ_NOREWIND) == 0)
|
|
(void) tzcommand(dev, SCSI_REWIND, 0, 0, 0);
|
|
sc->sc_flags &= ~(TZF_OPEN | TZF_WRITTEN);
|
|
tprintf_close(sc->sc_ctty);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
tzread(dev, uio)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
{
|
|
register struct tz_softc *sc = &tz_softc[tzunit(dev)];
|
|
|
|
/*XXX*/ /* check for hardware write-protect? */
|
|
#if 0
|
|
if (sc->sc_type == SCSI_ROM_TYPE)
|
|
return (EROFS);
|
|
|
|
if (sc->sc_format_pid && sc->sc_format_pid != curproc->p_pid)
|
|
return (EPERM);
|
|
#endif
|
|
|
|
return (physio(tzstrategy, (struct buf *)0, dev,
|
|
B_READ, minphys, uio));
|
|
}
|
|
|
|
int
|
|
tzwrite(dev, uio)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
{
|
|
register struct tz_softc *sc = &tz_softc[tzunit(dev)];
|
|
|
|
#if 0
|
|
if (sc->sc_format_pid && sc->sc_format_pid != curproc->p_pid)
|
|
return (EPERM);
|
|
#endif
|
|
|
|
return (physio(tzstrategy, (struct buf *)0, dev,
|
|
B_WRITE, minphys, uio));
|
|
}
|
|
|
|
int
|
|
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;
|
|
count = mtop->mt_count;
|
|
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, 0));
|
|
|
|
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;
|
|
|
|
if (sc->sc_flags & TZF_SEENEOF) {
|
|
bp->b_resid = bp->b_bcount;
|
|
biodone(bp);
|
|
return;
|
|
}
|
|
bp->b_actf = NULL;
|
|
dp = &sc->sc_tab;
|
|
s = splbio();
|
|
bp->b_actb = dp->b_actb;
|
|
*dp->b_actb = bp;
|
|
dp->b_actb = &bp->b_actf;
|
|
if (dp->b_active == 0) {
|
|
dp->b_active = 1;
|
|
tzstart(unit);
|
|
}
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* Non-interrupt driven, non-dma dump routine.
|
|
*/
|
|
int
|
|
tzdump(dev)
|
|
dev_t dev;
|
|
{
|
|
/* Not implemented. */
|
|
return (ENXIO);
|
|
}
|
|
#endif
|