home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 mARCH / PCWK3A99.iso / Archiwiz / Tar320 / SOURCES.ZIP / ASPI.C < prev    next >
Text File  |  1995-08-15  |  27KB  |  857 lines

  1. #include "modern.h"
  2. #ifdef MSDOS
  3. #include <stdio.h>
  4. #include "pctimer.h"
  5. #include "define.h"
  6. #include <string.h>
  7.  
  8. #define NILTIME 0
  9. #define MINTIME 6 /* ~0.3s */
  10. #define TRESET  (unsigned short)SEC2TICK(3)
  11. #define MIDTIME (unsigned short)SEC2TICK(10)
  12. #define MAXTIME (unsigned short)SEC2TICK(300)
  13. #define HALTCPU
  14.  
  15. #define MAXLEX    8
  16. #define UNKNOWN 255
  17.  
  18. void far (*ASPI_ptr)(void far *) = (void far (*)())0xffff0000L;
  19. extern int ASPI_entry(void far (* far *)());
  20.  
  21. #define HA_INQ      0 /* host adapter inquiry        */
  22. #define GET_TYPE  1 /* get device type               */
  23. #define EXEC_SIO  2 /* execute SCSI I/O command    */
  24. #define ABORT_SRB 3 /* abort SCSI I/O command       */
  25. #define RESET_DEV 4 /* reset SCSI device           */
  26. #define SET_HAP   5 /* set host adapter parameters */
  27. #define GET_DI      6 /* get disk drive information  */
  28.  
  29. #define TAPE      1 /* SCSI device type */
  30.  
  31. #ifndef byte
  32. #    define byte unsigned char
  33. #endif
  34. #define SRBHEAD byte command, status, adapter, flags, reserved[4]
  35. #define MAX_CDB   6  /* Group 0 commands only */
  36. #define MAX_SENCE 14 /* ??? */
  37.  
  38. struct _srbinquiry {
  39.    SRBHEAD, ha_total, ha_type, mgr_id[16], ha_id[16], hap[16];
  40. };
  41. struct _srbgettype {
  42.    SRBHEAD, target, lun, devtype;
  43. };
  44. struct _srbabort {
  45.    SRBHEAD, pointer[4];
  46. };
  47. struct _srbio {
  48.    SRBHEAD, target, lun,
  49.    datalength[4], sencelength, databuffer[4], srblink[4], cdblength,
  50.    adapter_status, target_status, post_routine[4],
  51.    workspace[34], area[MAX_CDB + MAX_SENCE];
  52. };
  53. struct _srbreset {
  54.    SRBHEAD, target, lun, dummy[14],
  55.    adapter_status, target_status, post_routine[4], workspace[34];
  56. };
  57. struct _cdbtape {
  58.    byte opcode, unit, counter[3], control;
  59. };
  60.  
  61. /* SCSI tape opcodes */
  62. #define S0P_TEST   0  /* test unit ready */
  63. #define S0P_REWIND 1
  64. #define S0P_LIMITS 5  /* read block limits */
  65. #define S0P_READ   8
  66. #define S0P_WRITE  10
  67. #define S0P_WFM    16 /* write file mark */
  68. #define S0P_SPACE  17
  69. #define S0P_INQ    18
  70. #define S0P_MODE   21 /* mode select */
  71. #define S0P_ERASE  25
  72. #define S0P_MSENCE 26 /* mode sence */
  73.  
  74. /* Transfer direction bits */
  75. #define T_INHERIT 000 /* Command-dependent*/
  76. #define T_READ      010 /* Read from target */
  77. #define T_WRITE   020 /* Write to target  */
  78. #define T_NONE      030 /* No data transfer */
  79.  
  80. #define ASPI_EMPTY    0
  81. #define ASPI_QUEUED    0
  82. #define ASPI_SUCCESS    1
  83. #define ASPI_SENCE    2
  84. #define ASPI_ABORTED    2
  85. #define ASPI_BUSY       8
  86. #define ASPI_INVALID    0x80 /* illegal request */
  87. #define ASPI_NoHA    0x81 /* invalid host adapter */
  88. #define ASPI_ABSENT    0x82 /* device not installed */
  89. #define SCSI_SENCE    0x20
  90. #define SCSI_SENCEOK    0x20
  91. #define SCSI_RECOVER    0x21
  92. #define SCSI_BUSY       0x22
  93. #define SCSI_INVALID    0x25 /* illegal request */
  94. #define SCSI_ATTN    0x26 /* unit attention */
  95. #define SCSI_BLANK    0x28
  96. #define SCSI_ILI    0x35 /* incorrect length indicator */
  97. #define SCSI_EOM    0x36 /* end-of-medium */
  98. #define SCSI_FM     0x37 /* hit file mark */
  99.  
  100. int aspierrno;
  101. struct { byte code; char *text; } aspierrlist[] = {
  102.    { 0x00, "SCSI request in progress"            },
  103.    { 0x01, "SCSI request completed without error"},
  104.    { 0x02, "SCSI request aborted by host"        },
  105.    { 0x04, "SCSI request completed with error"   },
  106.    { 0x08, "Specified Target/LUN is busy"        },
  107.    { 0x11, "Selection timeout"                   },
  108.    { 0x12, "Data over-run/under-run"             },
  109.    { 0x13, "Unexpected Bus Free"                 },
  110.    { 0x14, "Target bus phase sequence failure"   },
  111.    { 0x18, "Reservation conflict"                },
  112.  
  113.    { 0x20, "No sence"          }, /* My codes */
  114.    { 0x21, "Recovered error"   },
  115.    { 0x22, "Not ready"         },
  116.    { 0x23, "Medium error"      },
  117.    { 0x24, "Hardware error"    },
  118.    { 0x25, "Illegal request"   },
  119.    { 0x26, "Unit attention"    },
  120.    { 0x27, "Data protect"      },
  121.    { 0x28, "Blank check"       },
  122.    { 0x29, "Vendor unique"     },
  123.    { 0x2A, "Copy aborted"      },
  124.    { 0x2b, "Aborted command"   },
  125.    { 0x2C, "Equal"             },
  126.    { 0x2d, "Volume overflow"   },
  127.    { 0x2E, "Miscompare"        },
  128.    { 0x2f, "Reserved sence key"},
  129.  
  130.    { 0x35, "Incorrect length"  },
  131.    { 0x36, "End of medium"     },
  132.    { 0x37, "File mark reached" },
  133.  
  134.    { 0x80, "Invalid SCSI request"       }, /* ASPI codes again */
  135.    { 0x81, "Invalid Host Adapter Number"},
  136.    { 0x82, "SCSI device not installed"  },
  137. };
  138.  
  139. char *aspierrmsg(int n)
  140. {
  141.    static char h[16] = "0123456789ABCDEF";
  142.    static char vendor[] = "Vendor unique, class ? code ?h";
  143.    static char other[] = "Error ??h";
  144.    register i;
  145.  
  146.    for (i=0; i<sizeof(aspierrlist)/sizeof(aspierrlist[0]); i++) {
  147.       if (aspierrlist[i].code == n) return aspierrlist[i].text;
  148.    }
  149.    if (n >= 0x70 && n <= 0xff) {
  150.       vendor[21] = n >= 0xf0 ? '0' : h[(n >> 4) & 7];
  151.       vendor[28] = h[n & 15];
  152.       return vendor;
  153.    }
  154.    other[6] = h[(n >> 4) & 15];
  155.    other[7] = h[ n       & 15];
  156.    return other;
  157. }
  158.  
  159. int aspicall(struct _srbio *s, unsigned timeout)
  160. {
  161.    unsigned long t0;
  162.    struct _srbabort sa;
  163.    register unsigned char *p;
  164.  
  165.    *(long *)(s->reserved) = 0L; /* clear reserved field */
  166.    (*ASPI_ptr)((void far *)s);
  167.    if (s->status != ASPI_QUEUED || timeout == 0) goto test;
  168.    for (t0 = pctimer();;) {
  169.      if (s->status != ASPI_QUEUED) goto test;
  170.      if (pctimer() - t0 > timeout) break;
  171.      HALTCPU;
  172.    }
  173.    /* Timeout expired - abort the request */
  174.    sa.command = ABORT_SRB;
  175.    sa.adapter = s->adapter;
  176.    sa.flags   = 0;
  177.    *(long *)sa.reserved = 0L;
  178.    *(void far * far *)sa.pointer = (void far *)s;
  179.    (void)(*ASPI_ptr)((void far *)&sa);
  180.    /* Wait till IO aborted */
  181.    for (t0=pctimer(); 1 >= pctimer()-t0;) HALTCPU;
  182.    if (s->status == ASPI_QUEUED) s->status = ASPI_ABORTED;
  183. test:
  184.    if (s->status & 0x80) return s->status;
  185.    if (s->adapter_status != ASPI_EMPTY) return s->adapter_status;
  186.    if (s->target_status  != ASPI_EMPTY) {
  187.       /* Is sence area information available? */
  188.       if (s->target_status != ASPI_SENCE) return s->target_status;
  189.       if (s->command == EXEC_SIO) {
  190.          p = s->area + s->cdblength;
  191.          /* Take out vendor-unique classes (remap class 0 to 15) */
  192.          if ((*p & 0x70) == 0x00) return *p | 0xf0;
  193.          if ((*p & 0x70) != 0x70) return *p | 0x80;
  194.          /* Take out vendor-unique codes */
  195.          if ((*p & 0x0f) != 0x00) return *p & 0x7f;
  196.          /* Is incorrect length indicator set? */
  197.          if (p[2] & 0x20) return SCSI_ILI;
  198.          /* Is nontrivial sence key available? */
  199.          if (p[2] & 0x0E) return (p[2] & 15) | SCSI_SENCE;
  200.          /* Check for file mark and end-of-medium conditions */
  201.          if (p[2] & 0x80) return SCSI_FM;
  202.          if (p[2] & 0x40) return SCSI_EOM;
  203.          /* Nothing looks like an error */
  204.          return (p[2] & 15) | SCSI_SENCE;
  205.       }
  206.    }
  207.    return s->status;
  208. }
  209.  
  210. #define END_OF_MEDIUM 0x80
  211. #define FIXED_MODE    1
  212. #define NO_RESET      2
  213. #define NO_REWIND     4
  214. #define ERASE         8
  215. #define FM_ON_CLOSE   0x10
  216. #define OPEN_FLAGS    0x1E
  217.  
  218. static struct {
  219.    unsigned long iosize;
  220.    unsigned long maxblock; unsigned short minblock;
  221.    byte adapter, target, lun, density, flags;
  222. } id = { -1L, 0, 0, 4, 0, 0, 0 };
  223. static long residue;
  224. static byte lastcmd = 0;
  225. static byte ok_to_close = FALSE;
  226. static unsigned to_skip = 0;
  227.  
  228. int aspisio(int cmd, void *buffer, long length, long cmdlen,
  229.             unsigned aspiflags, unsigned cdbflags, unsigned tout)
  230. {
  231.    register k;
  232.    register byte *p;
  233.    struct _srbio s;
  234.  
  235.    s.command = EXEC_SIO;
  236.    s.adapter = id.adapter;
  237.    s.flags   = aspiflags;
  238.    *(long *)(s.reserved) = 0L;
  239.    s.target  = id.target;
  240.    s.lun     = id.lun;
  241.    *(long *)(s.datalength) = length;
  242.    s.sencelength = MAX_SENCE;
  243.    *(void far * far *)(s.databuffer) = (void far *)buffer;
  244.    *(long *)(s.srblink) = 0L;
  245.    s.cdblength = 6; /* Group 0 commands only */
  246.    *(long *)(s.post_routine) = 0xffff0000L;
  247.  
  248.    ((struct _cdbtape *)(s.area))->opcode = lastcmd = cmd;
  249.    ((struct _cdbtape *)(s.area))->unit = (id.lun << 5) | cdbflags;
  250.    ((struct _cdbtape *)(s.area))->counter[0] = (byte)(cmdlen >> 16);
  251.    ((struct _cdbtape *)(s.area))->counter[1] = (byte)(cmdlen >> 8);
  252.    ((struct _cdbtape *)(s.area))->counter[2] = (byte) cmdlen;
  253.    ((struct _cdbtape *)(s.area))->control = 0;
  254.  
  255.    /* Clear sence area */ *(p = s.area + s.cdblength) = 0;
  256.  
  257.    k = aspicall(&s, tout);
  258.    if (k & SCSI_SENCE && k < 0x70) {
  259.       /* Check sence data area */
  260.       if (p[2] & 0x40) id.flags |= END_OF_MEDIUM;
  261.       else             id.flags &= ~END_OF_MEDIUM;
  262.       residue = (p[6] | ((unsigned)p[5] << 8)) |
  263.      ((long)(p[4] | ((unsigned)p[3] << 8)) << 16);
  264.    }
  265.    return k;
  266. }
  267.  
  268. int aspiexec(int cmd, unsigned cdbflags, unsigned tout)
  269. {
  270.    return aspisio(cmd, NULL, 0L, 0L, T_NONE, cdbflags, tout);
  271. }
  272.  
  273. int aspitrans(int wr, void *buffer, unsigned length)
  274. {
  275.    register k;
  276.    unsigned fixed, counter;
  277.  
  278.    if (id.iosize != 0L) {/* fixed mode */
  279.       if (length % id.iosize) return (aspierrno=SCSI_ILI, -1);
  280.       counter = (unsigned)(length / id.iosize);
  281.       fixed = 1;
  282.    } else {
  283.       if (length < id.minblock || length > id.maxblock)
  284.          return (aspierrno=SCSI_ILI, -1);
  285.       counter = length;
  286.       fixed = 0;
  287.    }
  288.    if (wr) {
  289.       wr = S0P_WRITE; k = T_WRITE;
  290.    } else {
  291.       wr = S0P_READ;  k = T_READ;
  292.    }
  293.    k = aspisio(wr, buffer, (long)length, (long)counter, k, fixed, MAXTIME);
  294.    if (k == ASPI_SUCCESS) return length;
  295.    if (k != SCSI_SENCEOK && k != SCSI_RECOVER && k != SCSI_BLANK &&
  296.        k != SCSI_EOM && k != SCSI_FM) return (aspierrno=k, -1);
  297.    if (!residue) return length;
  298.    aspierrno = k;
  299.    if (fixed) {
  300.       if (residue > 0x10000L/id.minblock) return -1;
  301.       residue *= id.minblock;
  302.    }
  303.    return (unsigned long)residue > length ? -1 : length - (int)residue;
  304. }
  305.  
  306. long aspiinvoke(int cmd, long counter, int flag)
  307. {
  308.    register k;
  309.  
  310.    k = aspisio(cmd, NULL, 0L, counter, T_NONE, flag, MAXTIME);
  311.    if (k == ASPI_SUCCESS) return 0L;
  312.    if (k != SCSI_SENCEOK && k != SCSI_RECOVER && k != SCSI_BLANK &&
  313.        k != SCSI_EOM && k != SCSI_FM) return (aspierrno=k, -0x80000000L);
  314.    if (residue) aspierrno = k;
  315.    return residue;
  316. }
  317.  
  318. int aspirewind(void)
  319. {
  320.    register k;
  321.  
  322.    if ((k=aspiexec(S0P_REWIND,0,MAXTIME)) != ASPI_SUCCESS) {
  323.       if ((k != SCSI_SENCEOK && k != SCSI_EOM && k != SCSI_FM) ||
  324.          !(id.flags & END_OF_MEDIUM)) return (aspierrno=k);
  325.    }
  326.    return ASPI_SUCCESS;
  327. }
  328.  
  329. int aspierase(int lbit)
  330. {
  331.    register k;
  332.  
  333.    k = aspiexec(S0P_ERASE, lbit, MAXTIME);
  334.    if (k!=ASPI_SUCCESS && k!=SCSI_SENCEOK && k!=SCSI_EOM)
  335.       return (aspierrno=k);
  336.    return ASPI_SUCCESS;
  337. }
  338.  
  339. int aspireset(void)
  340. {
  341.    struct _srbreset s;
  342.    unsigned long t0;
  343.    register i;
  344.  
  345.    s.command = RESET_DEV;
  346.    s.adapter = id.adapter;
  347.    s.flags   = 0;
  348.    s.target  = id.target;
  349.    s.lun     = id.lun;
  350.    for (i=0; i<sizeof(s.dummy); i++) s.dummy[i] = 0;
  351.    *(long *)(s.post_routine) = 0xffff0000L;
  352.    *(long *)(s.reserved) = 0L; /* clear reserved field */
  353.    (*ASPI_ptr)((void far *)&s);
  354.    for (t0 = pctimer(); s.status == ASPI_QUEUED;) {
  355.       if (pctimer() - t0 > TRESET) break;
  356.       HALTCPU;
  357.    }
  358.    if (!(s.status & 0x80)) {
  359.       if (s.adapter_status != ASPI_EMPTY) return s.adapter_status;
  360.       if (s.target_status  != ASPI_EMPTY) return s.target_status;
  361.    }
  362.    return s.status;
  363. }
  364.  
  365. int aspiread(char *buffer, unsigned length)
  366. {
  367.    register k;
  368.  
  369.    if (!cblock) {
  370.       if      (length > id.maxblock) length = (unsigned)id.maxblock;
  371.       else if (length < id.minblock) length = (unsigned)id.minblock;
  372.    }
  373.    if ((k = aspitrans(0, buffer, length)) == -1) {
  374.       (void)fprintf(stderr, "Tar: ASPI read error: %s\n",
  375.          aspierrmsg(aspierrno));
  376.    }
  377.    return k;
  378. }
  379.  
  380. int aspiwrite(char *buffer, unsigned length)
  381. {
  382.    register k;
  383.  
  384.    if (id.flags & END_OF_MEDIUM && lastcmd == S0P_WRITE) {
  385.       aspierrno = SCSI_EOM; k = 0;
  386.    } else {
  387.       k = aspitrans(1, buffer, length);
  388.    }
  389.    if (k != length) {
  390.       (void)fprintf(stderr, "Tar: ASPI write error: %s\n",
  391.          aspierrmsg(aspierrno));
  392.    }
  393.    return k;
  394. }
  395.  
  396. int aspiback(/* number of 512-byte blocks */ int n)
  397. {
  398.    if (aspiinvoke(S0P_SPACE, -1, 0) != 0L) {
  399.       (void)fprintf(stderr, "Tar: ASPI seek error: %s\n",
  400.          aspierrmsg(aspierrno));
  401.       return 0;
  402.    }
  403.    return n;
  404. }
  405.  
  406. static int GetType(void)
  407. {
  408.    struct _srbgettype s;
  409.  
  410.    s.command = GET_TYPE;
  411.    s.adapter = id.adapter;
  412.    s.target  = id.target;
  413.    s.lun     = id.lun;
  414.    s.flags   = 0;
  415.    *(long *)(s.reserved) = 0L;
  416.    (*ASPI_ptr)((void far *)&s);
  417.    if (s.status == ASPI_SUCCESS) return s.devtype;
  418.    aspierrno = s.status;
  419.    return -1;
  420. }
  421.  
  422. static int HAInquiry(struct _srbinquiry *s, int adapter)
  423. {
  424.    s->command = HA_INQ;
  425.    s->adapter = adapter;
  426.    s->flags   = 0;
  427.    *(long *)(s->reserved) = 0L;
  428.    (*ASPI_ptr)((void far *)s);
  429.    return s->status;
  430. }
  431.  
  432. int aspicount(int devno)
  433. {
  434.    register k;
  435.    struct _srbinquiry s;
  436.    register ha_max;
  437.  
  438.    /* Initialise device description */
  439.    id.maxblock = 0x800000L;
  440.    id.minblock = 1;
  441.  
  442.    /* Get number of host adapters */
  443.    if ((k=HAInquiry(&s, 0)) != ASPI_SUCCESS) return k;
  444.    ha_max = s.ha_total;
  445.  
  446.    /* Search for the tape device with given number */
  447.    for (id.adapter = 0; id.adapter < ha_max; id.adapter ++)
  448.       for (id.target = 0; id.target < 8; id.target ++)
  449.          for (id.lun = 0; id.lun < 8; id.lun ++) {
  450.             if ((k = GetType()) == -1) {
  451.                if (aspierrno != ASPI_ABSENT) return aspierrno;
  452.             } else if (k == TAPE) {
  453.                if (devno-- == 0) goto found;
  454.             }
  455.          }
  456.    /* There is no so many tape devices */ return ASPI_ABSENT;
  457. found:
  458.    return ASPI_SUCCESS;
  459. }
  460.  
  461. static int hex(register c)
  462. {
  463.    if       (c>='0' && c<='9') c -= '0';
  464.    else if (c>='A' && c<='F') c -= 'A'-10;
  465.    else if (c>='a' && c<='f') c -= 'a'-10;
  466.    else c = ERROR;
  467.    return c;
  468. }
  469.  
  470. static int rstrlen(char *s, int size)
  471. {
  472.    register i;
  473.    for (i=0; s[i] && i<size; i++) {
  474.       if (s[i] < ' ' || s[i] >= 127) return 0;
  475.    }
  476.    while (i>0 && (s[i-1]==' ' || s[i-1]=='\377')) --i;
  477.    return i;
  478. }
  479.  
  480. int aspiparse(char *s)
  481. {
  482.    static char already[] = "tape format already defined";
  483.    static struct { unsigned short nqic, code; } qiclist[] = {
  484.       {11,4}, {120,15}, {150,16}, {320,17}, {525,17}, {1350,18}
  485.    };
  486.    char lex[MAXLEX+1], *errmsg;
  487.    register i, k; register char *p;
  488.  
  489.    for (i=0; i<sizeof(lex) && *s!='.'; i++) {
  490.       if (*s == '\0' || *s == ':' || *s == ';' || *s == ',') goto test_name;
  491.       lex[i] = *s++;
  492.    }
  493.    return FALSE;
  494. test_name:
  495.    lex[i] = '\0';
  496.    if ((lex[0] & ~('z'^'Z')) != 'A' || strnicmp(lex,"ASPI",4)) return FALSE;
  497.    if (lex[4]!='\0' && stricmp(lex+4,"MGR$") && stricmp(lex+4,"TAPE"))
  498.       return FALSE;
  499.  
  500.    to_skip       = 0;
  501.    ok_to_close = FALSE;
  502.    id.maxblock = 0x800000L;
  503.    id.minblock = 1;
  504.    id.adapter  = UNKNOWN;
  505.    id.target   = UNKNOWN;
  506.    id.lun       = UNKNOWN;
  507.    id.density  = 0;
  508.    id.flags    = 0;
  509.  
  510.    if (*s) {
  511.       while (*++s) {
  512.          for (i=0; i<=MAXLEX; ++i, s++) {
  513.             if      (*s >= 'A' && *s <= 'Z') lex[i] = *s | ('z'^'Z');
  514.             else if (*s >= 'a' && *s <= 'z') lex[i] = *s;
  515.             else break;
  516.          }
  517.          if (i < 1 || i > MAXLEX) {
  518.             errmsg = "device parameter error"; goto error;
  519.          }
  520.          lex[i] = '\0'; /* for diagnostic printing */
  521.          if (!strncmp("fluent", lex, i)) {
  522.             id.flags |= NO_RESET;
  523.          } else if (!strncmp("norewind", lex, i)) {
  524.             if (to_skip || id.flags & ERASE) goto mutual;
  525.             id.flags |= NO_REWIND;
  526.          } else if (!strncmp("erase", lex, i)) {
  527.             if (to_skip || id.flags & NO_REWIND) goto mutual;
  528.             id.flags |= ERASE;
  529.          } else if (!strncmp("qic", lex, i)) {
  530.             if (id.density) {
  531.                errmsg = already; goto error;
  532.             }
  533.             if (*s == '-') {
  534.                ++s;
  535.             } else {
  536.                if (*s == ':') ++s;
  537.                if (*s == '=') ++s;
  538.             }
  539.             for (k=0, i=0; i<5 && *s>='0' && *s<='9'; ++s, i++)
  540.                k = (*s - '0') + 10*k;
  541.             if (i < 5)
  542.                for (i=0; i<dimof(qiclist); i++)
  543.                   if (qiclist[i].nqic == k) {
  544.                      id.density = qiclist[i].code; break;
  545.                   }
  546.             if (!id.density) {
  547.                errmsg = "invalid QIC number"; goto error;
  548.             }
  549.          } else {
  550.             if (*s == ':') ++s;
  551.             if (*s == '=') ++s;
  552.             if (strncmp("adapter", lex, i) == 0) {
  553.                if (*s<'0' || *s>'7') {
  554.                   errmsg = "invalid adapter number"; goto error;
  555.                }
  556.                id.adapter = *s++ & 7;
  557.             } else if (strncmp("target", lex, i) == 0) {
  558.                if (*s<'0' || *s>'7') {
  559.                   errmsg = "invalid target number"; goto error;
  560.                }
  561.                id.target = *s++ & 7;
  562.             } else if (strncmp("lun", lex, i) == 0) {
  563.                if (*s<'0' || *s>'7') {
  564.                   errmsg = "invalid LUN number"; goto error;
  565.                }
  566.                id.lun = *s++ & 7;
  567.             } else if (!strncmp("skip", lex, i)) {
  568.                if (id.flags & (ERASE|NO_REWIND)) goto mutual;
  569.                for (to_skip=0, i=0; i<5 && *s>='0' && *s<='9'; ++i, s++) {
  570.                   to_skip = (*s - '0') + 10*to_skip;
  571.                }
  572.             } else if (!strncmp("code", lex, i) ||
  573.                        !strncmp("density", lex, i)) {
  574.                id.density = 0; p = s;
  575.                if (*p == '0') ++p;
  576.                if ((*p | ('z'^'Z')) == 'x') {
  577.                   for (i=0, s=p+1; i<2; ++s, i++) {
  578.                      if ((k = hex(*s)) == ERROR) break;
  579.                         id.density = k | (id.density << 4);
  580.                      }
  581.                } else {
  582.                   while (hex(*p)!=ERROR) ++p;
  583.                   if ((*p | ('z'^'Z')) == 'h') {
  584.                      for (i=0; i<2; ++s, i++) {
  585.                        if ((k = hex(*s)) == ERROR) break;
  586.                        id.density = k | (id.density << 4);
  587.                      }
  588.                      if ((*s | ('z'^'Z')) == 'h') ++s;
  589.                   } else {/* decimal code */
  590.                      for (i=0; i<4 && *s>='0' && *s<='9'; ++i, s++) {
  591.                         id.density = (*s - '0') + 10*id.density;
  592.                      }
  593.                   }
  594.                }
  595.                if (!i || ~255 & id.density) {
  596.                   errmsg = "invalid density code"; goto error;
  597.                }
  598.             } else {
  599.                (void)fprintf(stderr,"Tar: unknown parameter \'%s\'\n",lex);
  600.                return ERROR;
  601.             }
  602.          }
  603.          if (*s == '\0') break;
  604.          if (*s != ',' && *s != ':' && *s != '.') {
  605.             (void)fprintf(stderr,"Tar: invalid character after \'%s\'\n",lex);
  606.             return ERROR;
  607.          }
  608.       }
  609.    }
  610.    if (ASPI_entry((void far *)&ASPI_ptr) != 0) {
  611.       errmsg = "no ASPI manager found"; goto error;
  612.    }
  613.    if (setdrive ||
  614.       (id.adapter==UNKNOWN && id.target==UNKNOWN && id.lun==UNKNOWN)) {
  615.       if (id.adapter!=UNKNOWN || id.target!=UNKNOWN || id.lun!=UNKNOWN) {
  616.          errmsg = "both device number and SCSI parameters specified";
  617.          goto error;
  618.       }
  619.       if ((k=aspicount(ndrive)) != ASPI_SUCCESS) {
  620.          errmsg = k!=ASPI_ABSENT ? aspierrmsg(k) :
  621.                   "there is no so many tape devices" ;
  622.          goto error;
  623.       }
  624.    } else {
  625.       if (id.target == UNKNOWN) {
  626.          errmsg = "unknown target number"; goto error;
  627.       }
  628.       if (id.adapter == UNKNOWN) id.adapter = 0;
  629.       if (id.lun     == UNKNOWN) id.lun = 0;
  630.       if ((k=GetType()) != TAPE) {
  631.          errmsg = k == -1 ? aspierrmsg(k) : "not a tape device specified";
  632.          goto error;
  633.       }
  634.    }
  635.    if (v_flag) {
  636.       struct _srbinquiry s;
  637.       if (HAInquiry(&s, id.adapter) != ASPI_SUCCESS) goto reported;
  638.  
  639.       i = rstrlen((char*)s.mgr_id, 16);
  640.       k = rstrlen((char*)s.ha_id,  16);
  641.       if (!i && !k) goto reported;
  642.  
  643.       (void)fprintf(myout, "Tar: ");
  644.       if (i) {
  645.          (void)fprintf(myout, "ASPI manager: <%-.*s>", i, s.mgr_id);
  646.          if (k) (void)fprintf(myout, ", ");
  647.       }
  648.       if (k) {
  649.          (void)fprintf(myout, "SCSI adapter: <%-.*s>", k, s.ha_id);
  650.       }
  651.       (void)fprintf(myout, "\n");
  652. reported:;
  653.    }
  654.    if (a_flag && !(id.flags & NO_REWIND)) id.flags |= FM_ON_CLOSE;
  655.    return TRUE;
  656. mutual:
  657.    errmsg = "\'erase\', \'norewind\' and \'skip\' are mutaully exclusive";
  658. error:
  659.    (void)fprintf(stderr, "Tar: %s\n", errmsg);
  660.    return ERROR;
  661. }
  662.  
  663. static void printape(void)
  664. {
  665.    char b[96];
  666.    int dlen, ilen=0, plen=0, rlen=0;
  667.  
  668.    /* Inquiry command does not interfere with 'Unit Attension' */
  669.    if (aspisio(S0P_INQ, b, 96L, 96L, T_READ, 0, MINTIME) != ASPI_SUCCESS)
  670.       return;
  671.    if ((dlen = *(unsigned char *)(b+4) + 5) > 8) {
  672.       ilen = rstrlen(b+8,  8);
  673.       if (dlen > 16) {
  674.          plen = rstrlen(b+16, 16);
  675.          if (dlen > 32) rlen = rstrlen(b+32, 4);
  676.       }
  677.    }
  678.    if (ilen || plen || rlen) {
  679.       (void)fprintf(myout, "Tar: SCSI tape drive:");
  680.       if (ilen) (void)fprintf(myout, "\t%-8.*s", ilen, b+8);
  681.       if (plen) (void)fprintf(myout, "\t%-.*s", plen, b+16);
  682.       if (rlen) (void)fprintf(myout, "\trev. %-.*s", rlen, b+32);
  683.       (void)fprintf(myout, "\n");
  684.    }
  685. }
  686.  
  687. int aspisetmode(long bsize, int speed, int bm, int pf)
  688. {
  689.    unsigned char b[12];
  690.  
  691.    b[1] = b[0] = 0;
  692.    b[2] = (unsigned char)speed;
  693.    if (bm) b[2] |= 16;
  694.    b[3] = 8; /* block descriptor length */
  695.  
  696.    b[4] = id.density;
  697.    b[7] = b[6] = b[5] = 0; /* whole tape */
  698.    b[8] = 0; /* reserved */
  699.    b[ 9] = (unsigned char)(255 & (bsize >> 16));
  700.    b[10] = (unsigned char)(255 & (bsize >> 8));
  701.    b[11] = (unsigned char)(255 &  bsize);
  702.    return aspisio(S0P_MODE, b, 12L, 12L, T_WRITE, (pf ? 16 : 0), MINTIME);
  703. }
  704.  
  705. static int longrewind(void)
  706. /* Wait until previous operation completed and rewind */
  707. {
  708.    register i, k;
  709.    unsigned long t0;
  710.  
  711.    for (i=0, t0=pctimer();;) {
  712.       if ((k=aspirewind()) == SCSI_ATTN) {
  713.          if (++i < 7) continue;
  714.       } else if (k == ASPI_BUSY || k == SCSI_BUSY) {
  715.          if (pctimer() - t0 < MAXTIME) continue;
  716.       }
  717.       break;
  718.    }
  719.    return k;
  720. }
  721.  
  722. int aspistart(void)
  723. {
  724.    register k;
  725.    unsigned long t0;
  726.    unsigned char b[12];
  727.    int gotBL = FALSE, gotMS = FALSE;
  728.    extern void printbs(int);
  729.  
  730.    if (!(id.flags & (NO_RESET|NO_REWIND)) && id.density != 127) {
  731.       (void)aspireset();
  732.       /* Wait until POST and/or clear 'unit attension' condition */
  733.       for (k=0, t0=pctimer();;) {
  734.          switch (aspiexec(S0P_TEST, 0, MINTIME)) {
  735.             case SCSI_ATTN: if (++k < 7) continue; break;
  736.             case ASPI_BUSY:
  737.             case SCSI_BUSY: if (pctimer() - t0 < MAXTIME) continue;
  738.          }
  739.          break;
  740.       }
  741.    }
  742.    if (v_flag) printape();
  743.    if (!(id.flags & NO_REWIND)) {
  744.       if (longrewind() != ASPI_SUCCESS) goto error;
  745.       if (id.flags & ERASE) {
  746.          if (aspierase(1) != ASPI_SUCCESS) goto error;
  747.          if (aspirewind() != ASPI_SUCCESS) goto error;
  748.       }
  749.    }
  750.    id.iosize = 0L;
  751.    /* Read block limits */
  752.    k = aspisio(S0P_LIMITS, b, 6L, 0L, T_READ, 0, MINTIME);
  753.    if (k == ASPI_SUCCESS || k == SCSI_SENCEOK || k == SCSI_EOM) {
  754.       id.minblock = b[5] | ((unsigned)b[4] << 8);
  755.       id.maxblock = b[3] | ((unsigned)b[2] << 8) | ((unsigned long)b[1]<<16);
  756.       if (!id.maxblock) id.maxblock = 0x800000L;
  757.       gotBL = TRUE;
  758.    }
  759.    k = aspisio(S0P_MSENCE, b, 12L, 12L, T_READ, 0, MINTIME);
  760.    if ((k == ASPI_SUCCESS || k == SCSI_SENCEOK || k == SCSI_EOM) &&
  761.        b[0] >= 12 && b[3] == 8) {
  762.       id.iosize = ((unsigned long)b[9]<<16) | ((unsigned)b[10]<<8) | b[11];
  763.       gotMS = TRUE;
  764.       if (gotBL && id.iosize != 0L) {
  765.          if (id.iosize < id.minblock || id.iosize > id.maxblock) {
  766.             (void)fprintf(stderr,
  767.                "Tar: SCSI blocksize %ld is out of hardware limits (%u-%ld)\n",
  768.                id.iosize, id.minblock, id.maxblock);
  769.             return ERROR;
  770.          }
  771.          if (!cblock && id.iosize%BLKSIZE == 0 &&
  772.              id.iosize <= BLKSIZE*MAXBLOCK) {
  773.             printbs(cblock = (int)(id.iosize / BLKSIZE));
  774.             goto ok;
  775.          }
  776.       }
  777.    }
  778.    if (gotMS && id.iosize == 0L || !gotMS &&
  779.        gotBL && id.minblock != id.maxblock) {
  780.       id.flags &= ~FIXED_MODE;
  781.       id.iosize = 0L;
  782.       if (cblock) {
  783.          if ((k = cblock * BLKSIZE) < id.minblock || k > id.maxblock) {
  784.             (void)fprintf(stderr,
  785.                "Tar: blocksize is out of hardware limits (%d - %ld)\n",
  786.                id.minblock / BLKSIZE, id.maxblock / BLKSIZE);
  787.             return ERROR;
  788.          }
  789.       } else if (c_flag) {
  790.          printbs(cblock = id.maxblock < BLKSIZE*MAXBLOCK ?
  791.                     (int)(id.maxblock/BLKSIZE) : MAXBLOCK);
  792.       }
  793.    } else if (gotMS || gotBL) {
  794.       id.flags |= FIXED_MODE;
  795.       if (!gotMS) id.iosize = id.minblock;
  796.       if ((id.iosize % BLKSIZE || id.iosize > BLKSIZE*MAXBLOCK) &&
  797.           (!cblock || id.density==0 || id.density==127)) {
  798.          (void)fprintf(stderr,
  799.             "Tar: hardware blocksize %ld bytes is not suitable for tar\n",
  800.             id.iosize);
  801.       }
  802.       if (cblock) {
  803.          if (id.density!=0 && id.density!=127 &&
  804.              (k = cblock*BLKSIZE) >= id.minblock && k <= id.maxblock) {
  805.             id.iosize = k;
  806.          } else if ((cblock*BLKSIZE) % id.iosize) {
  807.             (void)fprintf(stderr,
  808.                "Tar: blocksize must be multiple of %ld\n",
  809.                id.iosize/BLKSIZE);
  810.          }
  811.       } else {
  812.          printbs(cblock = (int)(id.iosize / BLKSIZE));
  813.       }
  814.    } else /* !gotMS && !gotBL */ {
  815.       if (cblock) {
  816.          id.iosize = id.maxblock = id.minblock = BLKSIZE*cblock;
  817.          id.flags |= FIXED_MODE;
  818.       } else {
  819.          id.maxblock = BLKSIZE*MAXBLOCK;
  820.          id.minblock = BLKSIZE;
  821.          id.flags &= ~FIXED_MODE;
  822.          id.iosize = 0L;
  823.       }
  824.    }
  825. ok:
  826.    if (id.density!=0 && id.density != 127) {
  827.       for (k=4; k--;) {
  828.          if (aspisetmode(id.iosize, 0, k&2, k&1)
  829.             != SCSI_INVALID) break;
  830.       }
  831.    }
  832.    ok_to_close = TRUE;
  833.    if (to_skip) {
  834.       if (aspisio(S0P_SPACE, NULL, 0L, (long)to_skip, T_NONE, 1, MAXTIME)
  835.          != ASPI_SUCCESS) goto error;
  836.    }
  837.    return 0;
  838. error:
  839.    (void)fprintf(stderr, "Tar: %s\n", aspierrmsg(aspierrno));
  840.    return ERROR;
  841. }
  842.  
  843. void aspiend(void)
  844. {
  845.    if (!ok_to_close) return;
  846.    if (id.flags & FM_ON_CLOSE || lastcmd == S0P_WRITE) {
  847.       if (aspiinvoke(S0P_WFM,
  848.             ((id.flags & (FM_ON_CLOSE|NO_REWIND)) == FM_ON_CLOSE ? 1L : 0L),
  849.             0) != 0L) goto error;
  850.    }
  851.    if (id.flags & NO_REWIND) return;
  852.    if (longrewind() == ASPI_SUCCESS) return;
  853. error:
  854.    (void)fprintf(stderr, "Tar: %s\n", aspierrmsg(aspierrno));
  855. }
  856. #endif
  857.