+
+#ifndef lint
+static char *sccsid = "@(#)tmscp.c 1.24 (ULTRIX) 1/21/86";
+#endif lint
+
+
+/************************************************************************
+ * *
+ * Copyright (c) 1985 by *
+ * Digital Equipment Corporation, Maynard, MA *
+ * *
+ * Permission to use, copy, modify, and distribute this software *
+ * and its documentation is hereby granted to licensees of the *
+ * Regents of the University of California pursuant to their *
+ * license agreement for the "Fourth Berkeley Software *
+ * Distribution". *
+ * *
+ * The information in this software is subject to change without *
+ * notice and should not be construed as a commitment by Digital *
+ * Equipment Corporation. Digital makes no representations *
+ * about suitability of this software for any purpose. It is *
+ * supplied "as is" without express or implied warranty. *
+ * *
+ * This software is not subject to any license of the American *
+ * Telephone and Telegraph Company. *
+ * *
+ ************************************************************************
+ *
+ * tmscp.c - TMSCP (TK50/TU81) tape device driver
+ *
+ * Modification History:
+ *
+ * 06-Jan-86 - afd
+ * Changed the probe routine to use DELAY (not TODR). This now
+ * works for MicroVAXen as well. This eliminates the busy-wait
+ * for MicroVAXen so a dead TK50 controller will not hang autoconf.
+ *
+ * 06-Dec-85 - afd
+ * Fixed a bug in density selection. The "set unit characteristics"
+ * command to select density, was clearing the "unit flags" field
+ * where the CACHE bit was for TU81-E. Now the unit's "format" and
+ * "unitflgs" are saved in tms_info struct. And are used on STUNT
+ * commands.
+ *
+ * 19-Oct-85 - afd
+ * Added support to the open routine to allow drives to be opened
+ * for low density (800 or 1600 bpi) use. When the slave routine
+ * initiates a "get-unit-char" cmd, the format menu for the unit
+ * is saved in the tms_info structure. The format menu is used in the
+ * start routine to select the proper low density.
+ *
+ * 02-Oct-85 - afd
+ * When a tmscp-type controller is initializing, it is possible for
+ * the sa reg to become 0 between states. Thus the init code in
+ * the interrupt routine had to be modified to reflect this.
+ *
+ * 21-Sep-85 - afd
+ * The TK50 declares a serious exception when a tape mark is encountered.
+ * This causes problems to dd (& other UN*X utilities). So a flag
+ * is set in the rsp() routine when a tape mark is encountered. If
+ * this flag is set, the start() routine appends the Clear Serious
+ * Exception modifier to the next command.
+ *
+ * 03-Sep-85 -- jaw
+ * messed up previous edit..
+ *
+ * 29-Aug-85 - jaw
+ * fixed bugs in 8200 and 750 buffered datapath handling.
+ *
+ * 06-Aug-85 - afd
+ * 1. When repositioning records or files, the count of items skipped
+ * does NOT HAVE to be returned by controllers (& the TU81 doesn't).
+ * So tmscprsp() had to be modified to stop reporting
+ * residual count errors on reposition commands.
+ *
+ * 2. Fixed bug in the open routine which allowed multiple opens.
+ *
+ * 18-Jul-85 - afd
+ * 1. Need to return status when mt status (or corresponding ioctl) is done.
+ * Save resid, flags, endcode & status in tmscprsp() routine (except on
+ * clear serious exception no-op). Return these fields when status
+ * ioctl is done (in tmscpcommand()). How they are returned:
+ * mt_resid = resid
+ * mt_dsreg = flags|endcode
+ * mt_erreg = status
+ *
+ * 2. Added latent support for enabling/disabling caching. This is
+ * handled along with all other ioctl commands.
+ *
+ * 3. Need to issue a no-op on unrecognized ioctl in tmscpstart(), since
+ * we have already commited to issuing a command at that point.
+ *
+ * 4. In tmscprsp() routine if encode is 0200 (invalid command issued);
+ * We need to: Unlink the buffer from the I/O wait queue,
+ * and signal iodone, so the higher level command can exit!
+ * Just as if it were a valid command.
+ *
+ * 11-jul-85 -- jaw
+ * fix bua/bda map registers.
+ *
+ * 19-Jun-85 -- jaw
+ * VAX8200 name change.
+ *
+ * 06-Jun-85 - jaw
+ * fixes for 8200.
+ *
+ * 9-Apr-85 - afd
+ * Added timeout code to the probe routine, so if the controller
+ * fails to init in 10 seconds we return failed status.
+ *
+ * 13-Mar-85 -jaw
+ * Changes for support of the VAX8200 were merged in.
+ *
+ * 27-Feb-85 -tresvik
+ * Changes for support of the VAX8600 were merged in.
+ *
+ */
+
+#include "tms.h"
+#define TMSCPDEVNUM (15) /* entry in bdevsw */
+#if NTMSCP > 0 || defined(BINARY)
+
+#include "../data/tmscp_data.c"
+
+/* Bits in minor device */
+#define TMSUNIT(dev) (minor(dev)&03)
+#define T_NOREWIND 04
+#define T_HIDENSITY 010
+
+/* Slave unit to controller mapping */
+#define TMSCPCTLR(dev) (utoctlr[TMSUNIT(dev)])
+
+/*
+ * Internal (ioctl) command codes (these must also be declared in the
+ * tmscpioctl routine). These correspond to ioctls in mtio.h
+ */
+#define TMS_WRITM 0 /* write tape mark */
+#define TMS_FSF 1 /* forward space file */
+#define TMS_BSF 2 /* backward space file */
+#define TMS_FSR 3 /* forward space record */
+#define TMS_BSR 4 /* backward space record */
+#define TMS_REW 5 /* rewind tape */
+#define TMS_OFFL 6 /* rewind tape & mark unit offline */
+#define TMS_SENSE 7 /* noop - do a get unit status */
+#define TMS_CACHE 8 /* enable cache */
+#define TMS_NOCACHE 9 /* disable cache */
+/* These go last: after all real mt cmds, just bump the numbers up */
+#define TMS_CSE 10 /* clear serious exception */
+#define TMS_LOWDENSITY 11 /* set unit to low density */
+#define TMS_HIDENSITY 12 /* set unit to high density */
+
+/*
+ * Controller states
+ */
+#define S_IDLE 0 /* hasn't been initialized */
+#define S_STEP1 1 /* doing step 1 init */
+#define S_STEP2 2 /* doing step 2 init */
+#define S_STEP3 3 /* doing step 3 init */
+#define S_SCHAR 4 /* doing "set controller characteristics" */
+#define S_RUN 5 /* running */
+
+int tmscperror = 0; /* causes hex dump of packets */
+int tmscp_cp_wait = 0; /* Something to wait on for command */
+ /* packets and or credits. */
+int wakeup();
+extern int hz; /* Should find the right include */
+
+#ifdef DEBUG
+#define printd if (tmscpdebug) printf
+int tmscpdebug = 1;
+#define printd10 if(tmscpdebug >= 10) printf
+#endif
+
+int tmscpprobe(), tmscpslave(), tmscpattach(), tmscpintr();
+struct mscp *tmscpgetcp();
+
+#define DRVNAME "tms"
+#define CTRLNAME "tmscp"
+
+u_short tmscpstd[] = { 0174500, 0 };
+struct uba_driver tmscpdriver =
+{ tmscpprobe, tmscpslave, tmscpattach, 0, tmscpstd, DRVNAME, tmsdinfo, CTRLNAME
+, tmscpminfo, 0};
+
+#define b_qsize b_resid /* queue size per drive, in tmsutab */
+#define b_ubinfo b_resid /* Unibus mapping info, per buffer */
+
+
+/*************************************************************************/
+
+#define DELAYTEN 1000
+
+tmscpprobe(reg, ctlr)
+ caddr_t reg; /* address of the IP register */
+ int ctlr; /* index of controller in the tmscp_softc array
+ */
+{
+ register int br, cvec; /* MUST be 1st (r11 & r10): IPL and intr vec */
+ register struct tmscp_softc *sc = &tmscp_softc[ctlr];
+ /* ptr to software controller structure */
+ struct tmscpdevice *tmscpaddr; /* ptr to tmscpdevice struct (IP & SA) *
+/
+ struct uba_ctlr *um; /* UNUSED ptr to uba_ctlr (controller) struct *
+/
+ int count; /* for probe delay time out */
+
+# ifdef lint
+ br = 0; cvec = br; br = cvec; reg = reg;
+ tmscpreset(0); tmscpintr(0);
+# endif
+
+ tmscpaddr = (struct tmscpdevice *) reg;
+ /*
+ * Set host-settable interrupt vector.
+ * Assign 0 to the ip register to start the tmscp-device initialization
+.
+ * The device is not really initialized at this point, this is just to
+ * find out if the device exists.
+ */
+ sc->sc_ivec = (uba_hd[numuba].uh_lastiv -= 4);
+ tmscpaddr->tmscpip = 0;
+
+ count=0;
+ while(count < DELAYTEN)
+ { /* wait for at most 10 secs */
+ if((tmscpaddr->tmscpsa & TMSCP_STEP1) != 0)
+ break;
+ DELAY(10000);
+ count=count+1;
+ }
+ if (count == DELAYTEN)
+ return(0);
+
+ tmscpaddr->tmscpsa = TMSCP_ERR|(NCMDL2<<11)|(NRSPL2<<8)|TMSCP_IE|(sc->s
+c_ivec/4);
+
+ count=0;
+ while(count < DELAYTEN)
+ {
+ if((tmscpaddr->tmscpsa & TMSCP_STEP2) != 0)
+ break;
+ DELAY(10000);
+ count = count+1;
+ }
+ if (count == DELAYTEN)
+ return(0);
+
+ return(sizeof (struct tmscpdevice));
+}
+
+/*
+ * Try to find a slave (a drive) on the controller.
+ * If the controller is not in the run state, call init to initialize it.
+ */
+tmscpslave (ui, reg)
+ struct uba_device *ui; /* ptr to the uba device structure */
+ caddr_t reg; /* addr of the device controller */
+{
+ register struct uba_ctlr *um = tmscpminfo[ui->ui_ctlr];
+ register struct tmscp_softc *sc = &tmscp_softc[ui->ui_ctlr];
+ struct tmscpdevice *tmscpaddr; /* ptr to IP & SA */
+ struct mscp *mp;
+ int i; /* Something to write into to start */
+ /* the tmscp polling */
+
+# ifdef lint
+ ui = ui; reg = reg; i = i;
+# endif
+ tmscpaddr = (struct tmscpdevice *)um->um_addr;
+ /*
+ * If its not in the run state, start the initialization process
+ * (tmscpintr will complete it); if the initialization doesn't start;
+ * then return.
+ */
+ if(sc->sc_state != S_RUN)
+ {
+# ifdef DEBUG
+ printd("tmscpslave: ctlr not running: calling init \n");
+# endif
+ if(!tmscpinit(ui->ui_ctlr))
+ return(0);
+ }
+ /*
+ * Wait for the controller to come into the run state or go idle.
+ * If it goes idle return.
+ */
+# ifdef DEBUG
+ i=1;
+# endif
+ while(sc->sc_state != S_RUN && sc->sc_state != S_IDLE)
+# ifdef DEBUG
+ if (tmscpaddr->tmscpsa & TMSCP_ERR && i)
+ {
+ printd("tmscp-device: fatal error (%o)\n", tmscpaddr->t
+mscpsa&0xffff);
+ i=0;
+ }
+# endif
+ ; /* wait */
+ if(sc->sc_state == S_IDLE)
+ { /* The tmscp device failed to initialize */
+ printf("tmscp controller failed to init\n");
+ return(0);
+ }
+ /* The controller is up so see if the drive is there */
+ if(0 == (mp = tmscpgetcp(um)))
+ {
+ printf("tmscp can't get command packet\n");
+ return(0);
+ }
+ /* Need to determine the drive type for generic driver */
+ mp->mscp_opcode = M_OP_GTUNT; /* This should give us the device type
+*/
+ mp->mscp_unit = ui->ui_slave;
+ mp->mscp_cmdref = (long) ui->ui_slave;
+ tms_info[ui->ui_unit].tms_status = 0; /* set to zero */
+ tmscpip[ui->ui_ctlr][ui->ui_slave] = ui;
+ *((long *) mp->mscp_dscptr ) |= TMSCP_OWN | TMSCP_INT;/* maybe we shoul
+d poll*/
+ i = tmscpaddr->tmscpip;
+ while(!tms_info[ui->ui_unit].tms_status)
+ ; /* Wait for some status */
+# ifdef DEBUG
+ printd("tmscpslave: status = %o\n",tms_info[ui->ui_unit].tms_status & M
+_ST_MASK);
+# endif
+ tmscpip[ui->ui_ctlr][ui->ui_slave] = 0;
+ if(!tms_info[ui->ui_unit].tms_type) /* packet from a GTUNT */
+ return(0); /* Failed No such drive */
+ else
+ return(1); /* Got it and it is there */
+}
+
+
+/*
+ * Set ui flags to zero to show device is not online & set tmscpip.
+ * Unit to Controller mapping is set up here.
+ * Open routine will issue the online command, later.
+ */
+tmscpattach (ui)
+ register struct uba_device *ui; /* ptr to unibus dev struct */
+{
+ register struct uba_ctlr *um = ui->ui_mi; /* ptr to controller struct *
+/
+ struct tmscpdevice *tmscpaddr = (struct tmscpdevice *) um->um_addr; /*
+IP & SA */
+ struct mscp *mp;
+ int i; /* Assign to here to start the tmscp-dev polling */
+
+# ifdef lint
+ i = i;
+# endif lint
+ ui->ui_flags = 0;
+ tmscpip[ui->ui_ctlr][ui->ui_slave] = ui;
+# ifdef DEBUG
+ /*
+ * Check to see if the drive is available.
+ * If not then just print debug.
+ */
+ if(tms_info[ui->ui_unit].tms_status != M_ST_AVLBL)
+ printd("tmscpattach: unavailable \n");
+# endif
+ utoctlr[ui->ui_unit] = ui->ui_ctlr;
+}
+
+
+/*
+ * TMSCP interrupt routine.
+ */
+tmscpintr (d)
+ int d; /* index to the controller */
+{
+ register struct uba_ctlr *um = tmscpminfo[d];
+ register struct tmscpdevice *tmscpaddr = (struct tmscpdevice *)um->um_a
+ddr;
+ struct buf *bp;
+ register int i;
+ register struct tmscp_softc *sc = &tmscp_softc[d];
+ register struct tmscp *tm = &tmscp[d];
+ struct tmscp *ttm;
+ struct mscp *mp;
+
+# ifdef DEBUG
+ printd10("tmscpintr: state %d, tmscpsa %o\n", sc->sc_state, tmscpaddr->
+tmscpsa);
+# endif
+
+ /*
+ * How the interrupt is handled depends on the state of the controller.
+ */
+ switch (sc->sc_state) {
+
+ case S_IDLE:
+ printf("tmscp%d: random interrupt ignored\n", d);
+ return;
+
+ /* Controller was in step 1 last, see if its gone to step 2 */
+ case S_STEP1:
+# define STEP1MASK 0174377
+# define STEP1GOOD (TMSCP_STEP2|TMSCP_IE|(NCMDL2<<3)|NRSPL2)
+ for (i = 0; i < 150; i++)
+ {
+ if ((tmscpaddr->tmscpsa&STEP1MASK) != STEP1GOOD)
+ { /* still in step 1 (wait 1/100 sec) */
+ DELAY(10000);
+# ifdef DEBUG
+ printd("still in step 1, delaying\n");
+# endif DEBUG
+ }
+ else
+ break;
+ }
+ if (i > 149)
+ {
+ sc->sc_state = S_IDLE;
+ printf("failed to initialize, in step1: sa 0x%x", tmscp
+addr->tmscpsa);
+ wakeup((caddr_t)um);
+ return;
+ }
+ tmscpaddr->tmscpsa = ((int)&sc->sc_tmscp->tmscp_ca.ca_ringbase)
+ | ((cpu == VAX_780 || cpu == VAX_8600) ? TMSCP_PI : 0);
+ sc->sc_state = S_STEP2;
+ return;
+
+ /* Controller was in step 2 last, see if its gone to step 3 */
+ case S_STEP2:
+# define STEP2MASK 0174377
+# define STEP2GOOD (TMSCP_STEP3|TMSCP_IE|(sc->sc_ivec/4))
+ for (i = 0; i < 150; i++)
+ {
+ if ((tmscpaddr->tmscpsa&STEP2MASK) != STEP2GOOD)
+ { /* still in step 2 (wait 1/100 sec) */
+ DELAY(10000);
+# ifdef DEBUG
+ printd("still in step 2, delaying\n");
+# endif DEBUG
+ }
+ else
+ break;
+ }
+ if (i > 149)
+ {
+ sc->sc_state = S_IDLE;
+ printf("failed to initialize, in step2: sa 0x%x", tmscp
+addr->tmscpsa);
+ wakeup((caddr_t)um);
+ return;
+ }
+ tmscpaddr->tmscpsa = ((int)&sc->sc_tmscp->tmscp_ca.ca_ringbase)
+>>16;
+ sc->sc_state = S_STEP3;
+ return;
+
+ /* Controller was in step 3 last, see if its gone to step 4 */
+ case S_STEP3:
+# define STEP3MASK 0174000
+# define STEP3GOOD TMSCP_STEP4
+ for (i = 0; i < 150; i++)
+ {
+ if ((tmscpaddr->tmscpsa&STEP3MASK) != STEP3GOOD)
+ { /* still in step 3 (wait 1/100 sec) */
+ DELAY(10000);
+# ifdef DEBUG
+ printd("still in step 3, delaying\n");
+# endif DEBUG
+ }
+ else
+ break;
+ }
+ if (i > 149)
+ {
+ sc->sc_state = S_IDLE;
+ printf("failed to initialize, in step3: sa 0x%x", tmscp
+addr->tmscpsa);
+ wakeup((caddr_t)um);
+ return;
+ }
+ /*
+ * Get microcode version and model number of controller;
+ * Signal initialization complete (_GO) (to the controller);
+ * ask for Last Fail response if tmscperror is set;
+ * Set state to "set controller characteristics".
+ */
+ tmscpmicro[d] = tmscpaddr->tmscpsa;
+ tmscpaddr->tmscpsa = TMSCP_GO | (tmscperror? TMSCP_LF : 0);
+ sc->sc_state = S_SCHAR;
+# ifdef DEBUG
+ printd("tmscpintr: completed state %d \n", sc->sc_state);
+ printd("tmscp%d Version %d model %d\n",d,tmscpmicro[d]&0xF,
+ (tmscpmicro[d]>>4) & 0xF);
+# endif
+
+ /*
+ * Initialize the data structures (response and command queues).
+ */
+ ttm = sc->sc_tmscp;
+ for (i = 0; i < NRSP; i++)
+ {
+ tm->tmscp_ca.ca_rspdsc[i] = TMSCP_OWN | TMSCP_INT |
+ (long)&ttm->tmscp_rsp[i].mscp_cmdref
+;
+ tm->tmscp_rsp[i].mscp_dscptr = &tm->tmscp_ca.ca_rspdsc[i];
+ tm->tmscp_rsp[i].mscp_header.tmscp_msglen = mscp_msglen;
+ }
+ for (i = 0; i < NCMD; i++)
+ {
+ tm->tmscp_ca.ca_cmddsc[i] = TMSCP_INT |
+ (long)&ttm->tmscp_cmd[i].mscp_cmdref;
+ tm->tmscp_cmd[i].mscp_dscptr = &tm->tmscp_ca.ca_cmddsc[i];
+ tm->tmscp_cmd[i].mscp_header.tmscp_msglen = mscp_msglen;
+ tm->tmscp_cmd[i].mscp_header.tmscp_vcid = 1;
+ }
+ bp = &tmscpwtab[d];
+ bp->av_forw = bp->av_back = bp;
+ sc->sc_lastcmd = 1;
+ sc->sc_lastrsp = 0;
+ mp = &tmscp[um->um_ctlr].tmscp_cmd[0];
+ mp->mscp_unit = mp->mscp_modifier = 0;
+ mp->mscp_flags = 0;
+ mp->mscp_version = 0;
+ mp->mscp_cntflgs = M_CF_ATTN|M_CF_MISC|M_CF_THIS;
+ /*
+ * A host time out value of 0 means that the controller will not
+ * time out. This is ok for the TK50.
+ */
+ mp->mscp_hsttmo = 0;
+ mp->mscp_time.val[0] = 0;
+ mp->mscp_time.val[1] = 0;
+ mp->mscp_cntdep = 0;
+ mp->mscp_opcode = M_OP_STCON;
+ *((long *)mp->mscp_dscptr) |= TMSCP_OWN|TMSCP_INT;
+ i = tmscpaddr->tmscpip; /* initiate polling */
+ return;
+
+ case S_SCHAR:
+ case S_RUN:
+ break;
+
+ default:
+ printf("tmscp%d: interrupt in unknown state %d ignored\n",d,sc->sc_
+state);
+ return;
+ } /* end switch */
+
+ /*
+ * The controller state is S_SCHAR or S_RUN
+ */
+
+ /*
+ * If the error bit is set in the SA register then print an error
+ * message and reinitialize the controller.
+ */
+ if (tmscpaddr->tmscpsa&TMSCP_ERR)
+ {
+ printf("tmscp%d: fatal error (%o)\n", d, tmscpaddr->tmscpsa&0xf
+fff);
+ tmscpaddr->tmscpip = 0;
+ wakeup((caddr_t)um);
+ }
+ /*
+ * Check for a buffer purge request. (Won't happen w/ TK50 on Q22 bus)
+ */
+ if (tm->tmscp_ca.ca_bdp)
+ {
+ /*
+ * THIS IS A KLUDGE.
+ * Maybe we should change the entire
+ * UBA interface structure.
+ */
+ int s = spl6();
+ i = um->um_ubinfo;
+# ifdef DEBUG
+ printd("tmscp: purge bdp %d\n", tm->tmscp_ca.ca_bdp);
+# endif
+ um->um_ubinfo = tm->tmscp_ca.ca_bdp<<28;
+ ubapurge(um);
+ um->um_ubinfo = i;
+ (void) splx(s);
+ tm->tmscp_ca.ca_bdp = 0;
+ tmscpaddr->tmscpsa = 0; /* signal purge complete */
+ }
+
+ /*
+ * Check for response ring transition.
+ */
+ if (tm->tmscp_ca.ca_rspint)
+ {
+ tm->tmscp_ca.ca_rspint = 0;
+ for (i = sc->sc_lastrsp;; i++)
+ {
+ i %= NRSP;
+ if (tm->tmscp_ca.ca_rspdsc[i]&TMSCP_OWN)
+ break;
+ tmscprsp(um, tm, sc, i);
+ tm->tmscp_ca.ca_rspdsc[i] |= TMSCP_OWN;
+ }
+ sc->sc_lastrsp = i;
+ }
+
+ /*
+ * Check for command ring transition.
+ */
+ if (tm->tmscp_ca.ca_cmdint)
+ {
+# ifdef DEBUG
+ printd("tmscpintr: command ring transition\n");
+# endif
+ tm->tmscp_ca.ca_cmdint = 0;
+ }
+ if(tmscp_cp_wait)
+ wakeup(&tmscp_cp_wait);
+ (void) tmscpstart(um);
+}
+
+
+/*
+ * Open a tmscp device and set the unit online. If the controller is not
+ * in the run state, call init to initialize the tmscp controller first.
+ */
+
+tmscpopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit;
+ register struct uba_device *ui;
+ register struct tmscp_softc *sc;
+ register struct mscp *mp;
+ register struct uba_ctlr *um;
+ struct tmscpdevice *tmscpaddr;
+ int s,i;
+ extern quota;
+
+# ifdef lint
+ flag = flag; i = i;
+# endif
+ unit = TMSUNIT(dev);
+# ifdef DEBUG
+ printd("tmscpopen unit %d\n",unit);
+ if(tmscpdebug)DELAY(10000);
+# endif
+ if (unit >= nNTMS || (ui = tmsdinfo[unit]) == 0 || ui->ui_alive == 0
+ || tms_info[ui->ui_unit].tms_openf)
+ return (ENXIO);
+ sc = &tmscp_softc[ui->ui_ctlr];
+ s = spl5();
+ if (sc->sc_state != S_RUN)
+ {
+ if (sc->sc_state == S_IDLE)
+ if(!tmscpinit(ui->ui_ctlr))
+ {
+ printf("tmscp controller failed to init\n");
+ (void) splx(s);
+ return(ENXIO);
+ }
+ /*
+ * Wait for initialization to complete
+ */
+ timeout(wakeup,(caddr_t)ui->ui_mi,11*hz); /* to be sure*/
+ sleep((caddr_t)ui->ui_mi, 0);
+ if (sc->sc_state != S_RUN)
+ {
+ (void) splx(s);
+ return (EIO);
+ }
+ }
+ /*
+ * Check to see if the device is really there.
+ * this code was taken from Fred Canters 11 driver
+ */
+ um = ui->ui_mi;
+ tmscpaddr = (struct tmscpdevice *) um->um_addr;
+ (void) splx(s);
+ if(ui->ui_flags == 0)
+ {
+ s = spl5();
+ while(0 ==(mp = tmscpgetcp(um)))
+ {
+ tmscp_cp_wait++;
+ sleep(&tmscp_cp_wait,PSWP+1);
+ tmscp_cp_wait--;
+ }
+ (void) splx(s);
+ mp->mscp_opcode = M_OP_ONLIN;
+ mp->mscp_unit = ui->ui_slave;
+ mp->mscp_cmdref = (long) & tms_info[ui->ui_unit].tms_type;
+ /* need to sleep on something */
+# ifdef DEBUG
+ printd("tmscpopen: bring unit %d online\n",ui->ui_unit);
+# endif
+ *((long *) mp->mscp_dscptr ) |= TMSCP_OWN | TMSCP_INT;
+ i = tmscpaddr->tmscpip;
+ /*
+ * To make sure we wake up, timeout in 240 seconds.
+ * Wakeup in tmscprsp routine.
+ * 240 seconds (4 minutes) is necessary since a rewind
+ * can take a few minutes.
+ */
+ timeout(wakeup,(caddr_t) mp->mscp_cmdref,240 * hz);
+ sleep((caddr_t) mp->mscp_cmdref,PSWP+1);
+ }
+ if(ui->ui_flags == 0)
+ return(ENXIO); /* Didn't go online */
+ tms_info[ui->ui_unit].tms_openf = 1;
+ tms_info[ui->ui_unit].tms_lastiow = 0;
+ /*
+ * If the high density device is not specified, set unit to low
+ * density. This is done as an "internal" ioctl command so
+ * that the command setup and response handling
+ * is done thru "regular" command routines.
+ */
+ if ((minor(dev) & T_HIDENSITY) == 0)
+ tmscpcommand(dev, TMS_LOWDENSITY, 1);
+ else
+ tmscpcommand(dev, TMS_HIDENSITY, 1);
+ return (0);
+}
+
+
+/*
+ * Close tape device.
+ *
+ * If tape was open for writing or last operation was
+ * a write, then write two EOF's and backspace over the last one.
+ * Unless this is a non-rewinding special file, rewind the tape.
+ *
+ * NOTE:
+ * We want to be sure that any serious exception is cleared on the
+ * close. A Clear Serious Exception (CSE) modifier is always done on
+ * the rewind command. For the non-rewind case we check to see if the
+ * "serex" field is set in the softc struct; if it is then issue a noop
+ * command with the CSE modifier.
+ * Make the tape available to others, by clearing openf flag.
+ */
+tmscpclose(dev, flag)
+ register dev_t dev;
+ register flag;
+{
+ register struct tmscp_softc *sc = &tmscp_softc[TMSCPCTLR(dev)];
+ register struct uba_device *ui;
+
+ ui = tmsdinfo[TMSUNIT(dev)];
+# ifdef DEBUG
+ printd("tmscpclose: ctlr = %d\n",TMSCPCTLR(dev));
+ printd("tmscpclose: unit = %d\n",TMSUNIT(dev));
+ if(tmscpdebug)DELAY(10000);
+# endif
+ if (flag == FWRITE || (flag&FWRITE) && tms_info[ui->ui_unit].tms_lastio
+w)
+ {
+ /* device, command, count */
+ tmscpcommand (dev, TMS_WRITM, 1);
+ tmscpcommand (dev, TMS_WRITM, 1);
+ tmscpcommand (dev, TMS_BSR, 1);
+ }
+ if ((minor(dev)&T_NOREWIND) == 0)
+ /*
+ * Don't hang waiting for rewind complete.
+ */
+ tmscpcommand(dev, TMS_REW, 0);
+ else
+ if (tms_info[ui->ui_unit].tms_serex)
+ {
+# ifdef DEBUG
+ printd("tmscpclose: clearing serex\n");
+ if(tmscpdebug)DELAY(10000);
+# endif
+ tmscpcommand(dev, TMS_CSE, 1);
+ }
+ tms_info[ui->ui_unit].tms_openf = 0;
+}
+
+
+/*
+ * Execute a command on the tape drive a specified number of times.
+ * This routine sets up a buffer and calls the strategy routine which
+ * links the buffer onto the drive's buffer queue.
+ * The start routine will take care of creating a tmscp command packet
+ * with the command. The start routine is called by the strategy or the
+ * interrupt routine.
+ */
+
+tmscpcommand (dev, com, count)
+ register dev_t dev;
+ int com, count;
+{
+ register struct uba_device *ui;
+ register struct buf *bp;
+ register int s;
+ int unit = TMSUNIT(dev);
+
+ if (unit >= nNTMS)
+ return (ENXIO);
+ ui = tmsdinfo[unit];
+ bp = &ctmscpbuf[ui->ui_ctlr];
+
+ s = spl5();
+ while (bp->b_flags&B_BUSY)
+ {
+ /*
+ * This special check is because B_BUSY never
+ * gets cleared in the non-waiting rewind case.
+ */
+ if (bp->b_bcount == 0 && (bp->b_flags&B_DONE))
+ break;
+ bp->b_flags |= B_WANTED;
+ sleep((caddr_t)bp, PRIBIO);
+ }
+ bp->b_flags = B_BUSY|B_READ;
+ splx(s);
+ /*
+ * Load the buffer. The b_count field gets used to hold the command
+ * count. the b_resid field gets used to hold the command mneumonic.
+ * These 2 fields are "known" to be "safe" to use for this purpose.
+ * (Most other drivers also use these fields in this way.)
+ */
+ bp->b_dev = dev;
+ bp->b_bcount = count;
+ bp->b_resid = com;
+ bp->b_blkno = 0;
+ tmscpstrategy(bp);
+ /*
+ * In case of rewind from close, don't wait.
+ * This is the only case where count can be 0.
+ */
+ if (count == 0)
+ return;
+ iowait(bp);
+ if (bp->b_flags&B_WANTED)
+ wakeup((caddr_t)bp);
+ bp->b_flags &= B_ERROR;
+}
+
+/*
+ * Find an unused command packet
+ */
+struct mscp *
+tmscpgetcp(um)
+ struct uba_ctlr *um;
+{
+ register struct mscp *mp;
+ register struct tmscpca *cp;
+ register struct tmscp_softc *sc;
+ register int i;
+ int s;
+
+ s = spl5();
+ cp = &tmscp[um->um_ctlr].tmscp_ca;
+ sc = &tmscp_softc[um->um_ctlr];
+ /*
+ * If no credits, can't issue any commands
+ * until some outstanding commands complete.
+ */
+ i = sc->sc_lastcmd;
+# ifdef DEBUG
+ printd10("tmscpgetcp: %d credits remain\n", sc->sc_credits);
+# endif
+ if(((cp->ca_cmddsc[i]&(TMSCP_OWN|TMSCP_INT))==TMSCP_INT) &&
+ (sc->sc_credits >= 2))
+ {
+ sc->sc_credits--; /* This commits to issuing a command */
+ cp->ca_cmddsc[i] &= ~TMSCP_INT;
+ mp = &tmscp[um->um_ctlr].tmscp_cmd[i];
+ mp->mscp_unit = mp->mscp_modifier = 0;
+ mp->mscp_opcode = mp->mscp_flags = 0;
+ mp->mscp_bytecnt = mp->mscp_buffer = 0;
+ sc->sc_lastcmd = (i + 1) % NCMD;
+ (void) splx(s);
+ return(mp);
+ }
+ (void) splx(s);
+ return(NULL);
+}
+
+
+/*
+ * Initialize a TMSCP device. Set up UBA mapping registers,
+ * initialize data structures, and start hardware
+ * initialization sequence.
+ */
+tmscpinit (d)
+ int d; /* index to the controller */
+{
+ register struct tmscp_softc *sc;
+ register struct tmscp *t; /* communications area; cmd & resp packets *
+/
+ struct tmscpdevice *tmscpaddr;
+ struct uba_ctlr *um;
+
+ sc = &tmscp_softc[d];
+ um = tmscpminfo[d];
+ um->um_tab.b_active++;
+ t = &tmscp[d];
+ tmscpaddr = (struct tmscpdevice *)um->um_addr;
+ if (sc->sc_mapped == 0)
+ {
+ /*
+ * Map the communications area and command
+ * and response packets into Unibus address
+ * space.
+ */
+ sc->sc_ubainfo = uballoc(um->um_ubanum, (caddr_t)t, sizeof (str
+uct tmscp), 0);
+# ifdef MVAX
+ if (cpu == MVAX_I)
+ sc->sc_tmscp = (struct tmscp *)(sc->sc_ubainfo & 0x3fff
+ff);
+ else
+# endif MVAX
+ sc->sc_tmscp = (struct tmscp *)(sc->sc_ubainfo & 0x3fff
+f);
+ sc->sc_mapped = 1;
+ }
+
+ /*
+ * Start the hardware initialization sequence.
+ */
+ tmscpaddr->tmscpip = 0; /* start initialization */
+
+ while((tmscpaddr->tmscpsa & TMSCP_STEP1) == 0)
+ {
+# ifdef DEBUG
+ printd("tmscpinit: tmscpsa = 0%o\n",tmscpaddr->tmscpsa);
+ DELAY(100000);
+# endif
+ if(tmscpaddr->tmscpsa & TMSCP_ERR)
+ return(0); /* CHECK */
+ }
+ tmscpaddr->tmscpsa=TMSCP_ERR|(NCMDL2<<11)|(NRSPL2<<8)|TMSCP_IE|(sc->sc_
+ivec/4);
+ /*
+ * Initialization continues in the interrupt routine.
+ */
+ sc->sc_state = S_STEP1;
+ sc->sc_credits = 0;
+ return(1);
+}
+
+
+/*
+ * Start I/O operation
+ * This code is convoluted. The majority of it was copied from the uda driver.
+ */
+
+tmscpstart(um)
+ register struct uba_ctlr *um;
+{
+ register struct buf *bp, *dp;
+ register struct mscp *mp;
+ register struct tmscp_softc *sc;
+ register struct uba_device *ui;
+ struct tmscpdevice *tmscpaddr;
+ struct tmscp *tm = &tmscp[um->um_ctlr];
+ int i,tempi;
+ char ioctl; /* flag: set true if its an IOCTL command */
+
+ sc = &tmscp_softc[um->um_ctlr];
+
+ for(;;)
+ {
+ if ((dp = um->um_tab.b_actf) == NULL)
+ {
+ /*
+ * Release unneeded UBA resources and return
+ * (drive was inactive)
+ */
+ um->um_tab.b_active = 0;
+ break;
+ }
+ if ((bp = dp->b_actf) == NULL)
+ {
+ /*
+ * No more requests for this drive, remove
+ * from controller queue and look at next drive.
+ * We know we're at the head of the controller queue.
+ */
+ dp->b_active = 0;
+ um->um_tab.b_actf = dp->b_forw;
+ continue; /* Need to check for loop */
+ }
+ um->um_tab.b_active++;
+ tmscpaddr = (struct tmscpdevice *)um->um_addr;
+ if ((tmscpaddr->tmscpsa&TMSCP_ERR) || sc->sc_state != S_RUN)
+ {
+ harderr(bp, "tms");
+ mprintf("tmscp%d: sa 0%o, state %d\n",um->um_ctlr,
+ tmscpaddr->tmscpsa&0xffff, sc->sc_state);
+ tmscpinit(um->um_ctlr);
+ /* SHOULD REQUEUE OUTSTANDING REQUESTS, LIKE TMSCPRESET */
+ break;
+ }
+ ui = tmsdinfo[(TMSUNIT(bp->b_dev))];
+ /*
+ * Default is that last command was NOT a write command;
+ * if a write command is done it will be detected in tmscprsp.
+ */
+ tms_info[ui->ui_unit].tms_lastiow = 0;
+ if (ui->ui_flags == 0)
+ { /* not online */
+ if ((mp = tmscpgetcp(um)) == NULL)
+ break;
+ mp->mscp_opcode = M_OP_ONLIN;
+ mp->mscp_unit = ui->ui_slave;
+ dp->b_active = 2;
+ um->um_tab.b_actf = dp->b_forw; /* remove from controller q */
+ *((long *)mp->mscp_dscptr) |= TMSCP_OWN|TMSCP_INT;
+ if (tmscpaddr->tmscpsa&TMSCP_ERR)
+ printf("tmscp%d fatal error (0%o)\n",um->um_ctlr,
+ tmscpaddr->tmscpsa&0xffff);
+ i = tmscpaddr->tmscpip;
+ continue;
+ }
+ switch (cpu) {
+
+ case VAX_8600:
+ case VAX_780:
+ i = UBA_NEEDBDP|UBA_CANTWAIT;
+ break;
+ case VAX_8200:
+ case VAX_750:
+ i = um->um_ubinfo|UBA_HAVEBDP|UBA_CANTWAIT;
+ break;
+ case VAX_730:
+ i = UBA_CANTWAIT;
+ break;
+ case MVAX_I:
+ case MVAX_II:
+ i = UBA_CANTWAIT|UBA_MAPANYWAY;
+ break;
+ } /* end switch (cpu) */
+ /*
+ * If command is an ioctl command then set the ioctl flag for later use
+.
+ * If not (i.e. it is a read or write) then attempt
+ * to set up a buffer pointer.
+ */
+ ioctl = 0;
+ if (bp == &ctmscpbuf[um->um_ctlr])
+ ioctl = 1;
+ else
+ if ((i = ubasetup(um->um_ubanum, bp, i)) == 0)
+ {
+ if(dp->b_qsize != 0)
+ break; /* When a command completes and */
+ /* frees a bdp tmscpstart will be called *
+/
+ if ((mp = tmscpgetcp(um)) == NULL)
+ break;
+# ifdef DEBUG
+ printd("tmscpstart: GTUNT %d ubasetup = %d\n",ui->ui_un
+it, i);
+ if(tmscpdebug)DELAY(10000);
+# endif
+ mp->mscp_opcode = M_OP_GTUNT;
+ mp->mscp_unit = ui->ui_slave;
+ *((long *)mp->mscp_dscptr) |= TMSCP_OWN|TMSCP_INT;
+ if (tmscpaddr->tmscpsa&TMSCP_ERR)
+ printf("tmscp%d: fatal error (0%o)\n",um->um_ct
+lr,
+ tmscpaddr->tmscpsa&0xffff);
+ i = tmscpaddr->tmscpip; /* initiate polling */
+ break;
+ }
+# if defined(VAX750) || defined(VAX8200)
+ if ((cpu == VAX_750) || (cpu == VAX_8200))
+ tempi = i & 0xfffffff; /* mask off bdp */
+ else
+# endif
+ tempi = i;
+ if ((mp = tmscpgetcp(um)) == NULL)
+ {
+ if (!ioctl) /* only need to release if NOT ioctl */
+ ubarelse(um->um_ubanum,&tempi);
+ break;
+ }
+ mp->mscp_cmdref = (long)bp; /* pointer to get back */
+ mp->mscp_unit = ui->ui_slave;
+ /*
+ * If its an ioctl-type command then set up the appropriate
+ * tmscp command; by doing a switch on the "b_resid" field where
+ * the command mneumonic is stored.
+ */
+ if (ioctl)
+ {
+# ifdef DEBUG
+ printd("tmscpstart: doing ioctl cmd %d\n", bp->b_resid);
+# endif
+ /*
+ * The reccnt and tmkcnt fields are set to zero by the getcp
+ * routine (as bytecnt and buffer fields). Thus reccnt and
+ * tmkcnt are only modified here if they need to be set to
+ * a non-zero value.
+ */
+ switch (bp->b_resid) {
+
+ case TMS_WRITM:
+ mp->mscp_opcode = M_OP_WRITM;
+ break;
+ case TMS_FSF:
+ mp->mscp_opcode = M_OP_REPOS;
+ mp->mscp_tmkcnt = bp->b_bcount;
+ break;
+ case TMS_BSF:
+ mp->mscp_opcode = M_OP_REPOS;
+ mp->mscp_modifier = M_MD_REVRS;
+ mp->mscp_tmkcnt = bp->b_bcount;
+ break;
+ case TMS_FSR:
+ mp->mscp_opcode = M_OP_REPOS;
+ mp->mscp_modifier = M_MD_OBJCT;
+ mp->mscp_reccnt = bp->b_bcount;
+ break;
+ case TMS_BSR:
+ mp->mscp_opcode = M_OP_REPOS;
+ mp->mscp_modifier = M_MD_REVRS | M_MD_OBJCT;
+ mp->mscp_reccnt = bp->b_bcount;
+ break;
+ /*
+ * Clear serious exception is done for Rewind & Available cmds
+ */
+ case TMS_REW:
+ mp->mscp_opcode = M_OP_REPOS;
+ mp->mscp_modifier = M_MD_REWND | M_MD_CLSEX;
+ if (bp->b_bcount == 0)
+ mp->mscp_modifier |= M_MD_IMMED;
+ tms_info[ui->ui_unit].tms_serex = 0;
+ break;
+ case TMS_OFFL:
+ mp->mscp_opcode = M_OP_AVAIL;
+ mp->mscp_modifier = M_MD_UNLOD | M_MD_CLSEX;
+ tms_info[ui->ui_unit].tms_serex = 0;
+ break;
+ case TMS_SENSE:
+ mp->mscp_opcode = M_OP_GTUNT;
+ break;
+ case TMS_CACHE:
+ mp->mscp_opcode = M_OP_STUNT;
+ tms_info[ui->ui_unit].tms_unitflgs |= M_UF_WBKNV;
+ mp->mscp_unitflgs = tms_info[ui->ui_unit].tms_unitflgs;
+ mp->mscp_format = tms_info[ui->ui_unit].tms_format;
+ /* default device dependant parameters */
+ mp->mscp_mediaid = 0;
+ break;
+ case TMS_NOCACHE:
+ mp->mscp_opcode = M_OP_STUNT;
+ tms_info[ui->ui_unit].tms_unitflgs &= ~(M_UF_WBKNV);
+ mp->mscp_unitflgs = tms_info[ui->ui_unit].tms_unitflgs;
+ mp->mscp_format = tms_info[ui->ui_unit].tms_format;
+ /* default device dependant parameters */
+ mp->mscp_mediaid = 0;
+ break;
+ case TMS_CSE:
+ /*
+ * This is a no-op command. It performs a
+ * clear serious exception only. (Done on a
+ * non-rewinding close after a serious exception.)
+ */
+ mp->mscp_opcode = M_OP_REPOS;
+ mp->mscp_modifier = M_MD_CLSEX;
+ tms_info[ui->ui_unit].tms_serex = 0;
+ tms_info[ui->ui_unit].tms_clserex = 1;
+ break;
+ case TMS_LOWDENSITY:
+ /*
+ * Set the unit to low density
+ */
+ mp->mscp_opcode = M_OP_STUNT;
+ mp->mscp_unitflgs = tms_info[ui->ui_unit].tms_unitflgs;
+ mp->mscp_mediaid = 0; /* default device dependant par
+ameters */
+ if ((tms_info[ui->ui_unit].tms_fmtmenu & M_TF_800) != 0
+)
+ mp->mscp_format = M_TF_800;
+ else
+ mp->mscp_format = M_TF_PE & tms_info[ui->ui_uni
+t].tms_fmtmenu;
+ tms_info[ui->ui_unit].tms_format = mp->mscp_format;
+ break;
+ case TMS_HIDENSITY:
+ /*
+ * Set the unit to high density (format == 0)
+ */
+ mp->mscp_opcode = M_OP_STUNT;
+ mp->mscp_unitflgs = tms_info[ui->ui_unit].tms_unitflgs;
+ mp->mscp_mediaid = 0; /* default device dependant par
+ameters */
+ mp->mscp_format = 0;
+ tms_info[ui->ui_unit].tms_format = 0;
+ break;
+ default:
+ printf("Bad ioctl on tms unit %d\n", ui->ui_unit);
+ /* Need a no-op. Reposition no amount */
+ mp->mscp_opcode = M_OP_REPOS;
+ break;
+ } /* end switch (bp->b_resid) */
+ }
+ else /* Its a read/write command (not an ioctl) */
+ {
+ mp->mscp_opcode = bp->b_flags&B_READ ? M_OP_READ : M_OP_WRITE;
+ mp->mscp_bytecnt = bp->b_bcount;
+# if MVAX
+ if (cpu == MVAX_I)
+ {
+ mp->mscp_buffer = (i & 0x3ffff) | TMSCP_MAP;
+ mp->mscp_mapbase = (long)&(uba_hd[um->um_ubanum].uh_phy
+suba->uba_map[0]);
+ }
+ else
+# endif MVAX
+ mp->mscp_buffer = (i & 0x3ffff) | (((i>>28)&0xf)<<24);
+
+ bp->b_ubinfo = tempi; /* save mapping info */
+ }
+ if (tms_info[ui->ui_unit].tms_serex == 2) /* if tape mark read */
+ {
+ mp->mscp_modifier |= M_MD_CLSEX; /* clear serious exc *
+/
+ tms_info[ui->ui_unit].tms_serex = 0;
+ }
+ *((long *)mp->mscp_dscptr) |= TMSCP_OWN|TMSCP_INT;
+# ifdef DEBUG
+ printd("tmscpstart: opcode 0%o mod %o unit %d cnt %d\n",mp->mscp_opcode
+,mp->mscp_modifier,mp->mscp_unit,mp->mscp_bytecnt);
+ if(tmscpdebug)DELAY(100000);
+# endif
+ i = tmscpaddr->tmscpip; /* initiate polling */
+ dp->b_qsize++;
+ /*
+ * Move drive to the end of the controller queue
+ */
+ if (dp->b_forw != NULL)
+ {
+ um->um_tab.b_actf = dp->b_forw;
+ um->um_tab.b_actl->b_forw = dp;
+ um->um_tab.b_actl = dp;
+ dp->b_forw = NULL;
+ }
+ /*
+ * Move buffer to I/O wait queue
+ */
+ dp->b_actf = bp->av_forw;
+ dp = &tmscpwtab[um->um_ctlr];
+ bp->av_forw = dp;
+ bp->av_back = dp->av_back;
+ dp->av_back->av_forw = bp;
+ dp->av_back = bp;
+ if (tmscpaddr->tmscpsa&TMSCP_ERR)
+ {
+ printf("tmscp%d: fatal error (0%o)\n", um->um_ctlr, tmscpaddr->
+tmscpsa&0xffff);
+ tmscpinit(um->um_ctlr);
+ break;
+ }
+ } /* end for */
+ /*
+ * Check for response ring transitions lost in the
+ * Race condition
+ */
+ for (i = sc->sc_lastrsp;; i++)
+ {
+ i %= NRSP;
+ if (tm->tmscp_ca.ca_rspdsc[i]&TMSCP_OWN)
+ break;
+ tmscprsp(um, tm, sc, i);
+ tm->tmscp_ca.ca_rspdsc[i] |= TMSCP_OWN;
+ }
+ sc->sc_lastrsp = i;
+}
+
+
+/*
+ * Process a response packet
+ */
+tmscprsp(um, tm, sc, i)
+ register struct uba_ctlr *um;
+ register struct tmscp *tm;
+ register struct tmscp_softc *sc;
+ int i;
+{
+ register struct mscp *mp;
+ struct uba_device *ui;
+ struct buf *dp, *bp, nullbp;
+ int st;
+
+ mp = &tm->tmscp_rsp[i];
+ mp->mscp_header.tmscp_msglen = mscp_msglen;
+ sc->sc_credits += mp->mscp_header.tmscp_credits & 0xf; /* low 4 bits *
+/
+ if ((mp->mscp_header.tmscp_credits & 0xf0) > 0x10) /* Check */
+ return;
+# ifdef DEBUG
+ printd("tmscprsp, opcode 0%o status 0%o\n",mp->mscp_opcode,mp->mscp_sta
+tus&M_ST_MASK);
+# endif
+ /*
+ * If it's an error log message (datagram),
+ * pass it on for more extensive processing.
+ */
+ if ((mp->mscp_header.tmscp_credits & 0xf0) == 0x10)
+ { /* check */
+ tmserror(um, (struct mslg *)mp);
+ return;
+ }
+ st = mp->mscp_status&M_ST_MASK;
+ /*
+ * The controller interrupts as drive 0.
+ * This means that you must check for controller interrupts
+ * before you check to see if there is a drive 0.
+ */
+ if((M_OP_STCON|M_OP_END) == mp->mscp_opcode)
+ {
+ if (st == M_ST_SUCC)
+ {
+# ifdef DEBUG
+ printd("ctlr has %d credits\n", mp->mscp_header.tmscp_c
+redits & 0xf);
+ printd("ctlr timeout = %d\n", mp->mscp_cnttmo);
+# endif
+ sc->sc_state = S_RUN;
+ }
+ else
+ sc->sc_state = S_IDLE;
+ um->um_tab.b_active = 0;
+ wakeup((caddr_t)um);
+ return;
+ }
+ if (mp->mscp_unit >= nNTMS)
+ return;
+ if ((ui = tmscpip[um->um_ctlr][mp->mscp_unit]) == 0)
+ return;
+ /*
+ * Save endcode, endflags, and status for mtioctl get unit status.
+ * NOTE: Don't do this on Clear serious exception (reposition no-op);
+ * which is done on close since this would
+ * overwrite the real status we want.
+ */
+ if (tms_info[ui->ui_unit].tms_clserex != 1)
+ {
+ tms_info[ui->ui_unit].tms_endcode = mp->mscp_opcode;
+ tms_info[ui->ui_unit].tms_flags = mp->mscp_flags;
+ tms_info[ui->ui_unit].tms_status = st;
+ }
+ else tms_info[ui->ui_unit].tms_clserex = 0;
+
+ switch (mp->mscp_opcode) {
+ case M_OP_ONLIN|M_OP_END:
+ tms_info[ui->ui_unit].tms_type = mp->mscp_mediaid;
+ dp = &tmsutab[ui->ui_unit];
+ if (st == M_ST_SUCC)
+ {
+ /*
+ * Link the drive onto the controller queue
+ */
+ dp->b_forw = NULL;
+ if (um->um_tab.b_actf == NULL)
+ um->um_tab.b_actf = dp;
+ else
+ um->um_tab.b_actl->b_forw = dp;
+ um->um_tab.b_actl = dp;
+ ui->ui_flags = 1; /* mark it online */
+ tms_info[ui->ui_unit].tms_dsize=(daddr_t)mp->mscp_maxwr
+t;
+# ifdef DEBUG
+ printd("tmscprsp: unit %d online\n", mp->mscp_unit);
+# endif
+ /*
+ * This define decodes the Media type identifier
+ */
+# define F_to_C(x,i) ( ((x)->mscp_mediaid) >> (i*5+7)
+ & 0x1f ? ( ( (((x)->mscp_mediaid) >>( i*5 + 7)) & 0x1f) + 'A' - 1): ' ')
+# ifdef DEBUG
+ printd("tmscprsp: unit %d online %x %c%c %c%c%c%d\n"
+ ,mp->mscp_unit, mp->mscp_mediaid ,F_to_C(mp,4)
+ ,F_to_C(mp,3), F_to_C(mp,2)
+ ,F_to_C(mp,1), F_to_C(mp,0), mp->mscp_mediaid &
+ 0x7f);
+# endif
+ dp->b_active = 1;
+ } /* end if st == M_ST_SUCC */
+ else
+ {
+ if(dp->b_actf)
+ harderr(dp->b_actf,"tms");
+ else
+ {
+ nullbp.b_blkno = 0;
+ nullbp.b_dev = makedev(TMSCPDEVNUM,ui->ui_unit)
+;
+ harderr(&nullbp, "tms" );
+ }
+ printf("OFFLINE\n");
+ while (bp = dp->b_actf)
+ {
+ dp->b_actf = bp->av_forw;
+ bp->b_flags |= B_ERROR;
+ iodone(bp);
+ }
+ }
+ if(mp->mscp_cmdref!=NULL)
+ /* Seems to get lost sometimes in uda */
+ wakeup((caddr_t *) mp->mscp_cmdref);
+ break;
+ /*
+ * The AVAILABLE ATTENTION message occurs when the
+ * unit becomes available after loading,
+ * marking the unit offline (ui_flags = 0) will force an
+ * online command prior to using the unit.
+ */
+ case M_OP_AVATN:
+ ui->ui_flags = 0;
+ tms_info[ui->ui_unit].tms_type = mp->mscp_mediaid;
+ break;
+ case M_OP_END:
+ /*
+ * An endcode without an opcode (0200) is an invalid command.
+ * The mscp specification states that this would be a protocol
+ * type error, such as illegal opcodes. The mscp spec. also
+ * states that parameter error type of invalid commands should
+ * return the normal end message for the command. This does not
+ appear
+ * to be the case. An invalid logical block number returned an
+endcode
+ * of 0200 instead of the 0241 (read) that was expected.
+ */
+
+ printf("tmscp%d: invalid cmd, endcode = %o, status=%o\n",
+ um->um_ctlr, mp->mscp_opcode, st);
+ bp = (struct buf *)mp->mscp_cmdref;
+ /*
+ * Unlink buffer from I/O wait queue.
+ * And signal iodone, so the higher level command can exit!
+ *
+ */
+ bp->av_back->av_forw = bp->av_forw;
+ bp->av_forw->av_back = bp->av_back;
+ dp = &tmsutab[ui->ui_unit];
+ dp->b_qsize--;
+ iodone(bp);
+ break;
+ case M_OP_WRITE|M_OP_END:
+ /* mark the last io op as a write */
+ tms_info[ui->ui_unit].tms_lastiow = 1;
+ case M_OP_READ|M_OP_END:
+ case M_OP_WRITM|M_OP_END:
+ case M_OP_REPOS|M_OP_END:
+ case M_OP_STUNT|M_OP_END:
+ /*
+ * The AVAILABLE message occurs when the mt ioctl "rewoffl" is
+ * issued. For the ioctl, "rewoffl", a tmscp AVAILABLE command is
+ * done with the UNLOAD modifier. This performs a rewind, followed
+ * by marking the unit offline. So mark the unit offline
+ * software wise as well (ui_flags = 0 and
+ * tms_info[ui->ui_unit].tms_openf = 0).
+ */
+ case M_OP_AVAIL|M_OP_END:
+# ifdef DEBUG
+ printd("tmscprsp: position = %d\n", mp->mscp_lbn);
+# endif
+ bp = (struct buf *)mp->mscp_cmdref;
+ /*
+ * Only need to release buffer if the command was read or write
+.
+ * No ubasetup was done in "tmscpstart" if it was an ioctl cmd.
+ */
+ if (mp->mscp_opcode == (M_OP_READ|M_OP_END) ||
+ mp->mscp_opcode == (M_OP_WRITE|M_OP_END))
+ ubarelse(um->um_ubanum, (int *)&bp->b_ubinfo);
+ /*
+ * Unlink buffer from I/O wait queue.
+ */
+ bp->av_back->av_forw = bp->av_forw;
+ bp->av_forw->av_back = bp->av_back;
+# if defined(VAX750) || defined(VAX8200)
+ if ((cpu == VAX_750) || (cpu == VAX_8200)) {
+ if ((tmscpwtab[um->um_ctlr].av_forw == &tmscpwtab[um->um_ct
+lr]) &&
+ (um->um_ubinfo != 0)) {
+ ubarelse(um->um_ubanum, &um->um_ubinfo);
+ }
+ else {
+ if (mp->mscp_opcode == (M_OP_READ|M_OP_END) ||
+ mp->mscp_opcode == (M_OP_WRITE|M_OP_END))
+ UBAPURGE(uba_hd[um->um_ubanum].uh_uba,(um->um_u
+binfo >>28) & 0x0f);
+ }
+ }
+# endif
+ dp = &tmsutab[ui->ui_unit];
+ dp->b_qsize--;
+ if (st == M_ST_OFFLN || st == M_ST_AVLBL)
+ {
+ ui->ui_flags = 0; /* mark unit offline */
+ tms_info[ui->ui_unit].tms_openf = 0;
+ tms_info[ui->ui_unit].tms_type = mp->mscp_mediaid;
+ /*
+ * Link the buffer onto the front of the drive queue
+ */
+ if ((bp->av_forw = dp->b_actf) == 0)
+ dp->b_actl = bp;
+ dp->b_actf = bp;
+ /*
+ * Link the drive onto the controller queue
+ */
+ if (dp->b_active == 0)
+ {
+ dp->b_forw = NULL;
+ if (um->um_tab.b_actf == NULL)
+ um->um_tab.b_actf = dp;
+ else
+ um->um_tab.b_actl->b_forw = dp;
+ um->um_tab.b_actl = dp;
+ dp->b_active = 1;
+ }
+# if defined(VAX750) || defined(VAX8200)
+ if (((cpu == VAX_750) || (cpu == VAX_8200)) && um->um_u
+binfo == 0)
+ um->um_ubinfo = uballoc(um->um_ubanum, (caddr_t
+)0, 0, UBA_NEEDBDP);
+# endif
+ return;
+ }
+ if (st != M_ST_SUCC)
+ {
+ if (mp->mscp_flags & M_EF_SEREX)
+ tms_info[ui->ui_unit].tms_serex = 1;
+ if (st != M_ST_TAPEM)
+ {
+ harderr(bp, "tms");
+ errinfo(st); /* produces more info *
+/
+# ifdef DEBUG
+ printd("tmscprsp: error; status sub-code = 0%o,
+ flags = 0%o\n",
+ (mp->mscp_status & 177740)>>5, mp->mscp
+_flags);
+# endif
+ bp->b_flags |= B_ERROR;
+ }
+ else
+ /* Hit a tape mark - Set serex flag to
+ * a special value so we can clear the
+ * serious exception on the next command.
+ */
+ tms_info[ui->ui_unit].tms_serex = 2;
+ }
+ /*
+ * The tmscp spec states that controllers do not have to
+ * report the number of records or files skipped. So on
+ * reposition commands we go strictly by cmd status.
+ */
+ if (mp->mscp_opcode != (M_OP_REPOS|M_OP_END))
+ bp->b_resid = bp->b_bcount - mp->mscp_bytecnt;
+ else
+ bp->b_resid = 0;
+ tms_info[ui->ui_unit].tms_resid = bp->b_resid;
+ iodone(bp);
+ break;
+
+ case M_OP_GTUNT|M_OP_END:
+# ifdef DEBUG
+ printd("tmscprsp: GTUNT end packet status = 0%o\n",st);
+ printd("tmscprsp: unit %d mediaid %x %c%c %c%c%c%d %x %x t=%d\n
+"
+ ,mp->mscp_unit, mp->mscp_mediaid
+ ,F_to_C(mp,4),F_to_C(mp,3),F_to_C(mp,2)
+ ,F_to_C(mp,1),F_to_C(mp,0)
+ ,mp->mscp_mediaid & 0x7f
+ ,mp->mscp_unitid.val[0]
+ ,mp->mscp_unitid.val[1]
+ ,mp->mscp_format);
+# endif
+ tms_info[ui->ui_unit].tms_type = mp->mscp_mediaid;
+ tms_info[ui->ui_unit].tms_fmtmenu = mp->mscp_fmtmenu;
+ tms_info[ui->ui_unit].tms_unitflgs = mp->mscp_unitflgs;
+ break;
+
+ default:
+ printf("tmscp unknown packet\n");
+ tmserror(um, (struct mslg *)mp);
+ } /* end switch mp->mscp_opcode */
+}
+
+
+/*
+ * Give a meaningful error when the mscp_status field returns an error code.
+ */
+
+errinfo(st)
+ int st; /* the status code */
+{
+ switch(st) {
+ case M_ST_ICMD:
+ printf("invalid command\n");
+ break;
+ case M_ST_ABRTD:
+ printf("command aborted\n");
+ break;
+ case M_ST_OFFLN:
+ printf("unit offline\n");
+ break;
+ case M_ST_WRTPR:
+ printf("unit write protected\n");
+ break;
+ case M_ST_COMP:
+ printf("compare error\n");
+ break;
+ case M_ST_DATA:
+ printf("data error\n");
+ break;
+ case M_ST_HSTBF:
+ printf("host buffer access error\n");
+ break;
+ case M_ST_CNTLR:
+ printf("controller error\n");
+ break;
+ case M_ST_DRIVE:
+ printf("drive error\n");
+ break;
+ case M_ST_FMTER:
+ printf("formatter error\n");
+ break;
+ case M_ST_BOT:
+ printf("BOT encountered\n");
+ break;
+ case M_ST_TAPEM:
+ printf("tape mark encountered\n");
+ break;
+ case M_ST_RDTRN:
+ printf("record data truncated\n");
+ break;
+ case M_ST_PLOST:
+ printf("position lost\n");
+ break;
+ case M_ST_SEX:
+ printf("serious exception\n");
+ break;
+ case M_ST_LED:
+ printf("LEOT detected\n");
+ break;
+ }
+}
+
+
+/*
+ * Manage buffers and perform block mode read and write operations.
+ */
+
+tmscpstrategy (bp)
+ register struct buf *bp;
+{
+ register struct uba_device *ui;
+ register struct uba_ctlr *um;
+ register struct buf *dp;
+ register int unit = TMSUNIT(bp->b_dev);
+ int s;
+
+ if (unit >= nNTMS)
+ {
+# ifdef DEBUG
+ printd ("tmscpstrategy: bad unit # %d\n",unit);
+# endif
+ bp->b_flags |= B_ERROR;
+ iodone(bp);
+ return;
+ }
+ ui = tmsdinfo[unit];
+ um = ui->ui_mi;
+ if (ui == 0 || ui->ui_alive == 0)
+ {
+ bp->b_flags |= B_ERROR;
+ iodone(bp);
+ return;
+ }
+ s = spl5();
+ /*
+ * Link the buffer onto the drive queue
+ */
+ dp = &tmsutab[ui->ui_unit];
+ if (dp->b_actf == 0)
+ dp->b_actf = bp;
+ else
+ dp->b_actl->av_forw = bp;
+ dp->b_actl = bp;
+ bp->av_forw = 0;
+ /*
+ * Link the drive onto the controller queue
+ */
+ if (dp->b_active == 0)
+ {
+ dp->b_forw = NULL;
+ if (um->um_tab.b_actf == NULL)
+ um->um_tab.b_actf = dp;
+ else
+ um->um_tab.b_actl->b_forw = dp;
+ um->um_tab.b_actl = dp;
+ dp->b_active = 1;
+ }
+ /*
+ * If the controller is not active, start it.
+ */
+ if (um->um_tab.b_active == 0)
+ {
+# if defined(VAX750) || defined(VAX8200)
+ if (((cpu == VAX_750) || (cpu == VAX_8200))
+ && tmscpwtab[um->um_ctlr].av_forw == &tmscpwta
+b[um->um_ctlr])
+ {
+ if (um->um_ubinfo != 0)
+ mprintf("tmscpstrategy: ubinfo 0x%x\n",um->um_u
+binfo);
+ else
+ um->um_ubinfo = uballoc(um->um_ubanum, (caddr_t
+)0, 0, UBA_NEEDBDP);
+ }
+# endif
+# ifdef DEBUG
+ printd10("tmscpstrategy: Controller not active, starting it\n")
+;
+# endif
+ (void) tmscpstart(um);
+ }
+ splx(s);
+ return;
+}
+
+#define DBSIZE 32
+
+#define ca_Rspdsc ca_rspdsc[0]
+#define ca_Cmddsc ca_rspdsc[1]
+#define tmscp_Rsp tmscp_rsp[0]
+#define tmscp_Cmd tmscp_cmd[0]
+
+struct tmscp tmscpd[NTMSCP];
+
+tmscpdump(dev)
+ dev_t dev;
+{
+ struct tmscpdevice *tmscpaddr;
+ struct tmscp *tmscp_ubaddr;
+ char *start;
+ int num, blk, unit;
+ register struct uba_regs *uba;
+ register struct uba_device *ui;
+ register struct tmscp *tmscpp;
+ register struct pte *io;
+ register int i;
+
+ unit = minor(dev) & 03;
+ if (unit >= nNTMS)
+ return (ENXIO);
+# define phys(cast, addr) ((cast)((int)addr & 0x7fffffff))
+ ui = phys(struct uba_device *, tmsdinfo[unit]);
+ if (ui->ui_alive == 0)
+ return (ENXIO);
+ uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba;
+ if (ui->ui_hd->uba_type)
+ buainit(uba);
+ else
+ ubainit(uba);
+ tmscpaddr = (struct tmscpdevice *)ui->ui_physaddr;
+ DELAY(2000000);
+ tmscpp = phys(struct tmscp *, &tmscpd[ui->ui_ctlr]);
+
+ num = btoc(sizeof(struct tmscp)) + 1;
+ io = &uba->uba_map[NUBMREG-num];
+ for(i = 0; i<num; i++)
+ *(int *)io++ = UBAMR_MRV|(btop(tmscpp)+i);
+# ifdef MVAX
+ if( cpu == MVAX_I )
+ tmscp_ubaddr = tmscpp;
+ else
+# endif MVAX
+ tmscp_ubaddr = (struct tmscp *)(((int)tmscpp & PGOFSET)|((NUBMR
+EG-num)<<9));
+
+ tmscpaddr->tmscpip = 0;
+ while ((tmscpaddr->tmscpsa & TMSCP_STEP1) == 0)
+ if(tmscpaddr->tmscpsa & TMSCP_ERR) return(EFAULT);
+ tmscpaddr->tmscpsa = TMSCP_ERR;
+ while ((tmscpaddr->tmscpsa & TMSCP_STEP2) == 0)
+ if(tmscpaddr->tmscpsa & TMSCP_ERR) return(EFAULT);
+ tmscpaddr->tmscpsa = (short)&tmscp_ubaddr->tmscp_ca.ca_ringbase;
+ while ((tmscpaddr->tmscpsa & TMSCP_STEP3) == 0)
+ if(tmscpaddr->tmscpsa & TMSCP_ERR) return(EFAULT);
+ tmscpaddr->tmscpsa = (short)(((int)&tmscp_ubaddr->tmscp_ca.ca_ringbase)
+ >> 16);
+ while ((tmscpaddr->tmscpsa & TMSCP_STEP4) == 0)
+ if(tmscpaddr->tmscpsa & TMSCP_ERR) return(EFAULT);
+ tmscpaddr->tmscpsa = TMSCP_GO;
+ tmscpp->tmscp_ca.ca_Rspdsc = (long)&tmscp_ubaddr->tmscp_Rsp.mscp_cmdref
+;
+ tmscpp->tmscp_ca.ca_Cmddsc = (long)&tmscp_ubaddr->tmscp_Cmd.mscp_cmdref
+;
+ tmscpp->tmscp_Cmd.mscp_header.tmscp_vcid = 1; /* for tape */
+ tmscpp->tmscp_Cmd.mscp_cntflgs = 0;
+ tmscpp->tmscp_Cmd.mscp_version = 0;
+ if (tmscpcmd(M_OP_STCON, tmscpp, tmscpaddr) == 0) {
+ return(EFAULT);
+ }
+ tmscpp->tmscp_Cmd.mscp_unit = ui->ui_slave;
+ if (tmscpcmd(M_OP_ONLIN, tmscpp, tmscpaddr) == 0) {
+ return(EFAULT);
+ }
+
+ num = maxfree;
+ start = 0;
+ while (num > 0)
+ {
+ blk = num > DBSIZE ? DBSIZE : num;
+ io = uba->uba_map;
+ for (i = 0; i < blk; i++)
+ *(int *)io++ = (btop(start)+i) | UBAMR_MRV;
+ *(int *)io = 0;
+ tmscpp->tmscp_Cmd.mscp_lbn = btop(start);
+ tmscpp->tmscp_Cmd.mscp_unit = ui->ui_slave;
+ tmscpp->tmscp_Cmd.mscp_bytecnt = blk*NBPG;
+# ifdef MVAX
+ if( cpu == MVAX_I )
+ tmscpp->tmscp_Cmd.mscp_buffer = (long) start;
+ else
+# endif MVAX
+ tmscpp->tmscp_Cmd.mscp_buffer = 0;
+ if (tmscpcmd(M_OP_WRITE, tmscpp, tmscpaddr) == 0)
+ return(EIO);
+ start += blk*NBPG;
+ num -= blk;
+ }
+ return (0);
+}
+
+
+/*
+ * Perform a standalone tmscp command. This routine is only used by tmscpdump.
+ */
+
+tmscpcmd(op, tmscpp, tmscpaddr)
+ int op;
+ register struct tmscp *tmscpp;
+ struct tmscpdevice *tmscpaddr;
+{
+ int i;
+
+# ifdef lint
+ i = i;
+# endif
+
+ tmscpp->tmscp_Cmd.mscp_opcode = op;
+ tmscpp->tmscp_Rsp.mscp_header.tmscp_msglen = mscp_msglen;
+ tmscpp->tmscp_Cmd.mscp_header.tmscp_msglen = mscp_msglen;
+ tmscpp->tmscp_ca.ca_Rspdsc |= TMSCP_OWN|TMSCP_INT;
+ tmscpp->tmscp_ca.ca_Cmddsc |= TMSCP_OWN|TMSCP_INT;
+ if (tmscpaddr->tmscpsa&TMSCP_ERR)
+ printf("tmscp fatal error (0%o)\n", tmscpaddr->tmscpsa&0xffff);
+ i = tmscpaddr->tmscpip;
+ for (;;)
+ {
+ if (tmscpp->tmscp_ca.ca_cmdint)
+ tmscpp->tmscp_ca.ca_cmdint = 0;
+ if (tmscpp->tmscp_ca.ca_rspint)
+ break;
+ }
+ tmscpp->tmscp_ca.ca_rspint = 0;
+ if (tmscpp->tmscp_Rsp.mscp_opcode != (op|M_OP_END) ||
+ (tmscpp->tmscp_Rsp.mscp_status&M_ST_MASK) != M_ST_SUCC)
+ {
+ printf("error: com %d opc 0x%x stat 0x%x\ndump ", op,
+ tmscpp->tmscp_Rsp.mscp_opcode, tmscpp->tmscp_Rsp.mscp_s
+tatus);
+ return(0);
+ }
+ return(1);
+}
+
+
+/*
+ * Perform raw read
+ */
+
+tmscpread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ register int unit = TMSUNIT(dev);
+
+ if (unit >= nNTMS)
+ return (ENXIO);
+ return (physio(tmscpstrategy, &rtmsbuf[unit], dev, B_READ, minphys, uio
+));
+}
+
+
+/*
+ * Perform raw write
+ */
+
+tmscpwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ register int unit = TMSUNIT(dev);
+
+ if (unit >= nNTMS)
+ return (ENXIO);
+ return (physio(tmscpstrategy, &rtmsbuf[unit], dev, B_WRITE, minphys, ui
+o));
+}
+
+
+/*
+ * Catch ioctl commands, and call the "command" routine to do them.
+ */
+
+tmscpioctl(dev, cmd, data, flag)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+{
+ register struct tmscp_softc *sc = &tmscp_softc[TMSCPCTLR(dev)];
+ register struct buf *bp = &ctmscpbuf[TMSCPCTLR(dev)];
+ register callcount; /* number of times to call cmd routine */
+ register struct uba_device *ui;
+ int fcount; /* number of files (or records) to space */
+ struct mtop *mtop; /* mag tape cmd op to perform */
+ struct mtget *mtget; /* mag tape struct to get info in */
+
+ /* we depend of the values and order of the TMS ioctl codes here */
+ static tmsops[] =
+ {TMS_WRITM,TMS_FSF,TMS_BSF,TMS_FSR,TMS_BSR,TMS_REW,TMS_OFFL,TMS_SENSE,
+ TMS_CACHE,TMS_NOCACHE};
+
+ switch (cmd) {
+ case MTIOCTOP: /* tape operation */
+ mtop = (struct mtop *)data;
+ switch (mtop->mt_op) {
+
+ case MTWEOF:
+ callcount = mtop->mt_count;
+ fcount = 1;
+ break;
+ case MTFSF: case MTBSF:
+ case MTFSR: case MTBSR:
+ callcount = 1;
+ fcount = mtop->mt_count;
+ break;
+ case MTREW: case MTOFFL: case MTNOP:
+ case MTCACHE: case MTNOCACHE:
+ callcount = 1;
+ fcount = 1; /* wait for this rewind */
+ break;
+ default:
+ return (ENXIO);
+ } /* end switch mtop->mt_op */
+
+ if (callcount <= 0 || fcount <= 0)
+ return (EINVAL);
+ while (--callcount >= 0)
+ {
+ tmscpcommand(dev, tmsops[mtop->mt_op], fcount);
+ if ((mtop->mt_op == MTFSR || mtop->mt_op == MTBSR) &&
+ bp->b_resid)
+ return (EIO);
+ if (bp->b_flags & B_ERROR) /* like hitting BOT */
+ break;
+ }
+ return (geterror(bp));
+
+ case MTIOCGET:
+ /*
+ * Return status info associated with the particular UNIT.
+ */
+ ui = tmsdinfo[TMSUNIT(dev)];
+ mtget = (struct mtget *)data;
+ mtget->mt_type = MT_ISTMSCP;
+ mtget->mt_dsreg = tms_info[ui->ui_unit].tms_flags << 8;
+ mtget->mt_dsreg |= tms_info[ui->ui_unit].tms_endcode;
+ mtget->mt_erreg = tms_info[ui->ui_unit].tms_status;
+ mtget->mt_resid = tms_info[ui->ui_unit].tms_resid;
+ break;
+
+ default:
+ return (ENXIO);
+ }
+ return (0);
+}
+
+
+/*
+ * Reset (for raw mode use only).
+ */
+
+tmscpreset (uban)
+ int uban;
+{
+ register struct uba_ctlr *um;
+ register struct uba_device *ui;
+ register struct buf *bp, *dp;
+ register int unit;
+ struct buf *nbp;
+ int d;
+
+ for (d = 0; d < NTMSCP; d++)
+ {
+ if ((um = tmscpminfo[d]) == 0 || um->um_ubanum != uban ||
+ um->um_alive == 0)
+ continue;
+ printf(" tmscp%d", d);
+ um->um_tab.b_active = 0;
+ um->um_tab.b_actf = um->um_tab.b_actl = 0;
+ tmscp_softc[d].sc_state = S_IDLE;
+ tmscp_softc[d].sc_mapped = 0;
+ for (unit = 0; unit < nNTMS; unit++)
+ {
+ if ((ui = tmsdinfo[unit]) == 0)
+ continue;
+ if (ui->ui_alive == 0 || ui->ui_mi != um)
+ continue;
+ tmsutab[unit].b_active = 0;
+ tmsutab[unit].b_qsize = 0;
+ }
+ for (bp = tmscpwtab[d].av_forw; bp != &tmscpwtab[d]; bp = nbp)
+ {
+ nbp = bp->av_forw;
+ bp->b_ubinfo = 0;
+ /*
+ * Link the buffer onto the drive queue
+ */
+ dp = &tmsutab[TMSUNIT(bp->b_dev)];
+ if (dp->b_actf == 0)
+ dp->b_actf = bp;
+ else
+ dp->b_actl->av_forw = bp;
+ dp->b_actl = bp;
+ bp->av_forw = 0;
+ /*
+ * Link the drive onto the controller queue
+ */
+ if (dp->b_active == 0)
+ {
+ dp->b_forw = NULL;
+ if (um->um_tab.b_actf == NULL)
+ um->um_tab.b_actf = dp;
+ else
+ um->um_tab.b_actl->b_forw = dp;
+ um->um_tab.b_actl = dp;
+ dp->b_active = 1;
+ }
+ }
+ tmscpinit(d);
+ }
+}
+
+
+/*
+ * Process an error log message
+ *
+ * Only minimal decoding is done, only "useful"
+ * information is printed. Eventually should
+ * send message to an error logger.
+ */
+
+tmserror(um, mp)
+ register struct uba_ctlr *um;
+ register struct mslg *mp;
+{
+ register i;
+
+# ifdef DEBUG
+ printd("tmserror:\n");
+# endif
+ if(!(mp->mslg_flags & (M_LF_SUCC | M_LF_CONT)))
+ mprintf("tmscp%d: %s error, ", um->um_ctlr,
+ mp->mslg_flags & ( M_LF_SUCC | M_LF_CONT ) ? "soft" : "hard");
+
+ switch (mp->mslg_format) {
+
+ case M_FM_CNTERR:
+ mprintf("controller error, event 0%o\n", mp->mslg_event);
+ break;
+ case M_FM_BUSADDR:
+ mprintf("host memory access error, event 0%o, addr 0%o\n",
+ mp->mslg_event, mp->mslg_busaddr);
+ break;
+ case M_FM_TAPETRN:
+ mprintf("tape transfer error, unit %d, grp 0x%x, event 0%o\n",
+ mp->mslg_unit, mp->mslg_group, mp->mslg_event);
+ break;
+ case M_FM_STIERR:
+ mprintf("STI error, unit %d, event 0%o\n",
+ mp->mslg_unit, mp->mslg_event);
+ for(i = 0; i < 62;i++)
+ mprintf("\t0x%x",mp->mslg_stiunsucc[i] & 0xff);
+ mprintf("\n");
+ break;
+ case M_FM_STIDEL:
+ mprintf("STI Drive Error Log, unit %d, event 0%o\n",
+ mp->mslg_unit, mp->mslg_event);
+ break;
+ case M_FM_STIFEL:
+ mprintf("STI Formatter Error Log, unit %d, event 0%o\n",
+ mp->mslg_unit, mp->mslg_event);
+ break;
+ default:
+ mprintf("unknown error, unit %d, format 0%o, event 0%o\n",
+ mp->mslg_unit, mp->mslg_format, mp->mslg_event);
+ }
+
+ if (tmscperror)
+ {
+ register long *p = (long *)mp;
+
+ for (i = 0; i < mp->mslg_header.tmscp_msglen; i += sizeof(*p))
+ printf("%x ", *p++);
+ printf("\n");
+ }
+}
+#endif
+