/* $NetBSD: tmscp.c,v 1.7 1995/12/13 19:02:53 ragge Exp $ */ /*- * Copyright (c) 1991 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tmscp.c 7.16 (Berkeley) 5/9/91 */ /* * sccsid = "@(#)tmscp.c 1.24 (ULTRIX) 1/21/86"; */ /************************************************************************ * * * Licensed from Digital Equipment Corporation * * Copyright (c) * * Digital Equipment Corporation * * Maynard, Massachusetts * * 1985, 1986 * * All rights reserved. * * * * 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 the suitability of this software for * * any purpose. It is supplied "As Is" without expressed or * * implied warranty. * * * * If the Regents of the University of California or its * * licensees modify the software in a manner creating * * diriviative copyright rights, appropriate copyright * * legends may be placed on the drivative work in addition * * to that set forth above. * * * ************************************************************************ * * 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. * */ #define NTMS 2 /* XXX - This is _wery_ kludgy! /ragge */ #include "tmscp.h" #if NTMSCP > 0 #include "sys/param.h" #include "sys/systm.h" #include "sys/buf.h" #include "sys/conf.h" #include "sys/errno.h" #include "sys/file.h" #include "sys/map.h" #include "sys/ioctl.h" #include "sys/syslog.h" #include "sys/mtio.h" /* #include "sys/cmap.h" */ #include "sys/uio.h" #include "sys/proc.h" #include "sys/tprintf.h" #include "vax/include/pte.h" #include "vax/include/cpu.h" #include "vax/include/mtpr.h" #include "vax/include/sid.h" #include "vax/uba/ubareg.h" #include "vax/uba/ubavar.h" #define TENSEC (1000) #define TMS_PRI LOG_INFO #define NRSPL2 3 /* log2 number of response packets */ #define NCMDL2 3 /* log2 number of command packets */ #define NRSP (1<= 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 extern struct cfdriver ubacd; /* * Unfortunately qbgetpri can't be used because the TK50 doesn't flip the * TMSCP_STEP2 flag in the tmscpsa register until after the pending interrupt * has been acknowledged by the cpu. If you are at spl6(), the TMSCP_STEP2 * flag never gets set and you return (0). */ tmscpprobe(reg, ctlr, um) caddr_t reg; /* address of the IP register */ int ctlr; /* index of controller in the tmscp_softc array */ struct uba_ctlr *um; { /* 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 */ volatile struct tmscpdevice *tmscpaddr; int count; /* for probe delay time out */ struct uba_softc *ubasc; # ifdef lint br = 0; cvec = br; br = cvec; reg = reg; tmscpreset(0); tmscpintr(0); # endif tmscpminfo[ctlr] = um; 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. */ ubasc = ubacd.cd_devs[0]; /* XXX */ sc->sc_ivec = (ubasc->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->sc_ivec/4); count=0; while(count < DELAYTEN) { if((tmscpaddr->tmscpsa & TMSCP_STEP2) != 0) break; DELAY(10000); count = count+1; } if (count == DELAYTEN) return(0); #ifdef QBA sc->sc_ipl = 0x15; #endif 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]; register struct tms_info *tms = &tms_info[ui->ui_unit]; volatile struct tmscpdevice *tmscpaddr; /* ptr to IP & SA */ volatile struct mscp *mp; volatile int i; /* Something to write into to start */ /* the tmscp polling */ # ifdef lint reg = reg; # 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->tmscpsa&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->tms_status = 0; /* set to zero */ tmscpip[ui->ui_ctlr][ui->ui_slave] = ui; *((long *) mp->mscp_dscptr ) |= TMSCP_OWN | TMSCP_INT;/* maybe we should poll*/ i = tmscpaddr->tmscpip; #ifdef lint i = i; #endif while(!tms->tms_status) ; /* Wait for some status */ # ifdef DEBUG printd("tmscpslave: status = %o\n",tms->tms_status & M_ST_MASK); # endif tmscpip[ui->ui_ctlr][ui->ui_slave] = 0; if(!tms->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 */ { 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) { volatile struct uba_ctlr *um = tmscpminfo[d]; volatile struct tmscpdevice *tmscpaddr = (struct tmscpdevice *)um->um_addr; struct buf *bp; volatile int i; volatile struct tmscp_softc *sc = &tmscp_softc[d]; struct tmscp *tm = &tmscp[d]; struct tmscp *ttm; volatile struct mscp *mp; # ifdef DEBUG printd10("tmscpintr: state %d, tmscpsa %o\n", sc->sc_state, tmscpaddr->tmscpsa); # endif #ifdef QBA if (cpunumber == VAX_78032) splx(sc->sc_ipl); #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", tmscpaddr->tmscpsa); wakeup((caddr_t)um); return; } tmscpaddr->tmscpsa = ((int)&sc->sc_tmscp->tmscp_ca.ca_ringbase) | ((cpunumber == VAX_780 || cpunumber == 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", tmscpaddr->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", tmscpaddr->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->b_actf = NULL; sc->sc_lastcmd = 1; sc->sc_lastrsp = 0; mp = &tmscp[d].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 = 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&0xffff); 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) { UBAPURGE(um->um_hd->uh_uba, tm->tmscp_ca.ca_bdp); 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((caddr_t)&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. */ /* ARGSUSED */ tmscpopen(dev, flag) dev_t dev; int flag; { register int unit; register struct uba_device *ui; register struct tmscp_softc *sc; register struct tms_info *tms; register volatile struct mscp *mp; register struct uba_ctlr *um; volatile struct tmscpdevice *tmscpaddr; volatile int i; int s; unit = TMSUNIT(dev); # ifdef DEBUG printd("tmscpopen unit %d\n",unit); if(tmscpdebug)DELAY(10000); # endif if (unit >= NTMS || (ui = tmsdinfo[unit]) == 0 || ui->ui_alive == 0) return (ENXIO); tms = &tms_info[ui->ui_unit]; if (tms->tms_openf) return (EBUSY); sc = &tmscp_softc[ui->ui_ctlr]; tms->tms_openf = 1; tms->tms_tpr = tprintf_open(curproc); s = splbio(); 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); tms->tms_openf = 0; 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); tms->tms_openf = 0; 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 = splbio(); while(0 ==(mp = tmscpgetcp(um))) { tmscp_cp_wait++; sleep((caddr_t)&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->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; #ifdef lint i = i; #endif /* * 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) { tms->tms_openf = 0; return(ENXIO); /* Didn't go online */ } tms->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 tms_info *tms; 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 tms = &tms_info[ui->ui_unit]; if (flag == FWRITE || (flag&FWRITE) && tms->tms_lastiow) { /* 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->tms_serex) { # ifdef DEBUG printd("tmscpclose: clearing serex\n"); if(tmscpdebug)DELAY(10000); # endif tmscpcommand(dev, TMS_CSE, 1); } tprintf_close(tms->tms_tpr); tms->tms_openf = 0; return (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); ui = tmsdinfo[unit]; bp = &ctmscpbuf[ui->ui_ctlr]; s = splbio(); 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 volatile struct mscp *mp; register volatile struct tmscpca *cp; register struct tmscp_softc *sc; register int i; int s; s = splbio(); 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((struct mscp *)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; volatile 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 (struct tmscp), 0); sc->sc_tmscp = (struct tmscp *)(UBAI_ADDR(sc->sc_ubainfo)); 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 volatile struct mscp *mp; register struct tmscp_softc *sc; register struct tms_info *tms; register struct uba_device *ui; volatile struct tmscpdevice *tmscpaddr; volatile struct tmscp *tm = &tmscp[um->um_ctlr]; volatile int i; int 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_hash.le_next; continue; /* Need to check for loop */ } um->um_tab.b_active++; tmscpaddr = (struct tmscpdevice *)um->um_addr; ui = tmsdinfo[(TMSUNIT(bp->b_dev))]; tms = &tms_info[ui->ui_unit]; if ((tmscpaddr->tmscpsa&TMSCP_ERR) || sc->sc_state != S_RUN) { tprintf(tms->tms_tpr, "tms%d: hard error bn%d\n", minor(bp->b_dev)&03, bp->b_blkno); log(TMS_PRI, "tmscp%d: sa 0%o, state %d\n",um->um_ctlr, tmscpaddr->tmscpsa&0xffff, sc->sc_state); (void)tmscpinit(um->um_ctlr); /* SHOULD REQUEUE OUTSTANDING REQUESTS, LIKE TMSCPRESET */ break; } /* * Default is that last command was NOT a write command; * if a write command is done it will be detected in tmscprsp. */ tms->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_hash.le_next; /* 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 (cpunumber) { case VAX_8600: case VAX_780: i = UBA_NEEDBDP|UBA_CANTWAIT; break; case VAX_750: i = um->um_ubinfo|UBA_HAVEBDP|UBA_CANTWAIT; break; case VAX_730: case VAX_78032: i = UBA_CANTWAIT; break; } /* end switch (cpunumber) */ /* * 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_unit, 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_ctlr, tmscpaddr->tmscpsa&0xffff); i = tmscpaddr->tmscpip; /* initiate polling */ break; } # if defined(VAX750) if (cpunumber == VAX_750) 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 ((int)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->tms_serex = 0; break; case TMS_OFFL: mp->mscp_opcode = M_OP_AVAIL; mp->mscp_modifier = M_MD_UNLOD | M_MD_CLSEX; tms->tms_serex = 0; break; case TMS_SENSE: mp->mscp_opcode = M_OP_GTUNT; break; case TMS_CACHE: mp->mscp_opcode = M_OP_STUNT; tms->tms_unitflgs |= M_UF_WBKNV; mp->mscp_unitflgs = tms->tms_unitflgs; mp->mscp_format = tms->tms_format; /* default device dependant parameters */ mp->mscp_mediaid = 0; break; case TMS_NOCACHE: mp->mscp_opcode = M_OP_STUNT; tms->tms_unitflgs &= ~(M_UF_WBKNV); mp->mscp_unitflgs = tms->tms_unitflgs; mp->mscp_format = tms->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->tms_serex = 0; tms->tms_clserex = 1; break; case TMS_LOWDENSITY: /* * Set the unit to low density */ mp->mscp_opcode = M_OP_STUNT; mp->mscp_unitflgs = tms->tms_unitflgs; mp->mscp_mediaid = 0; /* default device dependant parameters */ if ((tms->tms_fmtmenu & M_TF_800) != 0) mp->mscp_format = M_TF_800; else mp->mscp_format = M_TF_PE & tms->tms_fmtmenu; tms->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->tms_unitflgs; mp->mscp_mediaid = 0; /* default device dependant parameters */ mp->mscp_format = 0; tms->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; mp->mscp_buffer = UBAI_ADDR(i) | (UBAI_BDP(i) << 24); bp->b_ubinfo = tempi; /* save mapping info */ } if (tms->tms_serex == 2) /* if tape mark read */ { mp->mscp_modifier |= M_MD_CLSEX; /* clear serious exc */ tms->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_hash.le_next != NULL) { um->um_tab.b_actf = dp->b_hash.le_next; MSCP_APPEND(dp, &um->um_tab, b_hash.le_next); } /* * Move buffer to I/O wait queue */ dp->b_actf = bp->b_actf; dp = &tmscpwtab[um->um_ctlr]; MSCP_APPEND(bp, dp, b_actf); if (tmscpaddr->tmscpsa&TMSCP_ERR) { printf("tmscp%d: fatal error (0%o)\n", um->um_ctlr, tmscpaddr->tmscpsa&0xffff); (void)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 volatile struct tmscp *tm; register struct tmscp_softc *sc; int i; { register volatile struct mscp *mp; register struct tms_info *tms; struct uba_device *ui; struct buf *dp, *bp; int st; struct uba_softc *ubasc; 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_status&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_credits & 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 >= NTMS) return; if ((ui = tmscpip[um->um_ctlr][mp->mscp_unit]) == 0) return; tms = &tms_info[ui->ui_unit]; /* * 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->tms_clserex != 1) { tms->tms_endcode = mp->mscp_opcode; tms->tms_flags = mp->mscp_flags; tms->tms_status = st; } else tms->tms_clserex = 0; switch (mp->mscp_opcode) { case M_OP_ONLIN|M_OP_END: tms->tms_type = mp->mscp_mediaid; dp = &tmsutab[ui->ui_unit]; if (st == M_ST_SUCC) { /* * Link the drive onto the controller queue */ MSCP_APPEND(dp, &um->um_tab, b_hash.le_next); ui->ui_flags = 1; /* mark it online */ tms->tms_dsize=(daddr_t)mp->mscp_maxwrt; # 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 (bp = dp->b_actf) tprintf(tms->tms_tpr, "tms%d: hard error bn%d: OFFLINE\n", minor(bp->b_dev)&03, bp->b_blkno); else tprintf(tms->tms_tpr, "tms%d: hard error: OFFLINE\n", ui->ui_unit); while (bp = dp->b_actf) { dp->b_actf = bp->b_actf; 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->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! * */ dp = &tmscpwtab[um->um_ctlr]; while (dp && dp->b_actf != bp) dp = dp->b_actf; if (dp == NULL) panic("tmscp: don't work1!"); dp->b_actf = bp->b_actf; 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->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->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. */ dp = &tmscpwtab[um->um_ctlr]; while (dp && dp->b_actf != bp) dp = dp->b_actf; if (dp == NULL) panic("tmscp: don't work2!"); dp->b_actf = bp->b_actf; # if defined(VAX750) ubasc = ubacd.cd_devs[um->um_ubanum]; if (cpunumber == VAX_750) { if ((tmscpwtab[um->um_ctlr].b_actf == NULL) && (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(ubasc->uh_uba,(um->um_ubinfo >>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->tms_openf = 0; tms->tms_type = mp->mscp_mediaid; /* * Link the buffer onto the front of the drive queue */ bp->b_actf = dp->b_actf; dp->b_actf = bp; /* * Link the drive onto the controller queue */ if (dp->b_active == 0) { MSCP_APPEND(dp, &um->um_tab, b_hash.le_next); dp->b_active = 1; } # if defined(VAX750) if (cpunumber == VAX_750 && um->um_ubinfo == 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->tms_serex = 1; if (st != M_ST_TAPEM) { tprintf(tms->tms_tpr, "tms%d: hard error bn%d\n", minor(bp->b_dev)&03, bp->b_blkno); 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->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->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->tms_type = mp->mscp_mediaid; tms->tms_fmtmenu = mp->mscp_fmtmenu; tms->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. */ void 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 >= NTMS) { # 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 = splbio(); /* * Link the buffer onto the drive queue */ dp = &tmsutab[ui->ui_unit]; MSCP_APPEND(bp, dp, b_actf); /* * Link the drive onto the controller queue */ if (dp->b_active == 0) { MSCP_APPEND(dp, &um->um_tab, b_hash.le_next); dp->b_active = 1; } /* * If the controller is not active, start it. */ if (um->um_tab.b_active == 0) { # if defined(VAX750) if (cpunumber == VAX_750 && tmscpwtab[um->um_ctlr].b_actf == NULL) { if (um->um_ubinfo != 0) log(TMS_PRI, "tmscpstrategy: ubinfo 0x%x\n", um->um_ubinfo); 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; } int tmscpread(dev, uio) dev_t dev; struct uio *uio; { return (physio(tmscpstrategy, NULL, dev, B_READ, minphys, uio)); } int tmscpwrite(dev, uio) dev_t dev; struct uio *uio; { return (physio(tmscpstrategy, NULL, dev, B_WRITE, minphys, uio)); } #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; { #ifdef notyet volatile struct tmscpdevice *tmscpaddr; volatile struct tmscp *tmscp_ubaddr; char *start; int num, blk, unit; register struct uba_regs *uba; register struct uba_device *ui; register volatile struct tmscp *tmscpp; register struct pte *io; register int i; unit = minor(dev) & 03; if (unit >= NTMS) 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_softc *, ui->ui_hd)->uh_physuba; 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 = (struct pte *)&uba->uba_map[NUBMREG-num]; for(i = 0; itmscpip = 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)((int)&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 = (struct pte *)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; } #endif return (0); } /* * Perform a standalone tmscp command. This routine is only used by tmscpdump. */ tmscpcmd(op, tmscpp, tmscpaddr) int op; register volatile struct tmscp *tmscpp; volatile struct tmscpdevice *tmscpaddr; { volatile int i; 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; #ifdef lint i = i; #endif 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_status); return(0); } return(1); } /* * Catch ioctl commands, and call the "command" routine to do them. */ /* ARGSUSED */ tmscpioctl(dev, cmd, data, flag) dev_t dev; int cmd; caddr_t data; int flag; { register struct buf *bp = &ctmscpbuf[TMSCPCTLR(dev)]; register callcount; /* number of times to call cmd routine */ register struct uba_device *ui; register struct tms_info *tms; int fcount; /* number of files (or records) to space */ int error = 0; register struct mtop *mtop; /* mag tape cmd op to perform */ register 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; } if (bp->b_flags&B_ERROR) if ((error = bp->b_error)==0) return (EIO); return (error); case MTIOCGET: /* * Return status info associated with the particular UNIT. */ ui = tmsdinfo[TMSUNIT(dev)]; tms = &tms_info[ui->ui_unit]; mtget = (struct mtget *)data; mtget->mt_type = MT_ISTMSCP; mtget->mt_dsreg = tms->tms_flags << 8; mtget->mt_dsreg |= tms->tms_endcode; mtget->mt_erreg = tms->tms_status; mtget->mt_resid = tms->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 = 0; tmscp_softc[d].sc_state = S_IDLE; tmscp_softc[d].sc_mapped = 0; for (unit = 0; unit < NTMS; 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].b_actf; bp; bp = nbp) { nbp = bp->b_actf; bp->b_ubinfo = 0; /* * Link the buffer onto the drive queue */ dp = &tmsutab[TMSUNIT(bp->b_dev)]; MSCP_APPEND(bp, dp, b_actf); /* * Link the drive onto the controller queue */ if (dp->b_active == 0) { MSCP_APPEND(dp, &um->um_tab, b_hash.le_next); dp->b_active = 1; } } (void)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))) log(TMS_PRI, "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: log(TMS_PRI, "controller error, event 0%o\n", mp->mslg_event); break; case M_FM_BUSADDR: log(TMS_PRI, "host memory access error, event 0%o, addr 0%o\n", mp->mslg_event, mp->mslg_unitid & 0xffffffff); break; case M_FM_TAPETRN: log(TMS_PRI, "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: log(TMS_PRI, "STI error, unit %d, event 0%o\n", mp->mslg_unit, mp->mslg_event); #ifdef notdef /* too painful to do with log() */ for(i = 0; i < 62;i++) mprintf("\t0x%x",mp->mslg_stiunsucc[i] & 0xff); mprintf("\n"); #endif break; case M_FM_STIDEL: log(TMS_PRI, "STI Drive Error Log, unit %d, event 0%o\n", mp->mslg_unit, mp->mslg_event); break; case M_FM_STIFEL: log(TMS_PRI, "STI Formatter Error Log, unit %d, event 0%o\n", mp->mslg_unit, mp->mslg_event); break; default: log(TMS_PRI, "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"); } } tmscp_match(parent, match, aux) struct device *parent; void *match, *aux; { return 0; } void tmscp_attach(parent, self, aux) struct device *parent, *self; void *aux; { } #endif