+/*
+ * 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.1 (Berkeley) %G%
+ */
+
+/*
+ * 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 "param.h"
+#include "buf.h"
+#include "file.h"
+#include "proc.h"
+#include "mtio.h"
+#include "tprintf.h"
+
+#include "device.h"
+#include "scsireg.h"
+#include "scsivar.h"
+
+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