home *** CD-ROM | disk | FTP | other *** search
- /**** BTNtape Handler for SCSI tape drives ****/
- /**** Author: Bob Rethemeyer (DrBob@cup.portal.com) ****/
-
- #define TVERSION "-BTNTAPE V1.0 RAR-" ## __DATE__
-
- /* (c) Copyright 1990, 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 Lattice v5 or Manx v5.
- *
- * This handler works in conjunction with the accompanying TapeMon program.
- *----------------------------------------------------------------------------
- * Install this handler in your L: directory.
- * Make a devs:mountlist entry similar to this:
- *
- * TAPE: Handler = L:tape-handler
- * Stacksize = 4000
- * Priority = 5 (use 11 for Supra)
- * GlobVec = -1
- * Startup = "4/5/8192/1/0/yourscsi.device"
- * ( unit/BufMemType/blocksize/Buffers/Reserved/Driver )
- * #
- * Then use "MOUNT TAPE:" to make the device known to DOS.
- *
- * This handler can circumvent the "write phase" problem in the CBM 2090A
- * driver. To invoke the circumvention, prefix the name of the driver
- * in the Startup mountlist parameter with a dollar sign ("$").
- * Example: Startup = "4/5/8192/1/0/$hddisk.device"
- *
- * This handler can circumvent a byte count problem in the Supra v1.10
- * driver. To invoke the circumvention, prefix the name of the driver
- * in the Startup mountlist parameter with a plus sign ("+").
- * Example: Startup = "4/5/8192/1/0/+supradirect.device"
- *
- * ----------------------------------------------------------------------------
- */
-
- #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 <libraries/dos.h>
- #include <libraries/dosextens.h>
- #include <libraries/filehandler.h>
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #include <limits.h>
-
- #if defined AZTEC_C
- #include <functions.h>
- #elif defined LATTICE
- #include <proto/exec.h>
- #endif
-
- #include "tape.h"
- #include "tplink.h"
-
- struct things { /* a collection of things we will have to alloc */
- UBYTE cmdbuff[32];
- UBYTE snsarea[32];
- struct SCSICmd scsicmd;
- UBYTE pad[256]; /* SCSICmd may be larger than include? */
- } ;
-
- /*======== Global data */
-
- struct IntuitionBase *IntuitionBase;
- UBYTE *cdb; /* pointer to tape command buffer */
- UBYTE *sns; /* pointer to sense data buffer */
- 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 numblks; /* number of blocks per io operation */
- ULONG rwlen; /* bytes in a tape read/write */
- ULONG bugmask = 0; /* 2090A bug circumvention */
- long tpsize; /* tape size in blocks */
- short reserved; /* number of reserved blocks at BOT */
- short inprog = FALSE; /* io operation in progress flag */
- char *z; /* scratch */
- char dbb[80]; /* buffer for monitor messages */
-
- #define RAWNAME "$$RAWCMD$$"
- #define RAWLEN 10
-
- /*********************** 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 */
- ULONG dvnode; /* ptr to devnode passed in pkt Arg3 */
- struct things *xarea; /* ptr to dynamic misc. areas */
- ULONG unit; /* device SCSI unit address */
- ULONG bufmemtype; /* type of mem for dynamic buffers */
- ULONG blksize; /* bytes per tape block */
- ULONG TBSize; /* bytes in a tape buffer */
- char *driver; /* name of SCSI device driver */
- UBYTE *dptr; /* ptr to next byte in dos buffer */
- long dcnt; /* count of dos packet bytes to move */
- long mcnt; /* count of bytes to move */
- long Boff; /* current offset in tape buffer */
- long rem; /* bytes remaining in tape buffer */
- long x; /* scratch */
- short Bn; /* current buffer number, 0 or 1 */
- short raw; /* raw command mode flag */
- short rdmode; /* flag indicating open for reading */
- short norw; /* no-rewind flag */
- short open= FALSE; /* tape file open flag */
- short dirty=FALSE; /* buffer has unwritten data in it */
- BYTE acksig; /* monitor acknowledge signal number */
-
- /*======== 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;
- 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 '/'
- */
- z = (char *)BADDR(mypkt->dp_Arg2)+1 ; /* Arg2= BSTR to mountlist 'Startup'*/
- if(z[0]=='\"') { /* remove quotes if any */
- z++;
- z[strlen(z)-1]= '\0' ;
- }
- unit = Nextnum(0);
- bufmemtype = Nextnum(1);
- blksize = Nextnum(1);
- numblks = Nextnum(1);
- reserved = Nextnum(1);
- driver = (char *) Nextnum(-1);
- rwlen = TBSize = numblks * blksize; /* size of a tape buffer */
-
- /*======== Kludges to work around various SCSIdirect driver software problems */
-
- if (driver[0] == '$') { /* Activate 2090A bug circumvention */
- bugmask = 0x01000000; /* if driver name starts with '$' */
- driver++ ;
- }
- else if (driver[0] == '+') { /* Activate Supra bug circumvention */
- rwlen = 0; /* if driver name starts with '+' */
- driver++ ;
- }
- tpl.driver = driver;
-
- /*======== 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];
- cmd = &xarea->scsicmd;
- ior = (struct IOStdReq *) CreateExtIO( CreatePort(0,0),
- sizeof(struct IOStdReq));
-
- /*======== Open the SCSIdirect device */
-
- if ( OpenDevice(driver,unit,(struct IORequest *)ior,0L) ) {
- 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 } */
-
- DoSense(0);
-
- /* =========== The main packet processing loop =============== */
-
- for (;;) {
- mypkt = taskwait(); /* wait for a packet */
- switch(mypkt->dp_Type) {
-
- case ACTION_FINDINPUT: /*----------- Open() ------------*/
- case ACTION_FINDOUTPUT:
- if(open) returnpkt(mypkt,DOSFALSE,ERROR_OBJECT_IN_USE);
- else {
- TapeBuff[0] = (UBYTE *) AllocMem(TBSize, bufmemtype | MEMF_CLEAR);
- TapeBuff[1] = (UBYTE *) AllocMem(TBSize, bufmemtype | MEMF_CLEAR);
- if (!TapeBuff[0] || !TapeBuff[1]) {
- FreeStuff(TBSize);
- returnpkt(mypkt,DOSFALSE,ERROR_NO_FREE_STORE);
- MPR0("Can't get memory for tape buffers\n")
- }
- else {
- /* Detect open modes: raw command, continue, goto-block */
- raw=norw=rdmode=FALSE;
- for( z=(char *)BADDR(mypkt->dp_Arg3)+1 ; z[0]!=':' ; z++ );
- if(!memcmp(z+1,RAWNAME,RAWLEN)) raw=norw=TRUE;
- else if( z[1] == '*' ) norw=TRUE;
- else if( isdigit((int)z[1]) ) {
- blknum = (ULONG) strtol(z+1,NULL,0);
- norw=TRUE;
- }
-
- DoSense(0); /* eat tape-change status */
- if(norw) x=0;
- else x=TapeIO(TREWIND,0,CTLWAIT);
- if(x) {
- DoSense(x);
- FreeStuff(TBSize);
- returnpkt(mypkt,DOSFALSE,ERROR_DEVICE_NOT_MOUNTED);
- }
- else {
- tpsize = TapeIO(RDCAP,0,CTLWAIT) ? /* get tape capacity in sns */
- LONG_MAX : ((sns[2] << 8) | sns[3]) + 1;
- MPR3("%d * %d = %d\n", tpsize,blksize,tpsize*blksize)
- open=TRUE;
- dirty=FALSE;
- inprog=FALSE;
- Boff=0;
- Bn=0;
- rem = TBSize;
- if(!norw) blknum = reserved;
- MPR1("Opened at block %d\n",blknum)
- if (mypkt->dp_Type==ACTION_FINDINPUT) {
- rdmode=TRUE;
- x= TapeIO(TREAD,0,CTLWAIT); /* fill 1st buffer */
- blknum += numblks;
- if(!x) x=TapeIO(TREAD,1,CTLIMM); /* start reading 2nd */
- }
- returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);
- }
- }
- }
- break;
-
- case ACTION_END: /*----------- Close() -----------*/
- if(open) {
- if(dirty) {
- if(raw) TapeIO(RAWCMD,Bn,CTLWAIT); /* send user command */
- else TapeIO(TWRITE,Bn,CTLWAIT); /* write last block */
- }
- else if(inprog) TapeIO(TFINISH,0,CTLWAIT); /* wait for last one */
- open=FALSE;
- FreeStuff(TBSize);
- }
- returnpkt(mypkt,DOSTRUE,mypkt->dp_Res2);
- if(!rdmode) blknum += numblks;
- MPR1("Closed at block %d\n",blknum)
- break;
-
- case ACTION_READ: /*----------- Read() ------------*/
- if(x) {
- DoSense(x);
- mypkt->dp_Arg3 = -1;
- goto RDERR;
- }
- dptr = (UBYTE *) mypkt->dp_Arg2;
- dcnt = mypkt->dp_Arg3;
- while(dcnt) {
- if(!rem) {
- blknum += numblks; /* start reading next buffer */
- if(x=TapeIO(TREAD,Bn,CTLIMM)) {
- DoSense(x);
- mypkt->dp_Arg3 = -1;
- goto RDERR;
- }
- Bn ^= 1; /* switch to other (filled) buffer */
- rem = TBSize;
- Boff = 0;
- }
- mcnt = (dcnt>rem) ? rem : dcnt;
- memcpy (dptr, &TapeBuff[Bn][Boff], mcnt);
- dcnt -= mcnt ;
- Boff += mcnt ;
- rem -= mcnt ;
- dptr += mcnt ;
- }
- RDERR:
- returnpkt(mypkt,mypkt->dp_Arg3,mypkt->dp_Arg2);
- break;
-
- case ACTION_WRITE: /*----------- Write() -----------*/
- dptr = (UBYTE *) mypkt->dp_Arg2;
- dcnt = mypkt->dp_Arg3;
- while (dcnt) {
- if (dcnt >= rem) {
- memcpy (&TapeBuff[Bn][Boff], dptr, rem);
- if( x=TapeIO(TWRITE,Bn,CTLIMM) ) {
- DoSense(x);
- mypkt->dp_Arg3 = -1;
- goto WRTERR;
- }
- blknum += numblks;
- dcnt -= rem;
- dptr += rem;
- Boff = 0;
- rem = TBSize;
- Bn ^= 1;
- dirty = FALSE;
- }
- else {
- memcpy (&TapeBuff[Bn][Boff], dptr, dcnt);
- rem -= dcnt;
- Boff += dcnt;
- dcnt = 0;
- dirty = TRUE;
- }
- }
- WRTERR:
- returnpkt(mypkt,mypkt->dp_Arg3,mypkt->dp_Res2);
- break;
-
- case ACTION_CURRENT_VOLUME:
- 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;
-
- default: /* say what? */
- returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
- MPR1("Unsupported_Pkt=%d\n",mypkt->dp_Type)
-
- } /* end of switch */
- } /* end of loop */
- } /* end of _main() */
-
- /**************************************************************************/
-
- void DoSense(long x)
- {
- sns[2] = sns[12] = 0;
- x=x>>8;
- if(x==0) TapeIO(TSENSE,0,CTLWAIT);
- else if(x==HFERR_BadStatus) {
- TapeIO(TSENSE,0,CTLWAIT);
- if(!(sns[0] & 0x70)) sns[2]=sns[0] & 0x0f; /* non-extended sense */
- linktp->sense = sns[2]; /* keep last error info */
- linktp->xsense = sns[12];
- }
- MPR3("io_Error=%d Sense=%X,%02X\n", x, sns[2], sns[12])
- return;
- }
-
- /**************************************************************************/
-
- void FreeStuff(ULONG bs)
- {
- if(inprog) TapeIO(TFINISH,0,CTLWAIT); /* just in case */
- if(TapeBuff[0]) FreeMem(TapeBuff[0],bs);
- if(TapeBuff[1]) FreeMem(TapeBuff[1],bs);
- TapeBuff[0] = TapeBuff[1] = NULL;
- return;
- }
-
- /**************************************************************************/
- /* Nextnum parses and returns the next number in the startup string */
-
- ULONG Nextnum(short kk) /* kk=0 for first number */
- { /* kk=1 for other numbers */
- char *zz; /* kk=-1 for pointer to last token */
- zz = (kk) ? NULL : z;
- z = strtok(zz,"/");
- if(kk==-1) return( (ULONG) z);
- else return( (ULONG) strtol(z,NULL,0) );
- }
-
- /**************************************************************************
- 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;
- }
-
-