| /* |
| * 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 <u.h> |
| #include <libc.h> |
| #include <disk.h> |
| |
| 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->lk); |
| if(write(s->rawfd, cmd, ccount) != ccount) { |
| werrstr("cmd write: %r"); |
| if(dolock) |
| qunlock(&s->lk); |
| return -1; |
| } |
| |
| switch(io){ |
| case Sread: |
| n = read(s->rawfd, data, dcount); |
| if(n < 0 && scsiverbose) |
| 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->lk); |
| return -1; |
| } |
| if(dolock) |
| qunlock(&s->lk); |
| |
| 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->lk); |
| 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->lk); |
| return 0; |
| } |
| if(scsiverbose) |
| fprint(2, "target: bad status: %x\n", status); |
| bad:; |
| } |
| if(dolock) |
| qunlock(&s->lk); |
| 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); SET(code); |
| qlock(&s->lk); |
| for(tries=0; tries<2; tries++) { |
| n = _scsicmd(s, cmd, ccount, data, dcount, io, 0); |
| if(n >= 0) { |
| qunlock(&s->lk); |
| 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->lk); |
| return dcount; |
| } |
| if(code == 0x28 && cmd[0] == 0x43) { /* get info and media changed */ |
| s->nchange++; |
| s->changetime = time(0); |
| continue; |
| } |
| } |
| |
| /* drive not ready, or medium not present */ |
| if(cmd[0] == 0x43 && key == 2 && (code == 0x3a || code == 0x04)) { |
| s->changetime = 0; |
| qunlock(&s->lk); |
| return -1; |
| } |
| qunlock(&s->lk); |
| |
| if(cmd[0] == 0x43 && 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 = malloc(sizeof(*s)); |
| if(s == nil) { |
| Error1: |
| free(p); |
| goto Error; |
| } |
| memset(s, 0, sizeof(*s)); |
| |
| s->rawfd = rawfd; |
| s->inquire = p; |
| s->changetime = time(0); |
| |
| if(scsiready(s) < 0) |
| goto Error1; |
| |
| return s; |
| } |