/* * Now thread-safe. * * The codeqlock guarantees that once codes != nil, that pointer will never * change nor become invalid. * * The QLock in the Scsi structure moderates access to the raw device. * We should probably export some of the already-locked routines, but * there hasn't been a need. */ #include #include #include enum { Readtoc = 0x43, }; int scsiverbose; #define codefile "/sys/lib/scsicodes" static char *codes; static QLock codeqlock; static void getcodes(void) { Dir *d; int n, fd; if(codes != nil) return; qlock(&codeqlock); if(codes != nil) { qunlock(&codeqlock); return; } if((d = dirstat(codefile)) == nil || (fd = open(codefile, OREAD)) < 0) { qunlock(&codeqlock); return; } codes = malloc(1+d->length+1); if(codes == nil) { close(fd); qunlock(&codeqlock); free(d); return; } codes[0] = '\n'; /* for searches */ n = readn(fd, codes+1, d->length); close(fd); free(d); if(n < 0) { free(codes); codes = nil; qunlock(&codeqlock); return; } codes[n] = '\0'; qunlock(&codeqlock); } char* scsierror(int asc, int ascq) { char *p, *q; static char search[32]; static char buf[128]; getcodes(); if(codes) { sprint(search, "\n%.2ux%.2ux ", asc, ascq); if(p = strstr(codes, search)) { p += 6; if((q = strchr(p, '\n')) == nil) q = p+strlen(p); snprint(buf, sizeof buf, "%.*s", (int)(q-p), p); return buf; } sprint(search, "\n%.2ux00", asc); if(p = strstr(codes, search)) { p += 6; if((q = strchr(p, '\n')) == nil) q = p+strlen(p); snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p); return buf; } } sprint(buf, "scsi #%.2ux %.2ux", asc, ascq); return buf; } static int _scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io, int dolock) { uchar resp[16]; int n; long status; if(dolock) qlock(s); if(write(s->rawfd, cmd, ccount) != ccount) { werrstr("cmd write: %r"); if(dolock) qunlock(s); return -1; } switch(io){ case Sread: n = read(s->rawfd, data, dcount); /* read toc errors are frequent and not very interesting */ if(n < 0 && (scsiverbose == 1 || scsiverbose == 2 && cmd[0] != Readtoc)) fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]); break; case Swrite: n = write(s->rawfd, data, dcount); if(n != dcount && scsiverbose) fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]); break; default: case Snone: n = write(s->rawfd, resp, 0); if(n != 0 && scsiverbose) fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]); break; } memset(resp, 0, sizeof(resp)); if(read(s->rawfd, resp, sizeof(resp)) < 0) { werrstr("resp read: %r\n"); if(dolock) qunlock(s); return -1; } if(dolock) qunlock(s); resp[sizeof(resp)-1] = '\0'; status = atoi((char*)resp); if(status == 0) return n; werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n); return -1; } int scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io) { return _scsicmd(s, cmd, ccount, data, dcount, io, 1); } static int _scsiready(Scsi *s, int dolock) { uchar cmd[6], resp[16]; int status, i; if(dolock) qlock(s); for(i=0; i<3; i++) { memset(cmd, 0, sizeof(cmd)); cmd[0] = 0x00; /* unit ready */ if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) { if(scsiverbose) fprint(2, "ur cmd write: %r\n"); goto bad; } write(s->rawfd, resp, 0); if(read(s->rawfd, resp, sizeof(resp)) < 0) { if(scsiverbose) fprint(2, "ur resp read: %r\n"); goto bad; } resp[sizeof(resp)-1] = '\0'; status = atoi((char*)resp); if(status == 0 || status == 0x02) { if(dolock) qunlock(s); return 0; } if(scsiverbose) fprint(2, "target: bad status: %x\n", status); bad:; } if(dolock) qunlock(s); return -1; } int scsiready(Scsi *s) { return _scsiready(s, 1); } int scsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io) { uchar req[6], sense[255], *data; int tries, code, key, n; char *p; data = v; SET(key, code); qlock(s); for(tries=0; tries<2; tries++) { n = _scsicmd(s, cmd, ccount, data, dcount, io, 0); if(n >= 0) { qunlock(s); return n; } /* * request sense */ memset(req, 0, sizeof(req)); req[0] = 0x03; req[4] = sizeof(sense); memset(sense, 0xFF, sizeof(sense)); if((n=_scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread, 0)) < 14) if(scsiverbose) fprint(2, "reqsense scsicmd %d: %r\n", n); if(_scsiready(s, 0) < 0) if(scsiverbose) fprint(2, "unit not ready\n"); key = sense[2]; code = sense[12]; if(code == 0x17 || code == 0x18) { /* recovered errors */ qunlock(s); return dcount; } if(code == 0x28 && cmd[0] == Readtoc) { /* read toc and media changed */ s->nchange++; s->changetime = time(0); continue; } } /* drive not ready, or medium not present */ if(cmd[0] == Readtoc && key == 2 && (code == 0x3a || code == 0x04)) { s->changetime = 0; qunlock(s); return -1; } qunlock(s); if(cmd[0] == Readtoc && key == 5 && code == 0x24) /* blank media */ return -1; p = scsierror(code, sense[13]); werrstr("cmd #%.2ux: %s", cmd[0], p); if(scsiverbose) fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n", cmd[0], key, code, sense[13], p); // if(key == 0) // return dcount; return -1; } Scsi* openscsi(char *dev) { Scsi *s; int rawfd, ctlfd, l, n; char *name, *p, buf[512]; l = strlen(dev)+1+3+1; name = malloc(l); if(name == nil) return nil; snprint(name, l, "%s/raw", dev); if((rawfd = open(name, ORDWR)) < 0) { free(name); return nil; } snprint(name, l, "%s/ctl", dev); if((ctlfd = open(name, ORDWR)) < 0) { free(name); Error: close(rawfd); return nil; } free(name); n = readn(ctlfd, buf, sizeof buf); close(ctlfd); if(n <= 0) goto Error; if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil) goto Error; *p = '\0'; if((p = strdup(buf+8)) == nil) goto Error; s = mallocz(sizeof(*s), 1); if(s == nil) { Error1: free(p); goto Error; } s->rawfd = rawfd; s->inquire = p; s->changetime = time(0); if(scsiready(s) < 0) goto Error1; return s; } void closescsi(Scsi *s) { close(s->rawfd); free(s->inquire); free(s); }