diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /sys/arch/pmax/dev/tz.c |
initial import of NetBSD tree
Diffstat (limited to 'sys/arch/pmax/dev/tz.c')
-rw-r--r-- | sys/arch/pmax/dev/tz.c | 850 |
1 files changed, 850 insertions, 0 deletions
diff --git a/sys/arch/pmax/dev/tz.c b/sys/arch/pmax/dev/tz.c new file mode 100644 index 00000000000..b556191ad11 --- /dev/null +++ b/sys/arch/pmax/dev/tz.c @@ -0,0 +1,850 @@ +/* $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 |