/* * Copyright (c) 1992 OMRON Corporation. * Copyright (c) 1992 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * OMRON Corporation. * * %sccs.include.redist.c% * * @(#)st.c 7.4 (Berkeley) 10/11/92 */ /* * st.c -- TEAC MT-2ST/N60 SCSI TAPE UNIT Device Driver * remaked by A.Fujita, MAR-22-1992 */ /* * SCSI CCS (Command Command Set) disk driver. */ #include "st.h" #if NST > 0 #include #include #include #include #include #include #include #include #include #include extern int scsi_test_unit_rdy(); extern int scsi_request_sense(); extern int scsi_immed_command(); extern char *scsi_status(); extern int scgo(); extern void scfree(); char *sense_key(); int stinit(), ststrategy(), ststart(), stintr(); struct driver stdriver = { stinit, "st", ststart, (int (*)()) 0, stintr, (int (*)()) 0 }; struct st_softc { struct hp_device *sc_hd; struct scsi_queue sc_dq; int sc_flags; short sc_type; /* drive type */ short sc_punit; /* physical unit (scsi lun) */ tpr_t sc_ctty; } st_softc[NST]; /* softc flags */ #define STF_ALIVE 0x0001 #define STF_OPEN 0x0002 #define STF_WMODE 0x0004 #define STF_WRTTN 0x0008 #define STF_CMD 0x0010 #define STF_LEOT 0x0020 #define STF_MOVED 0x0040 u_char xsense_buff[60]; struct scsi_fmt_cdb st_read_cmd = { 6, CMD_READ }; struct scsi_fmt_cdb st_write_cmd = { 6, CMD_WRITE }; struct buf sttab[NST]; struct buf stbuf[NST]; #define stunit(x) (minor(x) & 3) #define stpunit(x) ((x) & 7) #define STDEV_NOREWIND 0x04 #define STRETRY 2 /* IO retry count */ struct st_iostat { int imax; int imin; int omax; int omin; }; struct st_iostat st_iostat[NST]; /* * Initialize */ int stinit(hd) register struct hp_device *hd; { register struct st_softc *sc = &st_softc[hd->hp_unit]; sc->sc_hd = hd; sc->sc_punit = stpunit(hd->hp_flags); sc->sc_type = stident(sc, hd); if (sc->sc_type < 0) return(0); sc->sc_dq.dq_ctlr = hd->hp_ctlr; sc->sc_dq.dq_unit = hd->hp_unit; sc->sc_dq.dq_slave = hd->hp_slave; sc->sc_dq.dq_driver = &stdriver; sc->sc_flags = STF_ALIVE; return(1); } static struct scsi_inquiry inqbuf; static struct scsi_fmt_cdb inq = { 6, CMD_INQUIRY, 0, 0, 0, sizeof(inqbuf), 0 }; int stident(sc, hd) struct st_softc *sc; struct hp_device *hd; { char idstr[32]; int unit; register int ctlr, slave; register int i, stat; register int tries = 10; ctlr = hd->hp_ctlr; slave = hd->hp_slave; unit = sc->sc_punit; /* * See if unit exists and is a disk then read block size & nblocks. */ while ((stat = scsi_immed_command(ctlr, slave, unit, &inq, (u_char *)&inqbuf, sizeof(inqbuf))) != 0) { if (stat < 0 || --tries < 0) return (-1); DELAY(1000); } switch (inqbuf.type) { case 1: /* tape */ break; default: /* not a disk */ printf("stident: inqbuf.type = %d\n", inqbuf.type); return (-1); } bcopy((caddr_t)&inqbuf.vendor_id, (caddr_t)idstr, 28); for (i = 27; i > 23; --i) if (idstr[i] != ' ') break; idstr[i+1] = 0; for (i = 23; i > 7; --i) if (idstr[i] != ' ') break; idstr[i+1] = 0; for (i = 7; i >= 0; --i) if (idstr[i] != ' ') break; idstr[i+1] = 0; printf("st%d: %s %s rev %s\n", hd->hp_unit, idstr, &idstr[8], &idstr[24]); return(inqbuf.type); } /* * Open */ int stopen(dev, flag, type, p) dev_t dev; int flag, type; struct proc *p; { register int unit = stunit(dev); register struct st_softc *sc = &st_softc[unit]; register struct scsi_xsense *sp = (struct scsi_xsense *) xsense_buff; int ctlr = sc->sc_dq.dq_ctlr; int slave = sc->sc_dq.dq_slave; int stat, retry = 9; if (unit >= NST || (sc->sc_flags & STF_ALIVE) == 0) return(-1); if (sc->sc_flags & STF_OPEN) return(-1); /* drive ready ? */ while ((stat = scsi_test_unit_rdy(ctlr, slave, 0)) != 0) { scsi_request_sense(ctlr, slave, 0, sp, 8); if (stat != STS_CHECKCOND) { printf("st%d: stopen: %s\n", scsi_status(stat)); return(EIO); } if (retry-- < 0) { printf("st%d: stopen: %s\n", sense_key(sp->key)); return(EIO); } DELAY(1000000); } sc->sc_ctty = tprintf_open(p); sc->sc_flags |= STF_OPEN; if (flag & FWRITE) sc->sc_flags |= STF_WMODE; sc->sc_flags &= ~STF_MOVED; return(0); } /*ARGSUSED*/ stclose(dev) dev_t dev; { register int unit = stunit(dev); register struct st_softc *sc = &st_softc[unit]; register struct scsi_xsense *sp = (struct scsi_xsense *) xsense_buff; int ctlr = sc->sc_hd->hp_ctlr; int slave = sc->sc_hd->hp_slave; int stat, retry = 9; if ((sc->sc_flags & (STF_WMODE|STF_WRTTN)) == (STF_WMODE|STF_WRTTN)) { st_write_EOF(dev); } if ((minor(dev) & STDEV_NOREWIND) == 0) { st_rewind(dev); } sc->sc_flags &= ~(STF_OPEN|STF_WMODE|STF_WRTTN); tprintf_close(sc->sc_ctty); return(0); } /* * Strategy */ int ststrategy(bp) register struct buf *bp; { register int unit = stunit(bp->b_dev); register struct buf *dp = &sttab[unit]; int s; bp->av_forw = NULL; 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; stustart(unit); } splx(s); } int stustart(unit) register int unit; { register struct st_softc *sc = &st_softc[unit]; register struct hp_device *hp = sc->sc_hd; register struct scsi_queue *dq = &sc->sc_dq; register struct buf *bp = sttab[unit].b_actf; register struct scsi_fmt_cdb *cmd; long nblks; cmd = bp->b_flags & B_READ ? &st_read_cmd : &st_write_cmd; cmd->cdb[1] = 1; /* unknown setup */ if (bp->b_flags & B_READ) sc->sc_flags &= ~STF_WRTTN; else sc->sc_flags |= STF_WRTTN; nblks = bp->b_bcount >> DEV_BSHIFT; if (bp->b_bcount % DEV_BSIZE) { tprintf(sc->sc_ctty, "st%d: I/O not block aligned %d/%ld\n", unit, DEV_BSIZE, bp->b_bcount); bp->b_flags |= B_ERROR; bp->b_error = EIO; sttab[unit].b_errcnt = 0; sttab[unit].b_actf = bp->b_actf; bp->b_resid = 0; biodone(bp); if (sttab[unit].b_actf) { stustart(unit); } else { sttab[unit].b_active = 0; } } *(u_char *)(&cmd->cdb[2]) = (u_char) (nblks >> 16); *(u_char *)(&cmd->cdb[3]) = (u_char) (nblks >> 8); *(u_char *)(&cmd->cdb[4]) = (u_char) nblks; cmd->cdb[5] = 0; /* unknown setup */ sc->sc_flags |= STF_MOVED; dq->dq_cdb = cmd; dq->dq_bp = bp; dq->dq_flags = 0; /* No Disconnect */ if (screq(dq)) ststart(unit); } int ststart(unit) register int unit; { register struct st_softc *sc = &st_softc[unit]; register struct hp_device *hp = sc->sc_hd; scstart(hp->hp_ctlr); } /* * Interrupt */ char * sense_key(key) int key; { if (key == 0) return("No Sense"); else if (key == 2) return("Not Ready"); else if (key == 3) return("Medium Error"); else if (key == 4) return("Hardware Error"); else if (key == 5) return("Illegal Request"); else if (key == 6) return("Unit Attention"); else if (key == 7) return("Data Protect"); else if (key == 8) return("No Data"); else if (key == 11) return("Aborted Command"); else if (key == 13) return("Volume Overflow"); else return("Unknown Error"); } int stintr(unit, stat) register int unit; int stat; { register struct st_softc *sc = &st_softc[unit]; register struct scsi_xsense *xp = (struct scsi_xsense *) xsense_buff; register struct scsi_queue *dq = &sc->sc_dq; register struct buf *bp = dq->dq_bp; int ctlr = dq->dq_ctlr; int slave = dq->dq_slave; if (bp->b_flags & B_READ) { st_iostat[unit].imin = min(dq->dq_imin, st_iostat[unit].imin); if (dq->dq_imax > st_iostat[unit].imax) { st_iostat[unit].imax = dq->dq_imax; #ifdef ST_IOSTAT printf("stintr: st%d INPUT MAX = %d, MIN = %d\n", unit, st_iostat[unit].imax, st_iostat[unit].imin); #endif } } else { st_iostat[unit].omin = min(dq->dq_omin, st_iostat[unit].omin); if (dq->dq_omax > st_iostat[unit].omax) { st_iostat[unit].omax = dq->dq_omax; #ifdef ST_IOSTAT printf("stintr: st%d OUTPUT MAX = %d, MIN = %d\n", unit, st_iostat[unit].omax, st_iostat[unit].omin); #endif } } if (stat < 0) { bp->b_flags |= B_ERROR; bp->b_error = EIO; goto done; } switch (stat) { /* scsi command completed ok */ case 0: bp->b_resid = 0; break; /* more status */ case STS_CHECKCOND: scsi_request_sense(ctlr, slave, 0, xp, 8); #ifdef DEBUG printf("stintr: xsense_buff[0] = 0x%s\n", hexstr(xsense_buff[0], 2)); printf("stintr: xsense_buff[2] = 0x%s\n", hexstr(xsense_buff[2], 2)); printf("stintr: Sense Key = [%s]\n", sense_key(xp->key)); #endif if (xp->valid) { bp->b_resid = (u_long)((xp->info1 << 24) | (xp->info2 << 16) | (xp->info3 << 8) | (xp->info4)); bp->b_resid <<= DEV_BSHIFT; } if (xp->filemark) { /* End of File */ tprintf(sc->sc_ctty, "st%d: End of File\n", unit); bp->b_flags |= B_ERROR; bp->b_error = EIO; break; } if (xp->key) { tprintf(sc->sc_ctty, "st%d: %s\n", unit, sense_key(xp->key)); bp->b_flags |= B_ERROR; bp->b_error = EIO; break; } if (xp->eom) { /* End of TAPE */ tprintf(sc->sc_ctty, "st%d: End of Tape\n", unit); bp->b_flags |= B_ERROR; bp->b_error = ENOSPC; break; } tprintf(sc->sc_ctty, "st%d: unknown scsi error\n", unit); bp->b_flags |= B_ERROR; bp->b_error = EIO; break; default: printf("st%d: stintr unknown stat 0x%x\n", unit, stat); break; } done: sttab[unit].b_errcnt = 0; sttab[unit].b_actf = bp->b_actf; bp->b_resid = 0; biodone(bp); scfree(&sc->sc_dq); if (sttab[unit].b_actf) { stustart(unit); } else { sttab[unit].b_active = 0; } } /* * RAW Device Routines */ stread(dev, uio) dev_t dev; struct uio *uio; { int unit = stunit(dev); return(physio(ststrategy, &stbuf[unit], dev, B_READ, minphys, uio)); } stwrite(dev, uio) dev_t dev; struct uio *uio; { int unit = stunit(dev); return(physio(ststrategy, &stbuf[unit], dev, B_WRITE, minphys, uio)); } int stioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { return(ENXIO); } struct scsi_fmt_cdb st_cmd; st_rewind(dev) dev_t dev; { register int unit = stunit(dev); register struct st_softc *sc = &st_softc[unit]; register struct scsi_fmt_cdb *cdb = &st_cmd; register struct scsi_xsense *sp = (struct scsi_xsense *) xsense_buff; int ctlr, slave, stat; int retry = 9; ctlr = sc->sc_hd->hp_ctlr; slave = sc->sc_hd->hp_slave; cdb->len = 6; cdb->cdb[0] = CMD_REWIND; cdb->cdb[1] = 1; /* command finished soon */ cdb->cdb[2] = 0; cdb->cdb[3] = 0; cdb->cdb[4] = 0; cdb->cdb[5] = 0; /* unknown setup */ rewind: stat = scsi_immed_command(ctlr, slave, 0, cdb, (char *) 0, 0); if (stat == 0) { return(1); } else { printf("st: rewind error\n"); scsi_request_sense(ctlr, slave, 0, sp, 8); printf("st: status = 0x%x, sens key = 0x%x\n", stat, sp->key); if (retry > 0) { DELAY(1000000); retry--; goto rewind; } return(0); } } st_write_EOF(dev) dev_t dev; { register int unit = stunit(dev); register struct st_softc *sc = &st_softc[unit]; register struct scsi_fmt_cdb *cdb = &st_cmd; int ctlr, slave, stat; int marks = 1; ctlr = sc->sc_hd->hp_ctlr; slave = sc->sc_hd->hp_slave; cdb->len = 6; cdb->cdb[0] = CMD_WRITE_FILEMARK; cdb->cdb[1] = 0; cdb->cdb[2] = 0; cdb->cdb[3] = 0; cdb->cdb[4] = marks; cdb->cdb[5] = 0; /* unknown setup */ stat = scsi_immed_command(ctlr, slave, 0, cdb, (char *) 0, 0); if (stat == 0) return(1); printf("st: write EOF error\n"); return(0); } /* * Dump */ int stdump(dev) dev_t dev; { } #endif