/* @(#)tmscp.c 7.14 (Berkeley) %G% */
static char *sccsid
= "@(#)tmscp.c 1.24 (ULTRIX) 1/21/86";
/************************************************************************
* Licensed from Digital Equipment Corporation *
* Digital Equipment Corporation *
* Maynard, Massachusetts *
* 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 *
* 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
* 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.
* 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
* 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.
* 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.
* 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.
* messed up previous edit..
* fixed bugs in 8200 and 750 buffered datapath handling.
* 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.
* 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_dsreg = flags|endcode
* 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.
* fix bua/bda map registers.
* Added timeout code to the probe routine, so if the controller
* fails to init in 10 seconds we return failed status.
* Changes for support of the VAX8200 were merged in.
* Changes for support of the VAX8600 were merged in.
#define NRSPL2 3 /* log2 number of response packets */
#define NCMDL2 3 /* log2 number of command packets */
#include "../vax/tmscp.h"
/* Software state per controller */
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) */
struct tmscpca tmscp_ca
; /* communications area */
struct mscp tmscp_rsp
[NRSP
]; /* response packets */
struct mscp tmscp_cmd
[NCMD
]; /* command packets */
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 */
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)
/* 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 */
#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. */
extern int hz
; /* Should find the right include */
#define printd if (tmscpdebug) printf
#define printd10 if(tmscpdebug >= 10) printf
int tmscpprobe(), tmscpslave(), tmscpattach(), tmscpintr();
struct mscp
*tmscpgetcp();
u_short tmscpstd
[] = { 0174500, 0 };
struct uba_driver tmscpdriver
=
{ tmscpprobe
, tmscpslave
, tmscpattach
, 0, tmscpstd
, DRVNAME
, tmsdinfo
, CTRLNAME
#define b_qsize b_resid /* queue size per drive, in tmsutab */
#define b_ubinfo b_resid /* Unibus mapping info, per buffer */
/*************************************************************************/
* 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).
caddr_t reg
; /* address of the IP register */
int ctlr
; /* index of controller in the tmscp_softc array */
register int br
, cvec
; /* MUST be 1st (r11 & r10): IPL and intr vec */
register struct tmscp_softc
*sc
= &tmscp_softc
[ctlr
];
/* ptr to software controller structure */
struct tmscpdevice
*tmscpaddr
; /* ptr to tmscpdevice struct (IP & SA) */
int count
; /* for probe delay time out */
br
= 0; cvec
= br
; br
= cvec
; reg
= reg
;
tmscpreset(0); tmscpintr(0);
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);
{ /* wait for at most 10 secs */
if((tmscpaddr
->tmscpsa
& TMSCP_STEP1
) != 0)
tmscpaddr
->tmscpsa
= TMSCP_ERR
|(NCMDL2
<<11)|(NRSPL2
<<8)|TMSCP_IE
|(sc
->sc_ivec
/4);
if((tmscpaddr
->tmscpsa
& TMSCP_STEP2
) != 0)
return(sizeof (struct tmscpdevice
));
* Try to find a slave (a drive) on the controller.
* If the controller is not in the run state, call init to initialize it.
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
];
struct tmscpdevice
*tmscpaddr
; /* ptr to IP & SA */
int i
; /* Something to write into to start */
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;
if(sc
->sc_state
!= S_RUN
)
printd("tmscpslave: ctlr not running: calling init \n");
if(!tmscpinit(ui
->ui_ctlr
))
* Wait for the controller to come into the run state or go idle.
* If it goes idle return.
while(sc
->sc_state
!= S_RUN
&& sc
->sc_state
!= S_IDLE
)
if (tmscpaddr
->tmscpsa
& TMSCP_ERR
&& i
)
printd("tmscp-device: fatal error (%o)\n", tmscpaddr
->tmscpsa
&0xffff);
if(sc
->sc_state
== S_IDLE
)
{ /* The tmscp device failed to initialize */
printf("tmscp controller failed to init\n");
/* The controller is up so see if the drive is there */
if(0 == (mp
= tmscpgetcp(um
)))
printf("tmscp can't get command packet\n");
/* 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*/
; /* Wait for some status */
printd("tmscpslave: status = %o\n",tms
->tms_status
& M_ST_MASK
);
tmscpip
[ui
->ui_ctlr
][ui
->ui_slave
] = 0;
if(!tms
->tms_type
) /* packet from a GTUNT */
return(0); /* Failed No such drive */
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.
register struct uba_device
*ui
; /* ptr to unibus dev struct */
tmscpip
[ui
->ui_ctlr
][ui
->ui_slave
] = ui
;
* 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");
utoctlr
[ui
->ui_unit
] = ui
->ui_ctlr
;
* TMSCP interrupt routine.
int d
; /* index to the controller */
register struct uba_ctlr
*um
= tmscpminfo
[d
];
register struct tmscpdevice
*tmscpaddr
= (struct tmscpdevice
*)um
->um_addr
;
register struct tmscp_softc
*sc
= &tmscp_softc
[d
];
register struct tmscp
*tm
= &tmscp
[d
];
printd10("tmscpintr: state %d, tmscpsa %o\n", sc
->sc_state
, tmscpaddr
->tmscpsa
);
* How the interrupt is handled depends on the state of the controller.
printf("tmscp%d: random interrupt ignored\n", d
);
/* Controller was in step 1 last, see if its gone to step 2 */
# 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) */
printd("still in step 1, delaying\n");
printf("failed to initialize, in step1: sa 0x%x", tmscpaddr
->tmscpsa
);
tmscpaddr
->tmscpsa
= ((int)&sc
->sc_tmscp
->tmscp_ca
.ca_ringbase
)
| ((cpu
== VAX_780
|| cpu
== VAX_8600
) ? TMSCP_PI
: 0);
/* Controller was in step 2 last, see if its gone to step 3 */
# 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) */
printd("still in step 2, delaying\n");
printf("failed to initialize, in step2: sa 0x%x", tmscpaddr
->tmscpsa
);
tmscpaddr
->tmscpsa
= ((int)&sc
->sc_tmscp
->tmscp_ca
.ca_ringbase
)>>16;
/* Controller was in step 3 last, see if its gone to step 4 */
# 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) */
printd("still in step 3, delaying\n");
printf("failed to initialize, in step3: sa 0x%x", tmscpaddr
->tmscpsa
);
* 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);
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);
* Initialize the data structures (response and command queues).
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
->av_forw
= bp
->av_back
= bp
;
mp
= &tmscp
[um
->um_ctlr
].tmscp_cmd
[0];
mp
->mscp_unit
= mp
->mscp_modifier
= 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_time
.val
[0] = 0;
mp
->mscp_time
.val
[1] = 0;
mp
->mscp_opcode
= M_OP_STCON
;
*((long *)mp
->mscp_dscptr
) |= TMSCP_OWN
|TMSCP_INT
;
i
= tmscpaddr
->tmscpip
; /* initiate polling */
printf("tmscp%d: interrupt in unknown state %d ignored\n",d
,sc
->sc_state
);
* 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);
* Check for a buffer purge request. (Won't happen w/ TK50 on Q22 bus)
UBAPURGE(um
->um_hd
->uh_uba
, tm
->tmscp_ca
.ca_bdp
);
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
++)
if (tm
->tmscp_ca
.ca_rspdsc
[i
]&TMSCP_OWN
)
tm
->tmscp_ca
.ca_rspdsc
[i
] |= TMSCP_OWN
;
* Check for command ring transition.
if (tm
->tmscp_ca
.ca_cmdint
)
printd("tmscpintr: command ring transition\n");
tm
->tmscp_ca
.ca_cmdint
= 0;
wakeup((caddr_t
)&tmscp_cp_wait
);
* 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.
register struct uba_device
*ui
;
register struct tmscp_softc
*sc
;
register struct tms_info
*tms
;
register struct mscp
*mp
;
register struct uba_ctlr
*um
;
struct tmscpdevice
*tmscpaddr
;
printd("tmscpopen unit %d\n",unit
);
if(tmscpdebug
)DELAY(10000);
if (unit
>= NTMS
|| (ui
= tmsdinfo
[unit
]) == 0 || ui
->ui_alive
== 0)
tms
= &tms_info
[ui
->ui_unit
];
sc
= &tmscp_softc
[ui
->ui_ctlr
];
tms
->tms_tpr
= tprintf_open();
if (sc
->sc_state
!= S_RUN
)
if (sc
->sc_state
== S_IDLE
)
if(!tmscpinit(ui
->ui_ctlr
))
printf("tmscp controller failed to init\n");
* 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
)
* Check to see if the device is really there.
* this code was taken from Fred Canters 11 driver
tmscpaddr
= (struct tmscpdevice
*) um
->um_addr
;
while(0 ==(mp
= tmscpgetcp(um
)))
sleep((caddr_t
)&tmscp_cp_wait
,PSWP
+1);
mp
->mscp_opcode
= M_OP_ONLIN
;
mp
->mscp_unit
= ui
->ui_slave
;
mp
->mscp_cmdref
= (long) & tms
->tms_type
;
/* need to sleep on something */
printd("tmscpopen: bring unit %d online\n",ui
->ui_unit
);
*((long *) mp
->mscp_dscptr
) |= TMSCP_OWN
| TMSCP_INT
;
* 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);
return(ENXIO
); /* Didn't go online */
* 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);
tmscpcommand(dev
, TMS_HIDENSITY
, 1);
* 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.
* 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.
register struct tms_info
*tms
;
register struct uba_device
*ui
;
ui
= tmsdinfo
[TMSUNIT(dev
)];
printd("tmscpclose: ctlr = %d\n",TMSCPCTLR(dev
));
printd("tmscpclose: unit = %d\n",TMSUNIT(dev
));
if(tmscpdebug
)DELAY(10000);
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);
printd("tmscpclose: clearing serex\n");
if(tmscpdebug
)DELAY(10000);
tmscpcommand(dev
, TMS_CSE
, 1);
tprintf_close(tms
->tms_tpr
);
* 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
tmscpcommand (dev
, com
, count
)
register struct uba_device
*ui
;
bp
= &ctmscpbuf
[ui
->ui_ctlr
];
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
))
sleep((caddr_t
)bp
, PRIBIO
);
bp
->b_flags
= B_BUSY
|B_READ
;
* 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.)
* In case of rewind from close, don't wait.
* This is the only case where count can be 0.
if (bp
->b_flags
&B_WANTED
)
* Find an unused command packet
register struct mscp
*mp
;
register struct tmscpca
*cp
;
register struct tmscp_softc
*sc
;
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.
printd10("tmscpgetcp: %d credits remain\n", sc
->sc_credits
);
if(((cp
->ca_cmddsc
[i
]&(TMSCP_OWN
|TMSCP_INT
))==TMSCP_INT
) &&
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
;
* Initialize a TMSCP device. Set up UBA mapping registers,
* initialize data structures, and start hardware
* initialization sequence.
int d
; /* index to the controller */
register struct tmscp_softc
*sc
;
register struct tmscp
*t
; /* communications area; cmd & resp packets */
struct tmscpdevice
*tmscpaddr
;
tmscpaddr
= (struct tmscpdevice
*)um
->um_addr
;
* Map the communications area and command
* and response packets into Unibus address
sc
->sc_ubainfo
= uballoc(um
->um_ubanum
, (caddr_t
)t
, sizeof (struct tmscp
), 0);
sc
->sc_tmscp
= (struct tmscp
*)(UBAI_ADDR(sc
->sc_ubainfo
));
* Start the hardware initialization sequence.
tmscpaddr
->tmscpip
= 0; /* start initialization */
while((tmscpaddr
->tmscpsa
& TMSCP_STEP1
) == 0)
printd("tmscpinit: tmscpsa = 0%o\n",tmscpaddr
->tmscpsa
);
if(tmscpaddr
->tmscpsa
& TMSCP_ERR
)
tmscpaddr
->tmscpsa
=TMSCP_ERR
|(NCMDL2
<<11)|(NRSPL2
<<8)|TMSCP_IE
|(sc
->sc_ivec
/4);
* Initialization continues in the interrupt routine.
* This code is convoluted. The majority of it was copied from the uda driver.
register struct uba_ctlr
*um
;
register struct buf
*bp
, *dp
;
register struct mscp
*mp
;
register struct tmscp_softc
*sc
;
register struct tms_info
*tms
;
register struct uba_device
*ui
;
struct tmscpdevice
*tmscpaddr
;
struct tmscp
*tm
= &tmscp
[um
->um_ctlr
];
char ioctl
; /* flag: set true if its an IOCTL command */
sc
= &tmscp_softc
[um
->um_ctlr
];
if ((dp
= um
->um_tab
.b_actf
) == NULL
)
* Release unneeded UBA resources and return
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.
um
->um_tab
.b_actf
= dp
->b_forw
;
continue; /* Need to check for loop */
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
)
"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 */
* Default is that last command was NOT a write command;
* if a write command is done it will be detected in tmscprsp.
if ((mp
= tmscpgetcp(um
)) == NULL
)
mp
->mscp_opcode
= M_OP_ONLIN
;
mp
->mscp_unit
= ui
->ui_slave
;
um
->um_tab
.b_actf
= dp
->b_forw
; /* remove from controller q */
*((long *)mp
->mscp_dscptr
) |= TMSCP_OWN
|TMSCP_INT
;
if (tmscpaddr
->tmscpsa
&TMSCP_ERR
)
printf("tmscp%d fatal error (0%o)\n",um
->um_ctlr
,
tmscpaddr
->tmscpsa
&0xffff);
i
= UBA_NEEDBDP
|UBA_CANTWAIT
;
i
= um
->um_ubinfo
|UBA_HAVEBDP
|UBA_CANTWAIT
;
* 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.
if (bp
== &ctmscpbuf
[um
->um_ctlr
])
if ((i
= ubasetup(um
->um_ubanum
, bp
, i
)) == 0)
break; /* When a command completes and */
/* frees a bdp tmscpstart will be called */
if ((mp
= tmscpgetcp(um
)) == NULL
)
printd("tmscpstart: GTUNT %d ubasetup = %d\n",ui
->ui_unit
, i
);
if(tmscpdebug
)DELAY(10000);
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 */
tempi
= i
& 0xfffffff; /* mask off bdp */
if ((mp
= tmscpgetcp(um
)) == NULL
)
if (!ioctl
) /* only need to release if NOT ioctl */
ubarelse(um
->um_ubanum
,&tempi
);
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.
printd("tmscpstart: doing ioctl cmd %d\n", bp
->b_resid
);
* 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
switch ((int)bp
->b_resid
) {
mp
->mscp_opcode
= M_OP_WRITM
;
mp
->mscp_opcode
= M_OP_REPOS
;
mp
->mscp_tmkcnt
= bp
->b_bcount
;
mp
->mscp_opcode
= M_OP_REPOS
;
mp
->mscp_modifier
= M_MD_REVRS
;
mp
->mscp_tmkcnt
= bp
->b_bcount
;
mp
->mscp_opcode
= M_OP_REPOS
;
mp
->mscp_modifier
= M_MD_OBJCT
;
mp
->mscp_reccnt
= bp
->b_bcount
;
mp
->mscp_opcode
= M_OP_REPOS
;
mp
->mscp_modifier
= M_MD_REVRS
| M_MD_OBJCT
;
mp
->mscp_reccnt
= bp
->b_bcount
;
* Clear serious exception is done for Rewind & Available cmds
mp
->mscp_opcode
= M_OP_REPOS
;
mp
->mscp_modifier
= M_MD_REWND
| M_MD_CLSEX
;
mp
->mscp_modifier
|= M_MD_IMMED
;
mp
->mscp_opcode
= M_OP_AVAIL
;
mp
->mscp_modifier
= M_MD_UNLOD
| M_MD_CLSEX
;
mp
->mscp_opcode
= M_OP_GTUNT
;
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_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 */
* 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
;
* 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
;
mp
->mscp_format
= M_TF_PE
& tms
->tms_fmtmenu
;
tms
->tms_format
= mp
->mscp_format
;
* 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 */
printf("Bad ioctl on tms unit %d\n", ui
->ui_unit
);
/* Need a no-op. Reposition no amount */
mp
->mscp_opcode
= M_OP_REPOS
;
} /* 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 */
*((long *)mp
->mscp_dscptr
) |= TMSCP_OWN
|TMSCP_INT
;
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);
i
= tmscpaddr
->tmscpip
; /* initiate polling */
* Move drive to the end of the controller queue
um
->um_tab
.b_actf
= dp
->b_forw
;
um
->um_tab
.b_actl
->b_forw
= dp
;
* Move buffer to I/O wait queue
dp
->b_actf
= bp
->av_forw
;
dp
= &tmscpwtab
[um
->um_ctlr
];
bp
->av_back
= dp
->av_back
;
dp
->av_back
->av_forw
= bp
;
if (tmscpaddr
->tmscpsa
&TMSCP_ERR
)
printf("tmscp%d: fatal error (0%o)\n", um
->um_ctlr
, tmscpaddr
->tmscpsa
&0xffff);
(void)tmscpinit(um
->um_ctlr
);
* Check for response ring transitions lost in the
for (i
= sc
->sc_lastrsp
;; i
++)
if (tm
->tmscp_ca
.ca_rspdsc
[i
]&TMSCP_OWN
)
tm
->tmscp_ca
.ca_rspdsc
[i
] |= TMSCP_OWN
;
* Process a response packet
register struct uba_ctlr
*um
;
register struct tmscp
*tm
;
register struct tmscp_softc
*sc
;
register struct mscp
*mp
;
register struct tms_info
*tms
;
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 */
printd("tmscprsp, opcode 0%o status 0%o\n",mp
->mscp_opcode
,mp
->mscp_status
&M_ST_MASK
);
* If it's an error log message (datagram),
* pass it on for more extensive processing.
if ((mp
->mscp_header
.tmscp_credits
& 0xf0) == 0x10)
tmserror(um
, (struct mslg
*)mp
);
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
)
printd("ctlr has %d credits\n", mp
->mscp_header
.tmscp_credits
& 0xf);
printd("ctlr timeout = %d\n", mp
->mscp_cnttmo
);
if (mp
->mscp_unit
>= NTMS
)
if ((ui
= tmscpip
[um
->um_ctlr
][mp
->mscp_unit
]) == 0)
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
;
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
];
* Link the drive onto the controller queue
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
ui
->ui_flags
= 1; /* mark it online */
tms
->tms_dsize
=(daddr_t
)mp
->mscp_maxwrt
;
printd("tmscprsp: unit %d online\n", mp
->mscp_unit
);
* 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): ' ')
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);
} /* end if st == M_ST_SUCC */
"tms%d: hard error bn%d: OFFLINE\n",
minor(bp
->b_dev
)&03, bp
->b_blkno
);
"tms%d: hard error: OFFLINE\n",
dp
->b_actf
= bp
->av_forw
;
if(mp
->mscp_cmdref
!=NULL
)
/* Seems to get lost sometimes in uda */
wakeup((caddr_t
)mp
->mscp_cmdref
);
* 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.
tms
->tms_type
= mp
->mscp_mediaid
;
* An endcode without an opcode (0200) is an invalid command.
* The mscp specification states that this would be a protocol
* type error, such as illegal opcodes. The mscp spec. also
* states that parameter error type of invalid commands should
* return the normal end message for the command. This does not appear
* to be the case. An invalid logical block number returned an endcode
* of 0200 instead of the 0241 (read) that was expected.
printf("tmscp%d: invalid cmd, endcode = %o, status=%o\n",
um
->um_ctlr
, mp
->mscp_opcode
, st
);
bp
= (struct buf
*)mp
->mscp_cmdref
;
* Unlink buffer from I/O wait queue.
* And signal iodone, so the higher level command can exit!
bp
->av_back
->av_forw
= bp
->av_forw
;
bp
->av_forw
->av_back
= bp
->av_back
;
dp
= &tmsutab
[ui
->ui_unit
];
case M_OP_WRITE
|M_OP_END
:
/* mark the last io op as a write */
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
case M_OP_AVAIL
|M_OP_END
:
printd("tmscprsp: position = %d\n", mp
->mscp_lbn
);
bp
= (struct buf
*)mp
->mscp_cmdref
;
* Only need to release buffer if the command was read or write.
* No ubasetup was done in "tmscpstart" if it was an ioctl cmd.
if (mp
->mscp_opcode
== (M_OP_READ
|M_OP_END
) ||
mp
->mscp_opcode
== (M_OP_WRITE
|M_OP_END
))
ubarelse(um
->um_ubanum
, (int *)&bp
->b_ubinfo
);
* Unlink buffer from I/O wait queue.
bp
->av_back
->av_forw
= bp
->av_forw
;
bp
->av_forw
->av_back
= bp
->av_back
;
if ((tmscpwtab
[um
->um_ctlr
].av_forw
== &tmscpwtab
[um
->um_ctlr
]) &&
ubarelse(um
->um_ubanum
, &um
->um_ubinfo
);
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);
dp
= &tmsutab
[ui
->ui_unit
];
if (st
== M_ST_OFFLN
|| st
== M_ST_AVLBL
)
ui
->ui_flags
= 0; /* mark unit offline */
tms
->tms_type
= mp
->mscp_mediaid
;
* Link the buffer onto the front of the drive queue
if ((bp
->av_forw
= dp
->b_actf
) == 0)
* Link the drive onto the controller queue
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
if (cpu
== VAX_750
&& um
->um_ubinfo
== 0)
um
->um_ubinfo
= uballoc(um
->um_ubanum
, (caddr_t
)0, 0, UBA_NEEDBDP
);
if (mp
->mscp_flags
& M_EF_SEREX
)
"tms%d: hard error bn%d\n",
minor(bp
->b_dev
)&03, bp
->b_blkno
);
errinfo(st
); /* produces more info */
printd("tmscprsp: error; status sub-code = 0%o, flags = 0%o\n",
(mp
->mscp_status
& 177740)>>5, mp
->mscp_flags
);
/* Hit a tape mark - Set serex flag to
* a special value so we can clear the
* serious exception on the next command.
* 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
;
tms
->tms_resid
= bp
->b_resid
;
case M_OP_GTUNT
|M_OP_END
:
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)
tms
->tms_type
= mp
->mscp_mediaid
;
tms
->tms_fmtmenu
= mp
->mscp_fmtmenu
;
tms
->tms_unitflgs
= mp
->mscp_unitflgs
;
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.
int st
; /* the status code */
printf("invalid command\n");
printf("command aborted\n");
printf("unit offline\n");
printf("unit write protected\n");
printf("compare error\n");
printf("host buffer access error\n");
printf("controller error\n");
printf("formatter error\n");
printf("BOT encountered\n");
printf("tape mark encountered\n");
printf("record data truncated\n");
printf("position lost\n");
printf("serious exception\n");
printf("LEOT detected\n");
* Manage buffers and perform block mode read and write operations.
register struct uba_device
*ui
;
register struct uba_ctlr
*um
;
register int unit
= TMSUNIT(bp
->b_dev
);
printd ("tmscpstrategy: bad unit # %d\n",unit
);
if (ui
== 0 || ui
->ui_alive
== 0)
* Link the buffer onto the drive queue
dp
= &tmsutab
[ui
->ui_unit
];
dp
->b_actl
->av_forw
= bp
;
* Link the drive onto the controller queue
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
* If the controller is not active, start it.
if (um
->um_tab
.b_active
== 0)
&& tmscpwtab
[um
->um_ctlr
].av_forw
== &tmscpwtab
[um
->um_ctlr
])
log(TMS_PRI
, "tmscpstrategy: ubinfo 0x%x\n",
um
->um_ubinfo
= uballoc(um
->um_ubanum
, (caddr_t
)0, 0, UBA_NEEDBDP
);
printd10("tmscpstrategy: Controller not active, starting it\n");
#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
];
struct tmscpdevice
*tmscpaddr
;
struct tmscp
*tmscp_ubaddr
;
register struct uba_regs
*uba
;
register struct uba_device
*ui
;
register struct tmscp
*tmscpp
;
# define phys(cast, addr) ((cast)((int)addr & 0x7fffffff))
ui
= phys(struct uba_device
*, tmsdinfo
[unit
]);
uba
= phys(struct uba_hd
*, ui
->ui_hd
)->uh_physuba
;
tmscpaddr
= (struct tmscpdevice
*)ui
->ui_physaddr
;
tmscpp
= phys(struct tmscp
*, &tmscpd
[ui
->ui_ctlr
]);
num
= btoc(sizeof(struct tmscp
)) + 1;
io
= &uba
->uba_map
[NUBMREG
-num
];
*(int *)io
++ = UBAMR_MRV
|(btop(tmscpp
)+i
);
tmscp_ubaddr
= (struct tmscp
*)(((int)tmscpp
& PGOFSET
)|((NUBMREG
-num
)<<9));
while ((tmscpaddr
->tmscpsa
& TMSCP_STEP1
) == 0)
if(tmscpaddr
->tmscpsa
& TMSCP_ERR
) return(EFAULT
);
tmscpaddr
->tmscpsa
= TMSCP_ERR
;
while ((tmscpaddr
->tmscpsa
& TMSCP_STEP2
) == 0)
if(tmscpaddr
->tmscpsa
& TMSCP_ERR
) return(EFAULT
);
tmscpaddr
->tmscpsa
= (short)&tmscp_ubaddr
->tmscp_ca
.ca_ringbase
;
while ((tmscpaddr
->tmscpsa
& TMSCP_STEP3
) == 0)
if(tmscpaddr
->tmscpsa
& TMSCP_ERR
) return(EFAULT
);
tmscpaddr
->tmscpsa
= (short)(((int)&tmscp_ubaddr
->tmscp_ca
.ca_ringbase
) >> 16);
while ((tmscpaddr
->tmscpsa
& TMSCP_STEP4
) == 0)
if(tmscpaddr
->tmscpsa
& TMSCP_ERR
) return(EFAULT
);
tmscpaddr
->tmscpsa
= TMSCP_GO
;
tmscpp
->tmscp_ca
.ca_Rspdsc
= (long)&tmscp_ubaddr
->tmscp_Rsp
.mscp_cmdref
;
tmscpp
->tmscp_ca
.ca_Cmddsc
= (long)&tmscp_ubaddr
->tmscp_Cmd
.mscp_cmdref
;
tmscpp
->tmscp_Cmd
.mscp_header
.tmscp_vcid
= 1; /* for tape */
tmscpp
->tmscp_Cmd
.mscp_cntflgs
= 0;
tmscpp
->tmscp_Cmd
.mscp_version
= 0;
if (tmscpcmd(M_OP_STCON
, tmscpp
, tmscpaddr
) == 0) {
tmscpp
->tmscp_Cmd
.mscp_unit
= ui
->ui_slave
;
if (tmscpcmd(M_OP_ONLIN
, tmscpp
, tmscpaddr
) == 0) {
blk
= num
> DBSIZE
? DBSIZE
: num
;
for (i
= 0; i
< blk
; i
++)
*(int *)io
++ = (btop(start
)+i
) | UBAMR_MRV
;
tmscpp
->tmscp_Cmd
.mscp_lbn
= btop(start
);
tmscpp
->tmscp_Cmd
.mscp_unit
= ui
->ui_slave
;
tmscpp
->tmscp_Cmd
.mscp_bytecnt
= blk
*NBPG
;
tmscpp
->tmscp_Cmd
.mscp_buffer
= (long) start
;
tmscpp
->tmscp_Cmd
.mscp_buffer
= 0;
if (tmscpcmd(M_OP_WRITE
, tmscpp
, tmscpaddr
) == 0)
* Perform a standalone tmscp command. This routine is only used by tmscpdump.
tmscpcmd(op
, tmscpp
, tmscpaddr
)
register struct tmscp
*tmscpp
;
struct tmscpdevice
*tmscpaddr
;
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);
if (tmscpp
->tmscp_ca
.ca_cmdint
)
tmscpp
->tmscp_ca
.ca_cmdint
= 0;
if (tmscpp
->tmscp_ca
.ca_rspint
)
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
);
* Catch ioctl commands, and call the "command" routine to do them.
tmscpioctl(dev
, cmd
, data
, 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 */
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 */
{TMS_WRITM
,TMS_FSF
,TMS_BSF
,TMS_FSR
,TMS_BSR
,TMS_REW
,TMS_OFFL
,TMS_SENSE
,
case MTIOCTOP
: /* tape operation */
mtop
= (struct mtop
*)data
;
callcount
= mtop
->mt_count
;
case MTREW
: case MTOFFL
: case MTNOP
:
case MTCACHE
: case MTNOCACHE
:
fcount
= 1; /* wait for this rewind */
} /* end switch mtop->mt_op */
if (callcount
<= 0 || fcount
<= 0)
tmscpcommand(dev
, tmsops
[mtop
->mt_op
], fcount
);
if ((mtop
->mt_op
== MTFSR
|| mtop
->mt_op
== MTBSR
) &&
if (bp
->b_flags
& B_ERROR
) /* like hitting BOT */
if ((error
= bp
->b_error
)==0)
* 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
;
* Reset (for raw mode use only).
register struct uba_ctlr
*um
;
register struct uba_device
*ui
;
register struct buf
*bp
, *dp
;
for (d
= 0; d
< NTMSCP
; d
++)
if ((um
= tmscpminfo
[d
]) == 0 || um
->um_ubanum
!= uban
||
um
->um_tab
.b_actf
= um
->um_tab
.b_actl
= 0;
tmscp_softc
[d
].sc_state
= S_IDLE
;
tmscp_softc
[d
].sc_mapped
= 0;
for (unit
= 0; unit
< NTMS
; unit
++)
if ((ui
= tmsdinfo
[unit
]) == 0)
if (ui
->ui_alive
== 0 || ui
->ui_mi
!= um
)
tmsutab
[unit
].b_active
= 0;
tmsutab
[unit
].b_qsize
= 0;
for (bp
= tmscpwtab
[d
].av_forw
; bp
!= &tmscpwtab
[d
]; bp
= nbp
)
* Link the buffer onto the drive queue
dp
= &tmsutab
[TMSUNIT(bp
->b_dev
)];
dp
->b_actl
->av_forw
= bp
;
* Link the drive onto the controller queue
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
* Process an error log message
* Only minimal decoding is done, only "useful"
* information is printed. Eventually should
* send message to an error logger.
register struct uba_ctlr
*um
;
register struct mslg
*mp
;
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
) {
log(TMS_PRI
, "controller error, event 0%o\n", mp
->mslg_event
);
log(TMS_PRI
, "host memory access error, event 0%o, addr 0%o\n",
mp
->mslg_event
, mp
->mslg_busaddr
);
log(TMS_PRI
, "tape transfer error, unit %d, grp 0x%x, event 0%o\n",
mp
->mslg_unit
, mp
->mslg_group
, mp
->mslg_event
);
log(TMS_PRI
, "STI error, unit %d, event 0%o\n",
mp
->mslg_unit
, mp
->mslg_event
);
/* too painful to do with log() */
mprintf("\t0x%x",mp
->mslg_stiunsucc
[i
] & 0xff);
log(TMS_PRI
, "STI Drive Error Log, unit %d, event 0%o\n",
mp
->mslg_unit
, mp
->mslg_event
);
log(TMS_PRI
, "STI Formatter Error Log, unit %d, event 0%o\n",
mp
->mslg_unit
, mp
->mslg_event
);
log(TMS_PRI
, "unknown error, unit %d, format 0%o, event 0%o\n",
mp
->mslg_unit
, mp
->mslg_format
, mp
->mslg_event
);
register long *p
= (long *)mp
;
for (i
= 0; i
< mp
->mslg_header
.tmscp_msglen
; i
+= sizeof(*p
))