/** * $Id:$ * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * * The contents of this file may be used under the terms of either the GNU * General Public License Version 2 or later (the "GPL", see * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or * later (the "BL", see http://www.blender.org/BL/ ) which has to be * bought from the Blender Foundation to become active, in which case the * above mentioned GPL option does not apply. * * The Original Code is Copyright (C) 1997 by Ton Roosendaal, Frank van Beek and Joeri Kassenaar. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ #ident "scsicontrol.c: $Revision $" /* Compile as: cc -O scsicontrol.c -lds_new -o scsicontrol This program should compile and work on any IRIX 4.0.1 or later release, although there might be some defines missing from releases earlier than IRIX 5.2. */ #include #include #include #include #include #include #include #include #include #include typedef struct { unchar pqt:3; /* peripheral qual type */ unchar pdt:5; /* peripheral device type */ unchar rmb:1, /* removable media bit */ dtq:7; /* device type qualifier */ unchar iso:2, /* ISO version */ ecma:3, /* ECMA version */ ansi:3; /* ANSI version */ unchar aenc:1, /* async event notification supported */ trmiop:1, /* device supports 'terminate io process msg */ res0:2, /* reserved */ respfmt:3; /* SCSI 1, CCS, SCSI 2 inq data format */ unchar ailen; /* additional inquiry length */ unchar res1; /* reserved */ unchar res2; /* reserved */ unchar reladr:1, /* supports relative addressing (linked cmds) */ wide32:1, /* supports 32 bit wide SCSI bus */ wide16:1, /* supports 16 bit wide SCSI bus */ synch:1, /* supports synch mode */ link:1, /* supports linked commands */ res3:1, /* reserved */ cmdq:1, /* supports cmd queuing */ softre:1; /* supports soft reset */ unchar vid[8]; /* vendor ID */ unchar pid[16]; /* product ID */ unchar prl[4]; /* product revision level*/ unchar vendsp[20]; /* vendor specific; typically firmware info */ unchar res4[40]; /* reserved for scsi 3, etc. */ unchar vendsp2[159]; /* more vendor specific (fill to 255 bytes) */ /* more vendor specific information may follow */ } inqdata; struct msel { unsigned char rsv, mtype, vendspec, blkdesclen; /* header */ unsigned char dens, nblks[3], rsv1, bsize[3]; /* block desc */ unsigned char pgnum, pglen; /* modesel page num and length */ unsigned char data[240]; /* some drives get upset if no data requested on sense*/ }; #define hex(x) "0123456789ABCDEF" [ (x) & 0xF ] /* only looks OK if nperline a multiple of 4, but that's OK. * value of space must be 0 <= space <= 3; */ void hprint(unsigned char *s, int n, int nperline, int space, int ascii) { int i, x, startl; for(startl=i=0;i>4), hex(x)); if(space) printf("%.*s", ((i%4)==3)+space, " "); if ( i%nperline == (nperline - 1) ) { if(ascii == 1) { putchar('\t'); while(startl < i) { if(isprint(s[startl])) putchar(s[startl]); else putchar('.'); startl++; } } putchar('\n'); if(ascii>1 && i<(n-1)) /* hack hack */ printf("%.*s", ascii-1, " "); } } if(space && (i%nperline)) putchar('\n'); } /* aenc, trmiop, reladr, wbus*, synch, linkq, softre are only valid if * if respfmt has the value 2 (or possibly larger values for future * versions of the SCSI standard). */ static char pdt_types[][16] = { "Disk", "Tape", "Printer", "Processor", "WORM", "CD-ROM", "Scanner", "Optical", "Jukebox", "Comm", "Unknown" }; #define NPDT (sizeof pdt_types / sizeof pdt_types[0]) void printinq(struct dsreq *dsp, inqdata *inq, int allinq) { int neednl = 1; if(DATASENT(dsp) < 1) { printf("No inquiry data returned\n"); return; } printf("%-10s", pdt_types[(inq->pdtpdt : NPDT-1]); if (DATASENT(dsp) > 8) printf("%12.8s", inq->vid); if (DATASENT(dsp) > 16) printf("%.16s", inq->pid); if (DATASENT(dsp) > 32) printf("%.4s", inq->prl); printf("\n"); if(DATASENT(dsp) > 1) printf("ANSI vers %d, ISO ver: %d, ECMA ver: %d; ", inq->ansi, inq->iso, inq->ecma); if(DATASENT(dsp) > 2) { unchar special = *(inq->vid-1); if(inq->respfmt >= 2 || special) { if(inq->respfmt < 2) printf("\nResponse format type %d, but has " "SCSI-2 capability bits set\n", inq->respfmt); printf("supports: "); if(inq->aenc) printf(" AENC"); if(inq->trmiop) printf(" termiop"); if(inq->reladr) printf(" reladdr"); if(inq->wide32) printf(" 32bit"); if(inq->wide16) printf(" 16bit"); if(inq->synch) printf(" synch"); if(inq->synch) printf(" linkedcmds"); if(inq->cmdq) printf(" cmdqueing"); if(inq->softre) printf(" softreset"); } if(inq->respfmt < 2) { if(special) printf(". "); printf("inquiry format is %s", inq->respfmt ? "SCSI 1" : "CCS"); } if(allinq) { if (DATASENT(dsp) > offsetof(inqdata, vendsp)) { printf("\nvendor specific data:\n"); /* bprint uses stderr, so can't use it */ hprint(inq->vendsp, DATASENT(dsp) - (int)offsetof(inqdata, vendsp), 16, 1, 1); neednl = 0; } if (DATASENT(dsp) > offsetof(inqdata, res4)) { printf("reserved (for SCSI 3) data:\n"); hprint(inq->res4, DATASENT(dsp) - (int)offsetof(inqdata, res4), 16, 1, 1); } if (DATASENT(dsp) > offsetof(inqdata, vendsp2)) { printf("more vendor data\n"); hprint(inq->vendsp2, DATASENT(dsp) - (int)offsetof(inqdata, vendsp2), 16, 1, 1); } } } if(neednl) putchar('\n'); printf("Device is "); /* do test unit ready only if inquiry successful, since many devices, such as tapes, return inquiry info, even if not ready (i.e., no tape in a tape drive). */ if(testunitready00(dsp) != 0) printf("%s\n", (RET(dsp)==DSRT_NOSEL) ? "not responding" : "not ready"); else printf("ready"); printf("\n"); } /* inquiry cmd that does vital product data as spec'ed in SCSI2 */ int vpinquiry12( struct dsreq *dsp, caddr_t data, long datalen, char vu, int page) { fillg0cmd(dsp, (uchar_t *)CMDBUF(dsp), G0_INQU, 1, page, 0, B1(datalen), B1(vu<<6)); filldsreq(dsp, (uchar_t *)data, datalen, DSRQ_READ|DSRQ_SENSE); return(doscsireq(getfd(dsp), dsp)); } int startunit1b(struct dsreq *dsp, int startstop, int vu) { fillg0cmd(dsp, (uchar_t *)CMDBUF(dsp), 0x1b, 0, 0, 0, (uchar_t)startstop, B1(vu<<6)); filldsreq(dsp, NULL, 0, DSRQ_READ|DSRQ_SENSE); dsp->ds_time = 1000 * 90; /* 90 seconds */ return(doscsireq(getfd(dsp), dsp)); } int myinquiry12(struct dsreq *dsp, uchar_t *data, long datalen, int vu, int neg) { fillg0cmd(dsp, (uchar_t *)CMDBUF(dsp), G0_INQU, 0, 0, 0, B1(datalen), B1(vu<<6)); filldsreq(dsp, data, datalen, DSRQ_READ|DSRQ_SENSE|neg); dsp->ds_time = 1000 * 30; /* 90 seconds */ return(doscsireq(getfd(dsp), dsp)); } /* for SGI specific CD-ROM drives, stop returning inquiry info as * though they were a hard disk, and return info for a CD-ROM again. * Needed at power on and after bus resets. */ int docmdc9(struct dsreq *dsp, int do80) { fillg1cmd(dsp, (uchar_t *)CMDBUF(dsp), 0xc9, 0, 0, 0, 0, 0, 0, 0, 0, do80==1?0:0x80); filldsreq(dsp, 0, 0, DSRQ_READ|DSRQ_SENSE); return(doscsireq(getfd(dsp), dsp)); } int logsense(struct dsreq *dsp, uchar_t *data, uint datalen, int pagectrl, int pagecode, uint lun) { fillg1cmd(dsp, (uchar_t*)CMDBUF(dsp), 0x4D, (lun<<5)|(0<<1)|0, ((pagectrl&3)<<6)|(pagecode&0x3F), 0, 0, 0, 0, B2(datalen), 0); filldsreq(dsp, (uchar_t *)data, datalen, DSRQ_READ|DSRQ_SENSE); dsp->ds_time = 30*1000; return(doscsireq(getfd(dsp), dsp)); } int setbsize(struct dsreq *dsp, unsigned bsz) { struct msel msel; unsigned obsz; memset(&msel, 0, sizeof(msel)); msel.pgnum = 0x3f; /* all pages */ msel.pglen = sizeof(msel.data)+2; modesense1a(dsp, (caddr_t)&msel, sizeof(msel), 0, msel.pgnum, 0); if(STATUS(dsp) != STA_GOOD) return 1; msel.blkdesclen = 8; obsz = msel.bsize[2]; obsz += ((unsigned)msel.bsize[1]) << 8; obsz += ((unsigned)msel.bsize[0]) << 16; msel.rsv = 0; msel.bsize[0] = (unsigned char)(bsz>>16); msel.bsize[1] = (unsigned char)(bsz>>8); msel.bsize[2] = (unsigned char)bsz; /* select back with just the header and block descriptor */ modeselect15(dsp, (caddr_t)&msel, offsetof(struct msel, pgnum), 0, 0); if(STATUS(dsp) != STA_GOOD) { printf("prev blksize = %u\n", obsz); return 1; } printf("prev blksize = %u, new blksize = %u", obsz, bsz); memset(&msel, 0, sizeof(msel)); msel.pgnum = 0x3f; /* all pages */ msel.pglen = sizeof(msel.data)+2; modesense1a(dsp, (caddr_t)&msel, sizeof(msel), 0, msel.pgnum, 0); if(STATUS(dsp) != STA_GOOD) { printf(", but not able to confirm\n"); return 1; } obsz = msel.bsize[2]; obsz += ((unsigned)msel.bsize[1]) << 8; obsz += ((unsigned)msel.bsize[0]) << 16; if(obsz != bsz) printf("; oops, new blksiz accepted, but %d\n", obsz); printf("\n"); return 0; } int gethaflags(struct dsreq *dsp) { int flags; if(ioctl(getfd(dsp), DS_GET, &flags)) { perror("unable to get hostadapter status flags"); return 1; } if(!flags) printf("no status bits set; adapter may not support reporting\n"); else { printf("host adapter status: "); if(flags & DSG_CANTSYNC) printf(" cantsync"); if(flags & DSG_SYNCXFR) printf(" sync"); if(flags & DSG_TRIEDSYNC) printf(" sync negotiated"); if(flags & DSG_BADSYNC) printf(" badsync"); if(flags & DSG_NOADAPSYNC) printf(" sync disabled"); if(flags & DSG_WIDE) printf(" wide scsi"); if(flags & DSG_DISC) printf(" disconnect enabled"); if(flags & DSG_TAGQ) printf(" tagged queueing"); printf("\n"); } return 0; } int dsreset(struct dsreq *dsp) { return ioctl(getfd(dsp), DS_RESET, dsp); } modesenseall(struct dsreq *dsp) { uchar_t msbuf[256], *mptr; uint dlen; /* all modesense pages, current values */ if(modesense1a(dsp, (caddr_t)msbuf, 255, 0, 0x3f, 0)) return 1; dlen = DATASENT(dsp); if(dlen < 4) return 1; mptr = msbuf; printf("Header:\n length %#x, medium type %#x, device specific %#x, block desc length %#x\n", msbuf[0], msbuf[1], msbuf[2], msbuf[3]); mptr += 4; dlen -= 4; if(msbuf[3]) printf("Block Descriptors:\n "); while(msbuf[3] && dlen >= 8) { hprint(mptr, 8, 8, 1, 2); msbuf[3] -= 8; dlen -= 8; mptr += 8; } while(dlen >= 3) { uint len; printf("Page %#x, length %#x:\n ", 0x3f & *mptr, mptr[1]); len = dlen > (mptr[1]+2) ? mptr[1] : dlen - 2; hprint(&mptr[2], mptr[1], 20, 1, 2); mptr += len + 2; dlen -= len + 2; } return 0; } /* print logsense flags in ascii form; print in left to right order, * (high to low) as that's the way most manuals show them. */ void lsflags(uchar_t f) { static char *ls_str[] = { "DU", "DS", "TSD", "ETC", "", "", "RSVD", "LS" }; static char *ls_tmc[] = { "all", "equal", "not_equal", "greater", }; int shft; uchar_t lastf = 0; for(shft=0; shft<8; shft++) { if(shft == 4) { /* 2 bits... */ f<<=1; shft++; if(lastf&0x80) /* TMC valid only if ETC valid */ printf("TMC-%s ", ls_tmc[(f>>6)&3]); } else if(f & 0x80) printf("%s ", ls_str[shft]); lastf = f; f<<=1; } } /* note: currently set to only return current cumulative values, returns all * values, requests no saving, and limits to 8KB per page * also currently only implements lun 0, regardless of what lun device * we opened... (not great, but simple...) */ logsenseall(struct dsreq *dsp) { uchar_t msbuf[8192], *mptr, pglist[0x144], *pg; uint dlen, npgs; /* first get the list of supported pages (up to 64, plus header) */ if(logsense(dsp, pglist, sizeof(pglist), 1, 0, 0) || DATASENT(dsp)<4) { printf("logsense cmd to get list of logsense pages fails\n"); return 1; } npgs = V2(&pglist[2]); if(DATASENT(dsp) < (npgs+4)) npgs = DATASENT(dsp)-4; for(pg=&pglist[4]; npgs-- > 0; pg++) { static char *lspage[] = { "", "buffer over/underrun", "write err count", "read err count", "read reverse err count", "verify err count", "non-media err count", "recent error events" }; if(*pg == 0) continue; memset(msbuf, 0, 8); /* make sure stuff we print before checking data returned not garbage */ if(logsense(dsp, msbuf, sizeof(msbuf), 1, *pg, 0) || DATASENT(dsp)<4) continue; dlen = V2(&msbuf[2]); printf("Log page %#x", msbuf[0]); if(msbuf[0] < 8) /* reset are reserved/vendor specific */ printf(" (%s)", lspage[msbuf[0]]); printf(", len=%#x", dlen); if(dlen <= 4) /* yes, I've seen this; just overal flags, etc. */ printf(" (no parameter data)"); printf("\n"); if(dlen > (DATASENT(dsp)-4)) dlen = DATASENT(dsp)-4; mptr = msbuf+4; while(dlen >= 4) { uint plen; plen = mptr[3]; printf(" Parameter %#x, len %#x, flags %x", V2(mptr), plen, mptr[2]); if(mptr[2]) { printf(" ( "); lsflags(mptr[2]); printf(")\n"); } else printf("\n"); if((plen+4)>dlen) /* paranoia */ plen = dlen - 4; if(plen) printf(" "); hprint(&mptr[4], plen, 20, 1, 3); mptr += mptr[3] + 4; dlen -= plen + 4; } } return 0; } void usage(char *prog) { fprintf(stderr, "Usage: %s [-i (inquiry)] [-e (exclusive)] [-s (sync) | -a (async)]\n" "\t[-I (all inq data)] [-v (vital proddata)] [-r (reset)] [-D (diagselftest)]\n" "\t[-S (spinup/start/load)] [-H (halt/stop)] [-C (cdrom mode)] [-b blksize]\n" "\t[-g (get host flags)] [-d (debug)] [-q (quiet)] [ -m (modesense all)\n" "\t[-l (logsense)] scsidevice [...]\n", prog); exit(1); } main(int argc, char **argv) { struct dsreq *dsp; char *fn; /* int because they must be word aligned. */ int errs = 0, c, printname = 1; int vital=0, doreset=0, exclusive=0, dosync=0, allinq=0, getflags=0; int docdrom = 0, dostart = 0, dostop = 0, dosenddiag = 0; int doinq = 0, dologsense = 0, domsense = 0; unsigned bsize = 0; extern char *optarg; extern int optind, opterr; setlinebuf(stdout); /* because -d output goes to stderr, and * we want things reasonably interleaved */ opterr = 0; /* handle errors ourselves. */ while ((c = getopt(argc, argv, "b:HIDSaserdvlgCiqm")) != -1) switch(c) { case 'i': doinq = 1; /* do inquiry */ break; case 'g': getflags = 1; /* get host adapter driver status bits */ break; case 'D': dosenddiag = 1; break; case 'C': docdrom++; break; case 'r': doreset = 1; /* do a scsi bus reset */ break; case 'e': exclusive = O_EXCL; break; case 'd': dsdebug++; /* enable debug info */ break; case 'I': allinq = 1; /* show the vendor specific and scsi3 data also */ break; case 'l': dologsense = 1; /* show the logsense data (just cumulative for now) */ break; case 'q': printname = 0; /* print devicename only if error */ break; case 'v': vital = 1; /* set evpd bit for scsi 2 vital product data */ break; case 'H': dostop = 1; /* send a stop (Halt) command */ break; case 'S': dostart = 1; /* send a startunit/spinup command */ break; case 's': dosync = DSRQ_SYNXFR; /* attempt to negotiate sync scsi */ break; case 'a': dosync = DSRQ_ASYNXFR; /* attempt to negotiate async scsi */ break; case 'b': bsize = strtoul((const char *)optarg, NULL, 0); if(bsize == 0 || bsize >= (16*1024*1024)) { fprintf(stderr, "Bogus blocksize %s (%u)\n", optarg, bsize); bsize = 0; } break; case 'm': /* dump all the (current, for now) modesense info */ domsense = 1; break; default: usage(argv[0]); } if(optind >= argc || optind == 1) /* need at 1 arg and one option */ usage(argv[0]); if(allinq && !(doinq || vital)) doinq = 1; while (optind < argc) { fn = argv[optind++]; if(printname) printf("%s: ", fn); if((dsp = dsopen(fn, O_RDONLY|exclusive)) == NULL) { /* if open fails, try pre-pending /dev/scsi */ char buf[256]; strcpy(buf, "/dev/scsi/"); if((strlen(buf) + strlen(fn)) < sizeof(buf)) { strcat(buf, fn); dsp = dsopen(buf, O_RDONLY|exclusive); } if(!dsp) { if(!printname) printf("%s: ", fn); fflush(stdout); perror("cannot open"); errs++; continue; } } /* try to order for reasonableness; reset first in case * hung, then inquiry, etc. */ if(doreset) { if(dsreset(dsp) != 0) { if(!printname) printf("%s: ", fn); printf("reset failed: %s\n", strerror(errno)); errs++; } } if(doinq) { int inqbuf[sizeof(inqdata)/sizeof(int)]; if(myinquiry12(dsp, (uchar_t *)inqbuf, sizeof inqbuf, 0, dosync)) { if(!printname) printf("%s: ", fn); printf("inquiry failure\n"); errs++; } else printinq(dsp, (inqdata *)inqbuf, allinq); } if(vital) { unsigned char *vpinq; int vpinqbuf[sizeof(inqdata)/sizeof(int)]; int vpinqbuf0[sizeof(inqdata)/sizeof(int)]; int i, serial = 0, asciidef = 0; if(vpinquiry12(dsp, (char *)vpinqbuf0, sizeof(vpinqbuf)-1, 0, 0)) { if(!printname) printf("%s: ", fn); printf("inquiry (vital data) failure\n"); errs++; continue; } if(DATASENT(dsp) <4) { printf("vital data inquiry OK, but says no" "pages supported (page 0)\n"); continue; } vpinq = (unsigned char *)vpinqbuf0; printf("Supported vital product pages: "); for(i = vpinq[3]+3; i>3; i--) { if(vpinq[i] == 0x80) serial = 1; if(vpinq[i] == 0x82) asciidef = 1; printf("%2x ", vpinq[i]); } printf("\n"); vpinq = (unsigned char *)vpinqbuf; if(serial) { if(vpinquiry12(dsp, (char *)vpinqbuf, sizeof(vpinqbuf)-1, 0, 0x80) != 0) { if(!printname) printf("%s: ", fn); printf("inquiry (serial #) failure\n"); errs++; } else if(DATASENT(dsp)>3) { printf("Serial #: "); fflush(stdout); /* use write, because there may well be *nulls; don't bother to strip them out */ write(1, vpinq+4, vpinq[3]); printf("\n"); } } if(asciidef) { if(vpinquiry12(dsp, (char *)vpinqbuf, sizeof(vpinqbuf)-1, 0, 0x82) != 0) { if(!printname) printf("%s: ", fn); printf("inquiry (ascii definition) failure\n"); errs++; } else if(DATASENT(dsp)>3) { printf("Ascii definition: "); fflush(stdout); /* use write, because there may well be *nulls; don't bother to strip them out */ write(1, vpinq+4, vpinq[3]); printf("\n"); } } if(allinq) { vpinq = (unsigned char *)vpinqbuf0; for(i=3; i<=(vpinq[3]+3); i++) { if(vpinquiry12(dsp, (char *)vpinqbuf, sizeof(vpinqbuf)-1, 0, vpinq[i]) == 0 && DATASENT(dsp) > 4) { printf("Vital product page %#2x:\n", vpinq[i]); hprint(4+(uchar_t *)vpinqbuf, DATASENT(dsp)-4, 16, 1, 1); } } } } if(docdrom && docmdc9(dsp, docdrom)) { if(!printname) printf("%s: ", fn); printf("revert to cdrom fails\n"); errs++; } if(getflags && gethaflags(dsp)) errs++; if(dostop && startunit1b(dsp, 0, 0)) { if(!printname) printf("%s: ", fn); printf("stopunit fails\n"); errs++; } if(dostart && startunit1b(dsp, 1, 0)) { if(!printname) printf("%s: ", fn); printf("startunit fails\n"); errs++; } if(bsize && setbsize(dsp, bsize)) { if(!printname) printf("%s: ", fn); printf("set blocksize to %u fails\n", bsize); errs++; } if(dosenddiag && senddiagnostic1d(dsp, NULL, 0, 1, 0, 0, 0)) { if(!printname) printf("%s: ", fn); printf("self test fails\n"); errs++; } if(domsense && modesenseall(dsp)) { if(!printname) printf("%s: ", fn); printf("modesense all pages current values fails\n"); errs++; } if(dologsense && logsenseall(dsp)) { if(!printname) printf("%s: ", fn); printf("logsense all pages cumulative values fails\n"); errs++; } dsclose(dsp); } return(errs); }