/* $NetBSD: tz.c,v 1.15 1997/01/31 02:00:59 thorpej 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.5 (Berkeley) 6/2/95 * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include int tzprobe __P(( void *sd /*struct pmax_scsi_device *sd*/)); int tzcommand __P((dev_t dev, int command, int code, int count, caddr_t data)); void tzstart __P((int unit)); void tzdone __P((int unit, int error, int resid, int status)); struct pmax_driver tzdriver = { "tz", tzprobe, (void (*) __P((struct ScsiCmd *cmd))) tzstart, tzdone, }; struct tz_softc { struct device sc_dev; /* new config glue */ 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 /* * Test to see if device is present. * Return true if found and initialized ok. */ int tzprobe(xxxsd) void *xxxsd; { register struct pmax_scsi_device *sd = xxxsd; register struct tz_softc *sc = &tz_softc[sd->sd_unit]; register int i; ScsiInquiryData inqbuf; if (sd->sd_unit >= NTZ) return (0); /* 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_lun; /* XXX set up device info */ /* XXX */ bzero(&sc->sc_dev, sizeof(sc->sc_dev)); /* XXX */ sprintf(sc->sc_dev.dv_xname, "tz%d", sd->sd_unit); /* XXX */ sc->sc_dev.dv_unit = sd->sd_unit; /* XXX */ sc->sc_dev.dv_class = DV_TAPE; /* XXX */ /* 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_lun, 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_lun, 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 lun %d", sd->sd_unit, sd->sd_cdriver->d_name, sd->sd_ctlr, sd->sd_drive, sd->sd_lun); 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, lun, 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); sc->sc_tapeid = 0; } } sd->sd_devp = &sc->sc_dev; /* XXX */ TAILQ_INSERT_TAIL(&alldevs, &sc->sc_dev, dv_list); /* XXX */ 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. */ int 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_lun; 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 %ld\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 transferred */ 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 = 0; 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 %ld\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 %ld read %ld\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_lun, 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) != 0) 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 */ int 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); } int tzclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { 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, iomode) dev_t dev; struct uio *uio; int iomode; { #if 0 /*XXX*/ /* check for hardware write-protect? */ register struct tz_softc *sc = &tz_softc[tzunit(dev)]; 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, iomode) dev_t dev; struct uio *uio; int iomode; { #if 0 register struct tz_softc *sc = &tz_softc[tzunit(dev)]; 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, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { register struct tz_softc *sc = &tz_softc[tzunit(dev)]; 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, blkno, va, size) dev_t dev; daddr_t blkno; caddr_t va; size_t size; { /* Not implemented. */ return (ENXIO); } #endif