#include #include "pctimer.h" #if 1 # include "modern.h" # include "define.h" # include #endif #define NILTIME 0 #define MINTIME 6 /* ~0.3s */ #define TRESET (unsigned short)SEC2TICK(3) #define MIDTIME (unsigned short)SEC2TICK(10) #define MAXTIME (unsigned short)SEC2TICK(300) #define HALTCPU #define MAXLEX 8 #define UNKNOWN 255 void far (*ASPI_ptr)(void far *) = (void far (*)())0xffff0000L; extern int ASPI_entry(void far (* far *)()); #define HA_INQ 0 /* host adapter inquiry */ #define GET_TYPE 1 /* get device type */ #define EXEC_SIO 2 /* execute SCSI I/O command */ #define ABORT_SRB 3 /* abort SCSI I/O command */ #define RESET_DEV 4 /* reset SCSI device */ #define SET_HAP 5 /* set host adapter parameters */ #define GET_DI 6 /* get disk drive information */ #define TAPE 1 /* SCSI device type */ #ifndef byte # define byte unsigned char #endif #define SRBHEAD byte command, status, adapter, flags, reserved[4] #define MAX_CDB 6 /* Group 0 commands only */ #define MAX_SENCE 14 /* ??? */ struct _srbinquiry { SRBHEAD, ha_total, ha_type, mgr_id[16], ha_id[16], hap[16]; }; struct _srbgettype { SRBHEAD, target, lun, devtype; }; struct _srbabort { SRBHEAD, pointer[4]; }; struct _srbio { SRBHEAD, target, lun, datalength[4], sencelength, databuffer[4], srblink[4], cdblength, adapter_status, target_status, post_routine[4], workspace[34], area[MAX_CDB + MAX_SENCE]; }; struct _srbreset { SRBHEAD, target, lun, dummy[14], adapter_status, target_status, post_routine[4], workspace[34]; }; struct _cdbtape { byte opcode, unit, counter[3], control; }; /* SCSI tape opcodes */ #define S0P_TEST 0 /* test unit ready */ #define S0P_REWIND 1 #define S0P_LIMITS 5 /* read block limits */ #define S0P_READ 8 #define S0P_WRITE 10 #define S0P_WFM 16 /* write file mark */ #define S0P_SPACE 17 #define S0P_MODE 21 /* mode select */ #define S0P_ERASE 25 /* Transfer direction bits */ #define T_INHERIT 000 /* Command-dependent*/ #define T_READ 010 /* Read from target */ #define T_WRITE 020 /* Write to target */ #define T_NONE 030 /* No data transfer */ #define ASPI_EMPTY 0 #define ASPI_QUEUED 0 #define ASPI_SUCCESS 1 #define ASPI_SENCE 2 #define ASPI_ABORTED 2 #define ASPI_INVALID 0x80 /* illegal request */ #define ASPI_NoHA 0x81 /* invalid host adapter */ #define ASPI_ABSENT 0x82 /* device not installed */ #define SCSI_SENCE 0x20 #define SCSI_SENCEOK 0x20 #define SCSI_RECOVER 0x21 #define SCSI_INVALID 0x25 /* illegal request */ #define SCSI_ATTN 0x26 /* unit attention */ #define SCSI_BLANK 0x28 #define SCSI_ILI 0x35 /* incorrect length indicator */ #define SCSI_EOM 0x36 /* end-of-medium */ #define SCSI_FM 0x37 /* hit file mark */ int aspierrno; struct { byte code; char *text; } aspierrlist[] = { { 0x00, "SCSI request in progress" }, { 0x01, "SCSI request completed without error"}, { 0x02, "SCSI request aborted by host" }, { 0x04, "SCSI request completed with error" }, { 0x08, "Specified Target/LUN is busy" }, { 0x11, "Selection timeout" }, { 0x12, "Data over-run/under-run" }, { 0x13, "Unexpected Bus Free" }, { 0x14, "Target bus phase sequence failure" }, { 0x18, "Reservation conflict" }, { 0x20, "No sence" }, /* My codes */ { 0x21, "Recovered error" }, { 0x22, "Not ready" }, { 0x23, "Medium error" }, { 0x24, "Hardware error" }, { 0x25, "Illegal request" }, { 0x26, "Unit attention" }, { 0x27, "Data protect" }, { 0x28, "Blank check" }, { 0x29, "Vendor unique" }, { 0x2A, "Copy aborted" }, { 0x2b, "Aborted command" }, { 0x2C, "Equal" }, { 0x2d, "Volume overflow" }, { 0x2E, "Miscompare" }, { 0x2f, "Reserved sence key"}, { 0x35, "Incorrect length" }, { 0x36, "End of medium" }, { 0x37, "File mark reached" }, { 0x80, "Invalid SCSI request" }, /* ASPI codes again */ { 0x81, "Invalid Host Adapter Number"}, { 0x82, "SCSI device not installed" }, }; char *aspierrmsg(int n) { static char h[16] = "0123456789ABCDEF"; static char vendor[] = "Vendor unique, class ? code ?h"; static char other[] = "Error ??h"; register i; for (i=0; i= 0x70 && n <= 0xff) { vendor[21] = n >= 0xf0 ? '0' : h[(n >> 4) & 7]; vendor[28] = h[n & 15]; return vendor; } other[6] = h[(n >> 4) & 15]; other[7] = h[ n & 15]; return other; } int aspicall(struct _srbio *s, unsigned timeout) { unsigned long t0; struct _srbabort sa; register unsigned char *p; *(long *)(s->reserved) = 0L; /* clear reserved field */ (*ASPI_ptr)((void far *)s); if (s->status != ASPI_QUEUED || timeout == 0) goto test; for (t0 = pctimer();;) { if (s->status != ASPI_QUEUED) goto test; if (pctimer() - t0 > timeout) break; HALTCPU; } /* Timeout expired - abort the request */ sa.command = ABORT_SRB; sa.adapter = s->adapter; sa.flags = 0; *(long *)sa.reserved = 0L; *(void far * far *)sa.pointer = (void far *)s; (void)(*ASPI_ptr)((void far *)&sa); /* Wait till IO aborted */ for (t0=pctimer(); 1 >= pctimer()-t0;) HALTCPU; if (s->status == ASPI_QUEUED) s->status = ASPI_ABORTED; test: if (s->status & 0x80) return s->status; if (s->adapter_status != ASPI_EMPTY) return s->adapter_status; if (s->target_status != ASPI_EMPTY) { /* Is sence area information available? */ if (s->target_status != ASPI_SENCE) return s->target_status; if (s->command == EXEC_SIO) { p = s->area + s->cdblength; /* Take out vendor-unique classes (remap class 0 to 15) */ if ((*p & 0x70) == 0x00) return *p | 0xf0; if ((*p & 0x70) != 0x70) return *p | 0x80; /* Take out vendor-unique codes */ if ((*p & 0x0f) != 0x00) return *p & 0x7f; /* Is incorrect length indicator set? */ if (p[2] & 0x20) return SCSI_ILI; /* Is nontrivial sence key available? */ if (p[2] & 0x0E) return (p[2] & 15) | SCSI_SENCE; /* Check for file mark and end-of-medium conditions */ if (p[2] & 0x80) return SCSI_FM; if (p[2] & 0x40) return SCSI_EOM; /* Nothing looks like an error */ return (p[2] & 15) | SCSI_SENCE; } } return s->status; } #define END_OF_MEDIUM 0x80 #define FIXED_MODE 1 #define NO_RESET 2 #define NO_REWIND 4 #define ERASE 8 #define FM_ON_CLOSE 0x10 #define OPEN_FLAGS 0x1E static struct { unsigned long maxblock; unsigned short minblock; byte adapter, target, lun, density, flags; } id = { -1L, 0, 0, 4, 0, 0, 0 }; static long residue; static byte lastcmd = 0; static byte ok_to_close = FALSE; static unsigned to_skip = 0; int aspisio(int cmd, void *buffer, long length, long cmdlen, unsigned aspiflags, unsigned cdbflags, unsigned tout) { register k; register byte *p; struct _srbio s; s.command = EXEC_SIO; s.adapter = id.adapter; s.flags = aspiflags; *(long *)(s.reserved) = 0L; s.target = id.target; s.lun = id.lun; *(long *)(s.datalength) = length; s.sencelength = MAX_SENCE; *(void far * far *)(s.databuffer) = (void far *)buffer; *(long *)(s.srblink) = 0L; s.cdblength = 6; /* Group 0 commands only */ *(long *)(s.post_routine) = 0xffff0000L; ((struct _cdbtape *)(s.area))->opcode = lastcmd = cmd; ((struct _cdbtape *)(s.area))->unit = (id.lun << 5) | cdbflags; ((struct _cdbtape *)(s.area))->counter[0] = (byte)(cmdlen >> 16); ((struct _cdbtape *)(s.area))->counter[1] = (byte)(cmdlen >> 8); ((struct _cdbtape *)(s.area))->counter[2] = (byte) cmdlen; ((struct _cdbtape *)(s.area))->control = 0; /* Clear sence area */ *(p = s.area + s.cdblength) = 0; k = aspicall(&s, tout); if (k & SCSI_SENCE && k < 0x70) { /* Check sence data area */ if (p[2] & 0x40) id.flags |= END_OF_MEDIUM; else id.flags &= ~END_OF_MEDIUM; residue = (p[6] | ((unsigned)p[5] << 8)) | ((long)(p[4] | ((unsigned)p[3] << 8)) << 16); } return k; } int aspiexec(int cmd, unsigned cdbflags, unsigned tout) { return aspisio(cmd, NULL, 0L, 0L, T_NONE, cdbflags, tout); } int aspitrans(int wr, void *buffer, unsigned length) { register k; unsigned fixed, counter; if (id.flags & FIXED_MODE) { if (length % id.minblock) return (aspierrno=SCSI_ILI, -1); counter = length / id.minblock; fixed = 1; } else { if (length < id.minblock || length > id.maxblock) return (aspierrno=SCSI_ILI, -1); counter = length; fixed = 0; } if (wr) { wr = S0P_WRITE; k = T_WRITE; } else { wr = S0P_READ; k = T_READ; } k = aspisio(wr, buffer, (long)length, (long)counter, k, fixed, MAXTIME); if (k == ASPI_SUCCESS) return length; if (k != SCSI_SENCEOK && k != SCSI_RECOVER && k != SCSI_BLANK && k != SCSI_EOM && k != SCSI_FM) return (aspierrno=k, -1); if (!residue) return length; aspierrno = k; if (fixed) { if (residue > 0x10000L/id.minblock) return -1; residue *= id.minblock; } return (unsigned long)residue > length ? -1 : length - (int)residue; } long aspiinvoke(int cmd, long counter, int flag) { register k; k = aspisio(cmd, NULL, 0L, counter, T_NONE, flag, MAXTIME); if (k == ASPI_SUCCESS) return 0L; if (k != SCSI_SENCEOK && k != SCSI_RECOVER && k != SCSI_BLANK && k != SCSI_EOM && k != SCSI_FM) return (aspierrno=k, -0x80000000L); if (residue) aspierrno = k; return residue; } int aspirewind(void) { register k; if ((k=aspiexec(S0P_REWIND,0,MAXTIME)) != ASPI_SUCCESS) { if ((k != SCSI_SENCEOK && k != SCSI_EOM && k != SCSI_FM) || !(id.flags & END_OF_MEDIUM)) return (aspierrno=k); } return ASPI_SUCCESS; } int aspierase(int lbit) { register k; k = aspiexec(S0P_ERASE, lbit, MAXTIME); if (k!=ASPI_SUCCESS && k!=SCSI_SENCEOK && k!=SCSI_EOM) return (aspierrno=k); return ASPI_SUCCESS; } int aspireset(void) { struct _srbreset s; unsigned long t0; register i; s.command = RESET_DEV; s.adapter = id.adapter; s.flags = 0; s.target = id.target; s.lun = id.lun; for (i=0; i TRESET) break; HALTCPU; } if (!(s.status & 0x80)) { if (s.adapter_status != ASPI_EMPTY) return s.adapter_status; if (s.target_status != ASPI_EMPTY) return s.target_status; } return s.status; } int aspiread(char *buffer, unsigned length) { register k; if (!cblock) { if (length < id.maxblock) length = (unsigned)id.maxblock; else if (length > id.minblock) length = (unsigned)id.minblock; } if ((k = aspitrans(0, buffer, length)) == -1) { (void)fprintf(stderr, "Tar: ASPI read error: %s\n", aspierrmsg(aspierrno)); } return k; } int aspiwrite(char *buffer, unsigned length) { register k; if (id.flags & END_OF_MEDIUM && lastcmd == S0P_WRITE) { aspierrno = SCSI_EOM; k = 0; } else { k = aspitrans(1, buffer, length); } if (k != length) { (void)fprintf(stderr, "Tar: ASPI write error: %s\n", aspierrmsg(aspierrno)); } return k; } int aspiback(/* number of 512-byte blocks */ int n) { if (aspiinvoke(S0P_SPACE, -1, 0) != 0L) { (void)fprintf(stderr, "Tar: ASPI seek error: %s\n", aspierrmsg(aspierrno)); return 0; } return n; } static int GetType(void) { struct _srbgettype s; s.command = GET_TYPE; s.adapter = id.adapter; s.target = id.target; s.lun = id.lun; s.flags = 0; *(long *)(s.reserved) = 0L; (*ASPI_ptr)((void far *)&s); if (s.status == ASPI_SUCCESS) return s.devtype; aspierrno = s.status; return -1; } static int HAInquiry(struct _srbinquiry *s, int adapter) { s->command = HA_INQ; s->adapter = adapter; s->flags = 0; *(long *)(s->reserved) = 0L; (*ASPI_ptr)((void far *)s); return s->status; } int aspicount(int devno) { register k; struct _srbinquiry s; register ha_max; /* Initialise device description */ id.maxblock = 0x800000L; id.minblock = 1; id.flags = 0; /* Get number of host adapters */ if ((k=HAInquiry(&s, 0)) != ASPI_SUCCESS) return k; ha_max = s.ha_total; /* Search for the tape device with given number */ for (id.adapter = 0; id.adapter < ha_max; id.adapter ++) for (id.target = 0; id.target < 8; id.target ++) for (id.lun = 0; id.lun < 8; id.lun ++) { if ((k = GetType()) == -1) { if (aspierrno != ASPI_ABSENT) return aspierrno; } else if (k == TAPE) { if (devno-- == 0) goto found; } } /* There is no so many tape devices */ return ASPI_ABSENT; found: return ASPI_SUCCESS; } static int hex(register c) { if (c>='0' && c<='9') c -= '0'; else if (c>='A' && c<='F') c -= 'A'-10; else if (c>='a' && c<='f') c -= 'a'-10; else c = ERROR; return c; } int aspiparse(char *s) { static char already[] = "tape format already defined"; static struct { unsigned short nqic, code; } qiclist[] = { {11,4}, {120,15}, {150,16}, {320,17}, {525,17}, {1350,18} }; char lex[MAXLEX+1], *errmsg; register i, k; register char *p; for (i=0; i= 'A' && *s <= 'Z') lex[i] = *s | ('z'^'Z'); else if (*s >= 'a' && *s <= 'z') lex[i] = *s; else break; } if (i < 1 || i > MAXLEX) { errmsg = "device parameter error"; goto error; } lex[i] = '\0'; /* for diagnostic printing */ if (!strncmp("norewind", lex, i)) { if (to_skip || id.flags & ERASE) goto mutual; id.flags |= NO_REWIND; } else if (!strncmp("erase", lex, i)) { if (to_skip || id.flags & NO_REWIND) goto mutual; id.flags |= ERASE; } else if (!strncmp("qic", lex, i)) { if (id.density) { errmsg = already; goto error; } if (*s == '-') { ++s; } else { if (*s == ':') ++s; if (*s == '=') ++s; } for (k=0, i=0; i<5 && *s>='0' && *s<='9'; ++s, i++) k = (*s - '0') + 10*k; if (i < 5) for (i=0; i'7') { errmsg = "invalid adapter number"; goto error; } id.adapter = *s++ & 7; } else if (strncmp("target", lex, i) == 0) { if (*s<'0' || *s>'7') { errmsg = "invalid target number"; goto error; } id.target = *s++ & 7; } else if (strncmp("lun", lex, i) == 0) { if (*s<'0' || *s>'7') { errmsg = "invalid LUN number"; goto error; } id.lun = *s++ & 7; } else if (!strncmp("skip", lex, i)) { if (id.flags & (ERASE|NO_REWIND)) goto mutual; for (to_skip=0, i=0; i<5 && *s>='0' && *s<='9'; ++i, s++) { to_skip = (*s - '0') + 10*to_skip; } } else if (!strncmp("code", lex, i) || !strncmp("density", lex, i)) { id.density = 0; p = s; if (*p == '0') ++p; if ((*p | ('z'^'Z')) == 'x') { for (i=0, s=p+1; i<2; ++s, i++) { if ((k = hex(*s)) == ERROR) break; id.density = k | (id.density << 4); } } else { while (hex(*p)!=ERROR) ++p; if ((*p | ('z'^'Z')) == 'h') { for (i=0; i<2; ++s, i++) { if ((k = hex(*s)) == ERROR) break; id.density = k | (id.density << 4); } if ((*s | ('z'^'Z')) == 'h') ++s; } else {/* decimal code */ for (i=0; i<4 && *s>='0' && *s<='9'; ++i, s++) { id.density = (*s - '0') + 10*id.density; } } } if (!i || ~255 & id.density) { errmsg = "invalid density code"; goto error; } } else { (void)fprintf(stderr,"Tar: unknown parameter \'%s\'\n",lex); return ERROR; } } if (*s == '\0') break; if (*s != ',' && *s != ':' && *s != '.') { (void)fprintf(stderr,"Tar: invalid character after \'%s\'\n",lex); return ERROR; } } } if (ASPI_entry((void far *)&ASPI_ptr) != 0) { errmsg = "no ASPI manager found"; goto error; } if (setdrive || (id.adapter==UNKNOWN && id.target==UNKNOWN && id.lun==UNKNOWN)) { if (id.adapter!=UNKNOWN || id.target!=UNKNOWN || id.lun!=UNKNOWN) { errmsg = "both device number and SCSI parameters specified"; goto error; } if ((k=aspicount(ndrive)) != ASPI_SUCCESS) { errmsg = k!=ASPI_ABSENT ? aspierrmsg(k) : "there is no so many tape devices" ; goto error; } } else { if (id.target == UNKNOWN) { errmsg = "unknown target number"; goto error; } if (id.adapter == UNKNOWN) id.adapter = 0; if (id.lun == UNKNOWN) id.lun = 0; if ((k=GetType()) != TAPE) { errmsg = k == -1 ? aspierrmsg(k) : "not a tape device specified"; goto error; } } if (v_flag) { struct _srbinquiry s; if (HAInquiry(&s, id.adapter) != ASPI_SUCCESS) goto reported; for (i=16; i && (s.mgr_id[i-1]=='\0' || s.mgr_id[i-1]==' '); i--); for (k=16; k && (s.ha_id [k-1]=='\0' || s.ha_id [k-1]==' '); k--); if (!i && !k) goto reported; (void)fprintf(myout, "Tar: "); if (i) { (void)fprintf(myout, "ASPI manager: <%-.*s>", i, s.mgr_id); if (k) (void)fprintf(myout, ", "); } if (k) { (void)fprintf(myout, "SCSI adapter: <%-.*s>", k, s.ha_id); } (void)fprintf(myout, "\n"); reported:; } if (a_flag && !(id.flags & NO_REWIND)) id.flags |= FM_ON_CLOSE; return TRUE; mutual: errmsg = "\'erase\', \'norewind\' and \'skip\' are mutaully exclusive"; error: (void)fprintf(stderr, "Tar: %s\n", errmsg); return ERROR; } int aspisetmode(long bsize, int speed, int bm, int pf) { unsigned char b[12]; b[1] = b[0] = 0; b[2] = (unsigned char)speed; if (bm) b[2] |= 16; b[3] = 8; /* block descriptor length */ b[4] = id.density; b[7] = b[6] = b[5] = 0; /* whole tape */ b[8] = 0; /* reserved */ b[ 9] = (unsigned char)(255 & (bsize >> 16)); b[10] = (unsigned char)(255 & (bsize >> 8)); b[11] = (unsigned char)(255 & bsize); return aspisio(S0P_MODE, b, 12L, 12L, T_WRITE, (pf ? 16 : 0), MINTIME); } int aspistart(void) { register k; unsigned char b[6]; extern void printbs(int); if (!(id.flags & (NO_RESET|NO_REWIND)) && id.density != 127) { (void)aspireset(); /* Clear 'unit attension' condition */ for (k=0; k<7; k++) { if (aspiexec(S0P_TEST, 0, MINTIME) != SCSI_ATTN) break; } } if (!(id.flags & NO_REWIND)) { if (aspirewind() != ASPI_SUCCESS) { if (aspierrno != SCSI_ATTN) goto error; if (aspirewind() != ASPI_SUCCESS) goto error; } if (id.flags & ERASE) { if (aspierase(1) != ASPI_SUCCESS) goto error; if (aspirewind() != ASPI_SUCCESS) goto error; } } /* Read block limits */ k = aspisio(S0P_LIMITS, b, 6L, 0L, T_READ, 0, MINTIME); if (k == ASPI_SUCCESS || k == SCSI_SENCEOK || k == SCSI_EOM) { id.minblock = b[5] | ((unsigned)b[4] << 8); id.maxblock = b[3] | ((unsigned)b[2] << 8) | ((unsigned long)b[1]<<16); if (!id.maxblock) id.maxblock = 0x800000L; if (id.minblock != id.maxblock) { id.flags &= ~FIXED_MODE; if (cblock) { if (k < id.minblock || k > id.maxblock) { (void)fprintf(stderr, "Tar: blocksize is out of hardware limits (%d - %ld)\n", id.minblock / BLKSIZE, id.maxblock / BLKSIZE); return ERROR; } } else if (c_flag) { printbs(cblock = id.maxblock < BLKSIZE*MAXBLOCK ? (int)(id.maxblock/BLKSIZE) : MAXBLOCK); } } else { id.flags |= FIXED_MODE; if (id.minblock % BLKSIZE || id.minblock > BLKSIZE*MAXBLOCK) { (void)fprintf(stderr, "Tar: hardware blocksize %ld bytes is not suitable for tar\n", id.minblock); } if (cblock) { if ((cblock*BLKSIZE) % id.minblock) { (void)fprintf(stderr, "Tar: blocksize must be multiple of %ld\n", id.minblock/BLKSIZE); } } else { printbs(cblock = id.minblock / BLKSIZE); } } } else { if (cblock) { id.maxblock = id.minblock = BLKSIZE*cblock; id.flags |= FIXED_MODE; } else { id.maxblock = BLKSIZE*MAXBLOCK; id.minblock = BLKSIZE; id.flags &= ~FIXED_MODE; } } if (id.density!=0 && id.density != 127) { for (k=4; k--;) { if (aspisetmode((long)(BLKSIZE*cblock), 0, k&2, k&1) != SCSI_INVALID) break; } } ok_to_close = TRUE; if (to_skip) { if (aspisio(S0P_SPACE, NULL, 0L, (long)to_skip, T_NONE, 1, MAXTIME) != ASPI_SUCCESS) goto error; } return 0; error: (void)fprintf(stderr, "Tar: %s\n", aspierrmsg(aspierrno)); return ERROR; } void aspiend(void) { if (!ok_to_close) return; if (id.flags & FM_ON_CLOSE || lastcmd == S0P_WRITE) { if (aspiinvoke(S0P_WFM, ((id.flags & (FM_ON_CLOSE|NO_REWIND)) == FM_ON_CLOSE ? 1L : 0L), 0) != 0L) goto error; } if (id.flags & NO_REWIND) return; if (aspirewind() == ASPI_SUCCESS) return; error: (void)fprintf(stderr, "Tar: %s\n", aspierrmsg(aspierrno)); }