/*
**  TapeIO: performs operations to the tape unit via SCSI-direct for BTNtape
**     Version 3.0  03/14/94
**
**   (c) Copyright 1990, 1994  Robert Rethemeyer.
**     This code is freely distributable and redistributable,
**     for non-commercial purposes, provided this notice is included.
**
**   This code was derived from programs written by Robert Mitchell.
*/

#define _STRICT_ANSI
#include <exec/types.h>
#include <exec/io.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <devices/scsidisk.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "btn.h"
#include "tplink.h"

#include <proto/exec.h>
#include <pragmas/exec_pragmas.h>

#define DBUG 0

/* SCSI commands used by handler */
#define CMD_TEST_UNIT_READY   0x00
#define CMD_REWIND            0x01
#define CMD_REQUEST_SENSE     0x03
#define CMD_SCSI_READ         0x08
#define CMD_SCSI_WRITE        0x0a
#define CMD_FILEMARK          0x10
#define CMD_SPACE             0x11
#define CMD_INQUIRY           0x12
#define CMD_MODE_SELECT       0x15
#define CMD_MODE_SENSE        0x1a
#define CMD_LOAD_UNLOAD       0x1b
#define CMD_READ_CAPACITY     0x25

extern UBYTE  *cdb;
extern UBYTE  *sns;
extern UBYTE  *inq;
extern struct SCSICmd  *cmd;
extern UBYTE  *TapeBuff[2];
extern struct IOStdReq *ior;
extern ULONG  blknum;
extern ULONG  blksize;
extern ULONG  numblks;
extern ULONG  TBSize;
extern ULONG  rwlen;
extern ULONG  rdlen;
extern ULONG  expect;
extern ULONG  bugmask;
extern long   filenum;
extern long   lastwrtn;
extern long   actual;
extern long   Boff;
extern long   reserved;
extern long   tpsize;
extern struct tplink  *linktp;
extern BOOL   inprog;
extern BOOL   bswap;
extern BOOL   dbug;
extern char   dbb[80];
extern UBYTE  Lun;
extern UBYTE  varblk;
extern UBYTE  fixedbit;

long TapeIO(int toper, long bn, int ctl)
{
 char *z;  int i;  ULONG bc;

 if(inprog) {
    WaitIO((struct IORequest *)ior);
    inprog = FALSE;
    if(actual<0) actual = cmd->scsi_Actual;
    if(cdb[0]==CMD_SCSI_READ) Mprintf("%ld bytes\n",actual);
    if(ior->io_Error)  return((long)(ior->io_Error));
 }
 if(toper == TFINISH) return(0L);
 cmd->scsi_Command = cdb;
 cmd->scsi_CmdLength = 6;
 cmd->scsi_Status = 0;
 cmd->scsi_Length = 0;
 cmd->scsi_Actual = 0;
 cmd->scsi_Flags  = 0;
 ior->io_Command = HD_SCSICMD;
 ior->io_Data = (APTR) cmd;
 ior->io_Length = sizeof(struct SCSICmd);
 ior->io_Error = 0;
 for (i=0 ; i<10; i++) cdb[i] = 0x00;

 switch(toper) {

  case TWRITE:
    if(varblk) bc = expect = cmd->scsi_Length = Boff;
    else {
       bc = ((Boff-1)/blksize)+1;
       cmd->scsi_Length = expect = bc * blksize;
    }
    if(DAC) if((blknum+bc)>tpsize) return(FAKEOM);
    if(bswap) swapbytes(TapeBuff[bn]);
    cdb[0] = CMD_SCSI_WRITE;
    cdb[1] = Lun | fixedbit;
    *((UWORD *)&cdb[2]) = (SEQ) ? (UWORD)(bc>>8) : (UWORD) blknum;
    cdb[4] = (UBYTE) bc & 0xff;
 /* cmd->scsi_Data = (UWORD *) TapeBuff[bn]; */      /* 2090A bug fix */
    cmd->scsi_Data = (UWORD *) ((ULONG) TapeBuff[bn] | bugmask);
    cmd->scsi_Flags = SCSIF_WRITE;
    actual = bc;
    Mprintf("Writing block %lu: %lu bytes\n",blknum,expect);
    break;

  case TREAD:
    if(DAC) if((blknum+numblks)>tpsize) return(FAKEOM);
    cdb[0] = CMD_SCSI_READ;
    cdb[1] = Lun | fixedbit;
    *((UWORD *)&cdb[2]) = (SEQ) ? (UWORD)(rdlen>>8) : (UWORD) blknum;
    cdb[4] = (UBYTE) rdlen & 0xff;
    cmd->scsi_Data = (UWORD *) TapeBuff[bn];
    cmd->scsi_Length = expect = rwlen;
    cmd->scsi_Flags = SCSIF_READ;
    actual = -1;
    Mprintf("Reading block %lu:",blknum);
    break;

  case TSENSE:
    for (i=0 ; i<32; i++) sns[i] = 0;
    cdb[0] = CMD_REQUEST_SENSE;
    cdb[1] = Lun;
    cdb[4] = 32;  /* extended sense */
    cmd->scsi_Length = 32;
    cmd->scsi_Data = (UWORD *) sns;
    cmd->scsi_Flags = SCSIF_READ;
    break;

  case TREWIND:
    cdb[0] = CMD_REWIND;
    cdb[1] = Lun;
    filenum = reserved;
    lastwrtn = -1;
    Mprintf("Rewinding\n");
    break;

  case INQUIRY:  /* read drive information */
    cdb[0] = CMD_INQUIRY;
    cdb[1] = Lun;
    cdb[4] = 36;
    cmd->scsi_Length = 36;
    cmd->scsi_Data = (UWORD *) inq;
    cmd->scsi_Flags = SCSIF_READ;
    break;

  case TREADY:  /* test unit ready */
    cdb[0] = CMD_TEST_UNIT_READY;
    cdb[1] = Lun;
    break;

  case MDSNS:
    for (i=0 ; i<12; i++) sns[i] = 0;
    cdb[0] = CMD_MODE_SENSE;
    cdb[1] = Lun;
    cdb[4] = 12;
    cmd->scsi_Length = 12;
    cmd->scsi_Data = (UWORD *) sns;
    cmd->scsi_Flags = SCSIF_READ;
    break;

  case WFMARK:  /* write file mark */
    cdb[0] = CMD_FILEMARK;
    cdb[1] = Lun;
    cdb[4] = (UBYTE) bn;
    if(bn) Mprintf("Writing filemark\n");
    break;

  case TSKIPF:   /* skip over filemarks */
    cdb[0] = CMD_SPACE;
    cdb[1] = Lun | 0x01;
    cdb[2] = (UBYTE) (bn >> 16);
    cdb[3] = (UBYTE) (bn >> 8);
    cdb[4] = (UBYTE)  bn;
    break;

  case TSKIPE:   /* skip to end-of-data */
    cdb[0] = CMD_SPACE;
    cdb[1] = Lun | 0x03;
    Mprintf("Skipping to end of data\n");
    break;

  case TRETEN:  /* retension tape */
    cdb[0] = CMD_LOAD_UNLOAD;
    cdb[1] = Lun;
    cdb[4] = 0x03;
    Mprintf("Retensioning\n");
    break;

  case TEJECT:  /* eject tape */
    cdb[0] = CMD_LOAD_UNLOAD;
    cdb[1] = Lun;
    cdb[4] = 0x00;
    Mprintf("Ejecting tape\n");
    break;

  case RDCAP:  /* read tape capacity.  3M drive only */
    cdb[0] = CMD_READ_CAPACITY;
    cdb[1] = Lun;
    cmd->scsi_CmdLength = 10;
    cmd->scsi_Length = 8;
    cmd->scsi_Data = (UWORD *) sns;
    cmd->scsi_Flags = SCSIF_READ;
    break;

  case RAWCMD:  /* write user-provided control command */
    i = 0;
    z = (char *)TapeBuff[bn];
    while(*z!='\n' && i<10)  cdb[i++] = (UBYTE) strtoul(z,&z,16);
    cdb[1] |= Lun;
    if(cdb[0] == CMD_REWIND) { blknum = filenum = reserved;  lastwrtn = -1; }
    if(cdb[0] >= 0x20) cmd->scsi_CmdLength = 10;
    if(cdb[0] >= 0xa0) cmd->scsi_CmdLength = 12;
    cmd->scsi_Data = (UWORD *) sns;
    Mprintf("Raw command %02X\n",cdb[0]);
    break;

  case MDSET:  /* set fixed-mode block length */
    cdb[0] = CMD_MODE_SELECT;
    cdb[1] = Lun;
    cdb[4] = 12;
    for (i=0 ; i<12; i++) sns[i] = 0x00;
    sns[2] = 0x10;  /* buffered mode */
    sns[3] = 8;
    if(!bn) *((ULONG *)&sns[8]) = blksize & 0x00ffffff; /* block length */
    cmd->scsi_Length = 12;
    cmd->scsi_Data = (UWORD *) sns;
    cmd->scsi_Flags = SCSIF_WRITE;
    Mprintf("Mode Select, new block size %lu\n",blksize);
    break;

  case USRMODE:  /* write user-provided mode-select data */
    for (i=0 ; i<64; i++) sns[i] = 0x00;
    i = 0;
    z = (char *)TapeBuff[bn];
    while(*z!='\n' && i<64)  sns[i++] = (UBYTE) strtoul(z,&z,16);
    cdb[0] = CMD_MODE_SELECT;
    cdb[1] = Lun;
    cdb[4] = i;
    cmd->scsi_Length = i;
    cmd->scsi_Data = (UWORD *) sns;
    cmd->scsi_Flags  = SCSIF_WRITE;
    Mprintf("User Mode-Select\n");
    break;

  }

#ifdef DBUG
  if(dbug) Mprintf("CDB= %02X %02X %02X %02X %02X %02X  ADR=%08X LEN=%X\n",
            cdb[0],cdb[1],cdb[2],cdb[3],cdb[4],cdb[5],
            cmd->scsi_Data, cmd->scsi_Length);
#endif

  if(ctl == CTLWAIT) {
       DoIO ((struct IORequest *)ior);    /* start sync io */
       if(actual<0) actual = cmd->scsi_Actual;
       if(cdb[0]==CMD_SCSI_READ) Mprintf("%ld bytes\n",actual);
       return((long)(ior->io_Error));
  }
  else {
       SendIO((struct IORequest *)ior);  /* start async io */
       inprog = TRUE;
       return(0L);
  }
}

