[comp.sources.amiga] v91i081: BTNTape 2.0 - SCSI tape drive handler, Part02/02

amiga-request@ab20.larc.nasa.gov (Amiga Sources/Binaries Moderator) (04/05/91)

Submitted-by: DrBob@cup.portal.com
Posting-number: Volume 91, Issue 081
Archive-name: devices/btntape-2.0/part02

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 2)."
# Contents:  tape.c tape.doc
# Wrapped by tadguy@ab20 on Fri Apr  5 10:06:39 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'tape.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tape.c'\"
else
echo shar: Extracting \"'tape.c'\" \(25153 characters\)
sed "s/^X//" >'tape.c' <<'END_OF_FILE'
X/****     BTNtape Handler for SCSI tape drives        ****/
X/**** Author: Bob Rethemeyer  (DrBob@cup.portal.com)  ****/
X
X#define TVERSION "-BTNTAPE V2.0 RAR-" ## __DATE__
X
X/*  (c) Copyright 1990, 1991  Robert Rethemeyer.
X *  This software may be freely distributed and redistributed,
X *  for non-commercial purposes, provided this notice is included.
X *-----------------------------------------------------------------------
X *  BTNtape is an AmigaDOS device handler to make a simple DOS TAPE: device.
X *  It converts DOS packets for the device into I/O requests to a
X *  "SCSI-direct" compatible device driver.  It is based on "my.handler"
X *  by Phillip Lindsay and a SCSI-direct program by Robert Mitchell.
X *  Source is ANSI C compliant.  Compile with SAS/C v5 or Manx v5.
X *
X *  This handler works in conjunction with the accompanying TapeMon program.
X *----------------------------------------------------------------------------
X */
X
X#include <exec/types.h>
X#include <exec/nodes.h>
X#include <exec/lists.h>
X#include <exec/ports.h>
X#include <exec/tasks.h>
X#include <exec/libraries.h>
X#include <exec/io.h>
X#include <exec/memory.h>
X#include <devices/scsidisk.h>
X#include <intuition/intuition.h>
X#include <libraries/dos.h>
X#include <libraries/dosextens.h>
X#include <libraries/filehandler.h>
X#include <stdio.h>
X#include <string.h>
X
X#if defined AZTEC_C
X  #include <functions.h>
X/*#define strtoul strtol */
X#elif defined LATTICE
X  #include <proto/exec.h>
X  #include <proto/intuition.h>
X#endif
X
X#include "tape.h"
X#include "tplink.h"
X
X/* sense keys */
X#define NOS 0x00  /* no sense */
X#define RCV 0x01  /* recovered error */
X#define UAT 0x06  /* unit attention */
X#define VOF 0x0d  /* volume overflow */
X/* pseudo sense keys returned by DoSense */
X#define FMK 0x10  /* filemark */
X#define EOM 0x11  /* end of tape */
X#define ILI 0x12  /* incorr leng */
X#define SERR 0x13 /* SCSI error  */
X
Xstruct things {  /* a collection of things we will have to alloc */
X         UBYTE cmdbuff[16];         /* SCSI command buff */
X         UBYTE snsarea[64];         /* sense data buffer */
X         UBYTE inqdata[40];         /* inquiry data buff */
X         struct SCSICmd scsicmd;    /* SCSIdirect cmd buff */
X         UBYTE pad[16];  /* SCSICmd is smaller in old include files */
X         } ;
X
X/*======== Global data */
X
Xstruct IntuitionBase *IntuitionBase;
XUBYTE           *cdb;          /* pointer to tape command buffer */
XUBYTE           *sns;          /* pointer to sense data buffer   */
XUBYTE           *inq;          /* pointer to inquiry data        */
Xstruct SCSICmd  *cmd;          /* pointer to scsidirect command  */
Xstruct IOStdReq *ior;          /* pointer to io request structure*/
XUBYTE           *TapeBuff[2]   /* pointers to 2 tape buffers     */
X                            ={NULL,NULL};
Xstruct tplink   *linktp;       /* pointer to link structure      */
XULONG  blknum;                 /* block number for io operation  */
XULONG  blksize = 512;          /* bytes per tape block           */
XULONG  numblks = 1;            /* number of blocks per io oper   */
XULONG  TBSize;                 /* bytes in a tape buffer         */
XULONG  rwlen;                  /* bytes in a tape read/write     */
XULONG  tranlen;                /* bytes/blks in a read/write     */
XULONG  bugmask = 0;            /* 2090A bug circumvention        */
XULONG  fmarks  = 1;            /* # file marks for write/skip    */
Xlong   tpsize ;                /* tape size in blocks            */
Xlong   reserved = 0;           /* number of reserved blks at BOT */
Xint    tapenum;                /* tape volume number             */
Xint    inprog = FALSE;         /* io operation in progress flag  */
Xchar   *z;                     /* scratch                        */
Xchar   *devname, nbuf[32];     /* file name reference from Open()*/
Xchar   dbb[80];                /* buffer for monitor messages    */
XUBYTE  Lun = 0;                /* Logical Unit number << 5       */
XUBYTE  fixedbit = 0;           /* fixed-block mode bit for SEQ   */
X
X/***********************  Main program  ********************************/
X#ifdef AZTEC_C
X  #pragma intfunc(_main())
X#endif
Xvoid _main(void)
X{
X struct tplink      tpl;       /* structure to link hndlr & mon     */
X struct Process     *myproc;   /* ptr to handler's process struct   */
X struct DosPacket   *mypkt;    /* ptr to dos packet sent            */
X struct DeviceNode  *mynode;   /* ptr to devnode passed in pkt Arg3 */
X struct things      *xarea;    /* ptr to dynamic misc. areas        */
X ULONG   dvnode;        /* ptr to devnode passed in pkt Arg3 */
X ULONG   unit=99999;    /* device SCSI unit address          */
X ULONG   bufmemtype=0;  /* type of mem for dynamic buffers   */
X ULONG   gotopos=0;     /* position to open at (blk/filemark)*/
X char    *driver;       /* name of SCSI device driver        */
X UBYTE   *dptr;         /* ptr to next byte in dos buffer    */
X long    dflags=0;      /* device flags                      */
X long    iostat;        /* status of previous read           */
X long    dcnt;          /* count of dos packet bytes to move */
X long    mcnt;          /* count of bytes to move            */
X long    Boff;          /* current offset in tape buffer     */
X long    remain;        /* bytes remaining in tape buffer    */
X long    Supra=FALSE;   /* flag for Supra circumvention      */
X long    pkcnt;         /* flag to indicate data written     */
X long    x;
X int     y=0;
X int     opmode=0;      /* how has the handler been opened   */
X    #define CLOSED  0       /* not doing anything */
X    #define READING 1       /* reading tape */
X    #define WRITING 2       /* writing tape */
X    #define RAWMODE 3       /* raw command mode */
X    #define UMODSEL 4       /* user mode select */
X int     Bn;            /* current buffer number, 0 or 1     */
X int     dirty=FALSE;   /* buffer has unwritten data in it   */
X int     Ctl=CTLIMM;    /* Controls overlapped dos/scsi I/O  */
X BYTE    acksig;        /* monitor acknowledge signal number */
X UBYTE   varblk = 0;    /* variable-block flag for SEQ       */
X
X/*======== Startup */
X
X IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
X myproc  = (struct Process *) FindTask(0L);          /* find this process */
X mypkt = taskwait();                           /* wait for startup packet */
X /* packet: Arg1=BSTR to name, Arg2=BSTR to startup string, Arg3=BPTR devnode*/
X mynode = (struct DeviceNode *) BADDR(mypkt->dp_Arg3);
X dvnode = (ULONG) mypkt->dp_Arg3;
X
X/*======== Create linkage for the tape monitor:  install pointer to tplink
X  ======== structure in the free pointer of the task block, so the tape
X  ======== monitor can find it after FindTask().
X*/
X tpl.keyword = "TapeHandler";
X tpl.version = TVERSION;
X tpl.devnode = (void *)mynode;
X tpl.dbb     = dbb;
X tpl.unit    = &unit;
X tpl.Lun     = &Lun;
X linktp = &tpl;
X ((struct Task *)myproc)->tc_UserData = (APTR) linktp;
X
X/*======== Extract info from mountlist Startup parameter. It may be
X  ======== enclosed in quotes, and each item is separated by a single '/'.
X  ======== First item is always the driver name, others may be in any order.
X*/
X z = (char *)BADDR(mypkt->dp_Arg2)+1 ;  /* Arg2= BSTR to mountlist 'Startup'*/
X if(*z=='\"')  {                      /* remove quotes if any */
X     z++;
X     z[strlen(z)-1]= '\0' ;
X }
X driver = tpl.driver = z;
X for(; *z != '/'; z++);
X *(z++) = '\0';
X toUPPER(z);
X while (y!=100) {
X   switch(y=getstart(&x)) {
X     case 0:  /* UN */  unit = (ULONG) x;  break;
X     case 1:  /* LU */  Lun  = ((UBYTE)x & 7) << 5 ;  break;
X     case 2:  /* BS */  blksize = (ULONG) x;  break;
X     case 3:  /* NB */  numblks = (ULONG) x;  break;
X     case 4:  /* RB */  reserved = x;  break;
X     case 5:  /* BT */  bufmemtype = (ULONG) x;  break;
X     case 6:  /* FM */  fmarks = x & 0xff;  break;
X     case 7:  /* VB */  if(x) varblk = 1;   break;
X     case 8:  /* OV */  if(!x) Ctl = CTLWAIT; break;
X     case 9:  /* DF */  dflags = x; break;
X     case 10: /* SU */  Supra = x;  break;
X     case 11: /* C9 */  if(x) bugmask = 0x01000000; break;
X     case 99: /* ?? */  tpl.badparm=TRUE; break;
X     default: ;
X   }
X }
X rwlen = TBSize = numblks * blksize;   /* size of a tape buffer */
X if(Supra)  rwlen = 0;
X
X/*======== Allocate some memory for non-data buffers */
X
X if( !(xarea = (struct things *)
X           AllocMem(sizeof(struct things), bufmemtype | MEMF_CLEAR) ))
X           { returnpkt(mypkt,DOSFALSE,ERROR_NO_FREE_STORE);
X             CloseLibrary((struct Library *)IntuitionBase);
X             return;
X           }
X cdb = &xarea->cmdbuff[0];
X sns = &xarea->snsarea[0];
X inq = tpl.inquiry = &xarea->inqdata[0];
X cmd = &xarea->scsicmd;
X ior = (struct IOStdReq *) CreateExtIO( CreatePort(0,0),
X                                         sizeof(struct IOStdReq));
X
X/*======== Open the SCSIdirect device */
X
X if ( OpenDevice(driver,unit,(struct IORequest *)ior,dflags)  )  {
X    returnpkt(mypkt,DOSFALSE,ERROR_INVALID_COMPONENT_NAME);
X    CloseLibrary((struct Library *)IntuitionBase);
X    FreeMem(xarea,sizeof(struct things));
X    return;
X }
X mynode->dn_Task = &myproc->pr_MsgPort;    /* install handler taskid */
X returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);  /* reply to initial packet */
X
X/*======== Allocate the signal that TapeMon will
X  ======== use to acknowledge MPR requests.
X*/
X acksig = AllocSignal(-1);
X if(acksig != -1) tpl.handsig = 1UL << acksig;
X /* else { monitor will not attempt to signal us } */
X
X/*======= Find the SCSI device type by INQUIRY.  Sequential or direct access?
X*/
X if(TapeIO(INQUIRY,0,CTLWAIT)) TapeIO(TSENSE,0,CTLWAIT);
X inq[36] = 0;  /* null-terminate vendor info */
X if(SEQ) {
X   reserved = 0;
X   fixedbit = varblk ^ 0x01;  /* fixed or variable block mode */
X   tranlen = (fixedbit) ? numblks : TBSize;
X }
X else /* DAC */ tranlen = numblks;
X
X/* =========== The main packet processing loop =============== */
X
X for (;;)  {
X   mypkt = taskwait();          /* wait for a packet */
X   switch(mypkt->dp_Type) {
X
X     case ACTION_FINDINPUT:  /*----------- Open() ------------*/
X     case ACTION_FINDOUTPUT:
X          if(opmode != CLOSED)  {
X             returnpkt(mypkt,DOSFALSE,ERROR_OBJECT_IN_USE);
X             break;
X          }
X          /* Allocate storage for buffers */
X          TapeBuff[0] = (UBYTE *) AllocMem(TBSize, bufmemtype | MEMF_CLEAR);
X          TapeBuff[1] = (UBYTE *) AllocMem(TBSize, bufmemtype | MEMF_CLEAR);
X          if (!TapeBuff[0] || !TapeBuff[1]) {
X               freestuff();
X               returnpkt(mypkt,DOSFALSE,ERROR_NO_FREE_STORE);
X               MPR0("Can't get memory for tape buffers\n");
X               break;
X          }
X
X          /* Determine open mode */
X          z = (char *)BADDR(mypkt->dp_Arg3);
X          x = (UBYTE)(*z);
X          devname = (char *)memcpy(nbuf, z+1, x);
X          *(devname+x) = '\0';
X          toUPPER(devname);
X          for(z=devname; *(z++)!=':' ;);
X          if     (!strcmp(z,"RAWCMD"))  opmode=RAWMODE;
X          else if(!strcmp(z,"MODESEL")) opmode=UMODSEL;
X          else {    /* normal read/write */
X             opmode = (mypkt->dp_Type==ACTION_FINDINPUT) ? READING : WRITING;
X             if(!strcmp(z,"*")) gotopos=blknum;     /* current position */
X             else if(*z>='0' && *z<='9')     /* specific position */
X                   gotopos = (ULONG) strtoul (z,NULL,0);
X             else if(*z=='\0') gotopos=0;    /* beginning of tape */
X             else goto BADNAME;
X          }
X          if(opmode>=RAWMODE && mypkt->dp_Type==ACTION_FINDINPUT)  {
X            BADNAME:
X             freestuff();   /* can't read from raw/ms */
X             opmode=CLOSED;
X             returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
X             break;
X          }
X
X          /* Check device ready */
X          if(x=TapeIO(TREADY,0,CTLWAIT))  {
X             y=DoSense(x);
X             if     (y==UAT) blknum = reserved;
X             else if(y==NOS) ;
X             else {
X               freestuff();
X               opmode=CLOSED;
X               returnpkt(mypkt,DOSFALSE,ERROR_DEVICE_NOT_MOUNTED);
X               break;
X             }
X          }
X
X          if(opmode<=WRITING) {
X
X            /* Check write-prot, block length */
X            if(TapeIO(MDSNS,0,CTLWAIT))  TapeIO(TSENSE,0,CTLWAIT);
X            else {
X               if(opmode==WRITING && (sns[2] & 0x80)) {
X                 freestuff();
X                 opmode=CLOSED;
X                 returnpkt(mypkt,DOSFALSE,ERROR_DISK_WRITE_PROTECTED);
X                 break;
X               }
X               if(sns[3] >= 8) {
X                 x = *((long *) &sns[8]) & 0x00ffffff;  /* get block len */
X                 if(x != blksize) {  /* set block size with Mode Select */
X                   TapeIO(MDSET,(int)varblk,CTLWAIT);
X                   TapeIO(TSENSE,0,CTLWAIT);
X                 }
X               }
X            }
X
X            /* Position tape */
X            if(gotopos==0) {    /* tape: */
X               TapeIO(TREWIND,0,CTLWAIT);
X               blknum = reserved;
X            }
X            else if(gotopos != blknum) {   /* tape:n */
X               if(DAC) blknum = gotopos;
X               else {
X                 TapeIO(TREWIND,0,CTLWAIT);
X                 if(x=TapeIO(TSKIP,(int)gotopos,CTLWAIT)) DoSense(x);
X                 blknum = 0;
X               }
X            }
X            else if(SEQ) blknum = 0;  /* tape:*  */
X
X            /* Get capacity for 3M tape */
X            if(DAC) {
X               tpsize = 0x7fffffff;
X               if(TapeIO(RDCAP,0,CTLWAIT)) TapeIO(TSENSE,0,CTLWAIT);
X               else {
X                 tpsize = ((sns[2] << 8) | sns[3]) + 1;
X                 MPR1("Capacity: %u blocks\n", tpsize);
X               }
X            }
X          }
X
X          dirty=FALSE; inprog=FALSE; Boff=0; pkcnt=0; tapenum=1;
X          MPR2("%s Opened at block %u\n",devname,blknum);
X
X          if(opmode==READING) {  /* for reads, prefetch 1st buffer */
X             iostat = TapeIO(TREAD,0,CTLWAIT);
X             remain=0;  Bn=1;
X          }
X          else { remain=TBSize;  Bn=0;  iostat=0; }
X
X          returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);
X          break;
X
X
X     case ACTION_END:        /*----------- Close() -----------*/
X          switch(opmode) {
X             case CLOSED:
X                returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN); break;
X             case RAWMODE:
X                if(x=TapeIO(RAWCMD,Bn,CTLWAIT)) DoSense(x); break;
X             case UMODSEL:
X                if(x=TapeIO(USRMODE,Bn,CTLWAIT)) DoSense(x); break;
X             case READING:
X                if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
X                if(iostat) DoSense(iostat);
X                break;
X             case WRITING:
X                if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
X                if(iostat) iostat = wrteot(Bn^1,1,iostat);
X                if(iostat==0) {
X                   if(dirty) {
X                      memset(&TapeBuff[Bn][Boff],0,remain);
X                      iostat = TapeIO(TWRITE,Bn,CTLWAIT);
X                   }
X                   if(iostat) iostat = wrteot(Bn,0,iostat);
X                }
X                if(iostat) { MPR0("Error writing final block\n"); }
X                else if(pkcnt) {
X                   blknum += numblks;  x=0;
X                   if(SEQ) x=TapeIO(WFMARK,0,CTLWAIT);
X                   if(x) DoSense(x);
X                }
X          }
X          returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);
X          opmode=CLOSED;
X          freestuff();
X          MPR1("Closed at block %u\n",blknum);
X          break;
X
X     case ACTION_READ:       /*----------- Read() ------------*/
X          if(opmode != READING) {
X            MPR0("Function/mode error\n");
X            mypkt->dp_Arg3 = -1;
X            goto RDEND;
X          }
X          dptr = (UBYTE *) mypkt->dp_Arg2;
X          dcnt =           mypkt->dp_Arg3;
X          while(dcnt)   {
X            if(remain==0)   {
X               if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
X               if(iostat) {  /* check status of previous read */
X                  y=DoSense(iostat);  iostat=0;
X                  switch(y) {
X                    case RCV:
X                       y=0; break;
X                    case FMK:
X                       mypkt->dp_Arg3 = 0;  break;
X                    case VOF:
X                    case EOM:  /* End of Tape: (sequential) */
X                       if(NewTape())  {   /* ask for new tape */
X                          y=0;            /* and reread buffer */
X                          iostat=TapeIO(TREAD,(Bn^1),CTLWAIT);
X                          if(iostat) y=DoSense(iostat); /* and fall thru */
X                          else break;
X                       }
X                    default:
X                       MPR0("Error during read\n");
X                       mypkt->dp_Arg3 = -1;
X                  }
X                  if(y) goto RDEND;
X               }
X               blknum += numblks;
X               iostat = TapeIO(TREAD,Bn,Ctl); /* start refilling this buffer */
X               Bn ^= 1;      /* switch to other (full) buffer */
X               remain = TBSize;
X               Boff = 0;
X            }
X            mcnt = (dcnt>remain) ? remain : dcnt;
X            memcpy (dptr, &TapeBuff[Bn][Boff], mcnt);
X            dcnt -= mcnt ;
X            Boff += mcnt ;
X            dptr += mcnt ;
X            remain -= mcnt ;
X          }
X          RDEND:
X          returnpkt(mypkt,mypkt->dp_Arg3,mypkt->dp_Arg2);
X          break;
X
X
X     case ACTION_WRITE:      /*----------- Write() -----------*/
X          if(opmode < WRITING) {
X            MPR0("Function/mode error\n");
X            mypkt->dp_Arg3 = -1;
X            goto WRTEND;
X          }
X          pkcnt++;
X          dptr = (UBYTE *) mypkt->dp_Arg2;
X          dcnt =           mypkt->dp_Arg3;
X          while (dcnt)  {
X            if(dcnt >= remain) {
X               memcpy (&TapeBuff[Bn][Boff], dptr, remain);
X               if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
X               if(iostat) {  /* check status of previous write */
X                 if(iostat = wrteot(Bn^1,1,iostat)) {  /* possible EOT */
X                    mypkt->dp_Arg3 = -1;
X                    MPR0("Error during write\n");
X                    iostat=0;  dirty=FALSE;  pkcnt=0;
X                    goto WRTEND;
X                 }
X               }
X               iostat = TapeIO(TWRITE,Bn,Ctl); /* start writing full buffer */
X               blknum += numblks;
X               dcnt -= remain;
X               dptr += remain;
X               Boff  = 0;
X               remain= TBSize;
X               Bn   ^= 1;      /* switch to other (empty) buffer */
X               dirty = FALSE;
X            }
X            else {
X               memcpy (&TapeBuff[Bn][Boff], dptr, dcnt);
X               remain -= dcnt;
X               Boff += dcnt;
X               dcnt  = 0;
X               dirty = TRUE;
X            }
X          }
X          WRTEND:
X          returnpkt(mypkt,mypkt->dp_Arg3,mypkt->dp_Res2);
X          break;
X
X
X     case ACTION_CURRENT_VOLUME:  /* info not supported */
X          returnpkt(mypkt,dvnode,DOSFALSE);
X          break;
X
X     case ACTION_LOCATE_OBJECT:  /* lock */
X          returnpkt(mypkt,mypkt->dp_Arg1,mypkt->dp_Res2);
X          break;
X
X     case ACTION_FREE_LOCK:      /* unlock */
X          returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);
X          break;
X
X     case ACTION_FLUSH:   /* flush buffers, NOOP */
X          returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);
X          break;
X
X     default:  /* others not supported */
X          returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
X          MPR1("Unsupported_Pkt=%d\n",mypkt->dp_Type);
X
X     } /* end of switch */
X } /* end of loop */
X} /* end of _main() */
X
X/**************************************************************************/
X
Xint DoSense(long iocode)
X{
X   /* iocode is (ioerr<<8 + cmdstatus) */
X   UBYTE snskey;
X   static char *snstext[] = {
X       "NONE" , "RECOVERED ERROR", "NOT READY", "MEDIUM ERROR",
X       "HARDWARE ERROR", "ILLEGAL REQUEST", "UNIT ATTENTION", "DATA PROTECT",
X       "BLANK CHECK", "KEY=9", "COPY ABORTED", "ABORTED COMMAND", "KEY=C",
X       "VOLUME OVERFLOW", "KEY=E", "KEY=F", "FILEMARK", "END-OF-MEDIUM",
X       "INCORRECT BLOCK LENGTH"
X   };
X   static char *sderror[] = {
X       "SELF-UNIT", "DMA", "PHASE", "PARITY", "SELECT-TIMEOUT"
X   };
X   iocode=iocode>>8;
X
X   if(iocode==0 || iocode==HFERR_BadStatus)  {
X       TapeIO(TSENSE,0,CTLWAIT);
X       if((sns[0] & 0x70)==0x70) {  /* extended */
X          if     (sns[2] & 0x80) snskey = FMK;  /* pseudo sense keys */
X          else if(sns[2] & 0x40) snskey = EOM;
X          else if(sns[2] & 0x20) snskey = ILI;
X          else                   snskey = sns[2] & 0x0f;  /* real sense key */
X       }
X       else snskey = sns[0] & 0x0f; /* non-extended */
X       linktp->sense = snstext[snskey];  /* keep last error info */
X       linktp->xsns1 = sns[12];
X       linktp->xsns2 = sns[13];
X       linktp->sns = sns;  /* flag for tapemon to print all data */
X       MPR3("Sense: %s, other= %02X,%02X\n", snstext[snskey], sns[12],sns[13]);
X       linktp->sns = NULL;
X       return((int)snskey);
X   }
X   else { MPR1("SCSI %s ERROR\n",sderror[iocode-40]);
X       return(SERR);
X   }
X}
X
X/**************************************************************************/
X/* wrteot:  handle messiness that happens when writing at end of tape.
X      Returns non-zero status if not EOT or tape swap unsuccessful. */
X
Xlong wrteot(int bfn, int dec, long stat)
X{
X   long ios = stat;
X   int  s = DoSense(ios);
X   if(s==EOM || s==VOF) {   /* EOT? */
X      if(NewTape()) {       /* ask for new tape */
X         if(dec) blknum -= numblks;  /* and rewrite old buffer */
X         ios=TapeIO(TWRITE,bfn,CTLWAIT);
X         if(ios) s=DoSense(ios);
X         if(dec) blknum += numblks;
X      }
X   }
X   if(s==RCV) ios=0;  /* ignore recoveries */
X   return(ios);
X}
X
X/**************************************************************************/
X
Xvoid freestuff(void)
X{
X  if(TapeBuff[0]) FreeMem(TapeBuff[0],TBSize);
X  if(TapeBuff[1]) FreeMem(TapeBuff[1],TBSize);
X  TapeBuff[0] = TapeBuff[1] = NULL;
X  return;
X}
X
X/**************************************************************************/
X/* getstart:
X    Given pointer (z) to a string of the form "SS-nn/..."
X    returns a number corresponding to SS, the binary value of nn,
X    and the pointer is updated to point after the slash.
X*/
Xint getstart(long *num)
X{
X  extern char *z;
X  #define NOTFND 99
X  #define ENDLST 100
X  #define NKEYS  12  /* number of keywords */
X  static char *keys[NKEYS] =
X      { "UN","LU","BS","NB","RB","BT","FM","VB","OV","DF","SU","C9" };
X     /*  0    1    2    3    4    5    6    7    8    9    10   11 */
X  char *ii, *jj;  int kk;
X
X  if (*z == '\0') return(ENDLST);      /* return if end of string */
X  for (ii=z;  *ii != '-'; ii++);       /* find the dash */
X  for (jj=z;  *jj != '/' && *jj !='\0'; jj++); /* find the slash */
X  *ii = '\0'; *jj = '\0';              /* null-terminate name & number */
X  *num = (long) strtoul (++ii,NULL,0); /* return converted number */
X  ii = z;     z = ++jj;                /* update ptr to next item */
X  for (kk=0; kk <= NKEYS; kk++)  {     /* search for keyword */
X     if (*ii == *keys[kk] && *(ii+1) == *(keys[kk]+1) )
X        return(kk);                    /* return index of keyword if found */
X  }
X  return(NOTFND);                      /* didn't find keyword */
X}
X
X/**************************************************************************
X   MonPrint requests that the TapeMon program print the message in 'dbb'.
X    Since this handler cannot do DOS I/O, it must beg the TapeMon program,
X    possibly running in a CLI somewhere, to do the printf for it.  If the
X    TapeMon is running, it will have installed a pointer to its task in
X    the link structure, and we can Signal() it.
X*/
X
Xvoid MonPrint(void)
X{
X   if(linktp->montask)  {
X      Signal(linktp->montask, linktp->monsig);
X      Wait  (linktp->handsig);
X      Signal(linktp->montask, linktp->monsig);
X      Wait  (linktp->handsig);
X   }
X   return;
X}
X
X/**************************************************************************/
X/* toUPPER: convert string to upper case */
X
Xvoid toUPPER(char *zz)
X{
X  for(; *zz != '\0'; zz++)  if(*zz >= 'a') *zz -= 0x20;
X  return;
X}
X
X/**************************************************************************
X*  NewTape()   Displays a requester asking user to insert a new tape.
X*/
X
Xlong NewTape(void)
X{
X long choice;
X static char rqm[40];
X static struct IntuiText rtxt[] = {
X    { AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
X       AUTOITEXTFONT, (UBYTE *) &rqm[0], AUTONEXTTEXT },
X    { AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
X       AUTOITEXTFONT, (UBYTE *) "Continue", AUTONEXTTEXT },
X    { AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
X       AUTOITEXTFONT, (UBYTE *) "Abort", AUTONEXTTEXT }
X };
X
X MPR1("Time to insert tape# %d\n",++tapenum);
X sprintf(rqm," Insert tape %d for %s",tapenum,devname);
X choice=AutoRequest(NULL,&rtxt[0],&rtxt[1],&rtxt[2],NULL,NULL,250,50);
X if(choice) {
X     DoSense(TapeIO(TREADY,0,CTLWAIT));  /* eat unit-atten status */
X     if(TapeIO(TREWIND,0,CTLWAIT)) TapeIO(TSENSE,0,CTLWAIT);
X     if(DAC) blknum=reserved;  /* reset block number */
X }
X return(choice);
X}
X
END_OF_FILE
if test 25153 -ne `wc -c <'tape.c'`; then
    echo shar: \"'tape.c'\" unpacked with wrong size!
fi
# end of 'tape.c'
fi
if test -f 'tape.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tape.doc'\"
else
echo shar: Extracting \"'tape.doc'\" \(34658 characters\)
sed "s/^X//" >'tape.doc' <<'END_OF_FILE'
X****    BTNtape:  an AmigaDOS handler for SCSI tape drives
X****       Version 2.0   3/28/91
X****       Freeware by Bob Rethemeyer   (DrBob@cup.portal.com)
X****
X****    (c) Copyright 1990, 1991  Robert Rethemeyer.
X****      This software may be freely distributed and redistributed,
X****      for non-commercial purposes, provided this notice is included.
X****      All original files must be included in the distribution.
X
XBTNtape is a "Better Than Nothing" SCSI tape device handler.  It provides
Xflat-file access to a SCSI tape drive from application programs using
Xsimple DOS calls to Read() and Write().  It can also be used with the Amiga
XTAR utility for disk backups.  It requires a "SCSI-direct" compatible
XSCSI bus adapter.  This is version 2.0, update to version 1.0.  It now
Xsupports many more tape drives, and includes some new features.
X
XSome advantages of BTN over other handlers and dedicated tape backup programs:
X  * Includes a monitor program that keeps you informed of what's happening
X  * Supports many standard SCSI tape drives and Amiga SCSI bus adapters
X  * Supports the 3M MCD-40, a non-standard SCSI tape drive
X  * Supports any size of tape block, and multiple-block accesses
X  * Supports tape files that span multiple tape volumes
X  * Double buffering improves streaming performance
X  * Includes an interface to send SCSI control commands to the drive
X  * Includes an interface to set drive-specific Mode Select parameters
X  * TAR tapes can be exchanged with Un*x systems
X
X=================== ******> R E A D   T H I S <****** ======================
X
XDISCLAIMER:
X "This software is provided 'as-is', without warranty of any kind,
Xeither expressed or implied.  In no event will I, Robert Rethemeyer,
Xbe liable for direct, indirect, incidental or consequential damages or
Xdata loss resulting from the use or application of this software.  The entire
Xrisk as to the results and performance of this software is assumed by you."
X
XI cannot guarantee that this software is compatible with any combinations
Xof drives or adapters other than those I have specifically tested.
X
XIf you do not agree with this disclaimer,  I recommend you not use this
Xsoftware;  wait for commercial tape drivers.
X
X============================================================================
X
XThis handler is probably not the all-purpose tape driver you have been waiting
Xfor.  It won't work with any backup program which requires Exec devices.
XI claim only that it works with the Amiga TAR program ported by Jonathan Hue.
XBut until we see good commercial or shareware tape software, you might
Xfind this handler to be "better than nothing".
X
XI will try to provide support for this handler, especially for bug fixes,
Xas my time and resources allow.  But I cannot commit to adding all the
Xenhancements or special features requested by users.  If you have something
Xin mind, ask, but I can't guarantee anything.
X
XAlthough this documentation refers to alleged problems with some
Xmanufacturers' driver software, that should not be construed as a statement
Xabout the quality of their products.  In fact, there is still a possibility
Xthat the problems may be in my handler software or the equipment I used.
X
XMy apologies to those people who tried to use version 1.0 and didn't have
Xa 3M tape drive.  At that time I naively thought that all SCSI tape drives
Xmust look the same, and wrote as though that were true.  Wrongo...
XSo this version should handle many more types of drives, but there is
Xno way I can test all the combinations of drives and SCSI adapters.
X
X==============================================================================
X
XDISTRIBUTION:
X
X  The distribution should contain the following files:
X    tape-handler          handler binary
X    tape.c                source for main handler
X    tapeio.c              source for interface to SCSI-direct
X    pktstuff.c            source for packet handling, by Phillip Lindsay
X    tape.h                local include file
X    tplink.h              local include file for handler/monitor structure
X    lmkfile               SAS/Lattice make file for handler
X    makefile              Manx make file for handler
X    tapemon               directory
X       tapemon               monitor binary
X       tapemon.c             monitor source
X       tapemon.doc           documentation for the monitor program
X       tapemon.lmk           SAS/Lattice make file for tapemon
X    mountlist             example mountlist entry
X    changes.doc           a brief history of BTNtape
X    tape.doc              you're reading it
X
X
XCOMPATIBILITY:
X
X  You must have a SCSI adapter and software that honors the SCSI-direct
X  protocol as defined in the "devices/scsidisk.h" include file.  The list
X  below shows the manufacturers I know about that support SCSI-direct
X  (but this list I'm sure is incomplete).
X
X  This handler has been tested on the following equipment (but not in
X  all the combinations):
X
X    SCSI adapters:
X      * Commodore A2091
X      * Commodore A590
X      * Commodore A3000 (built-in), including DOS 2.0
X      * GVP Series II
X      * HardFrame
X      * Nexus
X      * Commodore 2090A- works *if* the hard drive is ST506.
X      * Supra 4x4 (Amiga 1000)- works, but see Supra user notes.
X      * Trumpcard- with 2.0F software, 4.0 ROMs, Interrupt 2 jumper.
X
X    Tape drives:
X      * Archive 2150S (Viper)
X      * Teac MT-2ST Cassette
X      * Exabyte EXB-8200 8mm Cartridge Tape Subsystem
X      * Emulex MT-02 SCSI/QIC-36 tape adapter
X      * 3M MCD-40  40MB cartridge tape drive (direct access)
X
X  Even if your hardware is not listed here, there is a good chance it
X  will still work if it adheres to the standards (SCSI-direct and SCSI).
X  The handler treats a drive as one of two types: sequential or direct access.
X  The 3M drive is the only direct access drive I currently know about.
X  Everything else is usually sequential.  The handler will adjust itself
X  for either type.  If you get it to work with some drive model I haven't
X  mentioned here, I would like to hear about it.
X
X
XINSTALLATION:
X
X  1.  Copy the TAPE-HANDLER file to L:TAPE-HANDLER.
X      Copy the TAPEMON program to C: or a directory appropriate for you.
X
X  2.  Create an entry in DEVS:MOUNTLIST something like this:
X
X       TAPE: Handler = L:tape-handler
X             Priority = 5         /* use 11 for Supra */
X             Mount = 1            /* optional */
X             Stacksize = 4000
X             GlobVec = -1
X             Startup = "yourscsi.device/AA-x/BB-x/..." /* EXPLAINED BELOW */
X        #
X
X      There is nothing magic about the name TAPE:   You may use some
X      other valid device name if you like.
X
X  3.  Install the handler in DOS using the CLI command "MOUNT TAPE:"
X      Do not use the ARP version of MOUNT !!
X      If you get a write-protect requester, click Cancel on it
X      (this is a side effect of MOUNT- it tries to open TAPE in write mode).
X
X      Unfortunately, if the handler fails due to a mountlist or OpenDevice
X      problem, there is no visible error indication- MOUNT still works.
X      [does anyone know how I can make MOUNT fail?]
X      The device will show up in "ASSIGN LIST", but no handler process exists.
X      Type "ASSIGN TAPE: REMOVE", fix the problem, and try again.
X
X
XTHE STARTUP PARAMETER
X
X      The mountlist Startup parameter is used by the handler to determine how
X      to access your drive.  FAILURE TO ENTER PROPER INFORMATION IN STARTUP
X      MAY RESULT IN PROBLEMS INCLUDING HANGS, VISITS FROM THE GURU,
X      OR DATA LOSS.  This is the messiest part of setting up.  Pay attention.
X
X   *NOTE*  The Startup string format has been changed from v1.0
X      of the handler.  You must change to the new format.  It is more
X      readable, more flexible, and can be more easily expanded in the future.
X
X   *NOTE*  The new Startup string format will NOT work with the
X      ARP (AmigaDOS Replacement Project) version of the MOUNT command.
X      You MUST use the Commodore version of MOUNT.  To tell the difference,
X      ARP 1.3 MOUNT is 2372 bytes long, and CBM MOUNT (1.3.2) is 5604 bytes.
X      ARP MOUNT inexplicably truncates Startup at 40 bytes.  There is no need
X      to completely abandon ARP- you can still use the CBM MOUNT.
X      Just give the two MOUNT commands different names in your C: directory.
X
X      The Startup string must be in the form:
X
X               Startup = "devicename/keywd-num/keywd-num/..."
X
X      It must not have any embedded blanks and must be enclosed in quotes.
X      The pieces must be separated by slashes.
X
X      The first parameter must be the name of the device driver which provides
X      the SCSI-direct command interface. Do not include any path information.
X      This name is defined by the manufacturer of your SCSI adapter.
X      (v1.0 users- DO NOT use the old circumvention characters: $ or + ).
X      Note letter cases.  The names for the adapters I know about are:
X
X        A3000,A2091,A590 -> scsi.device
X        A2090A           -> hddisk.device
X        Supra            -> supradirect.device
X        GVP              -> gvpscsi.device
X        HardFrame        -> HardFrame.device
X        Nexus            -> Nexus.device  (NOT NexusTape.device!)
X        Trumpcard        -> IVS_SCSI.device
X        ICD Advantage    -> icddisk.device
X
X      The name parameter MUST be the first item in the string.  All other
X      parameters may be in any order, but the name must be first.
X
X      The remaining items consist of pairs of two-letter keywords and a number
X      separated by a dash (minus sign).  The keywords are explained below.
X      You may omit any of them (except UNit), and a default value will be
X      assumed.  The numbers may be decimal, or hex in the "0xNN" format.
X
X      Keyword-default
X      ---------------
X      UN       UNit:  The unit number of your tape drive, as it will be
X                      addressed by your hard disk driver.  Usually you can
X                      just specify the SCSI bus ID, example: "UN-0" for 0.
X                      More complex setups may require the form "UN-0xBLI",
X                      where B=board#, L=logical unit#, I=SCSI bus ID#.
X                      Be sure you have set the drive ID switches or jumpers
X                      to a value that is unique on the SCSI bus.
X                      2090A owners- add 3 to the ID number.
X
X      LU-0     Logical Unit:  another number that may be required to address
X                      your drive.  Most drives are hardwired for 0, so this
X                      parameter may be omitted.  It is provided here for the
X                      few exceptions that are probably out there.
X
X      BS-512   Block Size: the number of bytes in a SCSI logical block for
X                      your tape drive.  Consult your drive documentation.
X                      Some drives allow you to specify different
X                      block sizes, within a legal range;  the handler will
X                      command the drive to use the size you specify.
X                      Others can use only one fixed size.  If you can vary
X                      the block size, you must use the same size when reading
X                      a tape as you did when writing it.
X
X      NB-1     Number of Blocks:  the number of SCSI logical blocks to write
X                      or read in each tape operation.  See below.
X
X      BT-0     BufmemType:  the type of memory to allocate for the tape
X                      buffers, same as BufMemType for mountlists.  Use
X                      0 or 1 for "any", 2 or 3 for "chip", 4 or 5 for "fast".
X
X      FM-1     FileMarks:  specifies number of file marks to write when
X                      closing at the end of a write, or the number to skip
X                      for each file when opening for read.
X                      Allowed range is 0-255.  Sequential drives only.
X
X      VB-0     Variable Block flag:  the handler normally operates sequential
X                      drives in fixed block mode.  Specifying VB-1 will force
X                      operation in variable block mode instead.  Use this
X                      if your drive does not support fixed block mode or
X                      you for some reason need variable block mode.
X
X      RB-0     Reserved Blocks:  for direct access drives only.  Sets how
X                      many blocks to skip at the beginning of the tape.
X
X      OV-1     OVerlapped I/O Flag:  the handler normally attempts to overlap
X                      tape drive I/O and DOS I/O to increase throughput.
X                      Specifying OV-0 disables overlapping and double buffering
X                      for systems that can't walk and chew gum at the same time.
X
X      DF-0     Device Flags:  equivalent to the Flags parameter in mountlists.
X                      It is passed in the OpenDevice() call to open the SCSI-
X                      direct driver. If you don't know what you would use
X                      this parameter for, then you probably don't need it.
X
X      SU-0     SUpra Flag: activates the circumvention for the Supra 4x4
X                      v1.10 problem.  Specify SU-1 to activate this.
X                      See below. (Replaces "+" in handler v1.0)
X
X      C9-0     CBM 2090A Flag:  activates the circumvention for the Commodore
X                      2090A problem.  Specify C9-1 to activate this.
X                      See below. (Replaces "$" in handler v1.0)
X
X
X      Here are some examples of valid Startup strings:
X
X       Startup = "scsi.device/UN-7"   /* the minimum info required */
X       Startup = "scsi.device/BS-0x400/NB-16/LU-3/UN-0x102"
X       Startup = "supradirect.device/un-4/bs-8192/nb-8/bt-1/su-1"
X
X      A note about the NB parameter:  Some drives may be mechanically more
X      efficient writing multiple blocks.  You may want to tweak this
X      parameter to find out.  The handler will wait until NB blocks have been
X      accumulated before starting a write.   Buffered drives may also have a
X      limit on the number of blocks they can hold at once, so the size of NB
X      relative to that number may have an effect.  Consult your drive docs.
X      In general, the more blocks the better.
X      This parameter also determines how much memory is needed for
X      buffers.  The handler uses double buffering, so it will need
X      2 * BS * NB bytes when you Open() it.
X      For example, if BS=8192 and NB=8, then 8192*8 = 64K,
X      for a total of 128K.
X
X
X2090A USERS- NOTE
X
X     Apparently (I don't own a 2090A) there is a software bug in the
X     CBM 2090A SCSI-direct driver that causes problems with tape
X     writes.  Bob Mitchell figured out that the problem can be
X     circumvented by asserting data address bit 24 for writes.
X     This handler can optionally invoke that circumvention.
X     To do so, specify "C9-1" in the Startup string.
X     The circumvention is NOT necessary for other adapters.
X
X     A worse problem- the 2090A cannot seem to handle both the disk
X     and the tape drive on the same SCSI bus.  Simple reads or writes
X     to the tape work fine, but intermixed tape reads and disk writes
X     (such as during a restore) will cause the 2090A to get confused.
X     This does not happen if the hard drive is on the ST506 bus.
X     It also does not happen if you restore the files to RAM or floppy.
X
X     Make sure the tape is out of the drive when you boot
X     AmigaDOS;  otherwise, DOS never comes up.
X
X     When you mount TAPE:,  make sure there is no cartridge in the
X     tape drive.  For some reason I do not understand, the 2090A
X     causes a reset to the tape drive when OpenDevice is called.
X     If a cartridge is in the drive at that time, the system
X     eventually enters an unusable state.  I recommend using
X     "Mount = 1" in your MountList entry for TAPE: so when TAPE:
X     is mounted, it will put up a requester because the drive is
X     empty;  click on cancel, then everything will be happy.
X
X
XSUPRA USERS- NOTE
X
X     Apparently (I DO own one) there is a problem with the Supra v1.10
X     SCSI-direct software that causes the host adapter to hang when a
X     non-zero byte count is supplied in the command (SCSICmd.scsi_Length).
X     It works fine when a data length of zero is used.  The handler can also
X     circumvent this problem.  Specify "SU-1" in the Startup parameter.
X     I have reported this to Supra, and they promised to look into it.
X
X     You MUST run the tape handler at a higher priority than the
X     Supra driver task.  You do this by changing the "Priority ="
X     line in the mountlist entry.  For Supra v1.10, the task runs
X     at priority 10.  Therefore, the tape handler should run at
X     "Priority = 11".  If some other Supra version runs at a different
X     priority, change the mountlist priority accordingly.
X     Use a utility such as Xoper or TaskX to find task priorities.
X
X     If the cartridge is in the drive at the time you boot AmigaDOS,
X     SupraMount will hang the system if it touches the tape drive.
X     To avoid this, use the selective mount feature of SupraMount
X     to mount only your disk unit.  Example: "SupraMount 6".
X
X
XHOW TO ACCESS TAPE DATA
X
X     Now that the handler is installed, how do you talk to it?
X     You can use TAR, or your own application program, including ARexx.
X     You can write data to the tape and later read it back in a manner
X     similar to other files, using DOS calls to Open, Read, Write, and Close.
X
X     The handler makes the tape data look like a sequential AmigaDOS file.
X     There is NO FILE SYSTEM on the tape, but you may place multiple files
X     on a tape by positioning the tape to specific file marks or blocks.
X     The handler does not support mixed reads and writes.  That is, you may
X     do only Reads or only Writes, but not both.  Seeks are not allowed.
X     You cannot Open the tape in "append" mode.
X
X     Tape data is written in quantities of "NB*BS" bytes.  No information
X     about the exact length of the file is written.  If a tape file
X     ends before filling a block, the remainder of the block is filled with
X     zeroes.  Therefore, any program reading the tape must know when to
X     stop reading data, otherwise it will read extraneous zeroes past
X     true end of the file.  For instance, this will not work, because the
X     second copy won't know the size of the file it is copying:
X        COPY filename TAPE:
X        COPY TAPE: newfile    ; will read too much data
X
X     TAR works because it essentially writes and interprets the tape data
X     as its own little file system that includes the length of each file.
X
X     When a tape file is closed in write mode, one or more filemarks
X     (as determined by "FM") are written at the end of the file to
X     delineate the file from subsequent files. (Sequential drives only).
X
X     If there is no tape in the drive at the time TAPE: is
X     opened, DOS will put up a requester asking for volume "TAPE"
X     "in any drive".  Insert the tape (in the tape drive, not
X     a floppy drive :-)  and click on retry after tape motion stops.
X
X     Refer to the tape with one of the following file name forms:
X
X     TAPE:     Rewinds and opens the tape at the first available block.
X
X     TAPE:*    Opens the tape at the current position, without rewinding.
X
X     TAPE:num  Opens the tape at the position specified by the number.
X        For sequential drives, the number indicates the file position.
X        The 0th file is at the beginning of the tape, file 1 follows it,
X        etc.  Note, the files must have been previously written on the tape.
X        Files are separated by one or more filemarks as specified by the
X        FM startup parameter.  For the 3M direct access drive, the number
X        specifies an absolute SCSI block number on the tape.  You must
X        remember (write down) the block number of the beginning of each
X        file on the tape, as reported by TAPEMON.
X
X     TAPE:RAWCMD   A special mode for issuing a raw SCSI command, not for
X        accessing the tape medium.  See "RAW COMMAND INTERFACE" below.
X
X     TAPE:MODESEL  A special mode for writing mode-select data, not for
X        accessing the tape medium.  See "USER-SPECIFIED MODE SELECT" below.
X
X
XA QUICK FUNCTIONALITY TEST
X
X     Before jumping into disk backups and restores, try the following
X     simple test to see if your setup is basically working.
X     Write a large ASCII text file, such as TAPE.DOC, to the tape
X     and see if you can read it back intact.  This can be done with the
X     CLI TYPE command:
X
X             TYPE tape.doc TO TAPE:     ; writes the file to tape
X     then... TYPE TAPE:                 ; reads the file back
X
X     The second TYPE should legibly print the ASCII file on your screen.
X     When you have seen enough, use control-C to abort it.
X
X
XUSING TAR FOR BACKUPS AND RESTORES
X
X     For backups, I use the version of TAR ported by Jonathan Hue.
X     It is 33860 bytes long.   Each TAR archive is written to the tape
X     as a single file, terminated by a file mark (on sequential drives).
X     NOTE: the version of TAR distributed with Amiga UUCP apparently
X     does NOT work with BTNtape.  I don't know why.   Other similar
X     programs, like PAX, may also work, but I haven't tried them.
X
X     TAR will backup all directories, files, and subdirectories in the
X     directory you specify, including the date, protection, and filenote
X     attributes.  The easiest way to do a backup is to CD to the desired
X     directory or partition (e.g. "cd DH0:"), then...
X
X     To make a backup archive with TAR  :   tar -cv -f tape: ""
X         (note the null string)
X     To add another archive file        :   tar -cv -f tape:* ""
X     To restore files from archive      :   tar -xv -f tape: *
X     To restore a specific file         :   tar -xv -f tape: path/filename
X     To restore from archive at file 6  :   tar -xv -f tape:6 *
X     To list the archive on the tape    :   tar -t  -f tape:
X      (this also tests archive integrity)
X     To make an archive log             :   tar >logfile -t -f tape:
X     To look at raw tape data           :   type tape: opt h  ... cntl-C
X
X     TAR can also make backups using a list of files from another file.
X     When you restore an individual file from an archive, provide the
X     exact path and file name;  these names are case sensitive.
X     Refer to the TAR documentation for more information.
X
X
XEND-OF-TAPE HANDLING
X
X     If the application reaches the end of the tape, the handler will put up
X     a requester asking you to insert the next tape and click "Continue",
X     or click "Abort".  The continue option allows the application program
X     to access a file across multiple tape volumes without interruption.
X     WAIT FOR TAPE MOTION TO STOP on the new tape before clicking Continue.
X     If you click Abort, an I/O error is returned to the application.
X
X  *NOTE*  EOT handling is invoked when any of the blocks in the group of "NB"
X     blocks gets an EOM or Volume Overflow error.  When you click Continue,
X     the entire group of blocks is rewritten/reread on the new tape.
X     Therefore, to use the multiple-volume capability of the handler, you must
X     use the same blocking factor (value of NB) when reading the tape as
X     when it was written.
X
X  *NOTE*  EOT handling for the 3M drive will work only if the ROM version
X     supports the READ_CAPACITY SCSI command.  If you see no "Capacity"
X     message from TAPEMON, then EOT handling is unavailable.
X
X
XERROR HANDLING
X
X     This version of the handler does not deal with tape errors.  If the
X     drive detects a medium error, the current operation will be aborted
X     by returning -1 for Read() or Write().  Some drives insulate the
X     user from bad blocks by automatically correcting them or remapping
X     to a good block, but other drives may not do this.
X
X
X3M DRIVE USERS- NOTE
X
X     The following applies only to the 3M MCD-40 direct access drive.
X
X     The 3M drive does not use file marks.  When you reference the
X     tape as "TAPE:num", num is the SCSI block number, not the file number.
X     When writing multiple files to a tape, you should write down the
X     block numbers where each file starts (unless you have a good memory).
X     Use the TAPEMON program to see the block numbers.
X
X     The RB parameter in Startup may be used to skip over a number of
X     blocks at the beginning of the tape, if there is something there
X     (like volume information) you wish to preserve.
X
X     Be sure your cartridge is formatted.  If you try to use an
X     unformatted tape, results will be non-optimal.  You can buy
X     preformatted tapes, or you can format your own- see the section on
X     raw commands below. Cartridges may be formatted with an interleave
X     factor. Interleave is the number of physical blocks separating adjacent
X     logical blocks.  The 3M unit defaults to interleave 2, but I find
X     for my Supra that 3 improves performance slightly.
X
X     The raw command to format a tape is:  04 80 20 00 00 80
X     which denotes the format command, immediate completion, precondition
X     the tape, default interleave, and retry on errors.
X
X     A note about the hardware.  If you haven't noticed it yet, the
X     3M drive is EXTREMELY sensitive to electrical noise.  It is
X     *essenstial* that the drive be properly grounded.  The ground
X     in the power connector is insufficient.  You should run a thick
X     wire or braided strap from the metal side-plate to a good ground,
X     preferrably the power supply ground.  I'm not kidding.
X
X
XHELPFUL HINTS:
X
X     Label your tapes.  Include the file number or block number
X     of each file on a multi-file tape.
X
X     Try backing up and restoring a floppy before doing the
X     same with your hard drive, to make sure the process works for you.
X     For the same reason, make a HD backup to floppies first.  Then
X     if the tape restore fails, you have something to fall back on.
X
X     This is obvious, right?  Make sure you have a copy of the handler and
X     TAR on an easily-accessible floppy.  If the only copy you have is
X     on your hard drive, you might find it difficult to restore
X     from a tape backup after a disk wipe-out.
X
X
XTHE MONITOR PROGRAM
X
X     Included in with the handler is the TAPEMON program.  TAPEMON is
X     a program that runs under a separate CLI and prints one-liner
X     messages from the handler.  The messages include the current
X     tape operation and block number, and error and sense codes.
X     The handler will run with or without TAPEMON; i.e. TAPEMON is optional.
X     TAPEMON must be started up AFTER the handler is mounted and activated.
X     Read TAPEMON.DOC for more information.
X
X
XRAW COMMAND INTERFACE
X
X     This handler provides a means to send your own SCSI control commands
X     to the tape drive, without the need for a special program.
X     Some control commands you might want to send it would be REWIND,
X     UNLOAD, ERASE, FORMAT, LOCK, etc.  Only control commands may be sent-
X     there is no provision for any data transfer; don't try it.
X     (As you might guess, raw commands can be dangerous.)
X
X     The raw command mode is invoked by writing data to the file name
X     "TAPE:RAWCMD".  (v1.0 users- the dollar signs are no longer used).
X     This magic word is how the handler knows you want to do a raw command.
X
X     There are two ways to send data via RAWCMD using CLI commands:
X        1) use the ECHO command:     ECHO >TAPE:RAWCMD "hex command bytes..."
X        2) use the TYPE command:     TYPE filename TO TAPE:RAWCMD
X
X     The ECHO command is useful to place in a script or create an alias
X     for the command.  You may also place the data in a file and use TYPE;
X     line 1 of the file contains the bytes to be sent as the raw command.
X
X     The exact value of the data bytes is determined by the byte format of
X     the SCSI command you want to send. (Refer to your drive documentation.)
X     The data must contain ONLY hex digits and spaces terminated, by a newline.
X     Each byte of the raw command should be specified as a pair of hex
X     digits;  separate bytes by spaces.  Up to 10 bytes may be specified.
X     The data for TYPE must be on the first line of the file.
X     You can use any following lines as comments, but the total size of
X     the file must be less than the size of BS*NB.
X
X     Command examples:
X        Rewind:     01 00 00 00 00 00
X        Erase:      19 00 00 00 00 00
X        Lock:       1E 00 00 00 01 00
X
X
XUSER-SPECIFIED MODE SELECT
X
X     In recognition of the fact that it will never be able to provide
X     switches for all possible modes of all tape drives, BTNtape
X     allows you to set the modes of the drive directly.
X     These modes might include:  buffering, speed, auto-loading,
X     enable disconnect, medium type, etc.
X
X   *NOTE* Do not use this feature unless you know what you are doing.
X     This feature is provided for the convenience of "power users".
X     Most drives can get by with defaults if you do not set the modes,
X     so it is unlikely that not setting a mode will cause problems.
X
X     Drive modes are set using the SCSI command MODE_SELECT (0x15).
X     BTNtape will send its own MODE_SELECT to the drive if necessary
X     to set the drive to fixed block mode and set the block length.
X     But you may also send this command to the drive at other times,
X     using any data you wish.
X
X     The command is sent in a manner very similar to the RAWCMD
X     method described above, but the magic word MODESEL is
X     used instead.  The handler provides the SCSI command,
X     and you provide the data.  The data is specified as pairs of
X     hex digits separated by spaces, in a file for TYPE, or directly
X     in an ECHO command.
X
X            ECHO >TAPE:MODESEL "hex data bytes..."
X       or   TYPE filename TO TAPE:MODESEL
X
X     Since each drive has different things that are affected by MODE_SELECT,
X     you MUST refer to the programming documentation for your drive.
X     But in general, the mode select data consists of a parameter
X     list, a block descriptor, and vendor unique parameters.
X    *NOTE*  If you specify a block descriptor, it will behoove you to
X     specify the same block length as specified in the mountlist entry
X     when using fixed block mode, or zero if using variable block mode.
X
X     The handler counts the number of bytes you specify (up to 64),
X     and uses that for the SCSI command data length.  The drive may
X     be fussy about the length, so specify exactly the number of bytes
X     as described in your docs.
X
X     Mode Select data example:
X
X       --parmlist-   ----block-descriptor---   vendorparms
X       00 10 00 08   03 00 00 00 00 00 04 00   11 22 33 44
X          |     |    |                 || ||
X  Buff mode  BD Len  Density         Blk Len
X
X
XFINE TUNING
X
X     There are a few factors you can tweak to get good performance
X     on your particular system.  Good performance means the tape tends
X     to run continuously for long periods of time and seldom has
X     to stop and back up to get a running start at the next block.
X     The tape may have to stop while TAR gets more data from the
X     hard disk, but you can minimize it.
X
X     The handler's NB parameter can be increased from 1 to
X     some larger number as your memory allows.  With more handler
X     blocks, a large quantity of data is available to dump to the tape,
X     but there is probably a point of diminishing return.
X
X     The TAR program also has a block parameter.  It controls how much
X     DOS data TAR collects before sending it to the handler.  If you
X     use many TAR blocks, it makes no sense to also use many handler
X     blocks,  since the data has to go through both buffers
X     anyway.  You can increase the TAR blocks if your SCSI-
X     direct driver does not support multi-block operations.
X
X     Using the TAPEMON program may slow down the handler slightly
X     when it prints messages, but it's not really a problem.
X     If you absolutely need maximum throughput, shut down TAPEMON.
X
X
XHOW IT WORKS
X
X     Two buffers are maintained so that one buffer can be used by DOS
X     while the other is exchanging data with the drive.  With the
X     multitasking nature of the Amiga, this should help keep the tape
X     in motion, but it may not help much with non-DMA SCSI adapters.
X
X     In write mode, the handler collects DOS data in one buffer while
X     the previously filled buffer is being written to tape.  In read mode,
X     the handler reads ahead on the tape to fill one buffer while
X     DOS gets data from the previously read buffer.
X
X     The handler does not write (or expect) anything special on
X     the tape for itself.  That is to say, the data written on the
X     tape is your raw data, nothing else.  If I were to use parts
X     of the tape for file system information, I would be
X     introducing a sort of standard-- I don't think that is a good
X     idea right now.  Raw data provides a lowest common denominator
X     which should help compatibility with other things.  For instance,
X     a TAR tape created on a non-Amiga system is readable using
X     this handler and Amiga TAR.
X
X
XBUGS
X
X     The variable block mode of operation (VB-1) has not been tested.
X
X     The handler has not been tested with AmigaDOS 1.2.
X
X     The handler does not multi-task well, but I believe that to be
X     a characteristic of the SCSI-direct drivers, not the handler.
X     Don't expect to do much else while a backup is in progress.
X     In fact, other things going on can reduce tape drive performance.
X
X
XAUTHOR:
X
X  Email:  DrBob@cup.portal.com   OR   ...!sun!portal!cup.portal.com!DrBob
X
X  USmail: Robert Rethemeyer
X          979-4 Belmont Terrace
X          Sunnyvale, CA  94086
X          USA
X
X  If you want to contact me about a problem, please try to include
X  with your mail the following:
X      *  a copy of your mountlist entry for TAPE:
X      *  a capture of the output from TAPEMON.  Just redirect its
X             output to a file:     TAPEMON >filename
X      *  a verbal description of what happens to the drive and/or Amiga
X             (e.g.  tape doesn't move, smoke issues from Amiga, etc.)
X      *  any other info that may help me figure out what's going on.
X          Remember, I'm not there to see what is happening, and I
X          probably don't have your drive model available to recreate
X          the problem.  I have only your description to go by.
X
X
XMANY THANKS GO TO:
X
X  Bob Mitchell for the SCSI-direct programs I used to get started,
X    and the 2090A circumvention.
X  Beta testers Bill Seymour, Don Phillips, Steve Goldstein,
X     Dick Wood, Don Camp, George Armhold, Gary Walborn.
X  Phillip Lindsay for the my.handler example code.
X  Rene' Vega for advice and Manxification.
X
END_OF_FILE
if test 34658 -ne `wc -c <'tape.doc'`; then
    echo shar: \"'tape.doc'\" unpacked with wrong size!
fi
# end of 'tape.doc'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@uunet.uu.net>.
Mail comments to the moderator at <amiga-request@uunet.uu.net>.
Post requests for sources, and general discussion to comp.sys.amiga.misc.