/**** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined AZTEC_C #include #elif defined LATTICE #include #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; }