home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
498b.lha
/
BTNtape_v2.0
/
tape.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-04-08
|
25KB
|
661 lines
/**** BTNtape Handler for SCSI tape drives ****/
/**** Author: Bob Rethemeyer (DrBob@cup.portal.com) ****/
#define TVERSION "-BTNTAPE V2.0 RAR-" ## __DATE__
/* (c) Copyright 1990, 1991 Robert Rethemeyer.
* This software may be freely distributed and redistributed,
* for non-commercial purposes, provided this notice is included.
*-----------------------------------------------------------------------
* BTNtape is an AmigaDOS device handler to make a simple DOS TAPE: device.
* It converts DOS packets for the device into I/O requests to a
* "SCSI-direct" compatible device driver. It is based on "my.handler"
* by Phillip Lindsay and a SCSI-direct program by Robert Mitchell.
* Source is ANSI C compliant. Compile with SAS/C v5 or Manx v5.
*
* This handler works in conjunction with the accompanying TapeMon program.
*----------------------------------------------------------------------------
*/
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/tasks.h>
#include <exec/libraries.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <devices/scsidisk.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/filehandler.h>
#include <stdio.h>
#include <string.h>
#if defined AZTEC_C
#include <functions.h>
/*#define strtoul strtol */
#elif defined LATTICE
#include <proto/exec.h>
#include <proto/intuition.h>
#endif
#include "tape.h"
#include "tplink.h"
/* sense keys */
#define NOS 0x00 /* no sense */
#define RCV 0x01 /* recovered error */
#define UAT 0x06 /* unit attention */
#define VOF 0x0d /* volume overflow */
/* pseudo sense keys returned by DoSense */
#define FMK 0x10 /* filemark */
#define EOM 0x11 /* end of tape */
#define ILI 0x12 /* incorr leng */
#define SERR 0x13 /* SCSI error */
struct things { /* a collection of things we will have to alloc */
UBYTE cmdbuff[16]; /* SCSI command buff */
UBYTE snsarea[64]; /* sense data buffer */
UBYTE inqdata[40]; /* inquiry data buff */
struct SCSICmd scsicmd; /* SCSIdirect cmd buff */
UBYTE pad[16]; /* SCSICmd is smaller in old include files */
} ;
/*======== Global data */
struct IntuitionBase *IntuitionBase;
UBYTE *cdb; /* pointer to tape command buffer */
UBYTE *sns; /* pointer to sense data buffer */
UBYTE *inq; /* pointer to inquiry data */
struct SCSICmd *cmd; /* pointer to scsidirect command */
struct IOStdReq *ior; /* pointer to io request structure*/
UBYTE *TapeBuff[2] /* pointers to 2 tape buffers */
={NULL,NULL};
struct tplink *linktp; /* pointer to link structure */
ULONG blknum; /* block number for io operation */
ULONG blksize = 512; /* bytes per tape block */
ULONG numblks = 1; /* number of blocks per io oper */
ULONG TBSize; /* bytes in a tape buffer */
ULONG rwlen; /* bytes in a tape read/write */
ULONG tranlen; /* bytes/blks in a read/write */
ULONG bugmask = 0; /* 2090A bug circumvention */
ULONG fmarks = 1; /* # file marks for write/skip */
long tpsize ; /* tape size in blocks */
long reserved = 0; /* number of reserved blks at BOT */
int tapenum; /* tape volume number */
int inprog = FALSE; /* io operation in progress flag */
char *z; /* scratch */
char *devname, nbuf[32]; /* file name reference from Open()*/
char dbb[80]; /* buffer for monitor messages */
UBYTE Lun = 0; /* Logical Unit number << 5 */
UBYTE fixedbit = 0; /* fixed-block mode bit for SEQ */
/*********************** Main program ********************************/
#ifdef AZTEC_C
#pragma intfunc(_main())
#endif
void _main(void)
{
struct tplink tpl; /* structure to link hndlr & mon */
struct Process *myproc; /* ptr to handler's process struct */
struct DosPacket *mypkt; /* ptr to dos packet sent */
struct DeviceNode *mynode; /* ptr to devnode passed in pkt Arg3 */
struct things *xarea; /* ptr to dynamic misc. areas */
ULONG dvnode; /* ptr to devnode passed in pkt Arg3 */
ULONG unit=99999; /* device SCSI unit address */
ULONG bufmemtype=0; /* type of mem for dynamic buffers */
ULONG gotopos=0; /* position to open at (blk/filemark)*/
char *driver; /* name of SCSI device driver */
UBYTE *dptr; /* ptr to next byte in dos buffer */
long dflags=0; /* device flags */
long iostat; /* status of previous read */
long dcnt; /* count of dos packet bytes to move */
long mcnt; /* count of bytes to move */
long Boff; /* current offset in tape buffer */
long remain; /* bytes remaining in tape buffer */
long Supra=FALSE; /* flag for Supra circumvention */
long pkcnt; /* flag to indicate data written */
long x;
int y=0;
int opmode=0; /* how has the handler been opened */
#define CLOSED 0 /* not doing anything */
#define READING 1 /* reading tape */
#define WRITING 2 /* writing tape */
#define RAWMODE 3 /* raw command mode */
#define UMODSEL 4 /* user mode select */
int Bn; /* current buffer number, 0 or 1 */
int dirty=FALSE; /* buffer has unwritten data in it */
int Ctl=CTLIMM; /* Controls overlapped dos/scsi I/O */
BYTE acksig; /* monitor acknowledge signal number */
UBYTE varblk = 0; /* variable-block flag for SEQ */
/*======== Startup */
IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
myproc = (struct Process *) FindTask(0L); /* find this process */
mypkt = taskwait(); /* wait for startup packet */
/* packet: Arg1=BSTR to name, Arg2=BSTR to startup string, Arg3=BPTR devnode*/
mynode = (struct DeviceNode *) BADDR(mypkt->dp_Arg3);
dvnode = (ULONG) mypkt->dp_Arg3;
/*======== Create linkage for the tape monitor: install pointer to tplink
======== structure in the free pointer of the task block, so the tape
======== monitor can find it after FindTask().
*/
tpl.keyword = "TapeHandler";
tpl.version = TVERSION;
tpl.devnode = (void *)mynode;
tpl.dbb = dbb;
tpl.unit = &unit;
tpl.Lun = &Lun;
linktp = &tpl;
((struct Task *)myproc)->tc_UserData = (APTR) linktp;
/*======== Extract info from mountlist Startup parameter. It may be
======== enclosed in quotes, and each item is separated by a single '/'.
======== First item is always the driver name, others may be in any order.
*/
z = (char *)BADDR(mypkt->dp_Arg2)+1 ; /* Arg2= BSTR to mountlist 'Startup'*/
if(*z=='\"') { /* remove quotes if any */
z++;
z[strlen(z)-1]= '\0' ;
}
driver = tpl.driver = z;
for(; *z != '/'; z++);
*(z++) = '\0';
toUPPER(z);
while (y!=100) {
switch(y=getstart(&x)) {
case 0: /* UN */ unit = (ULONG) x; break;
case 1: /* LU */ Lun = ((UBYTE)x & 7) << 5 ; break;
case 2: /* BS */ blksize = (ULONG) x; break;
case 3: /* NB */ numblks = (ULONG) x; break;
case 4: /* RB */ reserved = x; break;
case 5: /* BT */ bufmemtype = (ULONG) x; break;
case 6: /* FM */ fmarks = x & 0xff; break;
case 7: /* VB */ if(x) varblk = 1; break;
case 8: /* OV */ if(!x) Ctl = CTLWAIT; break;
case 9: /* DF */ dflags = x; break;
case 10: /* SU */ Supra = x; break;
case 11: /* C9 */ if(x) bugmask = 0x01000000; break;
case 99: /* ?? */ tpl.badparm=TRUE; break;
default: ;
}
}
rwlen = TBSize = numblks * blksize; /* size of a tape buffer */
if(Supra) rwlen = 0;
/*======== Allocate some memory for non-data buffers */
if( !(xarea = (struct things *)
AllocMem(sizeof(struct things), bufmemtype | MEMF_CLEAR) ))
{ returnpkt(mypkt,DOSFALSE,ERROR_NO_FREE_STORE);
CloseLibrary((struct Library *)IntuitionBase);
return;
}
cdb = &xarea->cmdbuff[0];
sns = &xarea->snsarea[0];
inq = tpl.inquiry = &xarea->inqdata[0];
cmd = &xarea->scsicmd;
ior = (struct IOStdReq *) CreateExtIO( CreatePort(0,0),
sizeof(struct IOStdReq));
/*======== Open the SCSIdirect device */
if ( OpenDevice(driver,unit,(struct IORequest *)ior,dflags) ) {
returnpkt(mypkt,DOSFALSE,ERROR_INVALID_COMPONENT_NAME);
CloseLibrary((struct Library *)IntuitionBase);
FreeMem(xarea,sizeof(struct things));
return;
}
mynode->dn_Task = &myproc->pr_MsgPort; /* install handler taskid */
returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2); /* reply to initial packet */
/*======== Allocate the signal that TapeMon will
======== use to acknowledge MPR requests.
*/
acksig = AllocSignal(-1);
if(acksig != -1) tpl.handsig = 1UL << acksig;
/* else { monitor will not attempt to signal us } */
/*======= Find the SCSI device type by INQUIRY. Sequential or direct access?
*/
if(TapeIO(INQUIRY,0,CTLWAIT)) TapeIO(TSENSE,0,CTLWAIT);
inq[36] = 0; /* null-terminate vendor info */
if(SEQ) {
reserved = 0;
fixedbit = varblk ^ 0x01; /* fixed or variable block mode */
tranlen = (fixedbit) ? numblks : TBSize;
}
else /* DAC */ tranlen = numblks;
/* =========== The main packet processing loop =============== */
for (;;) {
mypkt = taskwait(); /* wait for a packet */
switch(mypkt->dp_Type) {
case ACTION_FINDINPUT: /*----------- Open() ------------*/
case ACTION_FINDOUTPUT:
if(opmode != CLOSED) {
returnpkt(mypkt,DOSFALSE,ERROR_OBJECT_IN_USE);
break;
}
/* Allocate storage for buffers */
TapeBuff[0] = (UBYTE *) AllocMem(TBSize, bufmemtype | MEMF_CLEAR);
TapeBuff[1] = (UBYTE *) AllocMem(TBSize, bufmemtype | MEMF_CLEAR);
if (!TapeBuff[0] || !TapeBuff[1]) {
freestuff();
returnpkt(mypkt,DOSFALSE,ERROR_NO_FREE_STORE);
MPR0("Can't get memory for tape buffers\n");
break;
}
/* Determine open mode */
z = (char *)BADDR(mypkt->dp_Arg3);
x = (UBYTE)(*z);
devname = (char *)memcpy(nbuf, z+1, x);
*(devname+x) = '\0';
toUPPER(devname);
for(z=devname; *(z++)!=':' ;);
if (!strcmp(z,"RAWCMD")) opmode=RAWMODE;
else if(!strcmp(z,"MODESEL")) opmode=UMODSEL;
else { /* normal read/write */
opmode = (mypkt->dp_Type==ACTION_FINDINPUT) ? READING : WRITING;
if(!strcmp(z,"*")) gotopos=blknum; /* current position */
else if(*z>='0' && *z<='9') /* specific position */
gotopos = (ULONG) strtoul (z,NULL,0);
else if(*z=='\0') gotopos=0; /* beginning of tape */
else goto BADNAME;
}
if(opmode>=RAWMODE && mypkt->dp_Type==ACTION_FINDINPUT) {
BADNAME:
freestuff(); /* can't read from raw/ms */
opmode=CLOSED;
returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
break;
}
/* Check device ready */
if(x=TapeIO(TREADY,0,CTLWAIT)) {
y=DoSense(x);
if (y==UAT) blknum = reserved;
else if(y==NOS) ;
else {
freestuff();
opmode=CLOSED;
returnpkt(mypkt,DOSFALSE,ERROR_DEVICE_NOT_MOUNTED);
break;
}
}
if(opmode<=WRITING) {
/* Check write-prot, block length */
if(TapeIO(MDSNS,0,CTLWAIT)) TapeIO(TSENSE,0,CTLWAIT);
else {
if(opmode==WRITING && (sns[2] & 0x80)) {
freestuff();
opmode=CLOSED;
returnpkt(mypkt,DOSFALSE,ERROR_DISK_WRITE_PROTECTED);
break;
}
if(sns[3] >= 8) {
x = *((long *) &sns[8]) & 0x00ffffff; /* get block len */
if(x != blksize) { /* set block size with Mode Select */
TapeIO(MDSET,(int)varblk,CTLWAIT);
TapeIO(TSENSE,0,CTLWAIT);
}
}
}
/* Position tape */
if(gotopos==0) { /* tape: */
TapeIO(TREWIND,0,CTLWAIT);
blknum = reserved;
}
else if(gotopos != blknum) { /* tape:n */
if(DAC) blknum = gotopos;
else {
TapeIO(TREWIND,0,CTLWAIT);
if(x=TapeIO(TSKIP,(int)gotopos,CTLWAIT)) DoSense(x);
blknum = 0;
}
}
else if(SEQ) blknum = 0; /* tape:* */
/* Get capacity for 3M tape */
if(DAC) {
tpsize = 0x7fffffff;
if(TapeIO(RDCAP,0,CTLWAIT)) TapeIO(TSENSE,0,CTLWAIT);
else {
tpsize = ((sns[2] << 8) | sns[3]) + 1;
MPR1("Capacity: %u blocks\n", tpsize);
}
}
}
dirty=FALSE; inprog=FALSE; Boff=0; pkcnt=0; tapenum=1;
MPR2("%s Opened at block %u\n",devname,blknum);
if(opmode==READING) { /* for reads, prefetch 1st buffer */
iostat = TapeIO(TREAD,0,CTLWAIT);
remain=0; Bn=1;
}
else { remain=TBSize; Bn=0; iostat=0; }
returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);
break;
case ACTION_END: /*----------- Close() -----------*/
switch(opmode) {
case CLOSED:
returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN); break;
case RAWMODE:
if(x=TapeIO(RAWCMD,Bn,CTLWAIT)) DoSense(x); break;
case UMODSEL:
if(x=TapeIO(USRMODE,Bn,CTLWAIT)) DoSense(x); break;
case READING:
if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
if(iostat) DoSense(iostat);
break;
case WRITING:
if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
if(iostat) iostat = wrteot(Bn^1,1,iostat);
if(iostat==0) {
if(dirty) {
memset(&TapeBuff[Bn][Boff],0,remain);
iostat = TapeIO(TWRITE,Bn,CTLWAIT);
}
if(iostat) iostat = wrteot(Bn,0,iostat);
}
if(iostat) { MPR0("Error writing final block\n"); }
else if(pkcnt) {
blknum += numblks; x=0;
if(SEQ) x=TapeIO(WFMARK,0,CTLWAIT);
if(x) DoSense(x);
}
}
returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);
opmode=CLOSED;
freestuff();
MPR1("Closed at block %u\n",blknum);
break;
case ACTION_READ: /*----------- Read() ------------*/
if(opmode != READING) {
MPR0("Function/mode error\n");
mypkt->dp_Arg3 = -1;
goto RDEND;
}
dptr = (UBYTE *) mypkt->dp_Arg2;
dcnt = mypkt->dp_Arg3;
while(dcnt) {
if(remain==0) {
if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
if(iostat) { /* check status of previous read */
y=DoSense(iostat); iostat=0;
switch(y) {
case RCV:
y=0; break;
case FMK:
mypkt->dp_Arg3 = 0; break;
case VOF:
case EOM: /* End of Tape: (sequential) */
if(NewTape()) { /* ask for new tape */
y=0; /* and reread buffer */
iostat=TapeIO(TREAD,(Bn^1),CTLWAIT);
if(iostat) y=DoSense(iostat); /* and fall thru */
else break;
}
default:
MPR0("Error during read\n");
mypkt->dp_Arg3 = -1;
}
if(y) goto RDEND;
}
blknum += numblks;
iostat = TapeIO(TREAD,Bn,Ctl); /* start refilling this buffer */
Bn ^= 1; /* switch to other (full) buffer */
remain = TBSize;
Boff = 0;
}
mcnt = (dcnt>remain) ? remain : dcnt;
memcpy (dptr, &TapeBuff[Bn][Boff], mcnt);
dcnt -= mcnt ;
Boff += mcnt ;
dptr += mcnt ;
remain -= mcnt ;
}
RDEND:
returnpkt(mypkt,mypkt->dp_Arg3,mypkt->dp_Arg2);
break;
case ACTION_WRITE: /*----------- Write() -----------*/
if(opmode < WRITING) {
MPR0("Function/mode error\n");
mypkt->dp_Arg3 = -1;
goto WRTEND;
}
pkcnt++;
dptr = (UBYTE *) mypkt->dp_Arg2;
dcnt = mypkt->dp_Arg3;
while (dcnt) {
if(dcnt >= remain) {
memcpy (&TapeBuff[Bn][Boff], dptr, remain);
if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
if(iostat) { /* check status of previous write */
if(iostat = wrteot(Bn^1,1,iostat)) { /* possible EOT */
mypkt->dp_Arg3 = -1;
MPR0("Error during write\n");
iostat=0; dirty=FALSE; pkcnt=0;
goto WRTEND;
}
}
iostat = TapeIO(TWRITE,Bn,Ctl); /* start writing full buffer */
blknum += numblks;
dcnt -= remain;
dptr += remain;
Boff = 0;
remain= TBSize;
Bn ^= 1; /* switch to other (empty) buffer */
dirty = FALSE;
}
else {
memcpy (&TapeBuff[Bn][Boff], dptr, dcnt);
remain -= dcnt;
Boff += dcnt;
dcnt = 0;
dirty = TRUE;
}
}
WRTEND:
returnpkt(mypkt,mypkt->dp_Arg3,mypkt->dp_Res2);
break;
case ACTION_CURRENT_VOLUME: /* info not supported */
returnpkt(mypkt,dvnode,DOSFALSE);
break;
case ACTION_LOCATE_OBJECT: /* lock */
returnpkt(mypkt,mypkt->dp_Arg1,mypkt->dp_Res2);
break;
case ACTION_FREE_LOCK: /* unlock */
returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);
break;
case ACTION_FLUSH: /* flush buffers, NOOP */
returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);
break;
default: /* others not supported */
returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
MPR1("Unsupported_Pkt=%d\n",mypkt->dp_Type);
} /* end of switch */
} /* end of loop */
} /* end of _main() */
/**************************************************************************/
int DoSense(long iocode)
{
/* iocode is (ioerr<<8 + cmdstatus) */
UBYTE snskey;
static char *snstext[] = {
"NONE" , "RECOVERED ERROR", "NOT READY", "MEDIUM ERROR",
"HARDWARE ERROR", "ILLEGAL REQUEST", "UNIT ATTENTION", "DATA PROTECT",
"BLANK CHECK", "KEY=9", "COPY ABORTED", "ABORTED COMMAND", "KEY=C",
"VOLUME OVERFLOW", "KEY=E", "KEY=F", "FILEMARK", "END-OF-MEDIUM",
"INCORRECT BLOCK LENGTH"
};
static char *sderror[] = {
"SELF-UNIT", "DMA", "PHASE", "PARITY", "SELECT-TIMEOUT"
};
iocode=iocode>>8;
if(iocode==0 || iocode==HFERR_BadStatus) {
TapeIO(TSENSE,0,CTLWAIT);
if((sns[0] & 0x70)==0x70) { /* extended */
if (sns[2] & 0x80) snskey = FMK; /* pseudo sense keys */
else if(sns[2] & 0x40) snskey = EOM;
else if(sns[2] & 0x20) snskey = ILI;
else snskey = sns[2] & 0x0f; /* real sense key */
}
else snskey = sns[0] & 0x0f; /* non-extended */
linktp->sense = snstext[snskey]; /* keep last error info */
linktp->xsns1 = sns[12];
linktp->xsns2 = sns[13];
linktp->sns = sns; /* flag for tapemon to print all data */
MPR3("Sense: %s, other= %02X,%02X\n", snstext[snskey], sns[12],sns[13]);
linktp->sns = NULL;
return((int)snskey);
}
else { MPR1("SCSI %s ERROR\n",sderror[iocode-40]);
return(SERR);
}
}
/**************************************************************************/
/* wrteot: handle messiness that happens when writing at end of tape.
Returns non-zero status if not EOT or tape swap unsuccessful. */
long wrteot(int bfn, int dec, long stat)
{
long ios = stat;
int s = DoSense(ios);
if(s==EOM || s==VOF) { /* EOT? */
if(NewTape()) { /* ask for new tape */
if(dec) blknum -= numblks; /* and rewrite old buffer */
ios=TapeIO(TWRITE,bfn,CTLWAIT);
if(ios) s=DoSense(ios);
if(dec) blknum += numblks;
}
}
if(s==RCV) ios=0; /* ignore recoveries */
return(ios);
}
/**************************************************************************/
void freestuff(void)
{
if(TapeBuff[0]) FreeMem(TapeBuff[0],TBSize);
if(TapeBuff[1]) FreeMem(TapeBuff[1],TBSize);
TapeBuff[0] = TapeBuff[1] = NULL;
return;
}
/**************************************************************************/
/* getstart:
Given pointer (z) to a string of the form "SS-nn/..."
returns a number corresponding to SS, the binary value of nn,
and the pointer is updated to point after the slash.
*/
int getstart(long *num)
{
extern char *z;
#define NOTFND 99
#define ENDLST 100
#define NKEYS 12 /* number of keywords */
static char *keys[NKEYS] =
{ "UN","LU","BS","NB","RB","BT","FM","VB","OV","DF","SU","C9" };
/* 0 1 2 3 4 5 6 7 8 9 10 11 */
char *ii, *jj; int kk;
if (*z == '\0') return(ENDLST); /* return if end of string */
for (ii=z; *ii != '-'; ii++); /* find the dash */
for (jj=z; *jj != '/' && *jj !='\0'; jj++); /* find the slash */
*ii = '\0'; *jj = '\0'; /* null-terminate name & number */
*num = (long) strtoul (++ii,NULL,0); /* return converted number */
ii = z; z = ++jj; /* update ptr to next item */
for (kk=0; kk <= NKEYS; kk++) { /* search for keyword */
if (*ii == *keys[kk] && *(ii+1) == *(keys[kk]+1) )
return(kk); /* return index of keyword if found */
}
return(NOTFND); /* didn't find keyword */
}
/**************************************************************************
MonPrint requests that the TapeMon program print the message in 'dbb'.
Since this handler cannot do DOS I/O, it must beg the TapeMon program,
possibly running in a CLI somewhere, to do the printf for it. If the
TapeMon is running, it will have installed a pointer to its task in
the link structure, and we can Signal() it.
*/
void MonPrint(void)
{
if(linktp->montask) {
Signal(linktp->montask, linktp->monsig);
Wait (linktp->handsig);
Signal(linktp->montask, linktp->monsig);
Wait (linktp->handsig);
}
return;
}
/**************************************************************************/
/* toUPPER: convert string to upper case */
void toUPPER(char *zz)
{
for(; *zz != '\0'; zz++) if(*zz >= 'a') *zz -= 0x20;
return;
}
/**************************************************************************
* NewTape() Displays a requester asking user to insert a new tape.
*/
long NewTape(void)
{
long choice;
static char rqm[40];
static struct IntuiText rtxt[] = {
{ AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
AUTOITEXTFONT, (UBYTE *) &rqm[0], AUTONEXTTEXT },
{ AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
AUTOITEXTFONT, (UBYTE *) "Continue", AUTONEXTTEXT },
{ AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
AUTOITEXTFONT, (UBYTE *) "Abort", AUTONEXTTEXT }
};
MPR1("Time to insert tape# %d\n",++tapenum);
sprintf(rqm," Insert tape %d for %s",tapenum,devname);
choice=AutoRequest(NULL,&rtxt[0],&rtxt[1],&rtxt[2],NULL,NULL,250,50);
if(choice) {
DoSense(TapeIO(TREADY,0,CTLWAIT)); /* eat unit-atten status */
if(TapeIO(TREWIND,0,CTLWAIT)) TapeIO(TSENSE,0,CTLWAIT);
if(DAC) blknum=reserved; /* reset block number */
}
return(choice);
}