home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fish 'n' More 2
/
fishmore-publicdomainlibraryvol.ii1991xetec.iso
/
fish
/
devs&handlers
/
btntape
/
tape.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-10-27
|
17KB
|
429 lines
/**** 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;
}