/* $NetBSD: ct.c,v 1.64 2023/04/21 23:01:59 tsutsui Exp $ */ /*- * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Copyright (c) 1982, 1990, 1993 * The Regents of the University of California. All rights reserved. * * 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. 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. * * @(#)ct.c 8.2 (Berkeley) 1/12/94 */ /* * CS80 cartridge tape driver (9144, 88140, 9145) * * Reminder: * C_CC bit (character count option) when used in the CS/80 command * 'set options' will cause the tape not to stream. * * TODO: * make filesystem compatible * make block mode work according to mtio(4) spec. (if possible) * merge with cs80 disk driver * finish support of 9145 */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: ct.c,v 1.64 2023/04/21 23:01:59 tsutsui Exp $"); #include <sys/param.h> #include <sys/systm.h> #include <sys/buf.h> #include <sys/bufq.h> #include <sys/conf.h> #include <sys/device.h> #include <sys/ioctl.h> #include <sys/mtio.h> #include <sys/proc.h> #include <sys/tprintf.h> #include <hp300/dev/hpibvar.h> #include <hp300/dev/ctreg.h> #include "ioconf.h" /* number of eof marks to remember */ #define EOFS 128 struct ct_softc { device_t sc_dev; int sc_slave; /* HP-IB slave ID */ int sc_punit; /* physical unit */ struct ct_iocmd sc_ioc; struct ct_rscmd sc_rsc; struct ct_stat sc_stat; struct ct_ssmcmd sc_ssmc; struct ct_srcmd sc_src; struct ct_soptcmd sc_soptc; struct ct_ulcmd sc_ul; struct ct_wfmcmd sc_wfm; struct ct_clearcmd sc_clear; struct bufq_state *sc_tab; int sc_active; struct buf *sc_bp; struct buf sc_bufstore; /* XXX */ int sc_blkno; int sc_cmd; int sc_resid; char *sc_addr; int sc_flags; short sc_type; tpr_t sc_tpr; struct hpibqueue sc_hq; /* entry on hpib job queue */ int sc_eofp; int sc_eofs[EOFS]; }; /* flags */ #define CTF_OPEN 0x01 #define CTF_ALIVE 0x02 #define CTF_WRT 0x04 #define CTF_CMD 0x08 #define CTF_IO 0x10 #define CTF_BEOF 0x20 #define CTF_AEOF 0x40 #define CTF_EOT 0x80 #define CTF_STATWAIT 0x100 #define CTF_CANSTREAM 0x200 #define CTF_WRTTN 0x400 static int ctmatch(device_t, cfdata_t, void *); static void ctattach(device_t, device_t, void *); CFATTACH_DECL_NEW(ct, sizeof(struct ct_softc), ctmatch, ctattach, NULL, NULL); static dev_type_open(ctopen); static dev_type_close(ctclose); static dev_type_read(ctread); static dev_type_write(ctwrite); static dev_type_ioctl(ctioctl); static dev_type_strategy(ctstrategy); const struct bdevsw ct_bdevsw = { .d_open = ctopen, .d_close = ctclose, .d_strategy = ctstrategy, .d_ioctl = ctioctl, .d_dump = nodump, .d_psize = nosize, .d_discard = nodiscard, .d_flag = D_TAPE }; const struct cdevsw ct_cdevsw = { .d_open = ctopen, .d_close = ctclose, .d_read = ctread, .d_write = ctwrite, .d_ioctl = ctioctl, .d_stop = nostop, .d_tty = notty, .d_poll = nopoll, .d_mmap = nommap, .d_kqfilter = nokqfilter, .d_discard = nodiscard, .d_flag = D_TAPE }; static int ctident(device_t, struct ct_softc *, struct hpibbus_attach_args *); static void ctreset(struct ct_softc *); static void ctaddeof(struct ct_softc *); static void ctustart(struct ct_softc *); static void cteof(struct ct_softc *, struct buf *); static void ctdone(struct ct_softc *, struct buf *); static void ctstart(void *); static void ctgo(void *); static void ctintr(void *); static void ctcommand(dev_t, int, int); static const struct ctinfo { short hwid; short punit; const char *desc; } ctinfo[] = { { CT7946ID, 1, "7946A" }, { CT7912PID, 1, "7912P" }, { CT7914PID, 1, "7914P" }, { CT9144ID, 0, "9144" }, { CT9145ID, 0, "9145" }, { CT35401ID, 0, "35401A"}, }; static const int nctinfo = __arraycount(ctinfo); #define CT_NOREW 4 #define CT_STREAM 8 #define UNIT(x) (minor(x) & 3) #define ctpunit(x) ((x) & 7) #ifdef DEBUG int ctdebug = 0; #define CDB_FILES 0x01 #define CT_BSF 0x02 #endif static int ctmatch(device_t parent, cfdata_t cf, void *aux) { struct hpibbus_attach_args *ha = aux; return ctident(parent, NULL, ha); } static void ctattach(device_t parent, device_t self, void *aux) { struct ct_softc *sc = device_private(self); struct hpibbus_attach_args *ha = aux; sc->sc_dev = self; if (ctident(parent, sc, ha) == 0) { aprint_error(": didn't respond to describe command!\n"); return; } sc->sc_slave = ha->ha_slave; sc->sc_punit = ha->ha_punit; bufq_alloc(&sc->sc_tab, "fcfs", 0); /* Initialize hpib job queue entry. */ sc->sc_hq.hq_softc = sc; sc->sc_hq.hq_slave = sc->sc_slave; sc->sc_hq.hq_start = ctstart; sc->sc_hq.hq_go = ctgo; sc->sc_hq.hq_intr = ctintr; ctreset(sc); sc->sc_flags |= CTF_ALIVE; } static int ctident(device_t parent, struct ct_softc *sc, struct hpibbus_attach_args *ha) { struct cs80_describe desc; u_char stat, cmd[3]; char name[7]; int i, id, n, type, canstream; type = canstream = 0; /* Verify that we have a CS80 device. */ if ((ha->ha_id & 0x200) == 0) return 0; /* Is it one of the tapes we support? */ for (id = 0; id < nctinfo; id++) if (ha->ha_id == ctinfo[id].hwid && ha->ha_punit == ctinfo[id].punit) break; if (id == nctinfo) return 0; /* * So far, so good. Get drive parameters. Note command * is always issued to unit 0. */ cmd[0] = C_SUNIT(0); cmd[1] = C_SVOL(0); cmd[2] = C_DESC; hpibsend(device_unit(parent), ha->ha_slave, C_CMD, cmd, sizeof(cmd)); hpibrecv(device_unit(parent), ha->ha_slave, C_EXEC, &desc, sizeof(desc)); hpibrecv(device_unit(parent), ha->ha_slave, C_QSTAT, &stat, sizeof(stat)); memset(name, 0, sizeof(name)); if (stat == 0) { n = desc.d_name; for (i = 5; i >= 0; i--) { name[i] = (n & 0xf) + '0'; n >>= 4; } } switch (ha->ha_id) { case CT7946ID: if (memcmp(name, "079450", 6) == 0) return 0; /* not really a 7946 */ /* fall into... */ case CT9144ID: case CT9145ID: case CT35401ID: type = CT9144; canstream = 1; break; case CT7912PID: case CT7914PID: type = CT88140; break; } if (sc != NULL) { sc->sc_type = type; sc->sc_flags = canstream ? CTF_CANSTREAM : 0; aprint_normal(": %s %stape\n", ctinfo[id].desc, canstream ? "streaming " : ""); } return 1; } static void ctreset(struct ct_softc *sc) { int ctlr, slave; uint8_t stat; ctlr = device_unit(device_parent(sc->sc_dev)); slave = sc->sc_slave; sc->sc_clear.unit = C_SUNIT(sc->sc_punit); sc->sc_clear.cmd = C_CLEAR; hpibsend(ctlr, slave, C_TCMD, &sc->sc_clear, sizeof(sc->sc_clear)); hpibswait(ctlr, slave); hpibrecv(ctlr, slave, C_QSTAT, &stat, sizeof(stat)); sc->sc_src.unit = C_SUNIT(CTCTLR); sc->sc_src.nop = C_NOP; sc->sc_src.cmd = C_SREL; sc->sc_src.param = C_REL; hpibsend(ctlr, slave, C_CMD, &sc->sc_src, sizeof(sc->sc_src)); hpibswait(ctlr, slave); hpibrecv(ctlr, slave, C_QSTAT, &stat, sizeof(stat)); sc->sc_ssmc.unit = C_SUNIT(sc->sc_punit); sc->sc_ssmc.cmd = C_SSM; sc->sc_ssmc.refm = REF_MASK; sc->sc_ssmc.fefm = FEF_MASK; sc->sc_ssmc.aefm = AEF_MASK; sc->sc_ssmc.iefm = IEF_MASK; hpibsend(ctlr, slave, C_CMD, &sc->sc_ssmc, sizeof(sc->sc_ssmc)); hpibswait(ctlr, slave); hpibrecv(ctlr, slave, C_QSTAT, &stat, sizeof(stat)); sc->sc_soptc.unit = C_SUNIT(sc->sc_punit); sc->sc_soptc.nop = C_NOP; sc->sc_soptc.cmd = C_SOPT; sc->sc_soptc.opt = C_SPAR; hpibsend(ctlr, slave, C_CMD, &sc->sc_soptc, sizeof(sc->sc_soptc)); hpibswait(ctlr, slave); hpibrecv(ctlr, slave, C_QSTAT, &stat, sizeof(stat)); } static int ctopen(dev_t dev, int flag, int type, struct lwp *l) { struct ct_softc *sc; uint8_t stat; int cc, ctlr, slave; sc = device_lookup_private(&ct_cd, UNIT(dev)); if (sc == NULL) return ENXIO; if ((sc->sc_flags & CTF_ALIVE) == 0) return ENXIO; if (sc->sc_flags & CTF_OPEN) return EBUSY; ctlr = device_unit(device_parent(sc->sc_dev)); slave = sc->sc_slave; sc->sc_soptc.unit = C_SUNIT(sc->sc_punit); sc->sc_soptc.nop = C_NOP; sc->sc_soptc.cmd = C_SOPT; if ((dev & CT_STREAM) && (sc->sc_flags & CTF_CANSTREAM)) sc->sc_soptc.opt = C_SPAR | C_IMRPT; else sc->sc_soptc.opt = C_SPAR; /* * Check the return of hpibsend() and hpibswait(). * Drive could be loading/unloading a tape. If not checked, * driver hangs. */ cc = hpibsend(ctlr, slave, C_CMD, &sc->sc_soptc, sizeof(sc->sc_soptc)); if (cc != sizeof(sc->sc_soptc)) return EBUSY; hpibswait(ctlr, slave); cc = hpibrecv(ctlr, slave, C_QSTAT, &stat, sizeof(stat)); if (cc != sizeof(stat)) return EBUSY; sc->sc_tpr = tprintf_open(l->l_proc); sc->sc_flags |= CTF_OPEN; return 0; } static int ctclose(dev_t dev, int flag, int fmt, struct lwp *l) { struct ct_softc *sc = device_lookup_private(&ct_cd,UNIT(dev)); if ((sc->sc_flags & (CTF_WRT|CTF_WRTTN)) == (CTF_WRT|CTF_WRTTN) && (sc->sc_flags & CTF_EOT) == 0 ) { /* XXX return error if EOT ?? */ ctcommand(dev, MTWEOF, 2); ctcommand(dev, MTBSR, 1); if (sc->sc_eofp == EOFS - 1) sc->sc_eofs[EOFS - 1]--; else sc->sc_eofp--; #ifdef DEBUG if(ctdebug & CT_BSF) printf("%s: ctclose backup eofs prt %d blk %d\n", device_xname(sc->sc_dev), sc->sc_eofp, sc->sc_eofs[sc->sc_eofp]); #endif } if ((minor(dev) & CT_NOREW) == 0) ctcommand(dev, MTREW, 1); sc->sc_flags &= ~(CTF_OPEN | CTF_WRT | CTF_WRTTN); tprintf_close(sc->sc_tpr); #ifdef DEBUG if (ctdebug & CDB_FILES) printf("ctclose: flags %x\n", sc->sc_flags); #endif return 0; /* XXX */ } static void ctcommand(dev_t dev, int cmd, int cnt) { struct ct_softc *sc = device_lookup_private(&ct_cd,UNIT(dev)); struct buf *bp = &sc->sc_bufstore; struct buf *nbp = 0; if (cmd == MTBSF && sc->sc_eofp == EOFS - 1) { cnt = sc->sc_eofs[EOFS - 1] - cnt; ctcommand(dev, MTREW, 1); ctcommand(dev, MTFSF, cnt); cnt = 2; cmd = MTBSR; } if (cmd == MTBSF && sc->sc_eofp - cnt < 0) { cnt = 1; cmd = MTREW; } sc->sc_flags |= CTF_CMD; sc->sc_bp = bp; sc->sc_cmd = cmd; bp->b_dev = dev; if (cmd == MTFSF) { nbp = (struct buf *)geteblk(MAXBSIZE); bp->b_data = nbp->b_data; bp->b_bcount = MAXBSIZE; } while (cnt-- > 0) { bp->b_cflags = BC_BUSY; if (cmd == MTBSF) { sc->sc_blkno = sc->sc_eofs[sc->sc_eofp]; sc->sc_eofp--; #ifdef DEBUG if (ctdebug & CT_BSF) printf("%s: backup eof pos %d blk %d\n", device_xname(sc->sc_dev), sc->sc_eofp, sc->sc_eofs[sc->sc_eofp]); #endif } ctstrategy(bp); biowait(bp); } bp->b_flags = 0; sc->sc_flags &= ~CTF_CMD; if (nbp) brelse(nbp, 0); } static void ctstrategy(struct buf *bp) { int s; struct ct_softc *sc; sc = device_lookup_private(&ct_cd, UNIT(bp->b_dev)); s = splbio(); bufq_put(sc->sc_tab, bp); if (sc->sc_active == 0) { sc->sc_active = 1; ctustart(sc); } splx(s); } static void ctustart(struct ct_softc *sc) { struct buf *bp; bp = bufq_peek(sc->sc_tab); sc->sc_addr = bp->b_data; sc->sc_resid = bp->b_bcount; if (hpibreq(device_parent(sc->sc_dev), &sc->sc_hq)) ctstart(sc); } static void ctstart(void *arg) { struct ct_softc *sc = arg; struct buf *bp; int i, ctlr, slave; ctlr = device_unit(device_parent(sc->sc_dev)); slave = sc->sc_slave; bp = bufq_peek(sc->sc_tab); if ((sc->sc_flags & CTF_CMD) && sc->sc_bp == bp) { switch(sc->sc_cmd) { case MTFSF: bp->b_flags |= B_READ; goto mustio; case MTBSF: goto gotaddr; case MTOFFL: sc->sc_blkno = 0; sc->sc_ul.unit = C_SUNIT(sc->sc_punit); sc->sc_ul.cmd = C_UNLOAD; hpibsend(ctlr, slave, C_CMD, &sc->sc_ul, sizeof(sc->sc_ul)); break; case MTWEOF: sc->sc_blkno++; sc->sc_flags |= CTF_WRT; sc->sc_wfm.unit = C_SUNIT(sc->sc_punit); sc->sc_wfm.cmd = C_WFM; hpibsend(ctlr, slave, C_CMD, &sc->sc_wfm, sizeof(sc->sc_wfm)); ctaddeof(sc); break; case MTBSR: sc->sc_blkno--; goto gotaddr; case MTFSR: sc->sc_blkno++; goto gotaddr; case MTREW: sc->sc_blkno = 0; #ifdef DEBUG if(ctdebug & CT_BSF) printf("%s: clearing eofs\n", device_xname(sc->sc_dev)); #endif for (i=0; i<EOFS; i++) sc->sc_eofs[i] = 0; sc->sc_eofp = 0; gotaddr: sc->sc_ioc.saddr = C_SADDR; sc->sc_ioc.addr0 = 0; sc->sc_ioc.addr = sc->sc_blkno; sc->sc_ioc.unit = C_SUNIT(sc->sc_punit); sc->sc_ioc.nop2 = C_NOP; sc->sc_ioc.slen = C_SLEN; sc->sc_ioc.len = 0; sc->sc_ioc.nop3 = C_NOP; sc->sc_ioc.cmd = C_READ; hpibsend(ctlr, slave, C_CMD, &sc->sc_ioc, sizeof(sc->sc_ioc)); break; } } else { mustio: if ((bp->b_flags & B_READ) && sc->sc_flags & (CTF_BEOF|CTF_EOT)) { #ifdef DEBUG if (ctdebug & CDB_FILES) printf("ctstart: before flags %x\n", sc->sc_flags); #endif if (sc->sc_flags & CTF_BEOF) { sc->sc_flags &= ~CTF_BEOF; sc->sc_flags |= CTF_AEOF; #ifdef DEBUG if (ctdebug & CDB_FILES) printf("ctstart: after flags %x\n", sc->sc_flags); #endif } bp->b_resid = bp->b_bcount; ctdone(sc, bp); return; } sc->sc_flags |= CTF_IO; sc->sc_ioc.unit = C_SUNIT(sc->sc_punit); sc->sc_ioc.saddr = C_SADDR; sc->sc_ioc.addr0 = 0; sc->sc_ioc.addr = sc->sc_blkno; sc->sc_ioc.nop2 = C_NOP; sc->sc_ioc.slen = C_SLEN; sc->sc_ioc.len = sc->sc_resid; sc->sc_ioc.nop3 = C_NOP; if (bp->b_flags & B_READ) sc->sc_ioc.cmd = C_READ; else { sc->sc_ioc.cmd = C_WRITE; sc->sc_flags |= (CTF_WRT | CTF_WRTTN); } hpibsend(ctlr, slave, C_CMD, &sc->sc_ioc, sizeof(sc->sc_ioc)); } hpibawait(ctlr); } static void ctgo(void *arg) { struct ct_softc *sc = arg; struct buf *bp; int rw; bp = bufq_peek(sc->sc_tab); rw = bp->b_flags & B_READ; hpibgo(device_unit(device_parent(sc->sc_dev)), sc->sc_slave, C_EXEC, sc->sc_addr, sc->sc_resid, rw, rw != 0); } /* * Hideous grue to handle EOF/EOT (mostly for reads) */ static void cteof(struct ct_softc *sc, struct buf *bp) { long blks; /* * EOT on a write is an error. */ if ((bp->b_flags & B_READ) == 0) { bp->b_resid = bp->b_bcount; bp->b_error = ENOSPC; sc->sc_flags |= CTF_EOT; return; } /* * Use returned block position to determine how many blocks * we really read and update b_resid. */ blks = sc->sc_stat.c_blk - sc->sc_blkno - 1; #ifdef DEBUG if (ctdebug & CDB_FILES) printf("cteof: bc %d oblk %d nblk %ld read %ld, resid %ld\n", bp->b_bcount, sc->sc_blkno, sc->sc_stat.c_blk, blks, bp->b_bcount - CTKTOB(blks)); #endif if (blks == -1) { /* 9145 on EOF does not change sc_stat.c_blk */ blks = 0; sc->sc_blkno++; } else { sc->sc_blkno = sc->sc_stat.c_blk; } bp->b_resid = bp->b_bcount - CTKTOB(blks); /* * If we are at physical EOV or were after an EOF, * we are now at logical EOT. */ if ((sc->sc_stat.c_aef & AEF_EOV) || (sc->sc_flags & CTF_AEOF)) { sc->sc_flags |= CTF_EOT; sc->sc_flags &= ~(CTF_AEOF|CTF_BEOF); } /* * If we were before an EOF or we have just completed a FSF, * we are now after EOF. */ else if ((sc->sc_flags & CTF_BEOF) || ((sc->sc_flags & CTF_CMD) && sc->sc_cmd == MTFSF)) { sc->sc_flags |= CTF_AEOF; sc->sc_flags &= ~CTF_BEOF; } /* * Otherwise if we read something we are now before EOF * (and no longer after EOF). */ else if (blks) { sc->sc_flags |= CTF_BEOF; sc->sc_flags &= ~CTF_AEOF; } /* * Finally, if we didn't read anything we just passed an EOF */ else sc->sc_flags |= CTF_AEOF; #ifdef DEBUG if (ctdebug & CDB_FILES) printf("cteof: leaving flags %x\n", sc->sc_flags); #endif } static void ctintr(void *arg) { struct ct_softc *sc = arg; struct buf *bp; uint8_t stat; int ctlr, slave; ctlr = device_unit(device_parent(sc->sc_dev)); slave = sc->sc_slave; bp = bufq_peek(sc->sc_tab); if (bp == NULL) { printf("%s: bp == NULL\n", device_xname(sc->sc_dev)); return; } if (sc->sc_flags & CTF_IO) { sc->sc_flags &= ~CTF_IO; if (hpibustart(ctlr)) ctgo(sc); return; } if ((sc->sc_flags & CTF_STATWAIT) == 0) { if (hpibpptest(ctlr, slave) == 0) { sc->sc_flags |= CTF_STATWAIT; hpibawait(ctlr); return; } } else sc->sc_flags &= ~CTF_STATWAIT; hpibrecv(ctlr, slave, C_QSTAT, &stat, 1); #ifdef DEBUG if (ctdebug & CDB_FILES) printf("ctintr: before flags %x\n", sc->sc_flags); #endif if (stat) { sc->sc_rsc.unit = C_SUNIT(sc->sc_punit); sc->sc_rsc.cmd = C_STATUS; hpibsend(ctlr, slave, C_CMD, &sc->sc_rsc, sizeof(sc->sc_rsc)); hpibrecv(ctlr, slave, C_EXEC, &sc->sc_stat, sizeof(sc->sc_stat)); hpibrecv(ctlr, slave, C_QSTAT, &stat, 1); #ifdef DEBUG if (ctdebug & CDB_FILES) printf("ctintr: return stat 0x%x, A%x F%x blk %ld\n", stat, sc->sc_stat.c_aef, sc->sc_stat.c_fef, sc->sc_stat.c_blk); #endif if (stat == 0) { if (sc->sc_stat.c_aef & (AEF_EOF | AEF_EOV)) { cteof(sc, bp); ctaddeof(sc); goto done; } if (sc->sc_stat.c_fef & FEF_PF) { ctreset(sc); ctstart(sc); return; } if (sc->sc_stat.c_fef & FEF_REXMT) { ctstart(sc); return; } if (sc->sc_stat.c_aef & 0x5800) { if (sc->sc_stat.c_aef & 0x4000) tprintf(sc->sc_tpr, "%s: uninitialized media\n", device_xname(sc->sc_dev)); if (sc->sc_stat.c_aef & 0x1000) tprintf(sc->sc_tpr, "%s: not ready\n", device_xname(sc->sc_dev)); if (sc->sc_stat.c_aef & 0x0800) tprintf(sc->sc_tpr, "%s: write protect\n", device_xname(sc->sc_dev)); } else { printf("%s err: v%d u%d ru%d bn%ld, ", device_xname(sc->sc_dev), (sc->sc_stat.c_vu >> 4) & 0xF, sc->sc_stat.c_vu & 0xF, sc->sc_stat.c_pend, sc->sc_stat.c_blk); printf("R0x%x F0x%x A0x%x I0x%x\n", sc->sc_stat.c_ref, sc->sc_stat.c_fef, sc->sc_stat.c_aef, sc->sc_stat.c_ief); } } else printf("%s: request status failed\n", device_xname(sc->sc_dev)); bp->b_error = EIO; goto done; } else bp->b_resid = 0; if (sc->sc_flags & CTF_CMD) { switch (sc->sc_cmd) { case MTFSF: sc->sc_flags &= ~(CTF_BEOF|CTF_AEOF); sc->sc_blkno += CTBTOK(sc->sc_resid); ctstart(sc); return; case MTBSF: sc->sc_flags &= ~(CTF_AEOF|CTF_BEOF|CTF_EOT); break; case MTBSR: sc->sc_flags &= ~CTF_BEOF; if (sc->sc_flags & CTF_EOT) { sc->sc_flags |= CTF_AEOF; sc->sc_flags &= ~CTF_EOT; } else if (sc->sc_flags & CTF_AEOF) { sc->sc_flags |= CTF_BEOF; sc->sc_flags &= ~CTF_AEOF; } break; case MTWEOF: sc->sc_flags &= ~CTF_BEOF; if (sc->sc_flags & (CTF_AEOF|CTF_EOT)) { sc->sc_flags |= CTF_EOT; sc->sc_flags &= ~CTF_AEOF; } else sc->sc_flags |= CTF_AEOF; break; case MTREW: case MTOFFL: sc->sc_flags &= ~(CTF_BEOF|CTF_AEOF|CTF_EOT); break; } } else { sc->sc_flags &= ~CTF_AEOF; sc->sc_blkno += CTBTOK(sc->sc_resid); } done: #ifdef DEBUG if (ctdebug & CDB_FILES) printf("ctintr: after flags %x\n", sc->sc_flags); #endif ctdone(sc, bp); } static void ctdone(struct ct_softc *sc, struct buf *bp) { (void)bufq_get(sc->sc_tab); biodone(bp); hpibfree(device_parent(sc->sc_dev), &sc->sc_hq); if (bufq_peek(sc->sc_tab) == NULL) { sc->sc_active = 0; return; } ctustart(sc); } static int ctread(dev_t dev, struct uio *uio, int flags) { return physio(ctstrategy, NULL, dev, B_READ, minphys, uio); } static int ctwrite(dev_t dev, struct uio *uio, int flags) { /* XXX: check for hardware write-protect? */ return physio(ctstrategy, NULL, dev, B_WRITE, minphys, uio); } static int ctioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) { struct mtop *op; int cnt; switch (cmd) { case MTIOCTOP: op = (struct mtop *)data; switch(op->mt_op) { case MTWEOF: case MTFSF: case MTBSR: case MTBSF: case MTFSR: cnt = op->mt_count; break; case MTREW: case MTOFFL: cnt = 1; break; default: return EINVAL; } ctcommand(dev, op->mt_op, cnt); break; case MTIOCGET: break; default: return EINVAL; } return 0; } static void ctaddeof(struct ct_softc *sc) { if (sc->sc_eofp == EOFS - 1) sc->sc_eofs[EOFS - 1]++; else { sc->sc_eofp++; if (sc->sc_eofp == EOFS - 1) sc->sc_eofs[EOFS - 1] = EOFS; else /* save blkno */ sc->sc_eofs[sc->sc_eofp] = sc->sc_blkno - 1; } #ifdef DEBUG if (ctdebug & CT_BSF) printf("%s: add eof pos %d blk %d\n", device_xname(sc->sc_dev), sc->sc_eofp, sc->sc_eofs[sc->sc_eofp]); #endif }