home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d5xx / d558 / btntape.lha / BTNtape / src / tape.c < prev    next >
C/C++ Source or Header  |  1991-10-28  |  31KB  |  813 lines

  1. /****     BTNtape Handler for SCSI tape drives        ****/
  2. /**** Author: Bob Rethemeyer  (DrBob@cup.portal.com)  ****/
  3.  
  4. #define VERSTAG "$VER: BTNtape/RAR 2.1 "
  5. /*  (If you recompile BTN, put ^^^ your initials here) */
  6.  
  7. /*  (c) Copyright 1990, 1991  Robert Rethemeyer.
  8.  *  This software may be freely distributed and redistributed,
  9.  *  for non-commercial purposes, provided this notice is included.
  10.  *--------------------------------------------------------------------------
  11.  *  BTNtape is an AmigaDOS device handler to make a simple DOS TAPE: device.
  12.  *  It converts DOS packets for the device into I/O requests to a
  13.  *  "SCSI-direct" compatible device driver.  It is based on "my.handler"
  14.  *  by Phillip Lindsay and a SCSI-direct program by Robert Mitchell.
  15.  *  Seek() handling derived from code by Dennis J. Brueni, 8-24-91.
  16.  *  Source is ANSI C compliant.  Compile with SAS/C v5 or Manx v5.
  17.  *
  18.  *  This handler works in conjunction with the accompanying TapeMon program.
  19.  *--------------------------------------------------------------------------
  20.  */
  21.  
  22. #include <exec/types.h>
  23. #include <exec/nodes.h>
  24. #include <exec/lists.h>
  25. #include <exec/ports.h>
  26. #include <exec/tasks.h>
  27. #include <exec/libraries.h>
  28. #include <exec/io.h>
  29. #include <exec/memory.h>
  30. #include <devices/scsidisk.h>
  31. #include <intuition/intuition.h>
  32. #include <libraries/dos.h>
  33. #include <libraries/dosextens.h>
  34. #include <libraries/filehandler.h>
  35. #include <stdio.h>
  36. #include <string.h>
  37.  
  38. #if defined AZTEC_C
  39.   #include <functions.h>
  40. #elif defined LATTICE
  41.   #include <proto/exec.h>
  42.   #include <proto/intuition.h>
  43. #endif
  44.  
  45. #include "tape.h"
  46. #include "tplink.h"
  47.  
  48. /* 2.0 stuff when using 1.3 includes */
  49. #ifndef ACTION_IS_FILESYSTEM
  50.   #define ACTION_IS_FILESYSTEM 1027  /* packet for IsFileSystem() */
  51. #endif
  52.  
  53. /* sense keys of interest */
  54. #define NOS 0x00  /* no sense */
  55. #define RCV 0x01  /* recovered error */
  56. #define ILL 0x05  /* illegal request */
  57. #define UAT 0x06  /* unit attention */
  58. #define BLC 0x08  /* blank check */
  59. #define VOF 0x0d  /* volume overflow */
  60. /* pseudo sense keys returned by DoSense */
  61. #define FMK 0x10  /* filemark */
  62. #define EOM 0x11  /* end of tape */
  63. #define ILI 0x12  /* incorr leng */
  64. #define SERR 0x13 /* SCSI error  */
  65.  
  66. struct things {  /* a collection of things we will have to alloc */
  67.          UBYTE cmdbuff[16];         /* SCSI command buff */
  68.          UBYTE snsarea[64];         /* sense data buffer */
  69.          UBYTE inqdata[40];         /* inquiry data buff */
  70.          struct SCSICmd scsicmd;    /* SCSIdirect cmd buff */
  71.          UBYTE pad[16];  /* SCSICmd is smaller in old include files */
  72.          } ;
  73.  
  74. /*======== Global data */
  75.  
  76. struct ExecBase      *SysBase = (struct ExecBase *) 4;
  77. struct IntuitionBase *IntuitionBase;
  78. UBYTE           *cdb;          /* pointer to SCSI command buffer */
  79. UBYTE           *sns;          /* pointer to sense data buffer   */
  80. UBYTE           *inq;          /* pointer to inquiry data        */
  81. struct SCSICmd  *cmd;          /* pointer to scsidirect command  */
  82. struct IOStdReq *ior;          /* pointer to io request structure*/
  83. UBYTE           *TapeBuff[2]   /* pointers to 2 tape buffers     */
  84.                             ={NULL,NULL};
  85. struct tplink   *linktp;       /* pointer to link structure      */
  86. ULONG  blknum;                 /* block number for io operation  */
  87. ULONG  blksize = 512;          /* bytes per tape block           */
  88. ULONG  numblks = 1;            /* number of blocks per io oper   */
  89. ULONG  TBSize;                 /* bytes in a tape buffer         */
  90. ULONG  rwlen;                  /* bytes in a tape read/write     */
  91. ULONG  tranlen;                /* bytes/blks in a read/write     */
  92. ULONG  bugmask = 0;            /* 2090A bug circumvention        */
  93. long   tpsize ;                /* tape size in blocks            */
  94. long   reserved = 0;           /* number of reserved blks at BOT */
  95. int    tapenum;                /* tape volume number             */
  96. int    inprog = FALSE;         /* io operation in progress flag  */
  97. char   *z;                     /* scratch */
  98. char   *devname, nbuf[32];     /* file name reference from Open()*/
  99. char   dbb[80];                /* buffer for monitor messages    */
  100. int    reten = FALSE;          /* retension on UAT flag          */
  101. UBYTE  Lun = 0;                /* Logical Unit number << 5       */
  102. UBYTE  fixedbit = 0;           /* fixed-block mode bit for SEQ   */
  103.  
  104. /***********************  Main program  ********************************/
  105. #ifdef AZTEC_C
  106.   #pragma intfunc(_main())
  107. #endif
  108. void _main(void)
  109. {
  110.  struct tplink      tpl;       /* structure to link hndlr & mon     */
  111.  struct Process     *myproc;   /* ptr to handler's process struct   */
  112.  struct DosPacket   *mypkt;    /* ptr to dos packet sent            */
  113.  struct DeviceNode  *mynode;   /* ptr to devnode passed in pkt Arg3 */
  114.  struct things      *xarea;    /* ptr to dynamic misc. areas        */
  115.  ULONG   dvnode;        /* ptr to devnode passed in pkt Arg3 */
  116.  ULONG   unit=99999;    /* device SCSI unit address          */
  117.  ULONG   bufmemtype=0;  /* type of mem for dynamic buffers   */
  118.  ULONG   gotopos=0;     /* position to open at (blk/filemark)*/
  119.  ULONG   fmarks=1;      /* # file marks for write/skip       */
  120.  ULONG   dos2=0;        /* indicates if running under 2.0    */
  121.  char    *driver;       /* name of SCSI device driver        */
  122.  UBYTE   *dptr;         /* ptr to next byte in dos buffer    */
  123.  long    dflags=0;      /* device flags                      */
  124.  long    iostat;        /* status of previous read           */
  125.  long    dcnt;          /* count of dos packet bytes to move */
  126.  long    mcnt;          /* count of bytes to move            */
  127.  long    rcnt;          /* count of bytes moved for read     */
  128.  long    current;       /* current byte position in file     */
  129.  long    Boff;          /* current offset in tape buffer     */
  130.  long    remain;        /* bytes remaining in tape buffer    */
  131.  long    Supra=FALSE;   /* flag for Supra 1.10 circumvention */
  132.  long    pkcnt;         /* flag to indicate data written     */
  133.  long    x;
  134.  int     y=0;
  135.  int     opmode=0;      /* how has the handler been opened   */
  136.     #define CLOSED  0       /* not doing anything */
  137.     #define READING 1       /* reading tape */
  138.     #define WRITING 2       /* writing tape */
  139.     #define RAWMODE 3       /* raw command mode */
  140.     #define UMODSEL 4       /* user mode select */
  141.  int     Bn;            /* current buffer number, 0 or 1     */
  142.  int     position;      /* how to position tape at Open      */
  143.     #define POSREW  0       /* rewind to beginning */
  144.     #define POSNREW 1       /* don't move          */
  145.     #define POSAPP  2       /* append to eod (seq) */
  146.     #define POSSKIP 3       /* skip to file/block  */
  147.  int     nrflag=POSREW; /* rewind behavior of TAPE: */
  148.  int     dirty=FALSE;   /* buffer has unwritten data in it   */
  149.  int     atend;         /* flags filemark encountered        */
  150.  int     Ctl=CTLIMM;    /* Controls overlapped dos/scsi I/O  */
  151.  BYTE    acksig;        /* monitor acknowledge signal number */
  152.  UBYTE   varblk = 0;    /* variable-block flag for SEQ       */
  153.  
  154. /*======== Startup */
  155.  
  156.  IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
  157.  myproc  = (struct Process *) FindTask(0L);          /* find this process */
  158.  mypkt = taskwait();                           /* wait for startup packet */
  159.  /* packet: Arg1=BSTR to name, Arg2=BSTR to startup string, Arg3=BPTR devnode*/
  160.  mynode = (struct DeviceNode *) BADDR(mypkt->dp_Arg3);
  161.  dvnode = (ULONG) mypkt->dp_Arg3;
  162.  
  163. /*======== Create linkage for the tape monitor:  install pointer to tplink
  164.   ======== structure in the free pointer of the task block, so the tape
  165.   ======== monitor can find it after FindTask().
  166. */
  167.  tpl.keyword = "TapeHandler";
  168.  tpl.version = VERSTAG "(" __DATE__ ")";
  169.  tpl.devnode = (void *)mynode;
  170.  tpl.dbb     = dbb;
  171.  tpl.unit    = &unit;
  172.  tpl.Lun     = &Lun;
  173.  tpl.montask = NULL;
  174.  tpl.sense   = NULL;
  175.  tpl.badparm = 0;
  176.  linktp = &tpl;
  177.  ((struct Task *)myproc)->tc_UserData = (APTR) linktp;
  178.  
  179. /*======== Extract info from mountlist Startup parameter. It may be
  180.   ======== enclosed in quotes, and each item is separated by a single '/'.
  181.   ======== First item is always the driver name, others may be in any order.
  182. */
  183.  z = (char *)BADDR(mypkt->dp_Arg2)+1 ;  /* Arg2= BSTR to mountlist 'Startup'*/
  184.  if(*z=='\"')  {                      /* remove quotes if any */
  185.      z++;
  186.      z[strlen(z)-1]= '\0' ;
  187.  }
  188.  driver = tpl.driver = z;
  189.  for(; *z != '/'; z++);
  190.  *(z++) = '\0';
  191.  toUPPER(z);
  192.  while (y!=100) {
  193.    switch(y=getstart(&x)) {
  194.      case 0:  /* UN */  unit = (ULONG) x;  break;
  195.      case 1:  /* BS */  blksize = (ULONG) x;  break;
  196.      case 2:  /* NB */  numblks = (ULONG) x;  break;
  197.      case 3:  /* RB */  reserved = x;  break;
  198.      case 4:  /* BT */  bufmemtype = (ULONG) x;  break;
  199.      case 5:  /* FM */  fmarks = x & 0xff;  break;
  200.      case 6:  /* VB */  if(x) varblk = 1;   break;
  201.      case 7:  /* OV */  if(!x) Ctl = CTLWAIT; break;
  202.      case 8:  /* DF */  dflags = x; break;
  203.      case 9:  /* SU */  Supra = x;  break;
  204.      case 10: /* C9 */  if(x) bugmask = 0x01000000; break;
  205.      case 11: /* NR */  nrflag = x ? POSNREW : POSREW; break;
  206.      case 12: /* RT */  if(x) reten = 1; break;
  207.      case 99: /* ?? */  tpl.badparm=TRUE; break;
  208.      default: ;
  209.    }
  210.  }
  211.  Lun = ((UBYTE)unit & 0x70) << 1;
  212.  rwlen = TBSize = numblks * blksize;   /* size of a tape buffer */
  213.  if(Supra)  rwlen = 0;
  214.  
  215. /*======== Allocate some memory for non-data buffers */
  216.  
  217.  if( !(xarea = (struct things *)
  218.            AllocMem(sizeof(struct things), bufmemtype | MEMF_CLEAR) ))
  219.            { thud("Insufficient memory");
  220.              returnpkt(mypkt,DOSFALSE,ERROR_NO_FREE_STORE);
  221.              CloseLibrary((struct Library *)IntuitionBase);
  222.              return;
  223.            }
  224.  cdb = &xarea->cmdbuff[0];
  225.  sns = &xarea->snsarea[0];
  226.  inq = tpl.inquiry = &xarea->inqdata[0];
  227.  cmd = &xarea->scsicmd;
  228.  ior = (struct IOStdReq *) CreateExtIO( CreatePort(0,0),
  229.                                          sizeof(struct IOStdReq));
  230.  
  231. /*======== Open the SCSIdirect device */
  232.  
  233.  if ( OpenDevice(driver,unit,(struct IORequest *)ior,dflags)  )  {
  234.     thud("Can't open SCSI-direct");
  235.     LEAVE:
  236.     returnpkt(mypkt,DOSFALSE,ERROR_INVALID_COMPONENT_NAME);
  237.     CloseLibrary((struct Library *)IntuitionBase);
  238.     FreeMem(xarea,sizeof(struct things));
  239.     return;
  240.  }
  241.  
  242. /*======== Allocate the signal that TapeMon will
  243.   ======== use to acknowledge MPR requests.
  244. */
  245.  acksig = AllocSignal(-1);
  246.  if(acksig != -1) tpl.handsig = ((ULONG)1) << acksig;
  247.  /* else { monitor will not attempt to signal us } */
  248.  
  249. /*======= Find the SCSI device type by INQUIRY.  Sequential or direct access?
  250. */
  251.  if(x=TapeIO(INQUIRY,0,CTLWAIT)) {
  252.     if(x <= HFERR_SelTimeout) { thud("Can't select tape drive"); goto LEAVE; }
  253.     TapeIO(TSENSE,0,CTLWAIT);
  254.     inq[0] = 0x01; /* assume SEQ */
  255.     inq[8] = 0;
  256.  }
  257.  inq[36] = 0;  /* null-terminate vendor info */
  258.  if(SEQ) {
  259.    reserved = 0;
  260.    fixedbit = varblk ^ 0x01;  /* fixed or variable block mode */
  261.    tranlen = (fixedbit) ? numblks : TBSize;
  262.  }
  263.  else if(DAC) tranlen = numblks;
  264.  else { thud("UNit is not a tape drive"); goto LEAVE; }
  265.  
  266.  mynode->dn_Task = &myproc->pr_MsgPort;    /* install handler taskid */
  267.  returnpkt(mypkt,DOSTRUE,0);               /* reply to initial packet */
  268.  
  269. /* =========== The main packet processing loop =============== */
  270.  
  271.  for (;;)  {
  272.    mypkt = taskwait();          /* wait for a packet */
  273.    switch(mypkt->dp_Type) {
  274.  
  275.      case ACTION_FINDINPUT:  /*----------- Open() ------------*/
  276.      case ACTION_FINDOUTPUT:
  277.           if(opmode != CLOSED)  {
  278.              returnpkt(mypkt,DOSFALSE,ERROR_OBJECT_IN_USE);
  279.              break;
  280.           }
  281.           /* Allocate storage for buffers */
  282.           TapeBuff[0] = (UBYTE *) AllocMem(TBSize, bufmemtype | MEMF_CLEAR);
  283.           TapeBuff[1] = (UBYTE *) AllocMem(TBSize, bufmemtype | MEMF_CLEAR);
  284.           if (!TapeBuff[0] || !TapeBuff[1]) {
  285.                freestuff();
  286.                returnpkt(mypkt,DOSFALSE,ERROR_NO_FREE_STORE);
  287.                MPR1("Can't get %u bytes for tape buffers\n",TBSize*2);
  288.                break;
  289.           }
  290.  
  291.           /* Determine open mode */
  292.           z = (char *)BADDR(mypkt->dp_Arg3);
  293.           x = (UBYTE)(*z);
  294.           devname = (char *)memcpy(nbuf, z+1, x);
  295.           *(devname+x) = '\0';
  296.           toUPPER(devname);
  297.           for(z=devname; *(z++)!=':' ;);
  298.           if     (!strcmp(z,"RAWCMD"))  opmode=RAWMODE;
  299.           else if(!strcmp(z,"MODESEL")) opmode=UMODSEL;
  300.           else {    /* normal read/write */
  301.              opmode = (mypkt->dp_Type==ACTION_FINDINPUT) ? READING : WRITING;
  302.              if     (!strcmp(z,"*"))   position=POSNREW; /* current position */
  303.              else if(!strcmp(z,"NR"))  position=POSNREW; /* current position */
  304.              else if(!strcmp(z,"R"))   position=POSREW;  /* beginning of tape */
  305.              else if(!strcmp(z,"APP")) position=POSAPP;  /* append at end */
  306.              else if(*z>='0' && *z<='9') {               /* specific position */
  307.                    gotopos = (ULONG) strtoul (z,NULL,0);
  308.                    position=POSSKIP;
  309.              }
  310.              else if(*z=='\0') position = nrflag;  /* TAPE: */
  311.              else goto BADNAME;
  312.           }
  313.  
  314.           /* detect illegal combinations */
  315.           if((opmode>=RAWMODE && mypkt->dp_Type==ACTION_FINDINPUT)
  316.           || (opmode==READING && position==POSAPP)
  317.           || (SEQ && opmode==WRITING && position==POSSKIP)
  318.           || (DAC && position==POSAPP))  {
  319.             BADNAME:
  320.              MPR1("Invalid open name/combo %s\n",devname);
  321.              freestuff();
  322.              opmode=CLOSED;
  323.              returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
  324.              break;
  325.           }
  326.  
  327.           if(opmode<=WRITING) {
  328.  
  329.             /* Check device ready */
  330.             if(x=TapeIO(TREADY,0,CTLWAIT))  {
  331.                y=DoSense(x);
  332.                if (y==UAT) {
  333.                     blknum = reserved;
  334.                     if(reten) if(x=TapeIO(TRETEN,0,CTLWAIT)) DoSense(x);
  335.                }
  336.                else if(y==NOS) ;
  337.                else if(y==ILL) ;
  338.                else {
  339.                GIVEUP:
  340.                  freestuff();
  341.                  opmode=CLOSED;
  342.                  returnpkt(mypkt,DOSFALSE,ERROR_DEVICE_NOT_MOUNTED);
  343.                  break;
  344.                }
  345.             }
  346.  
  347.             /* Check write-prot, block length */
  348.             if(x=TapeIO(MDSNS,0,CTLWAIT)) DoSense(x);
  349.             else {
  350.                if(opmode==WRITING && (sns[2] & 0x80)) {
  351.                  freestuff();
  352.                  opmode=CLOSED;
  353.                  returnpkt(mypkt,DOSFALSE,ERROR_DISK_WRITE_PROTECTED);
  354.                  break;
  355.                }
  356.                if(sns[3] >= 8) {
  357.                  x = *((long *) &sns[8]) & 0x00ffffff;  /* get block len */
  358.                  if(x != blksize) {  /* set block size with Mode Select */
  359.                    MPR1("Current block size = %u\n",x);
  360.                    if(x=TapeIO(MDSET,(int)varblk,CTLWAIT)) DoSense(x);
  361.                    else TapeIO(TSENSE,0,CTLWAIT);
  362.                  }
  363.                }
  364.             }
  365.  
  366.             /* position the medium at starting spot */
  367.             if(position==POSNREW) gotopos=blknum;
  368.             if(postape(position,fmarks,gotopos)) goto GIVEUP;
  369.  
  370.             /* Get capacity for 3M tape */
  371.             if(DAC) {
  372.                tpsize = 0x7fffffff;
  373.                if(TapeIO(RDCAP,0,CTLWAIT)) TapeIO(TSENSE,0,CTLWAIT);
  374.                else {
  375.                  tpsize = ((sns[2] << 8) | sns[3]) + 1;
  376.                  MPR1("Capacity: %u blocks\n", tpsize);
  377.                }
  378.             }
  379.           }
  380.  
  381.           dirty=FALSE; inprog=FALSE; Boff=0; pkcnt=0; tapenum=1;
  382.           iostat=0;  Bn=0;  remain=TBSize;  current=0;  atend=FALSE;
  383.           MPR2("%s Opened at block %u\n",devname,blknum);
  384.           returnpkt(mypkt,DOSTRUE,0);
  385.           break;
  386.  
  387.  
  388.      case ACTION_END:        /*----------- Close() -----------*/
  389.           switch(opmode) {
  390.              case CLOSED:
  391.                 returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN); break;
  392.              case RAWMODE:
  393.                 if(x=TapeIO(RAWCMD,Bn,CTLWAIT)) DoSense(x); break;
  394.              case UMODSEL:
  395.                 if(x=TapeIO(USRMODE,Bn,CTLWAIT)) DoSense(x); break;
  396.              case READING:
  397.                 if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
  398.                 if(iostat) DoSense(iostat);
  399.                 break;
  400.              case WRITING:
  401.                 if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
  402.                 if(iostat) iostat = wrteot(Bn^1,1,iostat);
  403.                 if(iostat==0) {
  404.                    if(dirty) {
  405.                       memset(&TapeBuff[Bn][Boff],0,remain);
  406.                       iostat = TapeIO(TWRITE,Bn,CTLWAIT);
  407.                    }
  408.                    if(iostat) iostat = wrteot(Bn,0,iostat);
  409.                 }
  410.                 if(iostat) { MPR0("Error during write\n"); }
  411.                 else if(pkcnt) {
  412.                    blknum += numblks;  x=0;
  413.                    if(SEQ && fmarks) x=TapeIO(WFMARK,fmarks,CTLWAIT);
  414.                    if(x) DoSense(x);
  415.                 }
  416.           }
  417.           returnpkt(mypkt,DOSTRUE,0);
  418.           opmode=CLOSED;
  419.           freestuff();
  420.           MPR1("Closed at block %u\n\n",blknum);
  421.           break;
  422.  
  423.      case ACTION_READ:       /*------- Read(), Seek() --------*/
  424.      case ACTION_SEEK:
  425.           if(opmode != READING) {
  426.               MPR0("Function/mode error\n");
  427.               mypkt->dp_Arg3 = -1;
  428.               goto RDEND;
  429.           }
  430.           if(mypkt->dp_Type == ACTION_READ) {
  431.               dptr = (UBYTE *) mypkt->dp_Arg2;
  432.               dcnt =           mypkt->dp_Arg3;
  433.               rcnt = 0;
  434.           }
  435.           else {  /* ACTION_SEEK */
  436.               rcnt = -1;
  437.               dcnt = mypkt->dp_Arg2;  /* offset */
  438.               x    = mypkt->dp_Arg3;  /* type   */
  439.               if(atend || varblk) goto BADSEEK;
  440.               if(x==OFFSET_END) goto BADSEEK;
  441.               if(x==OFFSET_BEGINNING) dcnt -= current;
  442.               /* x==OFFSET_CURRENT, dcnt is correct */
  443.               mypkt->dp_Arg3 = current;
  444.               if(dcnt<0) { /* backwards seek, must be to BOF */
  445.                 if((dcnt+current)==0) {
  446.                    if(SEQ && position == POSNREW) goto BADSEEK;
  447.                    if((dcnt+Boff)<0) {  /* still in buffer? */
  448.                       if(inprog) TapeIO(TFINISH,0,CTLWAIT);
  449.                       if(postape(position,fmarks,gotopos)) goto BADSEEK;
  450.                       dirty=FALSE;  Bn=0;
  451.                    }
  452.                    current=0;  dcnt=0;  Boff=0;  remain=TBSize;
  453.                 }
  454.                 else {
  455.                 BADSEEK:
  456.                    MPR3("Disallowed seek type=%d off=%d curr=%d\n",x,dcnt,current);
  457.                    mypkt->dp_Arg3 = -1;
  458.                    goto RDEND;
  459.                 }
  460.               }
  461.           }
  462.           while(dcnt) {
  463.             if(atend)  { mypkt->dp_Arg3 = 0;  goto RDEND; }
  464.             if(!dirty) {  /* need to prefetch first buffer */
  465.                iostat = TapeIO(TREAD,Bn,CTLWAIT);
  466.                remain=0;  Bn=1;  dirty=TRUE;
  467.             }
  468.             if(remain==0)   {
  469.                if(inprog)  iostat = TapeIO(TFINISH,0,CTLWAIT);
  470.                if(iostat) {  /* check status of previous read */
  471.                   y=DoSense(iostat);  iostat=0;
  472.                   switch(y) {
  473.                     case RCV:
  474.                        y=0; break;
  475.                     case VOF:
  476.                     case EOM:  /* End of Tape: */
  477.                        if(NewTape())  {   /* ask for new tape */
  478.                           y=0;            /* and reread buffer */
  479.                           iostat=TapeIO(TREAD,(Bn^1),CTLWAIT);
  480.                           if(iostat) y=DoSense(iostat); /* and fall thru */
  481.                           else break;
  482.                        } /* else fall thru */
  483.                     case FMK:
  484.                        mypkt->dp_Arg3 = rcnt;
  485.                        atend=TRUE;
  486.                        break;
  487.                     default:
  488.                        MPR0("Error during read\n");
  489.                        mypkt->dp_Arg3 = -1;
  490.                   }
  491.                   if(y) goto RDEND;
  492.                }
  493.                blknum += numblks;
  494.                iostat = TapeIO(TREAD,Bn,Ctl); /* start refilling this buffer */
  495.                Bn ^= 1;      /* switch to other (full) buffer */
  496.                remain = TBSize;
  497.                Boff = 0;
  498.             }
  499.             mcnt = (dcnt>remain) ? remain : dcnt;
  500.             if(mypkt->dp_Type == ACTION_READ)  {
  501.                memcpy (dptr, &TapeBuff[Bn][Boff], mcnt);
  502.                rcnt += mcnt ;
  503.             }
  504.             dcnt -= mcnt ;
  505.             Boff += mcnt ;
  506.             dptr += mcnt ;
  507.             current += mcnt;
  508.             remain -= mcnt ;
  509.           }
  510.           RDEND:
  511.           returnpkt(mypkt,mypkt->dp_Arg3,0);
  512.           break;
  513.  
  514.  
  515.      case ACTION_WRITE:      /*----------- Write() -----------*/
  516.           if(opmode < WRITING) {
  517.             MPR0("Function/mode error\n");
  518.             mypkt->dp_Arg3 = -1;
  519.             goto WRTEND;
  520.           }
  521.           pkcnt++;
  522.           dptr = (UBYTE *) mypkt->dp_Arg2;
  523.           dcnt =           mypkt->dp_Arg3;
  524.           while (dcnt)  {
  525.             if(dcnt >= remain) {
  526.                memcpy (&TapeBuff[Bn][Boff], dptr, remain);
  527.                if(inprog) iostat = TapeIO(TFINISH,0,CTLWAIT);
  528.                if(iostat) {  /* check status of previous write */
  529.                  if(iostat = wrteot(Bn^1,1,iostat)) {  /* possible EOT */
  530.                     mypkt->dp_Arg3 = -1;
  531.                     MPR0("Error during write\n");
  532.                     iostat=0;  dirty=FALSE;  pkcnt=0;
  533.                     goto WRTEND;
  534.                  }
  535.                }
  536.                iostat = TapeIO(TWRITE,Bn,Ctl); /* start writing full buffer */
  537.                blknum += numblks;
  538.                dcnt -= remain;
  539.                dptr += remain;
  540.                Boff  = 0;
  541.                remain= TBSize;
  542.                Bn   ^= 1;      /* switch to other (empty) buffer */
  543.                dirty = FALSE;
  544.             }
  545.             else {
  546.                memcpy (&TapeBuff[Bn][Boff], dptr, dcnt);
  547.                remain -= dcnt;
  548.                Boff += dcnt;
  549.                dcnt  = 0;
  550.                dirty = TRUE;
  551.             }
  552.           }
  553.           WRTEND:
  554.           returnpkt(mypkt,mypkt->dp_Arg3,0);
  555.           break;
  556.  
  557.  
  558.      case ACTION_IS_FILESYSTEM:  /* DOS 2.0 only */
  559.           dos2 = ERROR_ACTION_NOT_KNOWN;
  560.           returnpkt(mypkt,DOSFALSE,0);
  561.           break;
  562.  
  563.      case ACTION_LOCATE_OBJECT:  /* lock */
  564.           returnpkt(mypkt,mypkt->dp_Arg1,dos2);
  565.           break;
  566.  
  567.      case ACTION_FREE_LOCK:      /* unlock */
  568.           returnpkt(mypkt,DOSTRUE,0);
  569.           break;
  570.  
  571.      case ACTION_CURRENT_VOLUME:
  572.           returnpkt(mypkt,dvnode,0);
  573.           break;
  574.  
  575.      case ACTION_FLUSH:   /* flush buffers, NOOP */
  576.           returnpkt(mypkt,DOSTRUE,0);
  577.           break;
  578.  
  579.      case ACTION_DISK_INFO:    /* info */
  580.           returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
  581.           break;
  582.  
  583.      default:  /* others not supported */
  584.           returnpkt(mypkt,DOSFALSE,ERROR_ACTION_NOT_KNOWN);
  585.           MPR1("Unsupported_Pkt=%d\n",mypkt->dp_Type);
  586.  
  587.      } /* end of switch */
  588.  } /* end of loop */
  589. } /* end of _main() */
  590.  
  591. /**************************************************************************/
  592.  
  593. int DoSense(long iocode)
  594. {
  595.    UBYTE snskey;
  596.    static char *snstext[] = {
  597.        "NONE" , "RECOVERED ERROR", "NOT READY", "MEDIUM ERROR",
  598.        "HARDWARE ERROR", "ILLEGAL REQUEST", "UNIT ATTENTION", "DATA PROTECT",
  599.        "BLANK CHECK", "KEY=9", "COPY ABORTED", "ABORTED COMMAND", "KEY=C",
  600.        "VOLUME OVERFLOW", "KEY=E", "KEY=F", "FILEMARK", "END-OF-MEDIUM",
  601.        "INCORRECT BLOCK LENGTH"
  602.    };
  603.    static char *sderror[] = {
  604.        "SELF-UNIT", "DMA", "PHASE", "PARITY", "SELECT-TIMEOUT"
  605.    };
  606.  
  607.    if(iocode==0 || iocode==HFERR_BadStatus)  {
  608.        TapeIO(TSENSE,0,CTLWAIT);
  609.        if((sns[0] & 0x70)==0x70) {  /* extended */
  610.           if     (sns[2] & 0x80) snskey = FMK;  /* pseudo sense keys */
  611.           else if(sns[2] & 0x40) snskey = EOM;
  612.           else if(sns[2] & 0x20) snskey = ILI;
  613.           else                   snskey = sns[2] & 0x0f;  /* real sense key */
  614.        }
  615.        else snskey = sns[0] & 0x0f; /* non-extended */
  616.        linktp->sense = snstext[snskey];  /* keep last error info */
  617.        linktp->xsns1 = sns[12];
  618.        linktp->xsns2 = sns[13];
  619.        linktp->sns = sns;  /* flag for tapemon to print all data */
  620.        MPR3("Sense: %s, other= %02X,%02X\n", snstext[snskey], sns[12],sns[13]);
  621.        linktp->sns = NULL;
  622.        return((int)snskey);
  623.    }
  624.    else if(iocode==FAKEOM) return(EOM);  /* DAC only */
  625.    else { MPR1("SCSI %s ERROR\n",sderror[iocode-40]);
  626.        return(SERR);
  627.    }
  628. }
  629.  
  630. /**************************************************************************/
  631. /* postape():  position the tape medium at the place we want to open it
  632. */
  633.  
  634. int postape(int how, int fm, long where)
  635. {
  636.  long x;  int y=0;
  637.    switch(how) {
  638.     case POSREW:             /* tape:   */
  639.        if(x=TapeIO(TREWIND,0,CTLWAIT)) y=DoSense(x);
  640.        blknum = reserved;
  641.        break;
  642.     case POSNREW:            /* tape:*  */
  643.        if(SEQ) blknum = 0;
  644.        break;
  645.     case POSAPP:            /* tape:app */
  646.        if(x=TapeIO(TSKIPE,0,CTLWAIT)) y=DoSense(x);
  647.        if(y==ILL) {  /* drive doesn't support skip-eod */
  648.           y=0;  MPR0("(doing it the hard way)\n");
  649.           while(y==0) { /* skip files until we hit a blank spot */
  650.              if(x=TapeIO(TSKIPF,1,CTLWAIT)) y=DoSense(x);
  651.              if(y==FMK) y=0;
  652.           }
  653.           if(y==BLC) y=0;
  654.        }
  655.        blknum = 0;
  656.        break;
  657.     case POSSKIP:            /* tape:n */
  658.        if(DAC) blknum = where;
  659.        else { /* SEQ */
  660.          if(x=TapeIO(TREWIND,0,CTLWAIT)) { y=DoSense(x); break; }
  661.          if(fm==0) break;
  662.          MPR1("Skipping to file %d\n",where);
  663.          if(x=TapeIO(TSKIPF,(int)(where*fm),CTLWAIT)) y=DoSense(x);
  664.          if(y==FMK) y=0;
  665.          blknum = 0;
  666.        }
  667.        break;
  668.    }
  669.    if(y) { MPR0("Unable to position tape\n"); }
  670.    return(y);
  671. }
  672.  
  673. /**************************************************************************/
  674. /* wrteot:  handle messiness that happens when writing at end of tape.
  675.       Returns non-zero status if not EOT or tape swap unsuccessful. */
  676.  
  677. long wrteot(int bfn, int dec, long stat)
  678. {
  679.    long ios = stat;
  680.    int  s = DoSense(ios);
  681.    if(s==EOM || s==VOF) {   /* EOT? */
  682.       if(NewTape()) {       /* ask for new tape */
  683.          if(SEQ && dec) blknum -= numblks;  /* and rewrite old buffer */
  684.          ios=TapeIO(TWRITE,bfn,CTLWAIT);
  685.          if(ios) s=DoSense(ios);
  686.          if(dec) blknum += numblks;
  687.       }
  688.    }
  689.    if(s==RCV) ios=0;  /* ignore recoveries */
  690.    return(ios);
  691. }
  692.  
  693. /**************************************************************************/
  694.  
  695. void freestuff(void)
  696. {
  697.   if(TapeBuff[0]) FreeMem(TapeBuff[0],TBSize);
  698.   if(TapeBuff[1]) FreeMem(TapeBuff[1],TBSize);
  699.   TapeBuff[0] = TapeBuff[1] = NULL;
  700.   return;
  701. }
  702.  
  703. /**************************************************************************/
  704. /* getstart:
  705.     Given pointer (z) to a string of the form "SS-nn/..."
  706.     returns a number corresponding to SS, the binary value of nn,
  707.     and the pointer is updated to point after the slash.
  708. */
  709. int getstart(long *num)
  710. {
  711.   extern char *z;
  712.   #define NOTFND 99
  713.   #define ENDLST 100
  714.   #define NKEYS  13  /* number of keywords */
  715.   static char *keys[NKEYS] =
  716.       { "UN","BS","NB","RB","BT","FM","VB","OV","DF","SU","C9","NR","RT"};
  717.      /*  0    1    2    3    4    5    6    7    8    9    10   11   12 */
  718.   char *ii, *jj;  int kk;
  719.  
  720.   if (*z == '\0') return(ENDLST);      /* return if end of string */
  721.   for (ii=z;  *ii != '-'; ii++);       /* find the dash */
  722.   for (jj=z;  *jj != '/' && *jj !='\0'; jj++); /* find the slash */
  723.   *ii = '\0'; *jj = '\0';              /* null-terminate name & number */
  724.   *num = (long) strtoul (++ii,NULL,0); /* return converted number */
  725.   ii = z;     z = ++jj;                /* update ptr to next item */
  726.   for (kk=0; kk <= NKEYS; kk++)  {     /* search for keyword */
  727.      if (*ii == *keys[kk] && *(ii+1) == *(keys[kk]+1) )
  728.         return(kk);                    /* return index of keyword if found */
  729.   }
  730.   return(NOTFND);                      /* didn't find keyword */
  731. }
  732.  
  733. /**************************************************************************
  734.    MonPrint requests that the TapeMon program print the message in 'dbb'.
  735.     Since this handler cannot do DOS I/O, it must beg the TapeMon program,
  736.     possibly running in a CLI somewhere, to do the printf for it.  If the
  737.     TapeMon is running, it will have installed a pointer to its task in
  738.     the link structure, and we can Signal() it.
  739. */
  740.  
  741. void MonPrint(void)
  742. {
  743.    if(linktp->montask)  {
  744.       Signal(linktp->montask, linktp->monsig);
  745.       Wait  (linktp->handsig);
  746.       Signal(linktp->montask, linktp->monsig);
  747.       Wait  (linktp->handsig);
  748.    }
  749.    return;
  750. }
  751.  
  752. /**************************************************************************/
  753. /* toUPPER: convert string to upper case */
  754.  
  755. void toUPPER(char *zz)
  756. {
  757.   for(; *zz != '\0'; zz++)  if(*zz >= 'a') *zz -= 0x20;
  758.   return;
  759. }
  760.  
  761. /**************************************************************************
  762. *  NewTape()   Displays a requester asking user to insert a new tape.
  763. */
  764.  
  765. long NewTape(void)
  766. {
  767.  long choice,s,t,v;
  768.  static char rqm[40];
  769.  static struct IntuiText rtxt[] = {
  770.     { AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
  771.        AUTOITEXTFONT, (UBYTE *) &rqm[0], NULL },
  772.     { AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
  773.        AUTOITEXTFONT, (UBYTE *) " Continue ", NULL },
  774.     { AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
  775.        AUTOITEXTFONT, (UBYTE *) " Abort ", NULL }
  776.  };
  777.  
  778.  MPR1("Time to insert tape# %d\n",++tapenum);
  779.  sprintf(rqm," Insert tape %d for %s",tapenum,devname);
  780.  do {
  781.     s=NOS;
  782.     choice=AutoRequest(NULL,&rtxt[0],&rtxt[1],&rtxt[2],NULL,NULL,250,50);
  783.     if(choice) do {
  784.         if(t=TapeIO(TREADY,0,CTLWAIT)) s=DoSense(t);  /* eat unit-atten */
  785.         else s=NOS;
  786.         if(s==UAT && reten) if(v=TapeIO(TRETEN,0,CTLWAIT)) DoSense(v);
  787.     } while(s==UAT);
  788.  } while(s!=NOS);
  789.  if(choice) {
  790.     if(s=TapeIO(TREWIND,0,CTLWAIT)) DoSense(s);
  791.     if(DAC) blknum=reserved;  /* reset block number */
  792.  }
  793.  return(choice);
  794. }
  795.  
  796. /**************************************************************************
  797. *  thud()   Displays a requester informing of a terminal problem.
  798. */
  799.  
  800. void thud(char *tm)
  801. {
  802.  static struct IntuiText ttxt[] = {
  803.     { AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
  804.        AUTOITEXTFONT, NULL, NULL },
  805.     { AUTOFRONTPEN,AUTOBACKPEN,AUTODRAWMODE,AUTOLEFTEDGE,AUTOTOPEDGE,
  806.        AUTOITEXTFONT, (UBYTE *) "     TERMINATE BTN     ", NULL }
  807.  };
  808.  ttxt[0].IText = (UBYTE *) tm;
  809.  AutoRequest(NULL,&ttxt[0],NULL,&ttxt[1],NULL,NULL,250,50);
  810.  return;
  811. }
  812.  
  813.