NetBSD/sys/arch/vax/uba/tmscp.c
1995-07-05 08:24:45 +00:00

2134 lines
58 KiB
C

/* $NetBSD: tmscp.c,v 1.4 1995/07/05 08:24:45 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<<NRSPL2)
#define NCMD (1<<NCMDL2)
#include "tmscpreg.h"
#include "vax/vax/tmscpinf.h"
#include "vax/vax/mscpvar.h"
/* Software state per controller */
struct tmscp_softc {
short sc_state; /* state of controller */
short sc_mapped; /* Unibus map allocated for tmscp struct? */
int sc_ubainfo; /* Unibus mapping info */
struct tmscp *sc_tmscp; /* Unibus address of tmscp struct */
int sc_ivec; /* interrupt vector address */
short sc_credits; /* transfer credits */
short sc_lastcmd; /* pointer into command ring */
short sc_lastrsp; /* pointer into response ring */
short sc_ipl; /* interrupt priority (Q-bus) */
} tmscp_softc[NTMSCP];
struct tmscp {
struct tmscpca tmscp_ca; /* communications area */
struct mscp tmscp_rsp[NRSP]; /* response packets */
struct mscp tmscp_cmd[NCMD]; /* command packets */
} tmscp[NTMSCP];
/*
* Per drive-unit info
*/
struct tms_info {
daddr_t tms_dsize; /* Max user size from online pkt */
unsigned tms_type; /* Drive type int field */
int tms_resid; /* residual from last xfer */
u_char tms_endcode; /* last command endcode */
u_char tms_flags; /* last command end flags */
unsigned tms_status; /* Command status from last command */
char tms_openf; /* lock against multiple opens */
char tms_lastiow; /* last op was a write */
char tms_serex; /* set when serious exception occurs */
char tms_clserex; /* set when serex being cleared by no-op */
short tms_fmtmenu; /* the unit's format (density) menu */
short tms_unitflgs; /* unit flag parameters */
short tms_format; /* the unit's current format (density) */
tpr_t tms_tpr; /* tprintf handle */
} tms_info[NTMS];
struct uba_ctlr *tmscpminfo[NTMSCP];
struct uba_device *tmsdinfo[NTMS];
/*
* ifdef other tmscp devices here if they allow more than 1 unit/controller
*/
struct uba_device *tmscpip[NTMSCP][1];
struct buf ctmscpbuf[NTMSCP]; /* internal cmd buffer (for ioctls) */
struct buf tmsutab[NTMS]; /* Drive queue */
struct buf tmscpwtab[NTMSCP]; /* I/O wait queue, per controller */
int tmscpmicro[NTMSCP]; /* to store microcode level */
short utoctlr[NTMS]; /* Slave unit to controller mapping */
/* filled in by the slave routine */
/* Bits in minor device */
#define TMSUNIT(dev) (minor(dev)&03)
#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. */
void 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
/*
* 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 */
# 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.
*/
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->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(uba,vector,level,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;
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)
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(uba_hd[um->um_ubanum].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.
*/
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_hd *, 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; i<num; i++)
*(int *)io++ = UBAMR_MRV|(btop(tmscpp)+i);
tmscp_ubaddr = (struct tmscp *)(((int)tmscpp & PGOFSET)|((NUBMREG-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)((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");
}
}
#endif